use serde_json::Value;
pub fn is_js_space(c: char) -> bool {
matches!(
c,
'\t' | '\n' | '\u{0B}' | '\u{0C}' | '\r' | ' ' | '\u{A0}' | '\u{1680}' | '\u{2000}'
..='\u{200A}'
| '\u{2028}'
| '\u{2029}'
| '\u{202F}'
| '\u{205F}'
| '\u{3000}'
| '\u{FEFF}'
)
}
pub fn sanitize(text: &str) -> String {
text.split(is_js_space)
.filter(|part| !part.is_empty())
.collect::<Vec<_>>()
.join(" ")
}
pub fn trim_start_js(text: &str) -> &str {
text.trim_start_matches(is_js_space)
}
pub fn js_round(x: f64) -> f64 {
let floor = x.floor();
if x - floor >= 0.5 {
floor + 1.0
} else {
floor
}
}
pub fn is_falsy(value: &Value) -> bool {
match value {
Value::Null => true,
Value::Bool(b) => !b,
Value::Number(n) => n.as_f64() == Some(0.0),
Value::String(s) => s.is_empty(),
Value::Array(_) | Value::Object(_) => false,
}
}
pub fn js_string(value: &Value) -> String {
match value {
Value::Null => "null".to_string(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => match (n.as_i64(), n.as_u64(), n.as_f64()) {
(Some(i), _, _) => i.to_string(),
(_, Some(u), _) => u.to_string(),
(_, _, Some(f)) if f.fract() == 0.0 && f.abs() < 1e21 => format!("{f:.0}"),
(_, _, Some(f)) => f.to_string(),
_ => n.to_string(),
},
Value::String(s) => s.clone(),
Value::Array(items) => items
.iter()
.map(|v| {
if v.is_null() {
String::new()
} else {
js_string(v)
}
})
.collect::<Vec<_>>()
.join(","),
Value::Object(_) => "[object Object]".to_string(),
}
}
pub fn js_string_or_empty(value: Option<&Value>) -> String {
match value {
Some(v) if !is_falsy(v) => js_string(v),
_ => String::new(),
}
}
pub fn get<'a>(obj: &'a Value, key: &str) -> Option<&'a Value> {
obj.as_object()?.get(key)
}
pub fn get_path<'a>(obj: &'a Value, path: &[&str]) -> Option<&'a Value> {
path.iter().try_fold(obj, |current, key| get(current, key))
}
pub fn nullish(value: Option<&Value>) -> Option<&Value> {
value.filter(|v| !v.is_null())
}