aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--src/lib.rs117
-rw-r--r--src/schema_schema.rs1
3 files changed, 119 insertions, 2 deletions
diff --git a/README.md b/README.md
index af92b5c..f13f32b 100644
--- a/README.md
+++ b/README.md
@@ -82,8 +82,9 @@ assert_eq!(kdl_schema::SCHEMA_SCHEMA.document.info.title[0].text, "KDL Schema");
- can generate KDL from schema object in Rust
- can choose kdl or knuffel as parser
- make sure `required false` in a `prop` works properly
-- validate the schema at parse time (ensure that e.g. refs are resolvable)
+- validate the schema at parse time (ensure that e.g. refs are resolvable, IDs are unique)
- make sure enums work with int values
+- allow rich types for language tags, modification dates, etc
## license
diff --git a/src/lib.rs b/src/lib.rs
index c0f742a..5f915cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,6 +3,7 @@
elided_lifetimes_in_paths,
explicit_outlives_requirements,
missing_debug_implementations,
+ missing_docs,
noop_method_call,
single_use_lifetimes,
trivial_casts,
@@ -20,7 +21,7 @@ use knuffel::{Decode, DecodeScalar};
mod schema_schema;
pub use schema_schema::SCHEMA_SCHEMA;
-pub trait BuildFromRef {
+pub(crate) trait BuildFromRef {
fn ref_to(query: impl Into<String>) -> Self;
}
@@ -30,14 +31,22 @@ fn get_id_from_ref(r#ref: &str) -> Option<&str> {
.and_then(|r#ref| r#ref.strip_suffix(r#""]"#))
}
+/// the schema itself
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Schema {
+ /// the document this schema defines
+ ///
+ /// this is redundant but it matches the schema document structure
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub document: Document,
}
impl Schema {
+ /// find the node matching the given ref
+ ///
+ /// # Panics
+ ///
/// Panics if ref is not of the form `[id="foo"]`.
#[must_use]
pub fn resolve_node_ref(&self, r#ref: &str) -> Option<&Node> {
@@ -48,6 +57,10 @@ impl Schema {
.find_map(|node| node.find_node_by_id(id))
}
+ /// find the prop matching the given ref
+ ///
+ /// # Panics
+ ///
/// Panics if ref is not of the form `[id="foo"]`.
#[must_use]
pub fn resolve_prop_ref(&self, r#ref: &str) -> Option<&Prop> {
@@ -58,6 +71,10 @@ impl Schema {
.find_map(|node| node.find_prop_by_id(id))
}
+ /// find the value matching the given ref
+ ///
+ /// # Panics
+ ///
/// Panics if ref is not of the form `[id="foo"]`.
#[must_use]
pub fn resolve_value_ref(&self, r#ref: &str) -> Option<&Value> {
@@ -68,6 +85,10 @@ impl Schema {
.find_map(|node| node.find_value_by_id(id))
}
+ /// find the children matching the given ref
+ ///
+ /// # Panics
+ ///
/// Panics if ref is not of the form `[id="foo"]`.
#[must_use]
pub fn resolve_children_ref(&self, r#ref: &str) -> Option<&Children> {
@@ -81,6 +102,11 @@ impl Schema {
#[cfg(feature = "parse-knuffel")]
impl Schema {
+ /// parse a KDL schema definition
+ ///
+ /// # Errors
+ ///
+ /// returns an error if knuffel can't parse the document as a Schema
pub fn parse(
schema_kdl: &str,
) -> Result<Self, knuffel::Error<impl knuffel::traits::ErrorSpan>> {
@@ -88,106 +114,146 @@ impl Schema {
}
}
+/// the schema document
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Document {
+ /// schema metadata
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub info: Info,
+ /// top-level node definitions
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "node")))]
pub nodes: Vec<Node>,
}
+/// schema metadata
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Info {
+ /// schema titles
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "title")))]
pub title: Vec<TextValue>,
+ /// schema descriptions
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "description")))]
pub description: Vec<TextValue>,
+ /// schema authors
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "author")))]
pub authors: Vec<Person>,
+ /// schema contributors
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "contributor")))]
pub contributors: Vec<Person>,
+ /// schema links
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))]
pub links: Vec<Link>,
+ /// schema licenses
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "license")))]
pub licenses: Vec<License>,
+ /// schema publication date
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub published: Option<Date>,
+ /// schema modification date
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub modified: Option<Date>,
}
+/// a text value with an optional language tag
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct TextValue {
+ /// text itself
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub text: String,
+ /// BCP 47 language tag
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub lang: Option<String>,
}
+/// information about a schema author/contributor
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Person {
+ /// name
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub name: String,
+ /// [ORCID](https://orcid.org)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub orcid: Option<String>,
+ /// relevant links
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))]
pub links: Vec<Link>,
}
+/// link related to specification metadata, with optional relationship and language tag
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Link {
+ /// URI/IRI of link target
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub iri: String,
+ /// relationship of link to schema (`self`, `documentation`)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub rel: Option<String>,
+ /// BCP 47 language tag
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub lang: Option<String>,
}
+/// schema license information
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct License {
+ /// license name
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub name: String,
+ /// license [SPDX identifier](https://spdx.org/licenses/)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub spdx: Option<String>,
+ /// links for license information
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "link")))]
pub link: Vec<Link>,
}
+/// date with optional time
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Date {
+ /// date
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub date: String,
+ /// time
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub time: Option<String>,
}
+/// schema for a node
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Node {
+ /// name of the node (applies to all nodes at this level if `None`)
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub name: Option<String>,
+ /// id of the node (can be used for refs)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub id: Option<String>,
+ /// human-readable description of the node's purpose
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
+ /// KDL query from which to load node information instead of specifying it inline (allows for recursion)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
+ /// minimum number of occurrences of this node
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub min: Option<usize>,
+ /// maximum number of occurrences of this node
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub max: Option<usize>,
+ /// properties allowed on this node
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "prop")))]
pub props: Vec<Prop>,
+ /// values allowed on this node
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "value")))]
pub values: Vec<Value>,
+ /// children allowed on this node
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "children")))]
pub children: Vec<Children>,
}
@@ -241,19 +307,26 @@ impl BuildFromRef for Node {
}
}
+/// schema for a property
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Prop {
+ /// property key (applies to all properties in this node if `None`)
#[cfg_attr(feature = "parse-knuffel", knuffel(argument))]
pub key: Option<String>,
+ /// id of the property (can be used for refs)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub id: Option<String>,
+ /// human-readable description of the property
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
+ /// KDL query from which to load property information instead of specifying it inline (allows for recursion)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
+ /// whether or not this property is required
#[cfg_attr(feature = "parse-knuffel", knuffel(child))]
pub required: bool,
+ /// validations to apply to the property value
#[cfg_attr(feature = "parse-knuffel", knuffel(children))]
pub validations: Vec<Validation>,
}
@@ -277,19 +350,26 @@ impl BuildFromRef for Prop {
}
}
+/// schema for a value
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Value {
+ /// id of the value (can be used for refs)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub id: Option<String>,
+ /// human readable description of the value
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
+ /// KDL query from which to load value information instead of specifying it inline (allows for recursion)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
+ /// minimum number of occurrences of this value
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub min: Option<usize>,
+ /// maximum number of occurrences of this value
#[cfg_attr(feature = "parse-knuffel", knuffel(child, unwrap(argument)))]
pub max: Option<usize>,
+ /// validations to apply to this value
#[cfg_attr(feature = "parse-knuffel", knuffel(children))]
pub validations: Vec<Validation>,
}
@@ -313,15 +393,20 @@ impl BuildFromRef for Value {
}
}
+/// schema for a node's children
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub struct Children {
+ /// id for these children (can be used for refs)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub id: Option<String>,
+ /// human readable description of these children
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub description: Option<String>,
+ /// KDL query from which to load children information instead of specifying it inline (allows for recursion)
#[cfg_attr(feature = "parse-knuffel", knuffel(property))]
pub ref_: Option<String>,
+ /// nodes which can appear as children
#[cfg_attr(feature = "parse-knuffel", knuffel(children(name = "node")))]
pub nodes: Vec<Node>,
}
@@ -359,40 +444,70 @@ impl BuildFromRef for Children {
}
}
+/// a validation to apply to some value or property value
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(Decode))]
pub enum Validation {
+ /// ensure the value is of the given type
Type(#[cfg_attr(feature = "parse-knuffel", knuffel(argument))] String),
+ /// ensure the value is one of the given options
Enum(#[cfg_attr(feature = "parse-knuffel", knuffel(arguments))] Vec<String>),
+ /// ensure the value matches the given regular expression
Pattern(#[cfg_attr(feature = "parse-knuffel", knuffel(argument))] String),
+ /// ensure the value is of the given format
Format(#[cfg_attr(feature = "parse-knuffel", knuffel(arguments))] Vec<Format>),
}
+/// a format to ensure a value has
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "parse-knuffel", derive(DecodeScalar))]
pub enum Format {
+ /// iso 8601 datetime string
DateTime,
+ /// iso 8601 date string
Date,
+ /// iso 8601 time string
Time,
+ /// iso 8601 duration string
Duration,
+ /// ieee 754-2008 decimal string
Decimal,
+ /// iso 4217 currency code string
Currency,
+ /// iso 3166-1 alpha-2 country code string
Country2,
+ /// iso 3166-1 alpha-3 country code string
Country3,
+ /// iso 3166-2 country subdivision code string
CountrySubdivision,
+ /// rfc 5302 email address string
Email,
+ /// rfc 6531 internationalized email address string
IdnEmail,
+ /// rfc 1132 internet hostname string
Hostname,
+ /// rfc 5890 internationalized internet hostname string
IdnHostname,
+ /// rfc 2673 ipv4 address string
Ipv4,
+ /// rfc 2373 ipv6 address string
Ipv6,
+ /// rfc 3986 uri string
Url,
+ /// rfc 3986 uri reference string
UrlReference,
+ /// rfc 3987 iri string
Irl,
+ /// rfc 3987 iri reference string
IrlReference,
+ /// rfc 6750 uri template string
UrlTemplate,
+ /// rfc 4122 uuid string
Uuid,
+ /// regular expression string
Regex,
+ /// base64 encoded string
Base64,
+ /// KDL query string
KdlQuery,
}
diff --git a/src/schema_schema.rs b/src/schema_schema.rs
index 11ccb4e..c17eb89 100644
--- a/src/schema_schema.rs
+++ b/src/schema_schema.rs
@@ -5,6 +5,7 @@ use lazy_static::lazy_static;
use super::*;
lazy_static! {
+ /// the schema schema defined by [the canonical `kdl-schema.kdl`](https://github.com/kdl-org/kdl/blob/cc1da35435abbe2f7d0138310f89b18c55a292e7/examples/kdl-schema.kdl)
pub static ref SCHEMA_SCHEMA: Schema = make_schema_schema();
}