use lino_objects_codec::LinoValue;
pub fn object<I, K>(members: I) -> LinoValue
where
I: IntoIterator<Item = (K, LinoValue)>,
K: Into<String>,
{
LinoValue::Object(
members
.into_iter()
.map(|(key, value)| (key.into(), value))
.collect(),
)
}
pub fn array<I: IntoIterator<Item = LinoValue>>(items: I) -> LinoValue {
LinoValue::Array(items.into_iter().collect())
}
pub fn string<S: Into<String>>(text: S) -> LinoValue {
LinoValue::String(text.into())
}
pub fn int(number: i64) -> LinoValue {
LinoValue::Int(number)
}
pub fn boolean(flag: bool) -> LinoValue {
LinoValue::Bool(flag)
}
pub fn as_object(value: &LinoValue) -> Option<&Vec<(String, LinoValue)>> {
match value {
LinoValue::Object(members) => Some(members),
_ => None,
}
}
pub fn as_array(value: &LinoValue) -> Option<&Vec<LinoValue>> {
match value {
LinoValue::Array(items) => Some(items),
_ => None,
}
}
pub fn as_str(value: &LinoValue) -> Option<&str> {
match value {
LinoValue::String(text) => Some(text),
_ => None,
}
}
pub fn get<'a>(value: &'a LinoValue, key: &str) -> Option<&'a LinoValue> {
as_object(value)?
.iter()
.find(|(name, _)| name == key)
.map(|(_, member)| member)
}
pub fn has(value: &LinoValue, key: &str) -> bool {
get(value, key).is_some()
}
pub fn set<S: Into<String>>(value: &mut LinoValue, key: S, member: LinoValue) {
let key = key.into();
if let LinoValue::Object(members) = value {
if let Some(existing) = members.iter_mut().find(|(name, _)| *name == key) {
existing.1 = member;
} else {
members.push((key, member));
}
}
}
pub fn with<S: Into<String>>(value: &LinoValue, key: S, member: LinoValue) -> LinoValue {
let mut copy = value.clone();
if !matches!(copy, LinoValue::Object(_)) {
copy = LinoValue::Object(Vec::new());
}
set(&mut copy, key, member);
copy
}
pub fn merge(base: &LinoValue, patch: &LinoValue) -> LinoValue {
let mut merged = base.clone();
if !matches!(merged, LinoValue::Object(_)) {
merged = LinoValue::Object(Vec::new());
}
if let Some(members) = as_object(patch) {
for (key, member) in members {
set(&mut merged, key.clone(), member.clone());
}
}
merged
}
pub fn to_query_text(value: &LinoValue) -> String {
match value {
LinoValue::Null => "null".to_string(),
LinoValue::Bool(flag) => flag.to_string(),
LinoValue::Int(number) => number.to_string(),
LinoValue::Float(number) => format_float(*number),
LinoValue::String(text) => text.clone(),
other => crate::codec::encode_single_line(other),
}
}
pub fn format_float(number: f64) -> String {
if number.is_finite() && number.fract() == 0.0 && number.abs() < 1e15 {
format!("{}", number as i64)
} else {
format!("{number}")
}
}
pub fn compare(left: &LinoValue, right: &LinoValue) -> std::cmp::Ordering {
use std::cmp::Ordering;
match (left, right) {
(LinoValue::Null, LinoValue::Null) => Ordering::Equal,
(LinoValue::Null, _) => Ordering::Less,
(_, LinoValue::Null) => Ordering::Greater,
(LinoValue::Int(a), LinoValue::Int(b)) => a.cmp(b),
(LinoValue::Int(a), LinoValue::Float(b)) => {
(*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal)
}
(LinoValue::Float(a), LinoValue::Int(b)) => {
a.partial_cmp(&(*b as f64)).unwrap_or(Ordering::Equal)
}
(LinoValue::Float(a), LinoValue::Float(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
(a, b) => to_query_text(a).cmp(&to_query_text(b)),
}
}