use super::node::{Map, UniNode};
const PATH_DELIMITER: char = '.';
macro_rules! impl_find_function {
($name:ident, $conv:ident, $type:ty) => {
pub fn $name(&self, path: &str) -> Option<$type> {
self.find(path).and_then(|v| v.$conv().ok())
}
};
}
impl UniNode {
impl_find_function!(find_bool, as_bool, bool);
impl_find_function!(find_int, as_int, i64);
impl_find_function!(find_uint, as_uint, u64);
impl_find_function!(find_float, as_float, f64);
impl_find_function!(find_str, as_str, &str);
impl_find_function!(find_bytes, as_bytes, &[u8]);
pub fn find_array(&self, path: &str) -> Option<&Vec<UniNode>> {
self.find(path).and_then(|v| v.as_array().ok())
}
pub fn find_object(&self, path: &str) -> Option<&Map<String, UniNode>> {
self.find(path).and_then(|v| v.as_object().ok())
}
pub fn find(&self, path: &str) -> Option<&Self> {
let names = path.split(PATH_DELIMITER);
fn find_path<'a, 'b, I>(
root: &'a UniNode, mut names: I,
) -> Option<&'a UniNode>
where
I: Iterator<Item = &'b str>,
{
if let Some(name) = names.next() {
if !name.is_empty() {
if let UniNode::Object(tbl) = root {
if let Some(chd) = tbl.get(name) {
return find_path(chd, names);
}
}
return None;
}
};
Some(root)
}
find_path(self, names)
}
pub fn find_mut(&mut self, path: &str) -> Option<&mut Self> {
let names = path.split(PATH_DELIMITER);
fn find_path<'a, 'b, I>(
root: &'a mut UniNode, mut names: I,
) -> Option<&'a mut UniNode>
where
I: Iterator<Item = &'b str>,
{
if let Some(name) = names.next() {
if !name.is_empty() {
if let UniNode::Object(tbl) = root {
if let Some(chd) = tbl.get_mut(name) {
return find_path(chd, names);
}
}
return None;
}
};
Some(root)
}
find_path(self, names)
}
pub fn merge(&mut self, other: UniNode) {
fn merge_node(dst: &mut UniNode, src: UniNode) {
match dst {
UniNode::Null => *dst = src,
UniNode::Array(a) => {
if src.is_array() {
if let UniNode::Array(src) = src {
a.extend(src);
}
} else {
a.push(src);
}
},
UniNode::Object(o) if src.is_object() => {
if let UniNode::Object(src) = src {
#[allow(clippy::map_entry)]
for (key, node) in src {
if o.contains_key(&key) {
merge_node(o.get_mut(&key).unwrap(), node);
} else {
o.insert(key, node);
}
}
}
},
_ => {
if !src.is_null() {
let v = match src {
a @ UniNode::Array(_) => a,
a => UniNode::Array(vec![a]),
};
let v = std::mem::replace(dst, v);
dst.as_array_mut().unwrap().insert(0, v);
}
},
}
}
merge_node(self, other)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::unode;
#[test]
fn find() {
let mut val = unode!(
"server" => unode!("host" => "localhost", "port" => 999),
"two" => 2);
assert!(val.find("tw").is_none());
assert!(val.find("server.url").is_none());
assert!(val.find("server.").is_some());
assert!(val.find("server.").unwrap().is_object());
assert!(val.find("server.host").is_some());
assert!(val.find("server.host").unwrap().is_string());
assert_eq!(
val.find("server.host").unwrap().as_str().unwrap(),
"localhost"
);
assert!(val.find("server.timeout").is_none());
let n = val.find_mut("server").unwrap().as_object_mut().unwrap();
n.insert("timeout".to_string(), UniNode::Integer(10));
assert!(val.find("server.timeout").is_some());
assert_eq!(
val.find("server.timeout")
.and_then(|v| v.as_int().ok())
.unwrap(),
10
);
}
#[test]
fn get() {
let val = unode!(
"server" => unode!(
"host" => "localhost",
"port" => 999,
"keys" => unode!(1, 2, 4, 7),
),
"bool" => true,
"int" => 33,
"uint" => 12u32,
"float" => 1.2,
"str" => "hello",
"bytes" => vec![1u8,2u8,3u8],
"opt" => Some("opt"),
"none" => Option::<u8>::None,
"arr" => unode!["one", 2, 3.3],
);
assert!(val.find_bool("bool").unwrap());
assert_eq!(val.find_int("int").unwrap(), 33);
assert_eq!(val.find_uint("uint").unwrap(), 12);
assert!((val.find_float("float").unwrap() - 1.2).abs() < f64::EPSILON);
assert_eq!(val.find_str("str").unwrap(), "hello");
assert_eq!(val.find_bytes("bytes").unwrap(), vec![1, 2, 3]);
assert_eq!(val.find_str("opt").unwrap(), "opt");
assert!(val.find("none").unwrap().is_null());
let keys = val
.find_array("server.keys")
.unwrap()
.iter()
.map(|v| v.as_int().unwrap())
.collect::<Vec<_>>();
assert_eq!(keys, vec![1, 2, 4, 7]);
}
#[test]
fn merge() {
fn test_merge(mut root: UniNode, other: UniNode, res: UniNode) {
root.merge(other);
assert_eq!(root, res);
}
test_merge(unode!(), unode!(), unode!());
test_merge(unode!(), unode![1, 2], unode![1, 2]);
test_merge(unode!(1), unode!(2), unode![1, 2]);
test_merge(unode!(1), unode![2, 3], unode![1, 2, 3]);
test_merge(unode!(1), unode! {"1" => 1}, unode![1, unode! {"1" => 1}]);
test_merge(unode![1, 2], unode![3, 4], unode![1, 2, 3, 4]);
test_merge(unode![1, 2], unode!(3), unode![1, 2, 3]);
test_merge(
unode![1, 2],
unode! {"3" => 3},
unode![1, 2, unode! {"3" => 3}],
);
test_merge(unode! {"1" => 1}, unode!(2), unode![unode! {"1" => 1}, 2]);
test_merge(
unode! {"1" => 1},
unode![2, 3],
unode![unode! {"1" => 1}, 2, 3],
);
test_merge(
unode! {"1" => 1},
unode! {"2" => 2},
unode! {"1" => 1, "2" => 2},
);
test_merge(
unode! {"zero" => unode!(), "one" => 1, "two" => unode!{"right" => 2}},
unode! {"zero" => 0, "one" => 1.1, "two" => unode!{"left" => 1}, "three" => 3},
unode! {
"zero" => 0,
"one" => unode![1, 1.1],
"two" => unode!{"right" => 2, "left" => 1},
"three" => 3
},
);
}
}