app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 9cc21a40c9ea2f4b91b033e0f1009ff1d9aa8964
parent 24655a2c2535a159513a2f1ca7df6a3ae6ab6cdb
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 07:53:13 +0000

app-lib: add symbols and sleep helpers

- add symbol constants for app UI

- add fmt_cl and value_constrain helpers

- add async sleep helper for wasm and native

- add unit tests for symbols and sleep

Diffstat:
MCargo.lock | 1+
Mcrates/app-lib/Cargo.toml | 3+++
Mcrates/app-lib/src/lib.rs | 6++++++
Acrates/app-lib/src/sleep.rs | 23+++++++++++++++++++++++
Acrates/app-lib/src/symbols.rs | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 87 insertions(+), 0 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1576,6 +1576,7 @@ name = "radroots-app-lib" version = "0.1.0" dependencies = [ "futures", + "gloo-timers", "js-sys", "once_cell", "regex", diff --git a/crates/app-lib/Cargo.toml b/crates/app-lib/Cargo.toml @@ -21,3 +21,6 @@ web-sys = { workspace = true } [dev-dependencies] futures = { workspace = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +gloo-timers = { workspace = true } diff --git a/crates/app-lib/src/lib.rs b/crates/app-lib/src/lib.rs @@ -3,7 +3,13 @@ pub mod browser; pub mod fetch; pub mod geo; +pub mod sleep; +pub mod symbols; pub use browser::{browser_platform, BrowserPlatformInfo}; pub use fetch::{fetch_json, FetchJsonError, FetchJsonErrorKind, FetchJsonResult}; pub use geo::{geop_init, geop_is_valid, AppGeolocationPoint}; +pub use sleep::sleep; +pub use symbols::{ + fmt_cl, value_constrain, SYMBOL_BULLET, SYMBOL_DASH, SYMBOL_DOWN, SYMBOL_PERCENT, SYMBOL_UP, +}; diff --git a/crates/app-lib/src/sleep.rs b/crates/app-lib/src/sleep.rs @@ -0,0 +1,23 @@ +#![forbid(unsafe_code)] + +pub async fn sleep(ms: u64) { + #[cfg(target_arch = "wasm32")] + { + let delay = ms.min(u32::MAX as u64) as u32; + gloo_timers::future::TimeoutFuture::new(delay).await; + } + #[cfg(not(target_arch = "wasm32"))] + { + std::thread::sleep(std::time::Duration::from_millis(ms)); + } +} + +#[cfg(test)] +mod tests { + use super::sleep; + + #[test] + fn sleep_returns() { + futures::executor::block_on(sleep(0)); + } +} diff --git a/crates/app-lib/src/symbols.rs b/crates/app-lib/src/symbols.rs @@ -0,0 +1,54 @@ +#![forbid(unsafe_code)] + +use regex::Regex; + +pub const SYMBOL_BULLET: &str = "\u{2022}"; +pub const SYMBOL_DASH: &str = "\u{2014}"; +pub const SYMBOL_UP: &str = "\u{2191}"; +pub const SYMBOL_DOWN: &str = "\u{2193}"; +pub const SYMBOL_PERCENT: &str = "%"; + +pub fn fmt_cl(classes: Option<&str>) -> String { + classes.unwrap_or("").to_string() +} + +pub fn value_constrain(regex_charset: &Regex, value: &str) -> String { + let mut output = String::with_capacity(value.len()); + let mut buf = [0u8; 4]; + for ch in value.chars() { + let encoded = ch.encode_utf8(&mut buf); + if regex_charset.is_match(encoded) { + output.push(ch); + } + } + output +} + +#[cfg(test)] +mod tests { + use super::{ + fmt_cl, value_constrain, SYMBOL_BULLET, SYMBOL_DASH, SYMBOL_DOWN, SYMBOL_PERCENT, + SYMBOL_UP, + }; + + #[test] + fn symbols_match_expected_values() { + assert_eq!(SYMBOL_BULLET, "\u{2022}"); + assert_eq!(SYMBOL_DASH, "\u{2014}"); + assert_eq!(SYMBOL_UP, "\u{2191}"); + assert_eq!(SYMBOL_DOWN, "\u{2193}"); + assert_eq!(SYMBOL_PERCENT, "%"); + } + + #[test] + fn fmt_cl_handles_none() { + assert_eq!(fmt_cl(None), ""); + assert_eq!(fmt_cl(Some("a b")), "a b"); + } + + #[test] + fn value_constrain_filters_chars() { + let regex = regex::Regex::new("[0-9]").expect("regex"); + assert_eq!(value_constrain(&regex, "a1b2c"), "12"); + } +}