app

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

commit 3331a8c2ca27a15d86dbd88a5c1dd93c7c889d22
parent 9cc21a40c9ea2f4b91b033e0f1009ff1d9aa8964
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 07:53:57 +0000

app-lib: add path helpers

- add trim_slashes helper

- add normalize_path and sanitize_path helpers

- add unit tests for path helpers

- re-export path helpers from app lib

Diffstat:
Mcrates/app-lib/src/lib.rs | 2++
Acrates/app-lib/src/path.rs | 50++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 0 deletions(-)

diff --git a/crates/app-lib/src/lib.rs b/crates/app-lib/src/lib.rs @@ -3,12 +3,14 @@ pub mod browser; pub mod fetch; pub mod geo; +pub mod path; 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 path::{normalize_path, sanitize_path, trim_slashes}; 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/path.rs b/crates/app-lib/src/path.rs @@ -0,0 +1,50 @@ +#![forbid(unsafe_code)] + +pub fn trim_slashes(path: &str) -> String { + path.trim_matches('/').to_string() +} + +pub fn normalize_path(path: &str) -> String { + let mut output = String::with_capacity(path.len()); + for ch in path.chars() { + let mapped = match ch { + '-' => '_', + '/' => '-', + _ => ch, + }; + if mapped == '-' && output.ends_with('-') { + continue; + } + output.push(mapped); + } + output +} + +pub fn sanitize_path(raw: &str) -> String { + raw.chars() + .filter(|ch| ch.is_ascii_alphanumeric() || *ch == '_' || *ch == '-') + .collect() +} + +#[cfg(test)] +mod tests { + use super::{normalize_path, sanitize_path, trim_slashes}; + + #[test] + fn trim_slashes_removes_edge_slashes() { + assert_eq!(trim_slashes("/a/b/"), "a/b"); + assert_eq!(trim_slashes("///a///"), "a"); + } + + #[test] + fn normalize_path_replaces_chars() { + assert_eq!(normalize_path("a-b/c"), "a_b-c"); + assert_eq!(normalize_path("a//b"), "a-b"); + } + + #[test] + fn sanitize_path_strips_invalid_chars() { + assert_eq!(sanitize_path("ab/c$%"), "abc"); + assert_eq!(sanitize_path("a_b-1"), "a_b-1"); + } +}