use super::*;
pub fn poke(root: &mut dyn IVisit, path: &str, args: Option<&str>, writer: &mut dyn IWrite) -> bool {
let mut result = false;
if path.len() > 0 {
if !find(root, path, |node| {
match node.as_node() {
Node::Prop(prop) => {
if let Some(val) = args {
let mut err = String::new();
if prop.set(val, &mut err) {
let value = prop.get_value().to_string();
let _ = writeln!(writer, "{path} is {value:?}");
result = true;
}
else {
let _ = writeln!(writer, "error: {path} {val:?}: {err}");
}
}
else {
let value = prop.get_value().to_string();
let _ = writeln!(writer, "{path} is {value:?}");
result = true;
}
},
Node::List(list) => {
_print_nodes(list.as_ivisit(), Some(path), writer);
result = true;
},
Node::Action(act) => {
act.invoke(args.unwrap_or(""), writer);
result = true;
},
}
}) {
let _ = writeln!(writer, "unknown: {path}");
}
}
else {
_print_nodes(root, None, writer);
result = true;
}
result
}
fn _print_node(node: &mut dyn INode, path: Option<&str>, writer: &mut dyn IWrite) -> fmt::Result {
if let Some(path) = path {
writer.write_str(path)?;
writer.write_str(".")?;
}
match node.as_node() {
Node::Prop(prop) => {
let value = prop.get_value().to_string();
let name = prop.name();
write!(writer, "{name} is {value:?}")?;
},
Node::List(list) => {
let name = list.name();
writer.write_str(name)?;
writer.write_str("...")?;
},
Node::Action(act) => {
let name = act.name();
writer.write_str(name)?;
},
}
writer.write_str("\n")?;
Ok(())
}
fn _print_nodes(root: &mut dyn IVisit, path: Option<&str>, writer: &mut dyn IWrite) {
root.visit(&mut move |node| {
let _ = _print_node(node, path, writer);
});
}
#[inline]
pub fn set(root: &mut dyn IVisit, path: &str, val: &str, writer: &mut dyn IWrite) -> bool {
let mut result = false;
if !find(root, path, |node| {
match node.as_node() {
Node::Prop(prop) => {
let mut err = String::new();
if prop.set(val, &mut err) {
result = true;
}
else {
let _ = writeln!(writer, "error: {path} {val:?}: {err}");
}
},
Node::List(_) => {},
Node::Action(act) => {
act.invoke(val, writer);
},
}
}) {
let _ = writeln!(writer, "unknown: {path}");
}
result
}
#[inline]
pub fn get(root: &mut dyn IVisit, path: &str) -> Option<String> {
let mut result = None;
find(root, path, |node| {
if let Node::Prop(prop) = node.as_node() {
result = Some(prop.get_value().to_string());
}
});
result
}
#[inline]
pub fn set_value(root: &mut dyn IVisit, path: &str, val: &dyn IValue, writer: &mut dyn IWrite) -> bool {
let mut result = false;
if !find(root, path, |node| {
match node.as_node() {
Node::Prop(prop) => {
let mut err = String::new();
if prop.set_value(val, &mut err) {
result = true;
}
else {
let _ = writeln!(writer, "error: {path} {val:?}: {err}");
}
},
Node::List(_) => {},
Node::Action(act) => {
act.invoke(&val.to_string(), writer);
},
}
}) {
let _ = writeln!(writer, "unknown: {path}");
}
result
}
#[inline]
pub fn get_value<T: Clone + 'static>(root: &mut dyn IVisit, path: &str) -> Option<T> {
let mut value = None;
find(root, path, |node| {
if let Node::Prop(prop) = node.as_node() {
if let Some(any) = prop.get_value().downcast_ref::<T>() {
value = Some(any.clone());
}
}
});
value
}
#[inline]
pub fn reset(root: &mut dyn IVisit, path: &str) -> bool {
find(root, path, |node| {
match node.as_node() {
Node::Prop(prop) => prop.reset(),
Node::List(list) => reset_all(list.as_ivisit()),
Node::Action(_) => (),
}
})
}
#[inline]
pub fn reset_all(root: &mut dyn IVisit) {
root.visit(&mut |node| {
match node.as_node() {
Node::Prop(prop) => prop.reset(),
Node::List(list) => reset_all(list.as_ivisit()),
Node::Action(_) => (),
}
});
}
#[inline]
pub fn print(root: &mut dyn IVisit, path: &str, writer: &mut dyn IWrite) {
if path.len() > 0 {
if !find(root, path, |node| {
let _ = _print_node(node, Some(path), writer);
}) {
let _ = writeln!(writer, "unknown: {path}");
}
}
else {
_print_nodes(root, None, writer);
}
}
#[inline]
fn split_at<'a>(path: &'a str, index: usize) -> Option<(&str, &u8, &str)> {
let at = path.as_bytes().get(index)?;
let prefix = path.get(..index)?;
let suffix = path.get(index + 1..)?;
Some((prefix, at, suffix))
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum ComparePath<'a> {
False,
True,
Part(&'a str),
}
impl<'a> ComparePath<'a> {
#[inline]
fn cmp(path: &'a str, name: &str) -> ComparePath<'a> {
match split_at(path, name.len()) {
Some((prefix, &b'.', suffix)) => {
if prefix == name {
ComparePath::Part(suffix)
}
else {
ComparePath::False
}
},
Some(_) => ComparePath::False,
None => if path == name { ComparePath::True } else { ComparePath::False },
}
}
}
#[test]
fn test_compare_id() {
assert_eq!(ComparePath::cmp("foo", "foo"), ComparePath::True);
assert_eq!(ComparePath::cmp("foo.bar", "foo"), ComparePath::Part("bar"));
assert_eq!(ComparePath::cmp("foo.bar", "foo.bar"), ComparePath::True);
assert_eq!(ComparePath::cmp("foo.bar.baz", "foo.bar"), ComparePath::Part("baz"));
assert_eq!(ComparePath::cmp("fooz", "foo"), ComparePath::False);
assert_eq!(ComparePath::cmp("foo.bar.baz", "bar.baz"), ComparePath::False);
assert_eq!(ComparePath::cmp("foo", "foo.bar"), ComparePath::False);
assert_eq!(ComparePath::cmp("foo.", "foo"), ComparePath::Part(""));
assert_eq!(ComparePath::cmp("foo.", "foo."), ComparePath::True);
assert_eq!(ComparePath::cmp("foo", "foo."), ComparePath::False);
assert_eq!(ComparePath::cmp("", ""), ComparePath::True);
assert_eq!(ComparePath::cmp(".", ""), ComparePath::Part(""));
assert_eq!(ComparePath::cmp(".", "."), ComparePath::True);
assert_eq!(ComparePath::cmp("", "."), ComparePath::False);
assert_eq!(ComparePath::cmp(".foo", "foo"), ComparePath::False);
assert_eq!(ComparePath::cmp(".foo", ".foo"), ComparePath::True);
assert_eq!(ComparePath::cmp("foo", ".foo"), ComparePath::False);
}
#[inline]
pub fn find<F: FnMut(&mut dyn INode)>(root: &mut dyn IVisit, path: &str, mut f: F) -> bool {
find_rec(root, path, &mut f)
}
#[inline]
fn find_rec(list: &mut dyn IVisit, path: &str, f: &mut dyn FnMut(&mut dyn INode)) -> bool {
let mut found = false;
list.visit(&mut |node| {
match ComparePath::cmp(path, node.name()) {
ComparePath::True => {
f(node);
found = true;
},
ComparePath::Part(tail) => {
if let Node::List(list) = node.as_node() {
found |= find_rec(list.as_ivisit(), tail, f);
}
},
ComparePath::False => {},
};
});
found
}
#[inline]
pub fn walk<F: FnMut(&str, &mut dyn INode)>(root: &mut dyn IVisit, mut f: F) {
let mut path = String::new();
walk_rec(root, &mut path, &mut f);
}
#[inline]
fn walk_rec(list: &mut dyn IVisit, path: &mut String, f: &mut dyn FnMut(&str, &mut dyn INode)) {
list.visit(&mut |node| {
let len = path.len();
if len > 0 {
path.push('.');
}
path.push_str(node.name());
f(&path, node);
if let Node::List(list) = node.as_node() {
walk_rec(list.as_ivisit(), path, f);
}
path.truncate(len);
});
}
#[inline]
pub fn invoke(root: &mut dyn IVisit, path: &str, args: &str, writer: &mut dyn IWrite) -> bool {
let mut found = false;
find(root, path, |node| {
if let Node::Action(act) = node.as_node() {
found = true;
act.invoke(args, writer);
}
});
found
}