#![doc = include_str!("../README.md")] #![warn(clippy::pedantic, clippy::cargo)] #[cfg(feature = "parse-knuffel")] use knuffel::{Decode, DecodeScalar}; mod schema_schema; pub use schema_schema::SCHEMA_SCHEMA; 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 { #[cfg_attr(feature = "parse-knuffel", knuffel(child))] 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( schema_kdl: &str, ) -> Result> { knuffel::parse("", schema_kdl) } } #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Document { #[cfg_attr(feature = "parse-knuffel", knuffel(child))] pub info: Info, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "node")))] pub nodes: Vec, } #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Info { #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "title")))] pub title: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "description")))] pub description: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "author")))] pub authors: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "contributor")))] pub contributors: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))] pub links: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "license")))] pub licenses: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(child))] pub published: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(child))] pub modified: Option, } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct TextValue { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub text: String, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub lang: Option, } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Person { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub name: String, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub orcid: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))] pub links: Vec, } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Link { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub iri: String, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub rel: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub lang: Option, } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct License { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub name: String, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub spdx: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))] pub link: Vec, } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Date { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub date: String, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub time: Option, } #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Node { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub name: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub id: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub description: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub ref_: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))] pub min: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))] pub max: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "prop")))] pub props: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "value")))] pub values: Vec, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "children")))] 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 { ref_: Some(query.into()), ..Self::default() } } } #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Prop { #[cfg_attr(feature = "parse-knuffel", knuffel(argument))] pub key: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub id: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub description: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub ref_: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(child))] pub required: bool, #[cfg_attr(feature = "parse-knuffel", knuffel(children))] 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 { ref_: Some(query.into()), ..Self::default() } } } #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Value { #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub id: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub description: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub ref_: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))] pub min: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))] pub max: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(children))] 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 { ref_: Some(query.into()), ..Self::default() } } } #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub struct Children { #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub id: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub description: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(property))] pub ref_: Option, #[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "node")))] 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 { ref_: Some(query.into()), ..Self::default() } } } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(Decode))] pub enum Validation { Type(#[cfg_attr(feature = "parse-knuffel", knuffel(argument))] String), Enum(#[cfg_attr(feature = "parse-knuffel", knuffel(arguments))] Vec), Pattern(#[cfg_attr(feature = "parse-knuffel", knuffel(argument))] String), Format(#[cfg_attr(feature = "parse-knuffel", knuffel(arguments))] Vec), } #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "parse-knuffel", derive(DecodeScalar))] pub enum Format { DateTime, Date, Time, Duration, Decimal, Currency, Country2, Country3, CountrySubdivision, Email, IdnEmail, Hostname, IdnHostname, Ipv4, Ipv6, Url, UrlReference, Irl, IrlReference, UrlTemplate, Uuid, Regex, Base64, KdlQuery, }