app

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

commit c8ac67bd8351bd41e50c6a32b70d9e4859d8d15c
parent 3331a8c2ca27a15d86dbd88a5c1dd93c7c889d22
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 07:55:04 +0000

app-lib: add storage key helpers

- add fmt_id_from_path helper

- add fmt_id and build_storage_key helpers

- normalize storage prefixes for base paths

- add unit tests for storage helpers

Diffstat:
Mcrates/app-lib/src/lib.rs | 2++
Acrates/app-lib/src/storage.rs | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 0 deletions(-)

diff --git a/crates/app-lib/src/lib.rs b/crates/app-lib/src/lib.rs @@ -5,6 +5,7 @@ pub mod fetch; pub mod geo; pub mod path; pub mod sleep; +pub mod storage; pub mod symbols; pub use browser::{browser_platform, BrowserPlatformInfo}; @@ -12,6 +13,7 @@ pub use fetch::{fetch_json, FetchJsonError, FetchJsonErrorKind, FetchJsonResult} pub use geo::{geop_init, geop_is_valid, AppGeolocationPoint}; pub use path::{normalize_path, sanitize_path, trim_slashes}; pub use sleep::sleep; +pub use storage::{build_storage_key, build_storage_key_with_prefix, fmt_id, fmt_id_from_path}; pub use symbols::{ fmt_cl, value_constrain, SYMBOL_BULLET, SYMBOL_DASH, SYMBOL_DOWN, SYMBOL_PERCENT, SYMBOL_UP, }; diff --git a/crates/app-lib/src/storage.rs b/crates/app-lib/src/storage.rs @@ -0,0 +1,65 @@ +#![forbid(unsafe_code)] + +use crate::path::{normalize_path, sanitize_path, trim_slashes}; + +pub fn fmt_id_from_path(pathname: &str, raw_id: Option<&str>) -> String { + let trimmed = trim_slashes(pathname); + let prefix = normalize_path(&trimmed); + let suffix = raw_id + .map(|id| format!("-{}", sanitize_path(id))) + .unwrap_or_default(); + format!("*{prefix}{suffix}") +} + +pub fn fmt_id(raw_id: Option<&str>) -> Option<String> { + #[cfg(target_arch = "wasm32")] + { + let window = web_sys::window()?; + let location = window.location(); + let pathname = location.pathname().ok()?; + Some(fmt_id_from_path(&pathname, raw_id)) + } + #[cfg(not(target_arch = "wasm32"))] + { + let _ = raw_id; + None + } +} + +pub fn build_storage_key_with_prefix(prefix: &str, raw_id: &str, base_prefix: &str) -> String { + let mut output = format!("{prefix}-{}", sanitize_path(raw_id)); + let base_prefix = normalize_path(&trim_slashes(base_prefix)); + if base_prefix.is_empty() { + return output; + } + let base = format!("*{base_prefix}"); + let base_with_dash = format!("{base}-"); + if output.starts_with(&base_with_dash) { + output.replace_range(..base_with_dash.len(), "*"); + } else if output.starts_with(&base) { + output.replace_range(..base.len(), "*"); + } + output +} + +pub fn build_storage_key(raw_id: &str, base_prefix: &str) -> Option<String> { + let prefix = fmt_id(None)?; + Some(build_storage_key_with_prefix(&prefix, raw_id, base_prefix)) +} + +#[cfg(test)] +mod tests { + use super::{build_storage_key_with_prefix, fmt_id_from_path}; + + #[test] + fn fmt_id_from_path_formats_prefix() { + assert_eq!(fmt_id_from_path("/app/home", None), "*app-home"); + assert_eq!(fmt_id_from_path("/app/home", Some("id")), "*app-home-id"); + } + + #[test] + fn build_storage_key_with_prefix_replaces_base_prefix() { + let key = build_storage_key_with_prefix("*app-home", "raw", "/app"); + assert_eq!(key, "*home-raw"); + } +}