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:
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(®ex, "a1b2c"), "12");
+ }
+}