This crate is very much experimental and is being developed as a personal learning exercise for getting acquainted with Rust and about parsing in general. It is far from complete. There are likely more performant and stable libraries out there for parsing CDDL. This one should not be used in production in any form or fashion.
A Rust implementation of the Concise data definition language (CDDL). CDDL is an IETF standard that "proposes a notational convention to express CBOR and JSON data structures." As of 2019-06-12, it is published as RFC 8610 (Proposed Standard) at https://tools.ietf.org/html/rfc8610.
This crate includes a handwritten parser and lexer for CDDL and is heavily inspired by Thorsten Ball's book "Writing An Interpretor In Go". The AST has been built to closely match the rules defined by the ABNF grammar in Appendix B. of the spec. All CDDL must use UTF-8 for its encoding per the spec.
This crate supports validation of both CBOR and JSON data structures. An extremely basic REPL is included as well, with plans to compile it for use in the browser with WebAssembly.
no_std
support (lexing and parsing only)Rust is a systems programming language designed around safety and is ideally-suited for resource-constrained systems. CDDL and CBOR are designed around small code and message sizes and constrained nodes, scenarios that Rust has also been designed for.
Incomplete. Under development
This crate uses the Serde framework, and more specifically, the serdejson crate, for parsing and validating JSON. Serde was chosen due to its maturity in the ecosystem and its support for serializing and deserializing CBOR via the serdecbor crate.
As outlined in Appendix E. of the standard, only the JSON data model subset of CBOR can be used for validation. The limited prelude from the spec has been included below for brevity:
```cddl any = #
uint = #0 nint = #1 int = uint / nint
tstr = #3 text = tstr
number = int / float
float16 = #7.25 float32 = #7.26 float64 = #7.27 float16-32 = float16 / float32 float32-64 = float32 / float64 float = float16-32 / float64
false = #7.20 true = #7.21 bool = false / true nil = #7.22 null = nil ```
The first non-group rule defined by a CDDL data structure definition determines the root type, which is subsequently used for validating the top-level JSON data type.
The following types and features of CDDL are supported by this crate for validating JSON:
|CDDL|JSON| |----|----| |structs|objects| |arrays|arrays| |text / tstr|string| |number / int / float|number*| |bool / true / false|boolean| |null / nil|null| |any|any valid JSON|
Occurrence indicators can be used to validate key/value pairs in a JSON object and the number of elements in a JSON array; depending on how the indicators are defined in a CDDL data definition. CDDL groups, generics, sockets/plugs and group-to-choice enumerations are all parsed into their full representations before being evaluated for JSON validation.
All CDDL control operators can be used for validating JSON, with the exception of the .cbor
and .cborseq
operators.
*Note: While JSON itself does not distinguish between integers and floating-point numbers, this crate does provide the ability to validate numbers against a more specific numerical CBOR type, provided that its equivalent representation is allowed by JSON.
CDDL, JSON schema and JSON schema language can all be used to define JSON data structures. However, the approaches taken to develop each of these are vastly different. A good place to find past discussions on the differences between thse formats is the IETF mail archive, specifically in the JSON and CBOR lists. The purpose of this crate is not to argue for the use of CDDL over any one of these formats, but simply to provide an example implementation in Rust.
Incomplete. Under development
This crate also uses Serde and serde_cbor for validating CBOR data structures. Similary to the JSON validation implementation, CBOR validation is done via the loosely typed Value
enum. Unfortunately, due to a limitation of Serde, CBOR tags are ignored during deserialization.
no_std
supportOnly the lexer and parser can be used in a no_std
context provided that a heap allocator is available. This can be enabled by opting out of the default features in your Cargo.toml
file as follows:
toml
[dependencies]
cddl = { version = "<version>", default-features = false }
Zero-copy parsing is implemented to the extent that is possible, with prefixed byte strings containing whitespace being one of the few exceptions where allocation is required.
Both JSON and CBOR validation are dependent on their respective heap allocated Value
types, but since these types aren't supported in a no_std
context, they subsequently aren't supported in a no_std
context in this crate.
Below is a graph of the dependencies used by this project. It was generated using cargo-deps
.