#![doc = include_str!("../README.md")]
#![warn(
elided_lifetimes_in_paths,
explicit_outlives_requirements,
missing_debug_implementations,
missing_docs,
noop_method_call,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unsafe_code,
unused_crate_dependencies,
unused_qualifications
)]
#![warn(clippy::pedantic, clippy::cargo)]
#[cfg(feature = "parse-knuffel")]
use knuffel::{Decode, DecodeScalar};
mod schema_schema;
pub use schema_schema::SCHEMA_SCHEMA;
pub(crate) trait BuildFromRef {
fn ref_to(query: impl Into<String>) -> 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 {
#[must_use]
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()
.find_map(|node| node.find_node_by_id(id))
}
#[must_use]
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()
.find_map(|node| node.find_prop_by_id(id))
}
#[must_use]
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()
.find_map(|node| node.find_value_by_id(id))
}
#[must_use]
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()
.find_map(|node| node.find_children_by_id(id))
}
}
#[cfg(feature = "parse-knuffel")]
impl Schema {
pub fn parse(
schema_kdl: &str,
) -> Result<Self, knuffel::Error<impl knuffel::traits::ErrorSpan>> {
knuffel::parse("<Schema::parse argument>", 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<Node>,
}
#[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<TextValue>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "description")))]
pub description: Vec<TextValue>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "author")))]
pub authors: Vec<Person>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "contributor")))]
pub contributors: Vec<Person>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))]
pub links: Vec<Link>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "license")))]
pub licenses: Vec<License>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub published: Option<Date>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub modified: Option<Date>,
}
#[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<String>,
}
#[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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))]
pub links: Vec<Link>,
}
#[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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub lang: Option<String>,
}
#[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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))]
pub link: Vec<Link>,
}
#[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<String>,
}
#[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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub id: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub min: Option<usize>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub max: Option<usize>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "prop")))]
pub props: Vec<Prop>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "value")))]
pub values: Vec<Value>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "children")))]
pub children: Vec<Children>,
}
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()
.find_map(|children| children.find_node_by_id(id))
}
}
fn find_prop_by_id(&self, id: &str) -> Option<&Prop> {
self.props
.iter()
.find_map(|prop| prop.find_prop_by_id(id))
.or_else(|| {
self.children
.iter()
.find_map(|children| children.find_prop_by_id(id))
})
}
fn find_value_by_id(&self, id: &str) -> Option<&Value> {
self.values
.iter()
.find_map(|value| value.find_value_by_id(id))
.or_else(|| {
self.children
.iter()
.find_map(|children| children.find_value_by_id(id))
})
}
fn find_children_by_id(&self, id: &str) -> Option<&Children> {
self.children
.iter()
.find_map(|children| children.find_children_by_id(id))
}
}
impl BuildFromRef for Node {
fn ref_to(query: impl Into<String>) -> 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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub id: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub required: bool,
#[cfg_attr(feature = "parse-knuffel", knuffel(children))]
pub validations: Vec<Validation>,
}
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<String>) -> 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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub min: Option<usize>,
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub max: Option<usize>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children))]
pub validations: Vec<Validation>,
}
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<String>) -> 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<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "node")))]
pub nodes: Vec<Node>,
}
impl Children {
fn find_node_by_id(&self, id: &str) -> Option<&Node> {
self.nodes.iter().find_map(|node| node.find_node_by_id(id))
}
fn find_prop_by_id(&self, id: &str) -> Option<&Prop> {
self.nodes.iter().find_map(|node| node.find_prop_by_id(id))
}
fn find_value_by_id(&self, id: &str) -> Option<&Value> {
self.nodes.iter().find_map(|node| node.find_value_by_id(id))
}
fn find_children_by_id(&self, id: &str) -> Option<&Children> {
if self.id.as_deref() == Some(id) {
Some(self)
} else {
self.nodes
.iter()
.find_map(|node| node.find_children_by_id(id))
}
}
}
impl BuildFromRef for Children {
fn ref_to(query: impl Into<String>) -> 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<String>),
Pattern(#[cfg_attr(feature = "parse-knuffel", knuffel(argument))] String),
Format(#[cfg_attr(feature = "parse-knuffel", knuffel(arguments))] Vec<Format>),
}
#[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,
}