app

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

commit 9c9a3c80e25da686f32079ed6bd05177ad8b14fd
parent a0834cedcdba2804b546c803fe9c6b90b095556c
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 07:07:43 +0000

app-utils: add object helpers

- add obj_en mapping helper
- add truthy and result helpers
- export object helpers from utils crate
- add unit tests for object helpers

Diffstat:
MCargo.lock | 1+
Mcrates/utils/Cargo.toml | 1+
Mcrates/utils/src/lib.rs | 2++
Acrates/utils/src/object/mod.rs | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 84 insertions(+), 0 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1568,6 +1568,7 @@ dependencies = [ "getrandom 0.2.17", "js-sys", "radroots-types", + "serde_json", "web-sys", ] diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml @@ -14,3 +14,4 @@ radroots-types = { workspace = true } getrandom = { workspace = true } js-sys = { workspace = true } web-sys = { workspace = true } +serde_json = { workspace = true } diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs @@ -4,6 +4,7 @@ pub mod error; pub mod errors; pub mod binary; pub mod numbers; +pub mod object; pub mod path; pub mod text; pub mod time; @@ -12,6 +13,7 @@ pub mod types; pub use binary::{as_array_buffer, RadrootsAppArrayBuffer}; pub use errors::{err_msg, handle_err, throw_err, ERR_PREFIX_APP, ERR_PREFIX_UTILS}; pub use numbers::{num_interval_range, num_str, parse_float, parse_int}; +pub use object::{obj_en, obj_result, obj_results_str, obj_truthy_fields}; pub use path::{ parse_route_path, resolve_route_path, diff --git a/crates/utils/src/object/mod.rs b/crates/utils/src/object/mod.rs @@ -0,0 +1,80 @@ +#![forbid(unsafe_code)] + +pub fn obj_en<K, V, F, I>(object: I, parse_function: F) -> Vec<(K, V)> +where + I: IntoIterator<Item = (String, V)>, + F: Fn(&str) -> K, +{ + object + .into_iter() + .map(|(key, value)| (parse_function(&key), value)) + .collect() +} + +pub fn obj_truthy_fields<I, V>(values: I) -> bool +where + I: IntoIterator<Item = V>, + V: AsRef<str>, +{ + values.into_iter().all(|value| !value.as_ref().is_empty()) +} + +pub fn obj_result(value: &serde_json::Value) -> Option<String> { + let obj = value.as_object()?; + let result = obj.get("result")?.as_str()?; + Some(result.to_string()) +} + +pub fn obj_results_str(value: &serde_json::Value) -> Option<Vec<String>> { + let obj = value.as_object()?; + let results = obj.get("results")?.as_array()?; + Some( + results + .iter() + .map(|entry| { + entry + .as_str() + .map(ToString::to_string) + .unwrap_or_else(|| entry.to_string()) + }) + .collect(), + ) +} + +#[cfg(test)] +mod tests { + use super::{obj_en, obj_result, obj_results_str, obj_truthy_fields}; + use serde_json::json; + use std::collections::BTreeMap; + + #[test] + fn obj_en_maps_entries() { + let mut map = BTreeMap::new(); + map.insert("one".to_string(), 1); + let entries = obj_en(map.into_iter(), |key| format!("key:{key}")); + assert_eq!(entries, vec![("key:one".to_string(), 1)]); + } + + #[test] + fn obj_truthy_fields_checks_values() { + let values = vec!["one", "two"]; + assert!(obj_truthy_fields(values)); + let values = vec!["one", ""]; + assert!(!obj_truthy_fields(values)); + } + + #[test] + fn obj_result_reads_result_field() { + let value = json!({ "result": "ok" }); + assert_eq!(obj_result(&value), Some("ok".to_string())); + } + + #[test] + fn obj_results_str_reads_results_list() { + let value = json!({ "results": ["a", "b"] }); + assert_eq!( + obj_results_str(&value), + Some(vec!["a".to_string(), "b".to_string()]) + ); + } +}