From bb4738da611b46504176ae4dad3670be4e3b034e Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sun, 27 Mar 2022 16:45:40 -0600 Subject: add API to resolve refs --- src/lib.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index c724687..6b61dea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,12 @@ pub trait BuildFromRef { fn ref_to(query: impl Into) -> Self; } +fn get_id_from_ref(r#ref: &str) -> Option<&str> { + r#ref + .strip_prefix(r#"[id=""#) + .and_then(|r#ref| r#ref.strip_suffix(r#""]"#)) +} + #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Schema { @@ -18,6 +24,48 @@ pub struct Schema { pub document: Document, } +impl Schema { + /// Panics if ref is not of the form `[id="foo"]`. + pub fn resolve_node_ref(&self, r#ref: &str) -> Option<&Node> { + let id = get_id_from_ref(r#ref).expect("invalid ref"); + self.document + .nodes + .iter() + .filter_map(|node| node.find_node_by_id(id)) + .next() + } + + /// Panics if ref is not of the form `[id="foo"]`. + pub fn resolve_prop_ref(&self, r#ref: &str) -> Option<&Prop> { + let id = get_id_from_ref(r#ref).expect("invalid ref"); + self.document + .nodes + .iter() + .filter_map(|node| node.find_prop_by_id(id)) + .next() + } + + /// Panics if ref is not of the form `[id="foo"]`. + pub fn resolve_value_ref(&self, r#ref: &str) -> Option<&Value> { + let id = get_id_from_ref(r#ref).expect("invalid ref"); + self.document + .nodes + .iter() + .filter_map(|node| node.find_value_by_id(id)) + .next() + } + + /// Panics if ref is not of the form `[id="foo"]`. + pub fn resolve_children_ref(&self, r#ref: &str) -> Option<&Children> { + let id = get_id_from_ref(r#ref).expect("invalid ref"); + self.document + .nodes + .iter() + .filter_map(|node| node.find_children_by_id(id)) + .next() + } +} + #[cfg(feature = "parse-knuffel")] impl Schema { pub fn parse( @@ -131,6 +179,50 @@ pub struct Node { pub children: Vec, } +impl Node { + fn find_node_by_id(&self, id: &str) -> Option<&Node> { + if self.id.as_deref() == Some(id) { + Some(self) + } else { + self.children + .iter() + .filter_map(|children| children.find_node_by_id(id)) + .next() + } + } + + fn find_prop_by_id(&self, id: &str) -> Option<&Prop> { + self.props + .iter() + .filter_map(|prop| prop.find_prop_by_id(id)) + .chain( + self.children + .iter() + .filter_map(|children| children.find_prop_by_id(id)), + ) + .next() + } + + fn find_value_by_id(&self, id: &str) -> Option<&Value> { + self.values + .iter() + .filter_map(|value| value.find_value_by_id(id)) + .chain( + self.children + .iter() + .filter_map(|children| children.find_value_by_id(id)), + ) + .next() + } + + fn find_children_by_id(&self, id: &str) -> Option<&Children> { + self.children + .iter() + .filter_map(|children| children.find_children_by_id(id)) + .next() + } +} + impl BuildFromRef for Node { fn ref_to(query: impl Into) -> Self { Self { @@ -157,6 +249,16 @@ pub struct Prop { pub validations: Vec, } +impl Prop { + fn find_prop_by_id(&self, id: &str) -> Option<&Prop> { + if self.id.as_deref() == Some(id) { + Some(self) + } else { + None + } + } +} + impl BuildFromRef for Prop { fn ref_to(query: impl Into) -> Self { Self { @@ -183,6 +285,16 @@ pub struct Value { pub validations: Vec, } +impl Value { + fn find_value_by_id(&self, id: &str) -> Option<&Value> { + if self.id.as_deref() == Some(id) { + Some(self) + } else { + None + } + } +} + impl BuildFromRef for Value { fn ref_to(query: impl Into) -> Self { Self { @@ -205,6 +317,40 @@ pub struct Children { pub nodes: Vec, } +impl Children { + fn find_node_by_id(&self, id: &str) -> Option<&Node> { + self.nodes + .iter() + .filter_map(|node| node.find_node_by_id(id)) + .next() + } + + fn find_prop_by_id(&self, id: &str) -> Option<&Prop> { + self.nodes + .iter() + .filter_map(|node| node.find_prop_by_id(id)) + .next() + } + + fn find_value_by_id(&self, id: &str) -> Option<&Value> { + self.nodes + .iter() + .filter_map(|node| node.find_value_by_id(id)) + .next() + } + + fn find_children_by_id(&self, id: &str) -> Option<&Children> { + if self.id.as_deref() == Some(id) { + Some(self) + } else { + self.nodes + .iter() + .filter_map(|node| node.find_children_by_id(id)) + .next() + } + } +} + impl BuildFromRef for Children { fn ref_to(query: impl Into) -> Self { Self { -- cgit v1.2.3