From b9e2e4999a067371b4944e623cef2cdafb669561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Fri, 17 Jan 2025 01:57:30 -0800 Subject: [PATCH] updated schema --- SCHEMA-SPEC.md | 10 +- schema/cargo.kdl | 168 +++++++++++++++++ schema/kdl-schema.kdl | 411 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 schema/cargo.kdl create mode 100644 schema/kdl-schema.kdl diff --git a/SCHEMA-SPEC.md b/SCHEMA-SPEC.md index 907c1af..d453edd 100644 --- a/SCHEMA-SPEC.md +++ b/SCHEMA-SPEC.md @@ -6,7 +6,7 @@ constrain the allowed semantics of a KDL document. This can be used for many purposes: documentation for users, automated verification, or even automated generation of bindings! -This document describes KDL Schema version `1.0.0`. It was released on September 11, 2021. +This document describes KDL Schema version `2.0.0`. It is unreleased. ## The Formal Schema @@ -39,6 +39,14 @@ None. * `tag-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of tags of child nodes. * `other-tags-allowed` (optional): Whether to allow node tags other than the ones explicitly listed here. Defaults to `#false`. +#### Example + +```kdl +document { + +} +``` + ### `info` node The `info` node describes the schema itself. diff --git a/schema/cargo.kdl b/schema/cargo.kdl new file mode 100644 index 0000000..3f02fbe --- /dev/null +++ b/schema/cargo.kdl @@ -0,0 +1,168 @@ +@kdl:schema "https://github.com/kdl-org/kdl/blob/main/schema/kdl-schema.kdl" + +metadata { + // TODO: update this link when we're ready to release something. + link "https://github.com/kdl-org/kdl/blob/main/schema/cargo.kdl" rel=self + title "Cargo Schema" lang=en + description "KDL-based translation of the Cargo.toml schema." lang=en + author "Kat Marchán" { + link "https://github.com/zkat" rel=self + } + link "https://github.com/kdl-org/kdl" rel=documentation + link "https://doc.rust-lang.org/cargo/reference/manifest.html" rel=documentation + license "Creative Commons Attribution-ShareAlike 4.0 International License" spdx=CC-BY-SA-4.0 { + link "https://creativecommons.org/licenses/by-sa/4.0/" lang=en + } +} + +children { + node package title="Describes a package" { + children { + node name title="The name of the package" { + required + arg { + type string + pattern #"^[a-zA-Z0-0\-_]+$"# + } + } + node version title="The version of the package." { + arg { + type string + // From https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + pattern #"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"# + } + } + node authors title="The authors of the package." { + repeat + args { + type string + distinct? #true + } + children { + node - { + multiple + arg title="Name" { + type string + } + prop email title="Email address" { + type string + format email + } + prop about title="Brief note about author (role, etc)" { + type string + } + } + } + } + node edition title="The Rust edition." { + arg { + type string + enum "2015" "2018" "2021" "2024" + } + } + node rust-version title="The minimal supported Rust version." { + arg { + type string + } + } + node description title="A description of the package." { + arg { + type string + } + } + node documentation title="URL of the package documentation." { + arg { + type string + format url + } + } + node readme title="Path to the package’s README file." { + arg { + type string #boolean + } + } + node homepage title="URL of the package homepage." { + arg { + type string + format url + } + } + node repository title="URL of the package source repository." { + arg { + type string + format url + } + } + node license title="The package license." { + arg { + type string + } + } + node license-file title="Path to the text of the license." { + arg { + type string + } + } + node keywords title="Keywords for the package." { + args { + type string + // No pattern because keyword restrictions are only on + // crates.io + } + } + node categories title="Categories of the package." { + args { + type string + // No pattern because category restrictions are only on + // crates.io + } + } + node workspace title="Path to the workspace for the package." { + arg { + type string + } + } + node build title="Path to the package build script." { + arg { + type string boolean + } + } + node links title="Name of the native library the package links with." { + arg { + type string + } + } + node exclude title="Files to exclude when publishing." { + args { + type string + } + } + node include title="Files to include when publishing." { + args { + type string + } + } + node publish title="Can be used to prevent publishing the package." { + // TODO: This is a good example of where we might need smarter + // comstraints ("either a single boolean, or 1+ strings") + args { + type string boolean + } + ] + node metadata title="Extra settings for external tools." { + repeat; args; props; children + } + node default-run title="The default binary to run by cargo run." { + arg { + type string + } + } + node no-autolib title="Disables library auto discovery." + node no-autobins title="Disables binary auto discovery." + node no-autoexamples title="Disables example auto discovery." + node no-autotests title="Disables test auto discovery." + node no-autobenches title="Disables bench auto discovery." + node resolver title="Sets the dependency resolver to use." + } + } +} \ No newline at end of file diff --git a/schema/kdl-schema.kdl b/schema/kdl-schema.kdl new file mode 100644 index 0000000..a65a98f --- /dev/null +++ b/schema/kdl-schema.kdl @@ -0,0 +1,411 @@ +// TODO: +// * examples +// * dependentRequired +// * dependentSchema +// * if-then-else +// * composition (anyOf, allOf, oneOf, not, etc: https://json-schema.org/understanding-json-schema/reference/combining) +// * followed-by (I think this might be useful: declaring relationships between children) +// * requires (a more general-purpose version of followed-by that uses kpath+children?) + +@kdl:schema "https://github.com/kdl-org/kdl/blob/main/examples/kdl-schema.kdl" + +metadata { + // TODO: update this link when we're ready to release something. + link "https://github.com/kdl-org/kdl/blob/main/examples/kdl-schema.kdl" rel=self + title "KDL Schema" lang=en + description "KDL Schema KDL schema in KDL" lang=en + author "Kat Marchán" { + link "https://github.com/zkat" rel=self + } + contributor "Lars Willighagen" { + link "https://github.com/larsgw" rel=self + } + link "https://github.com/kdl-org/kdl" rel=documentation + license "Creative Commons Attribution-ShareAlike 4.0 International License" spdx=CC-BY-SA-4.0 { + link "https://creativecommons.org/licenses/by-sa/4.0/" lang=en + } + published "2021-08-31" + modified "2021-09-01" +} +children { + node metadata description="Contains metadata about the schema itself." { + children { + node title description="The title of the schema or the format it describes" { + arg description="The title text" { + type string + } + prop lang id=metadata-lang description="The language of the text" { + type string + } + } + node description description="A description of the schema or the format it describes" { + arg description="The description text" { + type string + } + prop ref=metadata-lang + } + node author description="Author of the schema" { + arg id=metadata-person-name description="Person name" { + type string + } + prop orcid id=metadata-orcid description="The ORCID of the person" { + type string + pattern #"\d{4}-\d{4}-\d{4}-\d{4}"# + } + children { + node ref=metadata-link + } + } + node contributor description="Contributor to the schema" { + arg ref=metadata-person-name + prop ref=metadata-orcid + children { + node ref=metadata-link + } + } + node link id=metadata-link description="Links to itself, and to sources describing it" { + arg description="A URL that the link points to" { + type string + format url irl + } + prop rel description="The relation between the current entity and the URL" { + type string + enum self documentation + } + prop ref=metadata-lang + } + node license description="The license(s) that the schema is licensed under" { + arg description="Name of the used license" { + type string + } + prop spdx description="An SPDX license identifier" { + type string + // TODO: validation? + } + children { + node ref=metadata-link + } + } + node published description="When the schema was published" { + arg description="Publication date" { + type string + format date + } + prop time id=metadata-time description="A time to accompany the date" { + type string + format time + } + } + node modified description="When the schema was last modified" { + arg description="Modification date" { + type string + format date + } + prop ref=metadata-time + } + node version description="The version number of this version of the schema" { + arg description="Semver version number" { + type string + pattern #"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"# + } + } + } + } + node definitions description="An optional set of definitions that may be referenced elsewhere in the schema. They will otherwise be inert." + node children id=children-node description="Validations and definitions used for all nodes in this scope" { + children { + node names ref=string-validations description="Validations to apply to all node names in this scope." + node allow-others? title="Allow other children?" description=""" + Whether to allow child nodes in this scope other than the ones explicitly listed. + + Note: If this is `#true`, you will need to add `node @kdl:schema` to your schema definition if you want your data format to be able to reference KDL schemas. + """ { + max 1 + arg { + default #true + type boolean + } + } + node node ref=node-validations description="A KDL node belonging either to the top-level document or to another `node`'s children." { + arg description="The name of the node. If a node name is not supplied, the node rules apply to _all_ nodes belonging to the parent, including those with names present in the schema." { + type string + optional? #true + } + prop title title="Short descriptor for node." { + type string + } + prop description description="A longer description of this item's purpose and behavior." { + type string + } + prop id description="A globally-unique ID for this node." { + type string + } + prop ref description="A reference to a previously-defined unique ID." { + type string + } + prop refpath description="A reference to a previously-defined unique ID, using a KPath query instead of a plain ID." { + type string + format kpath + } + prop deprecated? description="Whether this node is deprecated." { + type boolean + } + children { + node ref description="A reference to a previously-defined unique ID." { + args { + min 1 + type string + } + } + node refpath description="A reference to a previously-defined unique ID, using a KPath query instead of a plain ID." { + args { + min 1 + type string + format kpath + } + } + node title title="Short descriptor for node" { + max 1 + arg { + type string + } + } + node description description="A longer description of this item's purpose and behavior." { + max 1 + arg { + type string + } + } + node deprecated? description="Whether this node is deprecated." { + max 1 + arg { + type string + } + } + } + } + } + } +} +definitions { + node id=string-validations ref=shared-validations description="String-related validations" { + children { + node pattern description="EcmaScript-compatible Regex pattern or patterns to test string values against." { + args { + min 1 + type string + } + } + node min-length description="Minimum length of value, if it's a string." { + max 1 + arg { + gte 0 + type integer + } + } + node max-length description="Maximum length of value, if it's a string." { + max 1 + arg { + gte 0 + type integer + } + } + node format title="Intended data format." { + args { + min 1 + type string + // https://json-schema.org/understanding-json-schema/reference/string.html#format + // TODO: Make sure this is up to date with the types listed in the spec. + enum date-time date time duration decimal currency country-2 country-3 \ + country-subdivision email idn-email hostname idn-hostname ipv4 ipv6 url \ + url-reference irl irl-reference url-template regex uuid kpath i8 i16 \ + i32 i64 i128 u8 u16 u32 u64 u128 isize usize f32 f64 decimal64 decimal128 + } + } + node media-type title="MIME type" description="MIME type of string value. May be applied to 'deserialized' data if value format is base64/base85 or some other stringly binary encoding." { + args { + min 1 + type string + } + } + } + } + node id=number-validations ref=shared-validations descriptions="Number-specific validations" { + children { + node div description="Only used for numeric values. Constrains them to be multiples of the given number(s)" { + args { + min 1 + type number + } + } + node gt description="Only used for numeric values. Constrains them to be greater than the given number" { + max 1 + arg { + type number + } + } + node gte description="Only used for numeric values. Constrains them to be greater than or equal to the given number" { + max 1 + arg { + type number + } + } + node lt description="Only used for numeric values. Constrains them to be less than the given number" { + max 1 + arg { + type number + } + } + node lte description="Only used for numeric values. Constrains them to be less than or equal to the given number" { + max 1 + arg { + type number + } + } + } + } + node id=shared-validations description="Validations shared across all types." { + children { + node type description="The type for this value. Multiple arguments signify a sum type." { + args { + min 1 + type string + enum string boolean number integer #null + distinct? #true + } + } + node const description="Exact value that this value must match. Equivalent to a single-value `enum` validation." { + max 1 + arg description="Constant value." + } + node enum description="An enumeration of possible values" { + args description="Enumeration choices" { + min 1 + } + children description="Enumeration choices" { + node - description="Enumeration choice" { + prop description description="Documentation for this enumerated item." + arg description="Enum value" + } + } + } + } + } + node id=value-validations description="General value validations." { + ref string-validations number-validations + children { + node annotations ref=string-validations description="Validations for the type annotations that can be applied to this value." + node default title="Default value" { + arg + } + } + } + node id=node-validations description="Validations that can be applied to nodes themselves." { + children { + node min description="minimum number of instances of this node in its parent's children." { + max 1 + arg { + type number + } + } + node max description="maximum number of instances of this node in its parent's children." { + max 1 + arg { + type number + } + } + node annotations ref=string-validations description="Validations to apply specifically to arbitrary node type annotation names" + node prop description="A node property key/value pair." { + ref value-validations + arg description="The property key." { + type string + } + children description="Property-specific validations." { + node required? description="Whether this property is required in the node." { + max 1 + arg { + type boolean + } + } + } + } + node props description="Validations to apply to all properties of this node." { + ref value-validations + node names ref=string-validations description="Validations to apply to all property names." + children { + node distinct? description="Whether all property values need to be distinct." { + max 1 + arg { + type boolean + } + } + node min description="minimum number of properties that match this validation." { + max 1 + arg { + gte 0 + type number + } + } + node max description="maximum number of properties that match this validation." { + max 1 + arg { + gte 0 + type number + } + } + node allow-others? description="Whether to allow other properties that don't match this validator. Defaults to `#false`." { + max 1 + arg { + type boolean + } + } + } + } + node arg description=""" + Specifies validations for a node argument. + + Each nth instance of this node will specify validations for the \ + corresponding nth instance of the arg. If `optional?` is true, this \ + arg cannot be followed by non-optional `arg`s. + """ { + ref value-validations + children { + node optional? description="Whether this arg is optional. Specified `arg`s are required by default." + } + } + node args description="Specifies validations for all arguments. Can be used in conjunction with `arg`." { + ref value-validations + children description="Additional validations when args can be variable-length" { + node distinct? description="Whether all items need to be distinct." { + max 1 + arg { + type boolean + } + } + node min description="minimum number of arguments that match this validation." { + max 1 + arg { + gte 0 + type number + } + } + node max description="maximum number of arguments that match this validation." { + max 1 + arg { + gte 0 + type number + } + } + node allow-others? description="Whether to allow other arguments that don't match this validator." { + max 1 + arg { + default #false + type boolean + } + } + } + } + node children ref=children-node + } + } +}