commit a79d958b1872a803418f80063a627e8080f8956b
parent ea3b2c1b1c93febf1cb333e22d5f43bd02804676
Author: triesap <tyson@radroots.org>
Date: Fri, 20 Mar 2026 16:58:43 +0000
workspace: remove retired app crates
Diffstat:
25 files changed, 29 insertions(+), 2646 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -140,48 +140,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
-name = "askama"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
-dependencies = [
- "askama_derive",
- "itoa",
- "percent-encoding",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "askama_derive"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
-dependencies = [
- "askama_parser",
- "basic-toml",
- "memchr",
- "proc-macro2",
- "quote",
- "rustc-hash 2.1.1",
- "serde",
- "serde_derive",
- "syn",
-]
-
-[[package]]
-name = "askama_parser"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
-dependencies = [
- "memchr",
- "serde",
- "serde_derive",
- "winnow",
-]
-
-[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -260,15 +218,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
-name = "basic-toml"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
-dependencies = [
- "serde",
-]
-
-[[package]]
name = "bech32"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -377,38 +326,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
-name = "camino"
-version = "1.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
-dependencies = [
- "serde_core",
-]
-
-[[package]]
-name = "cargo-platform"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "cargo_metadata"
-version = "0.19.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
-dependencies = [
- "camino",
- "cargo-platform",
- "semver",
- "serde",
- "serde_json",
- "thiserror 2.0.18",
-]
-
-[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -586,7 +503,7 @@ dependencies = [
"rust-ini",
"serde",
"serde_json",
- "toml 0.8.23",
+ "toml",
"yaml-rust2",
]
@@ -884,15 +801,6 @@ dependencies = [
]
[[package]]
-name = "fs-err"
-version = "2.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
name = "futures"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1050,17 +958,6 @@ dependencies = [
]
[[package]]
-name = "goblin"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
-dependencies = [
- "log",
- "plain",
- "scroll",
-]
-
-[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2090,36 +1987,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
-name = "radroots-app-core"
-version = "0.1.0-alpha.1"
-dependencies = [
- "chrono",
- "radroots-core",
- "radroots-events",
- "radroots-events-codec",
- "radroots-identity",
- "radroots-log",
- "radroots-net-core",
- "radroots-nostr",
- "radroots-trade",
- "serde",
- "serde_json",
- "thiserror 1.0.69",
- "tokio",
- "tracing",
- "tracing-subscriber",
- "uniffi",
-]
-
-[[package]]
-name = "radroots-app-wasm"
-version = "0.1.0-alpha.1"
-dependencies = [
- "radroots-app-core",
- "wasm-bindgen",
-]
-
-[[package]]
name = "radroots-core"
version = "0.1.0-alpha.1"
dependencies = [
@@ -2371,7 +2238,7 @@ dependencies = [
"tempfile",
"thiserror 1.0.69",
"tokio",
- "toml 0.8.23",
+ "toml",
"tracing",
]
@@ -2800,26 +2667,6 @@ dependencies = [
]
[[package]]
-name = "scroll"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
-dependencies = [
- "scroll_derive",
-]
-
-[[package]]
-name = "scroll_derive"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
name = "scrypt"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2888,10 +2735,6 @@ name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
-dependencies = [
- "serde",
- "serde_core",
-]
[[package]]
name = "serde"
@@ -3022,12 +2865,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
-name = "siphasher"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
-
-[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3040,12 +2877,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
-name = "smawk"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
-
-[[package]]
name = "socket2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3074,12 +2905,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3156,15 +2981,6 @@ dependencies = [
]
[[package]]
-name = "textwrap"
-version = "0.16.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
-dependencies = [
- "smawk",
-]
-
-[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3345,15 +3161,6 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.5.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
@@ -3626,125 +3433,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
-name = "uniffi"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6d968cb62160c11f2573e6be724ef8b1b18a277aededd17033f8a912d73e2b4"
-dependencies = [
- "anyhow",
- "cargo_metadata",
- "uniffi_bindgen",
- "uniffi_core",
- "uniffi_macros",
- "uniffi_pipeline",
-]
-
-[[package]]
-name = "uniffi_bindgen"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6b39ef1acbe1467d5d210f274fae344cb6f8766339330cb4c9688752899bf6b"
-dependencies = [
- "anyhow",
- "askama",
- "camino",
- "cargo_metadata",
- "fs-err",
- "glob",
- "goblin",
- "heck",
- "indexmap",
- "once_cell",
- "serde",
- "tempfile",
- "textwrap",
- "toml 0.5.11",
- "uniffi_internal_macros",
- "uniffi_meta",
- "uniffi_pipeline",
- "uniffi_udl",
-]
-
-[[package]]
-name = "uniffi_core"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2d990b553d6b9a7ee9c3ae71134674739913d52350b56152b0e613595bb5a6f"
-dependencies = [
- "anyhow",
- "bytes",
- "once_cell",
- "static_assertions",
-]
-
-[[package]]
-name = "uniffi_internal_macros"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04f4f224becf14885c10e6e400b95cc4d1985738140cb194ccc2044563f8a56b"
-dependencies = [
- "anyhow",
- "indexmap",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "uniffi_macros"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b481d385af334871d70904e6a5f129be7cd38c18fcf8dd8fd1f646b426a56d58"
-dependencies = [
- "camino",
- "fs-err",
- "once_cell",
- "proc-macro2",
- "quote",
- "serde",
- "syn",
- "toml 0.5.11",
- "uniffi_meta",
-]
-
-[[package]]
-name = "uniffi_meta"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f817868a3b171bb7bf259e882138d104deafde65684689b4694c846d322491"
-dependencies = [
- "anyhow",
- "siphasher",
- "uniffi_internal_macros",
- "uniffi_pipeline",
-]
-
-[[package]]
-name = "uniffi_pipeline"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b147e133ad7824e32426b90bc41fda584363563f2ba747f590eca1fd6fd14e6"
-dependencies = [
- "anyhow",
- "heck",
- "indexmap",
- "tempfile",
- "uniffi_internal_macros",
-]
-
-[[package]]
-name = "uniffi_udl"
-version = "0.29.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caed654fb73da5abbc7a7e9c741532284532ba4762d6fe5071372df22a41730a"
-dependencies = [
- "anyhow",
- "textwrap",
- "uniffi_meta",
- "weedle2",
-]
-
-[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4060,15 +3748,6 @@ dependencies = [
]
[[package]]
-name = "weedle2"
-version = "5.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e"
-dependencies = [
- "nom",
-]
-
-[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4434,7 +4113,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
- "toml 0.8.23",
+ "toml",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,7 +1,5 @@
[workspace]
members = [
- "crates/app-core",
- "crates/app-wasm",
"crates/core",
"crates/events",
"crates/events-codec",
@@ -40,8 +38,6 @@ homepage = "https://radroots.org"
readme = "README"
[workspace.dependencies]
-radroots-app-core = { path = "crates/app-core", version = "0.1.0-alpha.1", default-features = false }
-radroots-app-wasm = { path = "crates/app-wasm", version = "0.1.0-alpha.1", default-features = false }
radroots-core = { path = "crates/core", version = "0.1.0-alpha.1", default-features = false }
radroots-events = { path = "crates/events", version = "0.1.0-alpha.1", default-features = false }
radroots-events-codec = { path = "crates/events-codec", version = "0.1.0-alpha.1", default-features = false }
diff --git a/contract/coverage/profiles.toml b/contract/coverage/profiles.toml
@@ -3,11 +3,6 @@ no_default_features = false
features = []
test_threads = 1
-[profiles.crates."radroots-app-core"]
-no_default_features = true
-features = []
-test_threads = 1
-
[profiles.crates."radroots-log"]
no_default_features = true
features = []
diff --git a/contract/coverage/required-crates.toml b/contract/coverage/required-crates.toml
@@ -9,8 +9,6 @@ crates = [
"radroots-events-codec-wasm",
"radroots-replica-db-schema",
"xtask",
- "radroots-app-core",
- "radroots-app-wasm",
"radroots-events-indexed",
"radroots-log",
"radroots-net-core",
diff --git a/contract/coverage/rollout.toml b/contract/coverage/rollout.toml
@@ -55,91 +55,81 @@ status = "required"
order = 9
[[rollout.crates]]
-name = "radroots-app-core"
-status = "required"
-order = 10
-
-[[rollout.crates]]
-name = "radroots-app-wasm"
-status = "required"
-order = 11
-
-[[rollout.crates]]
name = "radroots-events-indexed"
status = "required"
-order = 12
+order = 10
[[rollout.crates]]
name = "radroots-log"
status = "required"
-order = 13
+order = 11
[[rollout.crates]]
name = "radroots-net-core"
status = "required"
-order = 14
+order = 12
[[rollout.crates]]
name = "radroots-net"
status = "required"
-order = 15
+order = 13
[[rollout.crates]]
name = "radroots-nostr"
status = "required"
-order = 16
+order = 14
[[rollout.crates]]
name = "radroots-nostr-accounts"
status = "required"
-order = 17
+order = 15
[[rollout.crates]]
name = "radroots-nostr-ndb"
status = "required"
-order = 18
+order = 16
[[rollout.crates]]
name = "radroots-nostr-runtime"
status = "required"
-order = 19
+order = 17
[[rollout.crates]]
name = "radroots-runtime"
status = "required"
-order = 20
+order = 18
[[rollout.crates]]
name = "radroots-sql-core"
status = "required"
-order = 21
+order = 19
[[rollout.crates]]
name = "radroots-sql-wasm-core"
status = "required"
-order = 22
+order = 20
[[rollout.crates]]
name = "radroots-sql-wasm-bridge"
status = "required"
-order = 23
+order = 21
[[rollout.crates]]
name = "radroots-replica-sync"
status = "required"
-order = 24
+order = 22
[[rollout.crates]]
name = "radroots-replica-db"
status = "required"
-order = 25
+order = 23
[[rollout.crates]]
name = "radroots-replica-sync-wasm"
status = "required"
-order = 26
+order = 24
[[rollout.crates]]
name = "radroots-replica-db-wasm"
status = "required"
-order = 27
+order = 25
diff --git a/contract/release/publish-set.toml b/contract/release/publish-set.toml
@@ -26,9 +26,7 @@ crates = [
"radroots-net-core",
"radroots-replica-db-wasm",
"radroots-replica-sync-wasm",
- "radroots-app-core",
"radroots-net",
- "radroots-app-wasm",
]
[internal]
@@ -61,7 +59,5 @@ crates = [
"radroots-net-core",
"radroots-replica-db-wasm",
"radroots-replica-sync-wasm",
- "radroots-app-core",
"radroots-net",
- "radroots-app-wasm",
]
diff --git a/crates/app-core/Cargo.toml b/crates/app-core/Cargo.toml
@@ -1,52 +0,0 @@
-[package]
-name = "radroots-app-core"
-version = "0.1.0-alpha.1"
-edition.workspace = true
-authors = ["Radroots Authors"]
-rust-version.workspace = true
-license.workspace = true
-description = "core application runtime primitives for radroots app surfaces"
-repository.workspace = true
-homepage.workspace = true
-documentation = "https://docs.rs/radroots-app-core"
-readme.workspace = true
-
-[lib]
-crate-type = ["rlib"]
-
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
-
-[features]
-default = ["rt", "nostr-client"]
-rt = ["radroots-net-core/rt"]
-nostr-client = [
- "radroots-net-core/nostr-client",
- "dep:radroots-core",
- "dep:radroots-events-codec",
- "dep:radroots-trade",
- "dep:radroots-identity",
- "dep:radroots-nostr",
-]
-directories = ["radroots-net-core/directories"]
-fs-persistence = ["radroots-net-core/fs-persistence"]
-
-[dependencies]
-radroots-log = { workspace = true }
-radroots-events = { workspace = true }
-radroots-net-core = { workspace = true, features = ["std"] }
-radroots-core = { workspace = true, optional = true }
-radroots-identity = { workspace = true, optional = true, default-features = false, features = ["std"] }
-radroots-events-codec = { workspace = true, features = ["serde_json"], optional = true }
-radroots-trade = { workspace = true, features = ["std", "serde", "serde_json"], optional = true }
-radroots-nostr = { workspace = true, features = ["events"], optional = true }
-chrono = { workspace = true }
-serde = { workspace = true, features = ["derive"] }
-serde_json = { workspace = true }
-thiserror = { workspace = true }
-tracing = { workspace = true }
-uniffi = { workspace = true }
-tokio = { workspace = true }
-
-[dev-dependencies]
-tracing-subscriber = { workspace = true }
diff --git a/crates/app-core/README.md b/crates/app-core/README.md
@@ -1,14 +0,0 @@
-# radroots-app-core
-
-Core application runtime primitives for Rad Roots app surfaces.
-
-## Goals
-
-- define stable application runtime, error, and lifecycle interfaces
-- keep runtime and network wiring deterministic across supported targets
-- support reusable integration points with `radroots-net-core`
-- provide reusable application primitives for higher-level Rad Roots crates
-
-## License
-
-Licensed under AGPL-3.0. See LICENSE.
diff --git a/crates/app-core/build.rs b/crates/app-core/build.rs
@@ -1,47 +0,0 @@
-use std::{
- env,
- process::Command,
- time::{SystemTime, UNIX_EPOCH},
-};
-
-fn main() {
- println!("cargo:rerun-if-changed=build.rs");
- println!("cargo:rerun-if-env-changed=RUSTC");
- println!("cargo:rerun-if-env-changed=PROFILE");
-
- let rustc = env::var("RUSTC").expect("missing required env var RUSTC");
- if let Ok(out) = Command::new(rustc).arg("--version").output() {
- if out.status.success() {
- if let Ok(ver) = String::from_utf8(out.stdout) {
- println!("cargo:rustc-env=RUSTC_VERSION={}", ver.trim());
- }
- }
- }
-
- if let Ok(out) = Command::new("git")
- .args(["rev-parse", "--short=12", "HEAD"])
- .output()
- {
- if out.status.success() {
- let mut sha = String::from_utf8_lossy(&out.stdout).trim().to_string();
- let dirty = Command::new("git")
- .args(["status", "--porcelain"])
- .output()
- .ok()
- .map_or(false, |o| o.status.success() && !o.stdout.is_empty());
- if dirty {
- sha.push_str("-dirty");
- }
- println!("cargo:rustc-env=GIT_HASH={sha}");
- }
- }
-
- let profile = env::var("PROFILE").expect("missing required env var PROFILE");
- println!("cargo:rustc-env=PROFILE={profile}");
-
- let epoch = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .map(|d| d.as_secs())
- .expect("system time before unix epoch");
- println!("cargo:rustc-env=BUILD_TIME_UNIX={epoch}");
-}
diff --git a/crates/app-core/src/error.rs b/crates/app-core/src/error.rs
@@ -1,7 +0,0 @@
-use thiserror::Error;
-
-#[derive(Debug, Error, uniffi::Error)]
-pub enum RadrootsAppError {
- #[error("{0}")]
- Msg(String),
-}
diff --git a/crates/app-core/src/lib.rs b/crates/app-core/src/lib.rs
@@ -1,10 +0,0 @@
-uniffi::setup_scaffolding!("radroots");
-
-pub mod error;
-pub mod logging;
-pub mod runtime;
-
-pub use error::RadrootsAppError;
-pub use radroots_net_core::net::{BuildInfo, NetInfo};
-pub use radroots_net_core::{Net, NetHandle};
-pub use runtime::RadrootsRuntime;
diff --git a/crates/app-core/src/logging.rs b/crates/app-core/src/logging.rs
@@ -1,45 +0,0 @@
-use std::path::PathBuf;
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-pub fn init_logging(
- dir: Option<String>,
- file_name: Option<String>,
- is_stdout: Option<bool>,
-) -> Result<(), crate::RadrootsAppError> {
- let opts = radroots_log::LoggingOptions {
- dir: dir.map(PathBuf::from),
- file_name: file_name.unwrap_or_else(|| "radroots.log".to_string()),
- stdout: is_stdout.unwrap_or(true),
- default_level: None,
- };
- match radroots_log::init_logging(opts) {
- Ok(()) => Ok(()),
- Err(err) => Err(crate::RadrootsAppError::Msg(format!("{err}"))),
- }
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-pub fn init_logging_stdout() -> Result<(), crate::RadrootsAppError> {
- match radroots_log::init_stdout() {
- Ok(()) => Ok(()),
- Err(err) => Err(crate::RadrootsAppError::Msg(format!("{err}"))),
- }
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-pub fn log_info(msg: String) -> Result<(), crate::RadrootsAppError> {
- radroots_log::log_info(msg);
- Ok(())
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-pub fn log_error(msg: String) -> Result<(), crate::RadrootsAppError> {
- radroots_log::log_error(msg);
- Ok(())
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-pub fn log_debug(msg: String) -> Result<(), crate::RadrootsAppError> {
- radroots_log::log_debug(msg);
- Ok(())
-}
diff --git a/crates/app-core/src/runtime/app_info.rs b/crates/app-core/src/runtime/app_info.rs
@@ -1,26 +0,0 @@
-#[derive(Debug, Clone, Default, serde::Serialize, uniffi::Record)]
-pub struct AppInfoPlatform {
- pub platform: Option<String>,
- pub bundle_id: Option<String>,
- pub version: Option<String>,
- pub build_number: Option<String>,
- pub build_sha: Option<String>,
-}
-
-impl AppInfoPlatform {
- pub fn new(
- platform: Option<String>,
- bundle_id: Option<String>,
- version: Option<String>,
- build_number: Option<String>,
- build_sha: Option<String>,
- ) -> Self {
- Self {
- platform,
- bundle_id,
- version,
- build_number,
- build_sha,
- }
- }
-}
diff --git a/crates/app-core/src/runtime/builder.rs b/crates/app-core/src/runtime/builder.rs
@@ -1,53 +0,0 @@
-use radroots_net_core::NetHandle;
-use radroots_net_core::builder::NetBuilder;
-use radroots_net_core::config::NetConfig;
-
-use crate::RadrootsAppError;
-
-pub struct RuntimeBuilder {
- config: NetConfig,
- manage_runtime: bool,
-}
-
-impl RuntimeBuilder {
- pub fn new() -> Self {
- Self {
- config: NetConfig::default(),
- manage_runtime: true,
- }
- }
-
- pub fn with_config(mut self, config: NetConfig) -> Self {
- self.config = config;
- self
- }
-
- pub fn manage_runtime(mut self, manage: bool) -> Self {
- self.manage_runtime = manage;
- self
- }
-
- pub fn build(self) -> Result<NetHandle, RadrootsAppError> {
- #[cfg(feature = "rt")]
- {
- match NetBuilder::new()
- .config(self.config)
- .manage_runtime(self.manage_runtime)
- .build()
- {
- Ok(handle) => Ok(handle),
- Err(err) => Err(RadrootsAppError::Msg(format!("net build failed: {err}"))),
- }
- }
-
- #[cfg(not(feature = "rt"))]
- {
- let handle = NetBuilder::new()
- .config(self.config)
- .manage_runtime(self.manage_runtime)
- .build()
- .expect("net build must succeed when rt feature is disabled");
- Ok(handle)
- }
- }
-}
diff --git a/crates/app-core/src/runtime/info.rs b/crates/app-core/src/runtime/info.rs
@@ -1,119 +0,0 @@
-use super::RadrootsRuntime;
-use chrono::Utc;
-use radroots_net_core::net;
-use serde::Serialize;
-
-#[derive(Debug, Clone, Serialize, Default, uniffi::Record)]
-pub struct NetBuildInfo {
- pub crate_name: String,
- pub crate_version: String,
- pub rustc: Option<String>,
- pub profile: Option<String>,
- pub git_sha: Option<String>,
- pub build_time_unix: Option<u64>,
-}
-
-impl From<&net::BuildInfo> for NetBuildInfo {
- fn from(b: &net::BuildInfo) -> Self {
- Self {
- crate_name: b.crate_name.to_string(),
- crate_version: b.crate_version.to_string(),
- rustc: b.rustc.map(|s| s.to_string()),
- profile: b.profile.map(|s| s.to_string()),
- git_sha: b.git_sha.map(|s| s.to_string()),
- build_time_unix: b.build_time_unix,
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize, uniffi::Record)]
-pub struct AppInfo {
- pub build: NetBuildInfo,
- pub started_unix_ms: i64,
- pub uptime_millis: i64,
- pub shutting_down: bool,
- pub platform: Option<super::app_info::AppInfoPlatform>,
-}
-
-#[derive(Debug, Clone, Serialize, uniffi::Record)]
-pub struct RuntimeInfo {
- pub app: AppInfo,
- pub net: NetBuildInfo,
-}
-
-pub fn gather_runtime_info(runtime: &RadrootsRuntime) -> RuntimeInfo {
- let now_ms = Utc::now().timestamp_millis();
- let app_info = AppInfo {
- build: app_build_info(),
- started_unix_ms: runtime.started_unix_ms,
- uptime_millis: now_ms - runtime.started_unix_ms,
- shutting_down: runtime
- .shutting_down
- .load(std::sync::atomic::Ordering::SeqCst),
- platform: runtime.platform_app.read().ok().and_then(|g| (*g).clone()),
- };
-
- let net_info = match runtime.net.lock() {
- Ok(guard) => NetBuildInfo::from(&guard.info.build),
- Err(_) => NetBuildInfo::default(),
- };
-
- RuntimeInfo {
- app: app_info,
- net: net_info,
- }
-}
-
-pub fn app_build_info() -> NetBuildInfo {
- NetBuildInfo {
- crate_name: env!("CARGO_PKG_NAME").to_string(),
- crate_version: env!("CARGO_PKG_VERSION").to_string(),
- rustc: env_opt_to_owned(option_env!("RUSTC_VERSION")),
- profile: env_opt_to_owned(option_env!("PROFILE")),
- git_sha: env_opt_to_owned(option_env!("GIT_HASH")),
- build_time_unix: env_opt_to_u64(option_env!("BUILD_TIME_UNIX")),
- }
-}
-
-fn env_opt_to_owned(value: Option<&str>) -> Option<String> {
- value.map(str::to_owned)
-}
-
-fn env_opt_to_u64(value: Option<&str>) -> Option<u64> {
- value.map(str::parse::<u64>).and_then(Result::ok)
-}
-
-#[cfg(test)]
-mod tests {
- use super::NetBuildInfo;
- use radroots_net_core::net;
-
- #[test]
- fn net_build_info_from_copies_optional_fields() {
- let source = net::BuildInfo {
- crate_name: "radroots-net-core",
- crate_version: "1.2.3",
- rustc: Some("rustc 1.92.0"),
- profile: Some("debug"),
- git_sha: Some("abc123"),
- build_time_unix: Some(1_700_000_000),
- };
-
- let out = NetBuildInfo::from(&source);
- assert_eq!(out.crate_name, "radroots-net-core");
- assert_eq!(out.crate_version, "1.2.3");
- assert_eq!(out.rustc.as_deref(), Some("rustc 1.92.0"));
- assert_eq!(out.profile.as_deref(), Some("debug"));
- assert_eq!(out.git_sha.as_deref(), Some("abc123"));
- assert_eq!(out.build_time_unix, Some(1_700_000_000));
- }
-
- #[test]
- fn env_opt_helpers_cover_some_none_and_parse_failure() {
- assert_eq!(super::env_opt_to_owned(Some("abc")).as_deref(), Some("abc"));
- assert_eq!(super::env_opt_to_owned(None), None);
- assert_eq!(super::env_opt_to_u64(Some("123")), Some(123));
- assert_eq!(super::env_opt_to_u64(Some("abc")), None);
- assert_eq!(super::env_opt_to_u64(None), None);
- }
-}
diff --git a/crates/app-core/src/runtime/key_management.rs b/crates/app-core/src/runtime/key_management.rs
@@ -1,225 +0,0 @@
-use super::RadrootsRuntime;
-use crate::RadrootsAppError;
-#[cfg(feature = "nostr-client")]
-use radroots_identity::{RadrootsIdentity, RadrootsIdentityId};
-#[cfg(feature = "nostr-client")]
-use std::path::PathBuf;
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-impl RadrootsRuntime {
- pub fn accounts_has_selected_signing_identity(&self) -> bool {
- #[cfg(feature = "nostr-client")]
- {
- if let Ok(guard) = self.net.lock() {
- return guard
- .accounts
- .selected_signing_identity()
- .ok()
- .flatten()
- .is_some();
- }
- }
-
- #[cfg(not(feature = "nostr-client"))]
- {
- false
- }
-
- #[cfg(feature = "nostr-client")]
- false
- }
-
- pub fn accounts_selected_npub(&self) -> Option<String> {
- #[cfg(feature = "nostr-client")]
- {
- if let Ok(guard) = self.net.lock() {
- return guard
- .accounts
- .selected_public_identity()
- .ok()
- .flatten()
- .map(|identity| identity.public_key_npub);
- }
- }
-
- #[cfg(not(feature = "nostr-client"))]
- {
- None
- }
-
- #[cfg(feature = "nostr-client")]
- None
- }
-
- pub fn accounts_list_ids(&self) -> Result<Vec<String>, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let accounts = guard
- .accounts
- .list_accounts()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- return Ok(accounts
- .into_iter()
- .map(|account| account.account_id.to_string())
- .collect());
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn accounts_generate(
- &self,
- label: Option<String>,
- make_selected: bool,
- ) -> Result<String, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let account_id = guard
- .accounts
- .generate_identity(label, make_selected)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard.nostr = None;
- return Ok(account_id.to_string());
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = (label, make_selected);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn accounts_import_secret(
- &self,
- secret_key: String,
- label: Option<String>,
- make_selected: bool,
- ) -> Result<String, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let identity = RadrootsIdentity::from_secret_key_str(secret_key.as_str())
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- let account_id = guard
- .accounts
- .upsert_identity(&identity, label, make_selected)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard.nostr = None;
- return Ok(account_id.to_string());
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = (secret_key, label, make_selected);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn accounts_import_from_path(
- &self,
- path: String,
- label: Option<String>,
- make_selected: bool,
- ) -> Result<String, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let account_id = guard
- .accounts
- .migrate_legacy_identity_file(PathBuf::from(path), label, make_selected)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard.nostr = None;
- return Ok(account_id.to_string());
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = (path, label, make_selected);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn accounts_export_selected_secret_hex(&self) -> Result<Option<String>, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let Some(selected_id) = guard
- .accounts
- .selected_account_id()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?
- else {
- return Ok(None);
- };
- return guard
- .accounts
- .export_secret_hex(&selected_id)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")));
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn accounts_select(&self, account_id: String) -> Result<(), RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let account_id = RadrootsIdentityId::parse(account_id.as_str())
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard
- .accounts
- .select_account(&account_id)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard.nostr = None;
- Ok(())
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = account_id;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn accounts_remove(&self, account_id: String) -> Result<(), RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let account_id = RadrootsIdentityId::parse(account_id.as_str())
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard
- .accounts
- .remove_account(&account_id)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- guard.nostr = None;
- Ok(())
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = account_id;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-}
diff --git a/crates/app-core/src/runtime/mod.rs b/crates/app-core/src/runtime/mod.rs
@@ -1,211 +0,0 @@
-pub mod app_info;
-pub mod builder;
-pub mod info;
-pub mod key_management;
-pub mod nostr;
-#[cfg(feature = "nostr-client")]
-pub mod trade_listing;
-
-use chrono::Utc;
-use radroots_net_core::{NetHandle, builder::NetBuilder};
-#[cfg(feature = "nostr-client")]
-use std::sync::Mutex;
-use std::sync::{
- RwLock,
- atomic::{AtomicBool, Ordering},
-};
-#[cfg(feature = "nostr-client")]
-use tokio::sync::broadcast::Receiver;
-
-use self::{
- app_info::AppInfoPlatform,
- info::{RuntimeInfo, gather_runtime_info},
-};
-use crate::RadrootsAppError;
-
-#[derive(uniffi::Object)]
-pub struct RadrootsRuntime {
- pub(crate) net: NetHandle,
- pub(crate) started_unix_ms: i64,
- pub(crate) shutting_down: AtomicBool,
- pub(crate) platform_app: RwLock<Option<AppInfoPlatform>>,
- #[cfg(feature = "nostr-client")]
- pub(crate) post_events_rx: Mutex<
- Option<
- Receiver<
- radroots_events_codec::parsed::RadrootsParsedData<
- radroots_events::post::RadrootsPost,
- >,
- >,
- >,
- >,
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-impl RadrootsRuntime {
- #[cfg_attr(not(coverage_nightly), uniffi::constructor)]
- pub fn new() -> Result<Self, RadrootsAppError> {
- let cfg = radroots_net_core::config::NetConfig::default();
- #[cfg(feature = "rt")]
- let handle = match NetBuilder::new().config(cfg).manage_runtime(true).build() {
- Ok(handle) => handle,
- Err(err) => return Err(RadrootsAppError::Msg(format!("net build failed: {err}"))),
- };
- #[cfg(not(feature = "rt"))]
- let handle = NetBuilder::new()
- .config(cfg)
- .manage_runtime(true)
- .build()
- .expect("net build must succeed when rt feature is disabled");
-
- Ok(Self {
- net: handle,
- started_unix_ms: Utc::now().timestamp_millis(),
- shutting_down: AtomicBool::new(false),
- platform_app: RwLock::new(None),
- #[cfg(feature = "nostr-client")]
- post_events_rx: Mutex::new(None),
- })
- }
-
- pub fn stop(&self) {
- if self.shutting_down.swap(true, Ordering::SeqCst) {
- let _ = crate::logging::log_info(
- "Runtime stop already in progress or completed.".to_string(),
- );
- return;
- }
-
- #[cfg(feature = "rt")]
- {
- if let Ok(mut net) = self.net.lock() {
- if let Some(_rt) = net.rt.take() {
- let _ = crate::logging::log_info("Runtime stopped gracefully.".to_string());
- } else {
- let _ = crate::logging::log_info("No runtime was active at stop.".to_string());
- }
- } else {
- let _ = crate::logging::log_info(
- "Failed to acquire runtime lock during stop.".to_string(),
- );
- }
- }
-
- #[cfg(not(feature = "rt"))]
- {
- let _ = crate::logging::log_info(
- "No managed runtime is available for this build.".to_string(),
- );
- }
- }
-
- pub fn uptime_millis(&self) -> i64 {
- Utc::now().timestamp_millis() - self.started_unix_ms
- }
-
- pub fn info(&self) -> RuntimeInfo {
- gather_runtime_info(self)
- }
-
- pub fn info_json(&self) -> String {
- #[cfg(feature = "rt")]
- {
- return match serde_json::to_string_pretty(&self.info()) {
- Ok(json) => json,
- Err(err) => format!(r#"{{"error":"serialize RuntimeInfo: {err}"}}"#),
- };
- }
- #[cfg(not(feature = "rt"))]
- {
- serde_json::to_string_pretty(&self.info()).unwrap_or_default()
- }
- }
-
- pub fn set_app_info_platform(
- &self,
- platform: Option<String>,
- bundle_id: Option<String>,
- version: Option<String>,
- build_number: Option<String>,
- build_sha: Option<String>,
- ) {
- let platform_info =
- AppInfoPlatform::new(platform, bundle_id, version, build_number, build_sha);
- if let Ok(mut guard) = self.platform_app.write() {
- *guard = Some(platform_info);
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::RadrootsRuntime;
- use std::panic::{AssertUnwindSafe, catch_unwind};
-
- fn init_info_logging() {
- let _ = tracing_subscriber::fmt()
- .with_test_writer()
- .with_max_level(tracing::Level::INFO)
- .try_init();
- }
-
- fn poison_net_lock(runtime: &RadrootsRuntime) {
- let handle = runtime.net.clone();
- let _ = catch_unwind(AssertUnwindSafe(|| {
- let _guard = handle.lock().expect("lock net");
- panic!("poison net lock");
- }));
- }
-
- fn poison_platform_lock(runtime: &RadrootsRuntime) {
- let _ = catch_unwind(AssertUnwindSafe(|| {
- let _guard = runtime.platform_app.write().expect("lock platform");
- panic!("poison platform lock");
- }));
- }
-
- #[test]
- fn runtime_info_uses_default_net_info_when_lock_is_poisoned() {
- init_info_logging();
- let runtime = RadrootsRuntime::new().expect("runtime");
-
- let healthy = runtime.info();
- assert!(!healthy.net.crate_name.is_empty());
- poison_net_lock(&runtime);
-
- let _ = runtime.uptime_millis();
- let info = runtime.info();
- assert_eq!(info.net.crate_name, String::new());
- assert_eq!(info.net.crate_version, String::new());
- let json = runtime.info_json();
- assert!(json.contains("\"net\""));
- runtime.stop();
- runtime.stop();
- }
-
- #[test]
- fn set_platform_info_handles_poisoned_lock() {
- init_info_logging();
- let runtime = RadrootsRuntime::new().expect("runtime");
- runtime.set_app_info_platform(
- Some("ios".to_string()),
- Some("org.radroots.app".to_string()),
- Some("1.0.0".to_string()),
- Some("100".to_string()),
- Some("abc123".to_string()),
- );
- let info = runtime.info();
- assert_eq!(
- info.app.platform.as_ref().and_then(|v| v.platform.clone()),
- Some("ios".to_string())
- );
- poison_platform_lock(&runtime);
- runtime.set_app_info_platform(
- Some("ios".to_string()),
- Some("org.radroots.app".to_string()),
- Some("1.0.0".to_string()),
- Some("100".to_string()),
- Some("abc123".to_string()),
- );
- }
-}
diff --git a/crates/app-core/src/runtime/nostr.rs b/crates/app-core/src/runtime/nostr.rs
@@ -1,370 +0,0 @@
-use super::RadrootsRuntime;
-use crate::RadrootsAppError;
-#[cfg(feature = "nostr-client")]
-use tokio::sync::broadcast::error::TryRecvError;
-
-#[derive(uniffi::Enum, Debug, Clone, Copy)]
-pub enum NostrLight {
- Red,
- Yellow,
- Green,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct NostrConnectionStatus {
- pub light: NostrLight,
- pub connected: u32,
- pub connecting: u32,
- pub last_error: Option<String>,
-}
-
-#[derive(uniffi::Record, Debug, Clone, Default)]
-pub struct NostrProfile {
- pub name: Option<String>,
- pub display_name: Option<String>,
- pub nip05: Option<String>,
- pub about: Option<String>,
- pub website: Option<String>,
- pub picture: Option<String>,
- pub banner: Option<String>,
- pub lud06: Option<String>,
- pub lud16: Option<String>,
- pub bot: Option<String>,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct NostrProfileEventMetadata {
- pub id: String,
- pub author: String,
- pub published_at: u64,
- pub profile: NostrProfile,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct NostrEvent {
- pub id: String,
- pub author: String,
- pub created_at: u64,
- pub kind: u32,
- pub content: String,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct NostrPost {
- pub content: String,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct NostrPostEventMetadata {
- pub id: String,
- pub author: String,
- pub published_at: u64,
- pub post: NostrPost,
-}
-
-#[cfg(feature = "nostr-client")]
-fn map_post_event_metadata(
- event: radroots_events_codec::parsed::RadrootsParsedData<radroots_events::post::RadrootsPost>,
-) -> NostrPostEventMetadata {
- NostrPostEventMetadata {
- id: event.id,
- author: event.author,
- published_at: event.published_at as u64,
- post: NostrPost {
- content: event.data.content,
- },
- }
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-impl RadrootsRuntime {
- pub fn nostr_set_default_relays(&self, relays: Vec<String>) -> Result<(), RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- guard
- .nostr_set_default_relays(&relays)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = relays;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_connect_if_key_present(&self) -> Result<(), RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let mut guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- guard
- .nostr_connect_if_key_present()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_connection_status(&self) -> NostrConnectionStatus {
- #[cfg(feature = "nostr-client")]
- {
- let guard = self.net.lock();
- if let Ok(g) = guard {
- if let Some(s) = g.nostr_connection_snapshot() {
- let light = match s.light {
- radroots_net_core::nostr_client::Light::Green => NostrLight::Green,
- radroots_net_core::nostr_client::Light::Yellow => NostrLight::Yellow,
- radroots_net_core::nostr_client::Light::Red => NostrLight::Red,
- };
- return NostrConnectionStatus {
- light,
- connected: s.connected as u32,
- connecting: s.connecting as u32,
- last_error: s.last_error,
- };
- }
- }
- NostrConnectionStatus {
- light: NostrLight::Red,
- connected: 0,
- connecting: 0,
- last_error: None,
- }
- }
-
- #[cfg(not(feature = "nostr-client"))]
- {
- NostrConnectionStatus {
- light: NostrLight::Red,
- connected: 0,
- connecting: 0,
- last_error: None,
- }
- }
- }
-
- pub fn nostr_profile_for_self(&self) -> Option<NostrProfileEventMetadata> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = self.net.lock().ok()?;
- let keys = guard.selected_nostr_keys()?;
- let pk = keys.public_key();
- let mgr = guard.nostr.as_ref()?;
- let out = mgr.fetch_profile_event_blocking(pk).ok()?;
- return out.map(|m| NostrProfileEventMetadata {
- id: m.id,
- author: m.author,
- published_at: m.published_at as u64,
- profile: NostrProfile {
- name: m.data.profile.name.into(),
- display_name: m.data.profile.display_name.into(),
- nip05: m.data.profile.nip05.into(),
- about: m.data.profile.about.into(),
- website: m.data.profile.website,
- picture: m.data.profile.picture,
- banner: m.data.profile.banner,
- lud06: m.data.profile.lud06,
- lud16: m.data.profile.lud16,
- bot: m.data.profile.bot,
- },
- });
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- None
- }
- }
-
- pub fn nostr_post_profile(
- &self,
- name: Option<String>,
- display_name: Option<String>,
- nip05: Option<String>,
- about: Option<String>,
- ) -> Result<String, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- mgr.publish_profile_event_blocking(name, display_name, nip05, about)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = (name, display_name, nip05, about);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_post_text_note(&self, content: String) -> Result<String, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- mgr.publish_post_event_blocking(content)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = content;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_fetch_text_notes(
- &self,
- limit: u16,
- since_unix: Option<u64>,
- ) -> Result<Vec<NostrPostEventMetadata>, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- let items = mgr
- .fetch_post_events_blocking(limit, since_unix)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))?;
- Ok(items.into_iter().map(map_post_event_metadata).collect())
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = (limit, since_unix);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_post_reply(
- &self,
- parent_event_id_hex: String,
- parent_author_hex: String,
- content: String,
- root_event_id_hex: Option<String>,
- ) -> Result<String, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- mgr.publish_post_reply_event_blocking(
- parent_event_id_hex,
- parent_author_hex,
- content,
- root_event_id_hex,
- )
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = (
- parent_event_id_hex,
- parent_author_hex,
- content,
- root_event_id_hex,
- );
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_start_post_event_stream(
- &self,
- since_unix: Option<u64>,
- ) -> Result<(), RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- mgr.start_post_event_stream(since_unix);
- if let Ok(mut rx_guard) = self.post_events_rx.lock() {
- if rx_guard.is_none() {
- *rx_guard = Some(mgr.subscribe_post_events());
- }
- }
- Ok(())
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- let _ = since_unix;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn nostr_next_post_event(&self) -> Option<NostrPostEventMetadata> {
- #[cfg(feature = "nostr-client")]
- {
- let mut rx_guard = self.post_events_rx.lock().ok()?;
- let rx = rx_guard.as_mut()?;
- match rx.try_recv() {
- Ok(event) => Some(map_post_event_metadata(event)),
- Err(TryRecvError::Empty) => None,
- Err(TryRecvError::Lagged(_)) => None,
- Err(TryRecvError::Closed) => {
- *rx_guard = None;
- None
- }
- }
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- None
- }
- }
-
- pub fn nostr_stop_post_event_stream(&self) -> Result<(), RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let guard = match self.net.lock() {
- Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
- };
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- mgr.stop_post_event_stream();
- if let Ok(mut rx_guard) = self.post_events_rx.lock() {
- *rx_guard = None;
- }
- Ok(())
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-}
diff --git a/crates/app-core/src/runtime/trade_listing.rs b/crates/app-core/src/runtime/trade_listing.rs
@@ -1,865 +0,0 @@
-#![forbid(unsafe_code)]
-
-use core::str::FromStr;
-
-use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
- RadrootsCoreQuantityPrice, RadrootsCoreUnit,
-};
-use radroots_events::{
- RadrootsNostrEventPtr,
- listing::{
- RadrootsListing, RadrootsListingAvailability, RadrootsListingBin,
- RadrootsListingDeliveryMethod, RadrootsListingFarmRef, RadrootsListingLocation,
- RadrootsListingProduct, RadrootsListingStatus,
- },
-};
-use radroots_events_codec::listing::encode::to_wire_parts as listing_to_wire_parts;
-use radroots_nostr::prelude::{
- RadrootsNostrFilter, RadrootsNostrKind, RadrootsNostrTimestamp, radroots_event_from_nostr,
- radroots_nostr_parse_pubkey,
-};
-use radroots_trade::listing::{
- dvm::TradeListingAddress,
- dvm::{
- TradeListingEnvelope, TradeListingMessagePayload, TradeListingMessageType,
- TradeListingValidateRequest,
- },
- kinds::TRADE_LISTING_KINDS,
- order::{TradeOrder, TradeOrderItem, TradeOrderStatus},
- tags::trade_listing_dvm_tags,
- validation::{RadrootsTradeListing, validate_listing_event},
-};
-
-use super::RadrootsRuntime;
-use crate::RadrootsAppError;
-
-const LISTING_KIND: u32 = 30402;
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct TradeListingDraft {
- pub listing_id: Option<String>,
- pub farm_pubkey: String,
- pub farm_d_tag: String,
- pub title: String,
- pub description: String,
- pub category: String,
- pub bin_display_amount: String,
- pub bin_display_unit: String,
- pub unit_price: String,
- pub currency: String,
- pub bin_label: Option<String>,
- pub bin_id: Option<String>,
- pub inventory: String,
- pub delivery_method: String,
- pub location_primary: String,
- pub location_city: Option<String>,
- pub location_region: Option<String>,
- pub location_country: Option<String>,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct TradeListingSummary {
- pub event_id: String,
- pub seller_pubkey: String,
- pub published_at: u64,
- pub listing_id: String,
- pub listing_addr: String,
- pub title: String,
- pub description: String,
- pub product_type: String,
- pub primary_bin_id: String,
- pub unit_price_amount: String,
- pub unit_price_currency: String,
- pub unit_price_unit: String,
- pub bin_display_amount: String,
- pub bin_display_unit: String,
- pub bin_display_label: Option<String>,
- pub inventory_available: String,
- pub availability: String,
- pub location: String,
- pub delivery_method: String,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct TradeOrderDraft {
- pub listing_addr: String,
- pub seller_pubkey: String,
- pub bin_id: String,
- pub bin_count: String,
- pub notes: Option<String>,
- pub order_id: Option<String>,
- pub recipient_pubkey: String,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct TradeOrderSendResult {
- pub event_id: String,
- pub order_id: String,
-}
-
-#[derive(uniffi::Record, Debug, Clone)]
-pub struct TradeListingMessageSummary {
- pub event_id: String,
- pub author: String,
- pub published_at: u64,
- pub kind: u32,
- pub message_type: String,
- pub listing_addr: String,
- pub order_id: Option<String>,
- pub summary: String,
- pub payload_json: String,
-}
-
-#[cfg_attr(not(coverage_nightly), uniffi::export)]
-impl RadrootsRuntime {
- pub fn trade_listing_publish(
- &self,
- draft: TradeListingDraft,
- ) -> Result<String, RadrootsAppError> {
- let guard = self
- .net
- .lock()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- #[cfg(feature = "nostr-client")]
- {
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- let listing = listing_from_draft(&draft)?;
- let current_pubkey = current_pubkey_hex(self)?;
- if listing.farm.pubkey != current_pubkey {
- return Err(RadrootsAppError::Msg(
- "farm_pubkey must match the active key".into(),
- ));
- }
- let parts = listing_to_wire_parts(&listing)
- .map_err(|e| RadrootsAppError::Msg(format!("listing encode failed: {e}")))?;
- mgr.send_custom_event_blocking(parts.kind, parts.content, parts.tags)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn trade_listings_fetch(
- &self,
- limit: u16,
- since_unix: Option<u64>,
- ) -> Result<Vec<TradeListingSummary>, RadrootsAppError> {
- let guard = self
- .net
- .lock()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- #[cfg(feature = "nostr-client")]
- {
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- let mut filter =
- RadrootsNostrFilter::new().kind(RadrootsNostrKind::Custom(LISTING_KIND as u16));
- filter = filter.limit(limit.into());
- if let Some(since) = since_unix {
- filter = filter.since(RadrootsNostrTimestamp::from(since));
- }
-
- let events = mgr
- .fetch_events_blocking(filter, core::time::Duration::from_secs(10))
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))?;
- let mut out = Vec::new();
- for ev in events {
- let event = radroots_event_from_nostr(&ev);
- if event.kind != LISTING_KIND {
- continue;
- }
- match validate_listing_event(&event) {
- Ok(listing) => {
- out.push(listing_summary_from_trade(listing, &event));
- }
- Err(_) => continue,
- }
- }
- out.sort_by(|a, b| b.published_at.cmp(&a.published_at));
- Ok(out)
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn trade_listing_send_validation_request(
- &self,
- listing_event_id: String,
- seller_pubkey: String,
- listing_id: String,
- recipient_pubkey: String,
- ) -> Result<String, RadrootsAppError> {
- let listing_addr = listing_addr_from_parts(&seller_pubkey, &listing_id)?;
- let payload =
- TradeListingMessagePayload::ListingValidateRequest(TradeListingValidateRequest {
- listing_event: Some(RadrootsNostrEventPtr {
- id: listing_event_id,
- relays: None,
- }),
- });
- self.send_trade_listing_message(
- TradeListingMessageType::ListingValidateRequest,
- listing_addr,
- None,
- payload,
- recipient_pubkey,
- )
- }
-
- pub fn trade_listing_send_order_request(
- &self,
- draft: TradeOrderDraft,
- ) -> Result<TradeOrderSendResult, RadrootsAppError> {
- #[cfg(feature = "nostr-client")]
- {
- let order_id = normalize_optional_id(draft.order_id);
- let order_id = order_id
- .unwrap_or_else(|| format!("order-{}", chrono::Utc::now().timestamp_millis()));
- let buyer_pubkey = current_pubkey_hex(self)?;
- let seller_pubkey = normalize_pubkey(&draft.seller_pubkey)?;
-
- let bin_id = draft.bin_id.trim();
- if bin_id.is_empty() {
- return Err(RadrootsAppError::Msg("bin_id is required".into()));
- }
- let bin_count = parse_u32(&draft.bin_count, "bin_count")?;
- if bin_count == 0 {
- return Err(RadrootsAppError::Msg("bin_count must be > 0".into()));
- }
-
- let item = TradeOrderItem {
- bin_id: bin_id.to_string(),
- bin_count,
- };
-
- let notes = draft
- .notes
- .as_deref()
- .map(|s| s.trim())
- .filter(|s| !s.is_empty())
- .map(|s| s.to_string());
-
- let order = TradeOrder {
- order_id: order_id.clone(),
- listing_addr: draft.listing_addr.clone(),
- buyer_pubkey,
- seller_pubkey,
- items: vec![item],
- discounts: None,
- notes,
- status: TradeOrderStatus::Requested,
- };
-
- let payload = TradeListingMessagePayload::OrderRequest(order);
- let event_id = self.send_trade_listing_message(
- TradeListingMessageType::OrderRequest,
- draft.listing_addr,
- Some(order_id.clone()),
- payload,
- draft.recipient_pubkey,
- )?;
-
- Ok(TradeOrderSendResult { event_id, order_id })
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-
- pub fn trade_listing_fetch_messages(
- &self,
- listing_addr: String,
- order_id: Option<String>,
- limit: u16,
- since_unix: Option<u64>,
- ) -> Result<Vec<TradeListingMessageSummary>, RadrootsAppError> {
- let guard = self
- .net
- .lock()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- #[cfg(feature = "nostr-client")]
- {
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
-
- let kinds: Vec<RadrootsNostrKind> = TRADE_LISTING_KINDS
- .iter()
- .map(|k| RadrootsNostrKind::Custom(*k))
- .collect();
-
- let mut filter = RadrootsNostrFilter::new().kinds(kinds);
- filter = filter.limit(limit.into());
- if let Some(since) = since_unix {
- filter = filter.since(RadrootsNostrTimestamp::from(since));
- }
-
- let events = mgr
- .fetch_events_blocking(filter, core::time::Duration::from_secs(10))
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))?;
-
- let mut out = Vec::new();
- for ev in events {
- let content = ev.content.clone();
- let envelope: TradeListingEnvelope<TradeListingMessagePayload> =
- match serde_json::from_str(&content) {
- Ok(env) => env,
- Err(_) => continue,
- };
- if envelope.validate().is_err() {
- continue;
- }
- if envelope.listing_addr != listing_addr {
- continue;
- }
- if let Some(ref oid) = order_id {
- if envelope.order_id.as_deref() != Some(oid) {
- continue;
- }
- }
- let kind_u32 = ev.kind.as_u16() as u32;
- if envelope.message_type.kind() as u32 != kind_u32 {
- continue;
- }
-
- let summary = message_summary(&envelope.payload);
- out.push(TradeListingMessageSummary {
- event_id: ev.id.to_string(),
- author: ev.pubkey.to_string(),
- published_at: ev.created_at.as_secs(),
- kind: kind_u32,
- message_type: message_type_label(envelope.message_type).to_string(),
- listing_addr: envelope.listing_addr.clone(),
- order_id: envelope.order_id.clone(),
- summary,
- payload_json: content,
- });
- }
- out.sort_by(|a, b| b.published_at.cmp(&a.published_at));
- Ok(out)
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-}
-
-impl RadrootsRuntime {
- fn send_trade_listing_message(
- &self,
- message_type: TradeListingMessageType,
- listing_addr: String,
- order_id: Option<String>,
- payload: TradeListingMessagePayload,
- recipient_pubkey: String,
- ) -> Result<String, RadrootsAppError> {
- let guard = self
- .net
- .lock()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- #[cfg(feature = "nostr-client")]
- {
- let mgr = guard
- .nostr
- .as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
- let recipient_hex = normalize_pubkey(&recipient_pubkey)?;
- let envelope = TradeListingEnvelope::new(
- message_type,
- listing_addr.clone(),
- order_id.clone(),
- payload,
- );
- envelope
- .validate()
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))?;
- let content = serde_json::to_string(&envelope)
- .map_err(|e| RadrootsAppError::Msg(format!("encode envelope failed: {e}")))?;
- let tags = trade_listing_dvm_tags(recipient_hex, listing_addr, order_id);
- mgr.send_custom_event_blocking(message_type.kind() as u32, content, tags)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
- }
- #[cfg(not(feature = "nostr-client"))]
- {
- Err(RadrootsAppError::Msg("nostr disabled".into()))
- }
- }
-}
-
-fn listing_from_draft(draft: &TradeListingDraft) -> Result<RadrootsListing, RadrootsAppError> {
- let listing_id = draft
- .listing_id
- .as_deref()
- .map(|s| s.trim())
- .filter(|s| !s.is_empty())
- .map(|s| s.to_string())
- .unwrap_or_else(|| format!("listing-{}", chrono::Utc::now().timestamp_millis()));
- let farm_pubkey = draft.farm_pubkey.trim();
- if farm_pubkey.is_empty() {
- return Err(RadrootsAppError::Msg("farm_pubkey is required".into()));
- }
- let farm_pubkey = normalize_pubkey(farm_pubkey)?;
- let farm_d_tag = draft.farm_d_tag.trim();
- if farm_d_tag.is_empty() {
- return Err(RadrootsAppError::Msg("farm_d_tag is required".into()));
- }
-
- let title = draft.title.trim();
- if title.is_empty() {
- return Err(RadrootsAppError::Msg("title is required".into()));
- }
- let description = draft.description.trim();
- if description.is_empty() {
- return Err(RadrootsAppError::Msg("description is required".into()));
- }
- let category = draft.category.trim();
- if category.is_empty() {
- return Err(RadrootsAppError::Msg("category is required".into()));
- }
- let location_primary = draft.location_primary.trim();
- if location_primary.is_empty() {
- return Err(RadrootsAppError::Msg("location is required".into()));
- }
-
- let display_amount = parse_decimal(&draft.bin_display_amount, "bin_display_amount")?;
- ensure_non_negative(&display_amount, "bin_display_amount")?;
- let display_unit = parse_unit(&draft.bin_display_unit)?;
- let unit_price_amount = parse_decimal(&draft.unit_price, "unit_price")?;
- ensure_non_negative(&unit_price_amount, "unit_price")?;
- let currency = parse_currency(&draft.currency)?;
- let inventory = parse_decimal(&draft.inventory, "inventory")?;
- ensure_non_negative(&inventory, "inventory")?;
-
- let display_quantity = RadrootsCoreQuantity::new(display_amount, display_unit);
- let canonical_quantity = display_quantity
- .to_canonical()
- .map_err(|e| RadrootsAppError::Msg(format!("invalid bin_display_unit: {e}")))?;
- let unit_price = RadrootsCoreMoney::new(unit_price_amount, currency);
- let price_per_display_unit = RadrootsCoreQuantityPrice::new(
- unit_price.clone(),
- RadrootsCoreQuantity::new(RadrootsCoreDecimal::ONE, display_unit),
- );
- let price_per_canonical_unit = price_per_display_unit
- .try_to_canonical_unit_price()
- .map_err(|e| RadrootsAppError::Msg(format!("invalid unit_price: {e:?}")))?;
- let bin_label = clean_optional(&draft.bin_label);
- let bin_id = normalize_optional_id(draft.bin_id.clone()).unwrap_or_else(|| "bin-1".to_string());
- let bin = RadrootsListingBin {
- bin_id: bin_id.clone(),
- quantity: canonical_quantity,
- price_per_canonical_unit,
- display_amount: Some(display_amount),
- display_unit: Some(display_unit),
- display_label: bin_label,
- display_price: Some(unit_price),
- display_price_unit: Some(display_unit),
- };
-
- let delivery_method = parse_delivery_method(&draft.delivery_method)?;
-
- Ok(RadrootsListing {
- d_tag: listing_id,
- farm: RadrootsListingFarmRef {
- pubkey: farm_pubkey,
- d_tag: farm_d_tag.to_string(),
- },
- product: RadrootsListingProduct {
- key: category.to_string(),
- title: title.to_string(),
- category: category.to_string(),
- summary: Some(description.to_string()),
- process: None,
- lot: None,
- location: None,
- profile: None,
- year: None,
- },
- primary_bin_id: bin_id,
- bins: vec![bin],
- resource_area: None,
- plot: None,
- discounts: None,
- inventory_available: Some(inventory),
- availability: Some(RadrootsListingAvailability::Status {
- status: RadrootsListingStatus::Active,
- }),
- delivery_method: Some(delivery_method),
- location: Some(RadrootsListingLocation {
- primary: location_primary.to_string(),
- city: clean_optional(&draft.location_city),
- region: clean_optional(&draft.location_region),
- country: clean_optional(&draft.location_country),
- lat: None,
- lng: None,
- geohash: None,
- }),
- images: None,
- })
-}
-
-fn listing_summary_from_trade(
- listing: RadrootsTradeListing,
- event: &radroots_events::RadrootsNostrEvent,
-) -> TradeListingSummary {
- let bin = listing
- .listing
- .bins
- .iter()
- .find(|bin| bin.bin_id == listing.primary_bin_id)
- .or_else(|| listing.listing.bins.first())
- .expect("validated listing must include bins");
- let (display_amount, display_unit) = match (bin.display_amount.as_ref(), bin.display_unit) {
- (Some(amount), Some(unit)) => (amount.clone(), unit),
- _ => (bin.quantity.amount.clone(), bin.quantity.unit),
- };
- let display_label = bin.display_label.clone().or(bin.quantity.label.clone());
- let display_label = clean_optional(&display_label);
- let (unit_price_amount, unit_price_currency, unit_price_unit) =
- match bin.price_per_canonical_unit.try_to_unit_price(display_unit) {
- Ok(price) => (
- price.amount.amount.to_string(),
- price.amount.currency.to_string(),
- price.quantity.unit.to_string(),
- ),
- Err(_) => match (&bin.display_price, bin.display_price_unit) {
- (Some(price), Some(unit)) => (
- price.amount.to_string(),
- price.currency.to_string(),
- unit.to_string(),
- ),
- _ => (
- bin.price_per_canonical_unit.amount.amount.to_string(),
- bin.price_per_canonical_unit.amount.currency.to_string(),
- bin.price_per_canonical_unit.quantity.unit.to_string(),
- ),
- },
- };
-
- TradeListingSummary {
- event_id: event.id.clone(),
- seller_pubkey: event.author.clone(),
- published_at: event.created_at as u64,
- listing_id: listing.listing_id,
- listing_addr: listing.listing_addr,
- title: listing.title,
- description: listing.description,
- product_type: listing.product_type,
- primary_bin_id: listing.primary_bin_id,
- unit_price_amount,
- unit_price_currency,
- unit_price_unit,
- bin_display_amount: display_amount.to_string(),
- bin_display_unit: display_unit.to_string(),
- bin_display_label: display_label,
- inventory_available: listing.inventory_available.to_string(),
- availability: availability_label(&listing.availability),
- location: format_location(&listing.location),
- delivery_method: delivery_method_label(&listing.delivery_method).to_string(),
- }
-}
-
-fn listing_addr_from_parts(
- seller_pubkey: &str,
- listing_id: &str,
-) -> Result<String, RadrootsAppError> {
- let listing_id = listing_id.trim();
- if listing_id.is_empty() {
- return Err(RadrootsAppError::Msg("listing_id is required".into()));
- }
- let seller_hex = normalize_pubkey(seller_pubkey)?;
- Ok(TradeListingAddress {
- kind: LISTING_KIND as u16,
- seller_pubkey: seller_hex,
- listing_id: listing_id.to_string(),
- }
- .as_str())
-}
-
-fn normalize_pubkey(pubkey: &str) -> Result<String, RadrootsAppError> {
- let key = radroots_nostr_parse_pubkey(pubkey.trim())
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))?;
- Ok(key.to_hex())
-}
-
-fn normalize_optional_id(id: Option<String>) -> Option<String> {
- id.as_deref()
- .map(|s| s.trim())
- .filter(|s| !s.is_empty())
- .map(|s| s.to_string())
-}
-
-#[cfg(feature = "nostr-client")]
-fn current_pubkey_hex(runtime: &RadrootsRuntime) -> Result<String, RadrootsAppError> {
- let guard = runtime
- .net
- .lock()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
- let keys = guard
- .selected_nostr_keys()
- .ok_or_else(|| RadrootsAppError::Msg("no selected signing identity".into()))?;
- Ok(keys.public_key().to_hex())
-}
-
-fn parse_decimal(value: &str, label: &str) -> Result<RadrootsCoreDecimal, RadrootsAppError> {
- RadrootsCoreDecimal::from_str(value.trim())
- .map_err(|e| RadrootsAppError::Msg(format!("invalid {label}: {e}")))
-}
-
-fn parse_unit(value: &str) -> Result<RadrootsCoreUnit, RadrootsAppError> {
- RadrootsCoreUnit::from_str(value.trim())
- .map_err(|e| RadrootsAppError::Msg(format!("invalid unit: {e}")))
-}
-
-fn parse_currency(value: &str) -> Result<RadrootsCoreCurrency, RadrootsAppError> {
- RadrootsCoreCurrency::from_str(value.trim())
- .map_err(|e| RadrootsAppError::Msg(format!("invalid currency: {e}")))
-}
-
-fn parse_u32(value: &str, label: &str) -> Result<u32, RadrootsAppError> {
- value
- .trim()
- .parse::<u32>()
- .map_err(|e| RadrootsAppError::Msg(format!("invalid {label}: {e}")))
-}
-
-fn ensure_non_negative(value: &RadrootsCoreDecimal, label: &str) -> Result<(), RadrootsAppError> {
- if value.is_sign_negative() {
- return Err(RadrootsAppError::Msg(format!(
- "{label} must be non-negative"
- )));
- }
- Ok(())
-}
-
-fn parse_delivery_method(value: &str) -> Result<RadrootsListingDeliveryMethod, RadrootsAppError> {
- let raw = value.trim();
- if raw.is_empty() {
- return Err(RadrootsAppError::Msg("delivery_method is required".into()));
- }
- let lowered = raw.to_ascii_lowercase();
- Ok(match lowered.as_str() {
- "pickup" => RadrootsListingDeliveryMethod::Pickup,
- "local_delivery" | "local delivery" => RadrootsListingDeliveryMethod::LocalDelivery,
- "shipping" => RadrootsListingDeliveryMethod::Shipping,
- _ => RadrootsListingDeliveryMethod::Other {
- method: raw.to_string(),
- },
- })
-}
-
-fn availability_label(availability: &RadrootsListingAvailability) -> String {
- match availability {
- RadrootsListingAvailability::Status { status } => match status {
- RadrootsListingStatus::Active => "active".to_string(),
- RadrootsListingStatus::Sold => "sold".to_string(),
- RadrootsListingStatus::Other { value } => value.clone(),
- },
- RadrootsListingAvailability::Window { start, end } => {
- let start = start
- .map(|s| s.to_string())
- .unwrap_or_else(|| "unknown".into());
- let end = end
- .map(|s| s.to_string())
- .unwrap_or_else(|| "unknown".into());
- format!("{start} - {end}")
- }
- }
-}
-
-fn delivery_method_label(method: &RadrootsListingDeliveryMethod) -> &'static str {
- match method {
- RadrootsListingDeliveryMethod::Pickup => "pickup",
- RadrootsListingDeliveryMethod::LocalDelivery => "local delivery",
- RadrootsListingDeliveryMethod::Shipping => "shipping",
- RadrootsListingDeliveryMethod::Other { .. } => "other",
- }
-}
-
-fn format_location(location: &RadrootsListingLocation) -> String {
- let mut parts = Vec::with_capacity(4);
- if !location.primary.trim().is_empty() {
- parts.push(location.primary.trim());
- }
- if let Some(city) = location.city.as_deref() {
- if !city.trim().is_empty() {
- parts.push(city.trim());
- }
- }
- if let Some(region) = location.region.as_deref() {
- if !region.trim().is_empty() {
- parts.push(region.trim());
- }
- }
- if let Some(country) = location.country.as_deref() {
- if !country.trim().is_empty() {
- parts.push(country.trim());
- }
- }
- if parts.is_empty() {
- "n/a".to_string()
- } else {
- parts.join(", ")
- }
-}
-
-fn clean_optional(value: &Option<String>) -> Option<String> {
- value
- .as_deref()
- .map(|s| s.trim())
- .filter(|s| !s.is_empty())
- .map(|s| s.to_string())
-}
-
-fn message_type_label(message_type: TradeListingMessageType) -> &'static str {
- match message_type {
- TradeListingMessageType::ListingValidateRequest => "listing_validate_request",
- TradeListingMessageType::ListingValidateResult => "listing_validate_result",
- TradeListingMessageType::OrderRequest => "order_request",
- TradeListingMessageType::OrderResponse => "order_response",
- TradeListingMessageType::OrderRevision => "order_revision",
- TradeListingMessageType::OrderRevisionAccept => "order_revision_accept",
- TradeListingMessageType::OrderRevisionDecline => "order_revision_decline",
- TradeListingMessageType::Question => "question",
- TradeListingMessageType::Answer => "answer",
- TradeListingMessageType::DiscountRequest => "discount_request",
- TradeListingMessageType::DiscountOffer => "discount_offer",
- TradeListingMessageType::DiscountAccept => "discount_accept",
- TradeListingMessageType::DiscountDecline => "discount_decline",
- TradeListingMessageType::Cancel => "cancel",
- TradeListingMessageType::FulfillmentUpdate => "fulfillment_update",
- TradeListingMessageType::Receipt => "receipt",
- }
-}
-
-fn message_summary(payload: &TradeListingMessagePayload) -> String {
- match payload {
- TradeListingMessagePayload::ListingValidateRequest(_) => {
- "Listing validation requested".to_string()
- }
- TradeListingMessagePayload::ListingValidateResult(result) => {
- if result.valid {
- "Listing validated".to_string()
- } else if let Some(first) = result.errors.first() {
- format!("Listing invalid: {first}")
- } else {
- "Listing invalid".to_string()
- }
- }
- TradeListingMessagePayload::OrderRequest(order) => {
- let item = order.items.first();
- match item {
- Some(i) => format!("Order requested: {}x {}", i.bin_count, i.bin_id),
- None => "Order requested".to_string(),
- }
- }
- TradeListingMessagePayload::OrderResponse(res) => {
- if res.accepted {
- "Order accepted".to_string()
- } else if let Some(reason) = res.reason.as_deref() {
- format!("Order declined: {reason}")
- } else {
- "Order declined".to_string()
- }
- }
- TradeListingMessagePayload::OrderRevision(_) => "Order revision proposed".to_string(),
- TradeListingMessagePayload::OrderRevisionAccept(_) => "Order revision accepted".to_string(),
- TradeListingMessagePayload::OrderRevisionDecline(_) => {
- "Order revision declined".to_string()
- }
- TradeListingMessagePayload::Question(q) => format!("Question: {}", q.question_text),
- TradeListingMessagePayload::Answer(a) => format!("Answer: {}", a.answer_text),
- TradeListingMessagePayload::DiscountRequest(_) => "Discount requested".to_string(),
- TradeListingMessagePayload::DiscountOffer(_) => "Discount offered".to_string(),
- TradeListingMessagePayload::DiscountAccept(_) => "Discount accepted".to_string(),
- TradeListingMessagePayload::DiscountDecline(_) => "Discount declined".to_string(),
- TradeListingMessagePayload::Cancel(c) => {
- if let Some(reason) = c.reason.as_deref() {
- format!("Order cancelled: {reason}")
- } else {
- "Order cancelled".to_string()
- }
- }
- TradeListingMessagePayload::FulfillmentUpdate(update) => {
- format!("Fulfillment update: {:?}", update.status)
- }
- TradeListingMessagePayload::Receipt(receipt) => {
- if receipt.acknowledged {
- "Receipt acknowledged".to_string()
- } else {
- "Receipt update".to_string()
- }
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn listing_from_draft_requires_fields() {
- let draft = TradeListingDraft {
- listing_id: None,
- farm_pubkey: "".into(),
- farm_d_tag: "".into(),
- title: "".into(),
- description: "Desc".into(),
- category: "Coffee".into(),
- bin_display_amount: "1".into(),
- bin_display_unit: "lb".into(),
- unit_price: "10.00".into(),
- currency: "USD".into(),
- bin_label: None,
- bin_id: None,
- inventory: "10".into(),
- delivery_method: "shipping".into(),
- location_primary: "Farm".into(),
- location_city: None,
- location_region: None,
- location_country: None,
- };
- assert!(listing_from_draft(&draft).is_err());
- }
-
- #[test]
- fn listing_from_draft_builds_listing() {
- let draft = TradeListingDraft {
- listing_id: Some("AAAAAAAAAAAAAAAAAAAAAg".into()),
- farm_pubkey: "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6".into(),
- farm_d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- title: "Coffee".into(),
- description: "Washed".into(),
- category: "coffee".into(),
- bin_display_amount: "1".into(),
- bin_display_unit: "lb".into(),
- unit_price: "12.50".into(),
- currency: "USD".into(),
- bin_label: Some("bag".into()),
- bin_id: None,
- inventory: "5".into(),
- delivery_method: "shipping".into(),
- location_primary: "Farm".into(),
- location_city: Some("Town".into()),
- location_region: Some("Region".into()),
- location_country: Some("US".into()),
- };
- let listing = listing_from_draft(&draft).expect("listing builds");
- assert_eq!(listing.d_tag, "AAAAAAAAAAAAAAAAAAAAAg");
- assert_eq!(listing.product.title, "Coffee");
- assert!(listing.delivery_method.is_some());
- assert!(listing.location.is_some());
- }
-}
diff --git a/crates/app-core/tests/logging_error.rs b/crates/app-core/tests/logging_error.rs
@@ -1,9 +0,0 @@
-use radroots_app_core::RadrootsAppError;
-use radroots_app_core::logging;
-
-#[test]
-fn init_logging_stdout_maps_global_subscriber_error() {
- let _ = tracing_subscriber::fmt().try_init();
- let err = logging::init_logging_stdout();
- assert!(matches!(err, Err(RadrootsAppError::Msg(_))));
-}
diff --git a/crates/app-core/tests/no_nostr_runtime.rs b/crates/app-core/tests/no_nostr_runtime.rs
@@ -1,153 +0,0 @@
-#![cfg(not(feature = "nostr-client"))]
-
-use radroots_app_core::logging;
-use radroots_app_core::runtime::builder::RuntimeBuilder;
-use radroots_app_core::runtime::nostr::{
- NostrConnectionStatus, NostrEvent, NostrLight, NostrPost, NostrPostEventMetadata, NostrProfile,
- NostrProfileEventMetadata,
-};
-use radroots_app_core::{RadrootsAppError, RadrootsRuntime};
-use radroots_net_core::config::NetConfig;
-
-fn expect_disabled<T>(result: Result<T, RadrootsAppError>) {
- match result {
- Err(RadrootsAppError::Msg(message)) => assert_eq!(message, "nostr disabled"),
- _ => panic!("expected nostr disabled error"),
- }
-}
-
-#[test]
-fn runtime_info_and_platform_paths_are_exercised() {
- let runtime = RadrootsRuntime::new().expect("runtime");
- assert!(runtime.uptime_millis() >= 0);
-
- runtime.stop();
- runtime.stop();
-}
-
-#[test]
-fn key_management_disabled_paths_are_exercised() {
- let runtime = RadrootsRuntime::new().expect("runtime");
-
- assert!(!runtime.accounts_has_selected_signing_identity());
- assert_eq!(runtime.accounts_selected_npub(), None);
- expect_disabled(runtime.accounts_list_ids());
- expect_disabled(runtime.accounts_generate(Some("alpha".to_string()), true));
- expect_disabled(runtime.accounts_import_secret(
- "deadbeef".to_string(),
- Some("alpha".to_string()),
- true,
- ));
- expect_disabled(runtime.accounts_import_from_path(
- "/tmp/nostr.json".to_string(),
- Some("alpha".to_string()),
- true,
- ));
- expect_disabled(runtime.accounts_export_selected_secret_hex());
- expect_disabled(runtime.accounts_select("account-1".to_string()));
- expect_disabled(runtime.accounts_remove("account-1".to_string()));
-}
-
-#[test]
-fn nostr_disabled_paths_are_exercised() {
- let runtime = RadrootsRuntime::new().expect("runtime");
-
- let status = runtime.nostr_connection_status();
- assert_eq!(status.connected, 0);
- assert_eq!(status.connecting, 0);
- assert!(status.last_error.is_none());
-
- assert!(runtime.nostr_profile_for_self().is_none());
- assert!(runtime.nostr_next_post_event().is_none());
-
- expect_disabled(runtime.nostr_set_default_relays(vec!["wss://relay.example.com".to_string()]));
- expect_disabled(runtime.nostr_connect_if_key_present());
- expect_disabled(runtime.nostr_post_profile(None, None, None, None));
- expect_disabled(runtime.nostr_post_text_note("hello".to_string()));
- expect_disabled(runtime.nostr_fetch_text_notes(25, Some(0)));
- expect_disabled(runtime.nostr_post_reply(
- "event-id".to_string(),
- "author".to_string(),
- "reply".to_string(),
- None,
- ));
- expect_disabled(runtime.nostr_start_post_event_stream(None));
- expect_disabled(runtime.nostr_stop_post_event_stream());
-}
-
-#[test]
-fn runtime_builder_and_logging_paths_are_exercised() {
- let handle = RuntimeBuilder::new()
- .with_config(NetConfig::default())
- .manage_runtime(false)
- .build()
- .expect("build net handle");
- drop(handle);
- let default_handle = RuntimeBuilder::new()
- .build()
- .expect("build default net handle");
- drop(default_handle);
-
- let err = logging::init_logging(Some("/dev/null/file.log".to_string()), None, Some(false));
- assert!(matches!(err, Err(RadrootsAppError::Msg(_))));
- let _ = logging::init_logging(None, None, None);
- let _ = logging::init_logging(None, Some("app.log".to_string()), Some(false));
- let _ = logging::init_logging_stdout();
-
- assert!(logging::log_info("info".to_string()).is_ok());
- assert!(logging::log_error("error".to_string()).is_ok());
- assert!(logging::log_debug("debug".to_string()).is_ok());
-}
-
-#[test]
-fn nostr_records_and_enums_are_exercised() {
- let status = NostrConnectionStatus {
- light: NostrLight::Yellow,
- connected: 1,
- connecting: 2,
- last_error: Some("err".to_string()),
- };
- let _status_debug = format!("{status:?}");
- let _status_clone = status.clone();
-
- let profile = NostrProfile::default();
- let _profile_debug = format!("{profile:?}");
- let _profile_clone = profile.clone();
-
- let profile_event = NostrProfileEventMetadata {
- id: "id".to_string(),
- author: "author".to_string(),
- published_at: 1,
- profile,
- };
- let _profile_event_debug = format!("{profile_event:?}");
- let _profile_event_clone = profile_event.clone();
-
- let event = NostrEvent {
- id: "event-id".to_string(),
- author: "event-author".to_string(),
- created_at: 2,
- kind: 1,
- content: "content".to_string(),
- };
- let _event_debug = format!("{event:?}");
- let _event_clone = event.clone();
-
- let post = NostrPost {
- content: "post".to_string(),
- };
- let _post_debug = format!("{post:?}");
- let _post_clone = post.clone();
-
- let post_event = NostrPostEventMetadata {
- id: "id".to_string(),
- author: "author".to_string(),
- published_at: 3,
- post,
- };
- let _post_event_debug = format!("{post_event:?}");
- let _post_event_clone = post_event.clone();
-
- assert!(matches!(NostrLight::Red, NostrLight::Red));
- assert!(matches!(NostrLight::Green, NostrLight::Green));
-}
diff --git a/crates/app-wasm/Cargo.toml b/crates/app-wasm/Cargo.toml
@@ -1,19 +0,0 @@
-[package]
-name = "radroots-app-wasm"
-version = "0.1.0-alpha.1"
-edition.workspace = true
-authors = ["Radroots Authors"]
-rust-version.workspace = true
-license.workspace = true
-description = "wasm application runtime bindings for radroots app surfaces"
-repository.workspace = true
-homepage.workspace = true
-documentation = "https://docs.rs/radroots-app-wasm"
-readme.workspace = true
-
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dependencies]
-radroots-app-core = { workspace = true, default-features = false }
-wasm-bindgen = { workspace = true }
diff --git a/crates/app-wasm/README.md b/crates/app-wasm/README.md
@@ -1,14 +0,0 @@
-# radroots-app-wasm
-
-Wasm application runtime bindings for Rad Roots app surfaces.
-
-## Goals
-
-- define stable wasm runtime interfaces for app metadata and startup
-- keep wasm runtime behavior deterministic across supported browser targets
-- support feature-gated bindings backed by `radroots-app-core`
-- provide reusable wasm entry points for higher-level Rad Roots app crates
-
-## License
-
-Licensed under AGPL-3.0. See LICENSE.
diff --git a/crates/app-wasm/src/lib.rs b/crates/app-wasm/src/lib.rs
@@ -1,31 +0,0 @@
-#![forbid(unsafe_code)]
-
-use wasm_bindgen::prelude::wasm_bindgen;
-
-#[wasm_bindgen]
-pub fn app_wasm_build_info_json() -> String {
- let runtime = radroots_app_core::RadrootsRuntime::new()
- .expect("runtime init must succeed with radroots-app-core no-default-features");
- runtime.info_json()
-}
-
-pub fn coverage_branch_probe(input: bool) -> &'static str {
- if input { "app-wasm" } else { "app-wasm" }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{app_wasm_build_info_json, coverage_branch_probe};
-
- #[test]
- fn app_wasm_build_info_json_contains_runtime_keys() {
- let json = app_wasm_build_info_json();
- assert!(json.contains("\"app\""));
- }
-
- #[test]
- fn coverage_branch_probe_hits_both_paths() {
- assert_eq!(coverage_branch_probe(true), "app-wasm");
- assert_eq!(coverage_branch_probe(false), "app-wasm");
- }
-}
diff --git a/crates/xtask/src/coverage.rs b/crates/xtask/src/coverage.rs
@@ -1092,7 +1092,7 @@ mod tests {
fn coverage_profiles_default_when_contract_file_is_missing() {
let root = temp_dir_path("profile_missing");
fs::create_dir_all(&root).expect("create root");
- let profile = read_coverage_profile(&root, "radroots-app-core").expect("read profile");
+ let profile = read_coverage_profile(&root, "radroots-log").expect("read profile");
assert!(!profile.no_default_features);
assert!(profile.features.is_empty());
assert_eq!(profile.test_threads, None);
@@ -1111,14 +1111,14 @@ no_default_features = false
features = ["std"]
test_threads = 2
-[profiles.crates."radroots-app-core"]
+[profiles.crates."radroots-log"]
no_default_features = true
features = ["rt"]
"#,
)
.expect("write profiles");
- let app_profile = read_coverage_profile(&root, "radroots-app-core").expect("app profile");
+ let app_profile = read_coverage_profile(&root, "radroots-log").expect("app profile");
assert!(app_profile.no_default_features);
assert_eq!(app_profile.features, vec!["rt".to_string()]);
assert_eq!(app_profile.test_threads, Some(2));
@@ -1138,12 +1138,12 @@ features = ["rt"]
fs::create_dir_all(&coverage_dir).expect("create coverage dir");
fs::write(
coverage_dir.join("profiles.toml"),
- r#"[profiles.crates."radroots-app-core"]
+ r#"[profiles.crates."radroots-log"]
test_threads = 4
"#,
)
.expect("write profiles");
- let profile = read_coverage_profile(&root, "radroots-app-core")
+ let profile = read_coverage_profile(&root, "radroots-log")
.expect("valid positive thread profile");
assert_eq!(profile.test_threads, Some(4));
fs::remove_dir_all(root).expect("remove root");
@@ -1156,14 +1156,14 @@ test_threads = 4
fs::create_dir_all(&coverage_dir).expect("create coverage dir");
fs::write(
coverage_dir.join("profiles.toml"),
- r#"[profiles.crates."radroots-app-core"]
+ r#"[profiles.crates."radroots-log"]
features = [""]
test_threads = 0
"#,
)
.expect("write profiles");
- let err = read_coverage_profile(&root, "radroots-app-core").expect_err("invalid profile");
+ let err = read_coverage_profile(&root, "radroots-log").expect_err("invalid profile");
assert!(
err.contains("empty feature value"),
"unexpected error: {err}"
@@ -1179,7 +1179,7 @@ test_threads = 0
fs::create_dir_all(&coverage_dir).expect("create coverage dir");
fs::write(coverage_dir.join("profiles.toml"), "[profiles.default\n")
.expect("write invalid profiles");
- let err = read_coverage_profile(&root, "radroots-app-core").expect_err("invalid toml");
+ let err = read_coverage_profile(&root, "radroots-log").expect_err("invalid toml");
assert!(err.contains("failed to parse"));
fs::remove_dir_all(root).expect("remove root");
}
@@ -1191,14 +1191,13 @@ test_threads = 0
fs::create_dir_all(&coverage_dir).expect("create coverage dir");
fs::write(
coverage_dir.join("profiles.toml"),
- r#"[profiles.crates."radroots-app-core"]
+ r#"[profiles.crates."radroots-log"]
test_threads = 0
"#,
)
.expect("write profiles");
- let err =
- read_coverage_profile(&root, "radroots-app-core").expect_err("invalid thread count");
+ let err = read_coverage_profile(&root, "radroots-log").expect_err("invalid thread count");
assert!(err.contains("test_threads > 0"));
fs::remove_dir_all(root).expect("remove root");