diff --git a/.github/workflows/anchore-analysis.yml b/.github/workflows/anchore-analysis.yml index 97ce7a93..64935e75 100644 --- a/.github/workflows/anchore-analysis.yml +++ b/.github/workflows/anchore-analysis.yml @@ -9,9 +9,17 @@ name: Anchore Container Scan on: push: branches: [main] + paths: + - Dockerfile + - '.dockerignore' + - '.github/workflows/anchore-analysis.yml' pull_request: # The branches below must be a subset of the branches above branches: [main] + paths: + - Dockerfile + - '.dockerignore' + - '.github/workflows/anchore-analysis.yml' schedule: - cron: '0 2 * * 4' @@ -22,8 +30,25 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 - - name: Build the Docker image - run: docker build -t ghcr.io/anweiss/cddl-cli:latest . + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Build image + uses: docker/build-push-action@v2 + with: + context: . + push: false + tags: ghcr.io/anweiss/cddl-cli:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new - name: Run the Anchore scan action itself with GitHub Advanced Security code scanning integration enabled uses: anchore/scan-action@main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2b666c9..8a50add1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: wasm-compilation-check: strategy: matrix: - rust_toolchain: [stable, beta] + rust_toolchain: [stable] name: Compilation check for wasm target runs-on: ubuntu-latest steps: @@ -83,7 +83,7 @@ jobs: test-suite: strategy: matrix: - rust_toolchain: [stable, beta] + rust_toolchain: [stable] os: [ubuntu-latest, macOS-latest, windows-latest] name: Test suite runs-on: ${{ matrix.os }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 36543e02..0ac58c12 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,28 +1,16 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: 'CodeQL' on: push: branches: [main] pull_request: - # The branches below must be a subset of the branches above branches: [main] schedule: - cron: '35 17 * * 4' jobs: - analyze: - name: Analyze + analyze-node: + name: Analyze Node.js runs-on: ubuntu-latest permissions: actions: read @@ -33,39 +21,102 @@ jobs: fail-fast: false matrix: language: ['javascript'] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 - # Initializes the CodeQL tools for scanning. + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + target: wasm32-unknown-unknown + + - name: Install Node.js + uses: actions/setup-node@v1 + with: + node-version: '16' + + - name: Install wasmpack + run: | + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build wasm for Node.js + run: | + wasm-pack build --target nodejs --scope anweiss --release + - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 + + - uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + analyze-web: + name: Analyze web + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write - #- run: | - # make bootstrap - # make release + strategy: + fail-fast: false + matrix: + language: ['javascript'] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + target: wasm32-unknown-unknown + + - name: Install Node.js + uses: actions/setup-node@v1 + with: + node-version: '16' + + - name: Install wasmpack + run: | + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Build wasm for web + run: | + wasm-pack build --release + + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 + + - uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- diff --git a/.github/workflows/release-cddl.yml b/.github/workflows/release-cddl.yml index 5f433b8d..92cbc363 100644 --- a/.github/workflows/release-cddl.yml +++ b/.github/workflows/release-cddl.yml @@ -107,6 +107,13 @@ jobs: npm install npm run build + - uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + draft-release: name: Draft Release needs: diff --git a/Cargo.toml b/Cargo.toml index 2196da2c..6abe8a9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/anweiss/cddl" homepage = "https://cddl.anweiss.tech" categories = ["parser-implementations", "encoding", "development-tools", "wasm"] license = "MIT" -version = "0.9.0-beta.0" +version = "0.9.0-beta.1" authors = ["Andrew Weiss "] readme = "README.md" edition = "2018" @@ -36,10 +36,13 @@ pest_vm = "2.1.0" displaydoc = { version = "0.2.3", default-features = false } log = "0.4.14" simplelog = "0.10.0" +fake = { version = "2.4.0", optional = true, features = ["derive", "chrono"] } +rand = { version = "0.8.0", optional = true } +getrandom = { version = "0.2.3", features = ["js"] } [dev-dependencies] indoc = "1.0.3" -pretty_assertions = "0.7.2" +pretty_assertions = "1.0.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] crossterm = { version = "0.21.0", optional = true } @@ -54,7 +57,7 @@ wasm-bindgen-test = "0.3.25" [features] default = ["std", "ast-span", "ast-comments", "json", "cbor", "additional-controls"] -std = ["base16/alloc", "base64/alloc", "serde_json", "ciborium", "serde", "chrono", "wasm-bindgen", "clap", "crossterm", "uriparse", "base64-url", "regex-syntax"] +std = ["base16/alloc", "base64/alloc", "serde_json", "ciborium", "serde", "chrono", "wasm-bindgen", "clap", "crossterm", "uriparse", "base64-url", "regex-syntax", "fake", "rand"] lsp = ["std"] additional-controls = [] ast-span = [] diff --git a/README.md b/README.md index 688b03ec..cb284d2a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # cddl-rs -[![crates.io](https://img.shields.io/crates/v/cddl.svg)](https://crates.io/crates/cddl) [![docs.rs](https://docs.rs/cddl/badge.svg)](https://docs.rs/cddl) [![Publish packages](https://github.com/anweiss/cddl/workflows/Publish%20packages/badge.svg?branch=0.9.0-beta.0&event=release)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Publish+packages%22) [![Build and Test](https://github.com/anweiss/cddl/workflows/Build%20and%20Test/badge.svg)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Build+and+Test%22) [![Active Development](https://img.shields.io/badge/Maintenance%20Level-Actively%20Developed-brightgreen.svg)](https://gist.github.com/cheerfulstoic/d107229326a01ff0f333a1d3476e068d) +[![crates.io](https://img.shields.io/crates/v/cddl.svg)](https://crates.io/crates/cddl) [![docs.rs](https://docs.rs/cddl/badge.svg)](https://docs.rs/cddl) [![Publish packages](https://github.com/anweiss/cddl/workflows/Publish%20packages/badge.svg?branch=0.9.0-beta.1&event=release)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Publish+packages%22) [![Build and Test](https://github.com/anweiss/cddl/workflows/Build%20and%20Test/badge.svg)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Build+and+Test%22) > This crate was originally developed as a personal learning exercise for getting acquainted with Rust and parsing in general. There are likely more performant and stable libraries out there for parsing CDDL. While there are some examples of this crate being used in production, careful consideration should be made prior to using this crate as such. @@ -19,7 +19,7 @@ Also bundled into this repository is a basic language server implementation and - [x] Validate CBOR data structures - [x] Validate JSON documents - [x] Basic REPL -- [ ] Generate dummy JSON from conformant CDDL +- [x] Generate dummy JSON from conformant CDDL - [x] As close to zero-copy as possible - [x] Compile WebAssembly target for browser and Node.js - [x] `no_std` support (lexing and parsing only) @@ -67,7 +67,7 @@ cddl help If using Docker: -> Replace `` with an appropriate [release](https://github.com/anweiss/cddl/releases) tag. Requires use of the `--volume` argument for mounting `.cddl` documents into the container when executing the command. `.json` or `.cbor` files can either be included in the volume mount or passed into the command via STDIN. +> Replace `` with an appropriate [release](https://github.com/anweiss/cddl/releases) tag. Requires use of the `--volume` argument for mounting `.cddl` documents into the container when executing the command. JSON or CBOR files can either be included in the volume mount or passed into the command via STDIN. ```sh docker run -it --rm -v $PWD:/cddl -w /cddl ghcr.io/anweiss/cddl-cli: help @@ -76,13 +76,13 @@ docker run -it --rm -v $PWD:/cddl -w /cddl ghcr.io/anweiss/cddl-cli: he You can validate JSON documents: ```sh -cddl validate --cddl --json [FILE.json]... +cddl validate --cddl --json [FILE]... ``` You can validate CBOR files: ```sh -cddl validate --cddl --cbor [FILE.cbor]... +cddl validate --cddl --cbor [FILE]... ``` It also supports validating files from STDIN (if it detects the input as valid UTF-8, it will attempt to validate the input as JSON, otherwise it will treat it as CBOR): @@ -140,7 +140,7 @@ Simply add the dependency to `Cargo.toml`: ```toml [dependencies] -cddl = "0.9.0-beta.0" +cddl = "0.9.0-beta.1" ``` Both JSON and CBOR validation require `std`. @@ -175,7 +175,7 @@ Enable validation support for the additional control operators proposed in [http use cddl::{lexer_from_str, parser::cddl_from_str}; let input = r#"myrule = int"#; -assert!(cddl_from_str(&mut lexer_from_str(input), input, true).is_ok()) +assert!(cddl_from_str(&mut lexer_from_str(input), input, true, false).is_ok()) ``` ### Validating JSON @@ -195,7 +195,7 @@ let json = r#"{ "address": "1234 Lakeshore Dr" }"#; -assert!(validate_json_from_str(cddl, json).is_ok()) +assert!(validate_json_from_str(cddl, json, false).is_ok()) ``` This crate uses the [Serde](https://serde.rs/) framework, and more specifically, the [serde_json](https://crates.io/crates/serde_json) 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 [ciborium](https://crates.io/crates/ciborium) crate. @@ -330,7 +330,7 @@ let cddl = r#"rule = false"#; let cbor = b"\xF4"; -assert!(validate_cbor_from_slice(cddl, cbor).is_ok()) +assert!(validate_cbor_from_slice(cddl, cbor, false).is_ok()) ``` This crate also uses [Serde](https://serde.rs/) and [ciborium](https://crates.io/crates/ciborium) for validating CBOR data structures. CBOR validation is done via the loosely typed [`ciborium::value::Value`](https://github.com/enarx/ciborium/blob/main/ciborium/src/value/mod.rs#L22) enum. In addition to all of the same features implemented by the JSON validator, this crate also supports validating CBOR tags (e.g. `#6.32(tstr)`), CBOR major types (e.g. `#1.2`), table types (e.g. `{ [ + tstr ] => int }`) and byte strings. The `.bits`, `.cbor` and `.cborseq` control operators are all supported as well. @@ -379,7 +379,7 @@ let cddl = r#" let cbor = b"\x02"; -assert!(validate_cbor_from_slice(cddl, cbor, Some(&["cbor"])).is_ok()) +assert!(validate_cbor_from_slice(cddl, cbor, false, Some(&["cbor"])).is_ok()) ``` ## `no_std` support @@ -388,7 +388,7 @@ Only the lexer and parser can be used in a `no_std` context provided that a heap ```toml [dependencies] -cddl = { version = "0.9.0-beta.0", default-features = false } +cddl = { version = "0.9.0-beta.1", default-features = false } ``` Zero-copy parsing is implemented to the extent that is possible. Allocation is required for error handling and diagnostics. diff --git a/src/ast.rs b/src/ast.rs index 6fd6b864..bbd9fd74 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -20,7 +20,9 @@ use alloc::{ /// Starting index, ending index and line number #[cfg(feature = "ast-span")] -pub type Span = (usize, usize, usize); +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub struct Span(pub usize, pub usize, pub usize); #[cfg(feature = "ast-comments")] #[derive(Default, Debug, PartialEq, Clone)] @@ -138,7 +140,7 @@ impl<'a> fmt::Display for CDDL<'a> { /// DIGIT = %x30-39 /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct Identifier<'a> { /// Identifier pub ident: &'a str, @@ -178,8 +180,7 @@ impl<'a> From<&'static str> for Identifier<'a> { return Identifier { ident, socket: Some(SocketPlug::GROUP), - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() }; } } @@ -187,8 +188,7 @@ impl<'a> From<&'static str> for Identifier<'a> { return Identifier { ident, socket: Some(SocketPlug::TYPE), - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() }; } } @@ -196,8 +196,7 @@ impl<'a> From<&'static str> for Identifier<'a> { Identifier { ident, socket: None, - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() } } } @@ -383,7 +382,7 @@ impl<'a> Rule<'a> { /// typename [genericparm] S assignt S type /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct TypeRule<'a> { /// Type name identifier #[cfg_attr(target_arch = "wasm32", serde(borrow))] @@ -499,7 +498,7 @@ impl<'a> fmt::Display for GroupRule<'a> { /// genericparm = "<" S id S *("," S id S ) ">" /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct GenericParams<'a> { /// List of generic parameters pub params: Vec>, @@ -508,19 +507,9 @@ pub struct GenericParams<'a> { pub span: Span, } -impl<'a> Default for GenericParams<'a> { - fn default() -> Self { - GenericParams { - params: Vec::new(), - #[cfg(feature = "ast-span")] - span: (0, 0, 0), - } - } -} - /// Generic parameter #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct GenericParam<'a> { /// Generic parameter pub param: Identifier<'a>, @@ -568,7 +557,7 @@ impl<'a> fmt::Display for GenericParams<'a> { /// genericarg = "<" S type1 S *("," S type1 S ) ">" /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct GenericArgs<'a> { /// Generic arguments pub args: Vec>, @@ -577,20 +566,9 @@ pub struct GenericArgs<'a> { pub span: Span, } -impl<'a> GenericArgs<'a> { - /// Default `GenericArg` - pub fn default() -> Self { - GenericArgs { - args: Vec::new(), - #[cfg(feature = "ast-span")] - span: (0, 0, 0), - } - } -} - /// Generic argument #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct GenericArg<'a> { /// Generic argument pub arg: Box>, @@ -632,13 +610,24 @@ impl<'a> fmt::Display for GenericArgs<'a> { } } +/// A generic rule that has undergone monomorphization +#[derive(Clone, Debug, Default)] +pub struct GenericRule<'a> { + /// Name + pub name: &'a str, + /// Parameters + pub params: Vec<&'a str>, + /// Arguments + pub args: Vec>, +} + /// Type choices /// /// ```abnf /// type = type1 *(S "/" S type1) /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct Type<'a> { /// Type choices pub type_choices: Vec>, @@ -670,9 +659,37 @@ impl<'a> Type<'a> { } } +impl<'a> From<&Identifier<'a>> for Type<'a> { + fn from(ident: &Identifier<'a>) -> Self { + Type { + type_choices: vec![TypeChoice { + type1: Type1 { + type2: Type2::Typename(Typename { + ident: ident.clone(), + generic_args: None, + #[cfg(feature = "ast-span")] + span: ident.span, + }), + operator: None, + #[cfg(feature = "ast-span")] + span: ident.span, + #[cfg(feature = "ast-comments")] + comments_after_type: None, + }, + #[cfg(feature = "ast-comments")] + comments_before_type: None, + #[cfg(feature = "ast-comments")] + comments_after_type: None, + }], + #[cfg(feature = "ast-span")] + span: ident.span, + } + } +} + /// Type choice #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct TypeChoice<'a> { /// Type choice @@ -733,11 +750,11 @@ impl<'a> Type<'a> { if self.type_choices.len() == 1 { if let Some(tc) = self.type_choices.first() { if tc.type1.operator.is_none() { - if let Type2::Typename { + if let Type2::Typename(Typename { ident, generic_args, span, - } = &tc.type1.type2 + }) = &tc.type1.type2 { return Some((ident.clone(), generic_args.clone(), *span)); } @@ -756,10 +773,10 @@ impl<'a> Type<'a> { if self.type_choices.len() == 1 { if let Some(tc) = self.type_choices.first() { if tc.type1.operator.is_none() { - if let Type2::Typename { + if let Type2::Typename(Typename { ident, generic_args, - } = &tc.type1.type2 + }) = &tc.type1.type2 { return Some((ident.clone(), generic_args.clone())); } @@ -777,7 +794,7 @@ impl<'a> Type<'a> { /// type1 = type2 [S (rangeop / ctlop) S type2] /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct Type1<'a> { /// Type pub type2: Type2<'a>, @@ -798,41 +815,41 @@ impl<'a> From> for Type1<'a> { #[cfg(feature = "ast-span")] let span = Span::default(); let type2 = match value { - Value::TEXT(value) => Type2::TextValue { + Value::TEXT(value) => Type2::TextValue(TextValue { value, #[cfg(feature = "ast-span")] span, - }, - Value::INT(value) => Type2::IntValue { + }), + Value::INT(value) => Type2::IntValue(IntValue { value, #[cfg(feature = "ast-span")] span, - }, - Value::FLOAT(value) => Type2::FloatValue { + }), + Value::FLOAT(value) => Type2::FloatValue(FloatValue { value, #[cfg(feature = "ast-span")] span, - }, - Value::UINT(value) => Type2::UintValue { + }), + Value::UINT(value) => Type2::UintValue(UintValue { value, #[cfg(feature = "ast-span")] span, - }, - Value::BYTE(ByteValue::B16(value)) => Type2::B16ByteString { + }), + Value::BYTE(ByteValue::B16(value)) => Type2::B16ByteString(B16ByteString { value, #[cfg(feature = "ast-span")] span, - }, - Value::BYTE(ByteValue::B64(value)) => Type2::B64ByteString { + }), + Value::BYTE(ByteValue::B64(value)) => Type2::B64ByteString(B64ByteString { value, #[cfg(feature = "ast-span")] span, - }, - Value::BYTE(ByteValue::UTF8(value)) => Type2::UTF8ByteString { + }), + Value::BYTE(ByteValue::UTF8(value)) => Type2::UTF8ByteString(Utf8ByteString { value, #[cfg(feature = "ast-span")] span, - }, + }), }; Type1 { @@ -976,218 +993,53 @@ impl<'a> fmt::Display for RangeCtlOp<'a> { #[derive(Debug, Clone, PartialEq)] pub enum Type2<'a> { /// Integer value - IntValue { - /// Value - value: isize, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + IntValue(IntValue), /// Unsigned integer value - UintValue { - /// Value - value: usize, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + UintValue(UintValue), /// Float value - FloatValue { - /// Value - value: f64, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + FloatValue(FloatValue), /// Text string value (enclosed by '"') - TextValue { - /// Value - value: Cow<'a, str>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + TextValue(TextValue<'a>), /// UTF-8 encoded byte string (enclosed by '') - UTF8ByteString { - /// Value - value: Cow<'a, [u8]>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + UTF8ByteString(Utf8ByteString<'a>), /// Base 16 encoded prefixed byte string - B16ByteString { - /// Value - value: Cow<'a, [u8]>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + B16ByteString(B16ByteString<'a>), /// Base 64 encoded (URL safe) prefixed byte string - B64ByteString { - /// Value - value: Cow<'a, [u8]>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + B64ByteString(B64ByteString<'a>), /// Type name identifier with optional generic arguments - Typename { - /// Identifier - ident: Identifier<'a>, - /// Generic arguments - generic_args: Option>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + Typename(Typename<'a>), /// Parenthesized type expression (for operator precedence) - ParenthesizedType { - /// Type - pt: Type<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_before_type: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_type: Option>, - }, + ParenthesizedType(ParenthesizedType<'a>), /// Map expression - Map { - /// Group - group: Group<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_before_group: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_group: Option>, - }, + Map(Map<'a>), /// Array expression - Array { - /// Span - group: Group<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_before_group: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_group: Option>, - }, + Array(Array<'a>), /// Unwrapped group - Unwrap { - /// Identifier - ident: Identifier<'a>, - /// Generic arguments - generic_args: Option>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments: Option>, - }, + Unwrap(Unwrap<'a>), /// Enumeration expression over an inline group - ChoiceFromInlineGroup { - /// Group - group: Group<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_before_group: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_group: Option>, - }, + ChoiceFromInlineGroup(ChoiceFromInlineGroup<'a>), /// Enumeration expression over previously defined group - ChoiceFromGroup { - /// Identifier - ident: Identifier<'a>, - /// Generic arguments - generic_args: Option>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments: Option>, - }, + ChoiceFromGroup(ChoiceFromGroup<'a>), /// Tagged data item where the first element is an optional tag and the second /// is the type of the tagged value - TaggedData { - /// Tag - tag: Option, - /// Type - t: Type<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_before_type: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_type: Option>, - }, + TaggedData(TaggedData<'a>), /// Data item of a major type with optional data constraint - DataMajorType { - /// Major type - mt: u8, - /// Constraint - constraint: Option, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - }, + DataMajorType(DataMajorType), /// Any data item #[cfg(feature = "ast-span")] @@ -1198,44 +1050,311 @@ pub enum Type2<'a> { Any, } +#[cfg(feature = "ast-span")] +impl<'a> Default for Type2<'a> { + fn default() -> Self { + Type2::Any(Span::default()) + } +} + +#[cfg(not(feature = "ast-span"))] +impl<'a> Default for Type2<'a> { + fn default() -> Self { + Type2::Any + } +} + +/// Int value +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct IntValue { + /// Value + pub value: isize, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Uint value +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct UintValue { + /// Value + pub value: usize, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Float value +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct FloatValue { + /// Value + pub value: f64, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Text value +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct TextValue<'a> { + /// Value + pub value: Cow<'a, str>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Utf8 byte string +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Utf8ByteString<'a> { + /// Value + pub value: Cow<'a, [u8]>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Base16 byte string +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct B16ByteString<'a> { + /// Value + pub value: Cow<'a, [u8]>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Base64 byte string +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct B64ByteString<'a> { + /// Value + pub value: Cow<'a, [u8]>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +/// Type name +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Typename<'a> { + /// Identifier + pub ident: Identifier<'a>, + /// Generic arguments + pub generic_args: Option>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + +#[doc(hidden)] +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Clone, PartialEq)] +pub enum ParenthesizedTypeOrGroup<'a> { + Group(Group<'a>), + Type(ParenthesizedType<'a>), +} + +/// Parenthesized type +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct ParenthesizedType<'a> { + /// Type + pub pt: Type<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_type: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_type: Option>, +} + +/// Map +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Map<'a> { + /// Group + pub group: Group<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_group: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_group: Option>, +} + +/// Array +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Array<'a> { + /// Group + pub group: Group<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_group: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_group: Option>, +} + +/// Unwrap +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Unwrap<'a> { + /// Identifier + pub ident: Identifier<'a>, + /// Generic arguments + pub generic_args: Option>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments: Option>, +} + +/// Choice from inline group +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct ChoiceFromInlineGroup<'a> { + /// Group + pub group: Group<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_group: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_group: Option>, +} + +/// Choice from group +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct ChoiceFromGroup<'a> { + /// Identifier + pub ident: Identifier<'a>, + /// Generic arguments + pub generic_args: Option>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments: Option>, +} + +/// Tagged data +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct TaggedData<'a> { + /// Tag + pub tag: Option, + /// Type + pub t: Type<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_type: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_type: Option>, +} + +/// Major data type +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct DataMajorType { + /// Major type + pub mt: u8, + /// Constraint + pub constraint: Option, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, +} + #[allow(clippy::cognitive_complexity)] impl<'a> fmt::Display for Type2<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Type2::IntValue { value, .. } => write!(f, "{}", value), - Type2::UintValue { value, .. } => write!(f, "{}", value), - Type2::FloatValue { value, .. } => write!(f, "{}", value), - Type2::TextValue { value, .. } => write!(f, "\"{}\"", value), - Type2::UTF8ByteString { value, .. } => write!( + Type2::IntValue(IntValue { value, .. }) => write!(f, "{}", value), + Type2::UintValue(UintValue { value, .. }) => write!(f, "{}", value), + Type2::FloatValue(FloatValue { value, .. }) => write!(f, "{}", value), + Type2::TextValue(TextValue { value, .. }) => write!(f, "\"{}\"", value), + Type2::UTF8ByteString(Utf8ByteString { value, .. }) => write!( f, "'{}'", std::str::from_utf8(value).map_err(|_| fmt::Error)? ), - Type2::B16ByteString { value, .. } => { + Type2::B16ByteString(B16ByteString { value, .. }) => { write!(f, "{}", std::str::from_utf8(value).map_err(|_| fmt::Error)?) } - Type2::B64ByteString { value, .. } => { + Type2::B64ByteString(B64ByteString { value, .. }) => { write!(f, "{}", std::str::from_utf8(value).map_err(|_| fmt::Error)?) } - Type2::Typename { + Type2::Typename(Typename { ident, generic_args, .. - } => { + }) => { if let Some(args) = generic_args { return write!(f, "{}{}", ident, args); } write!(f, "{}", ident) } - Type2::ParenthesizedType { + Type2::ParenthesizedType(ParenthesizedType { #[cfg(feature = "ast-comments")] comments_before_type, pt, #[cfg(feature = "ast-comments")] comments_after_type, .. - } => { + }) => { let mut pt_str = String::from("("); #[cfg(feature = "ast-comments")] @@ -1264,14 +1383,14 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", pt_str) } - Type2::Map { + Type2::Map(Map { #[cfg(feature = "ast-comments")] comments_before_group, group, #[cfg(feature = "ast-comments")] comments_after_group, .. - } => { + }) => { let mut t2_str = String::from("{"); #[cfg(feature = "ast-comments")] @@ -1319,14 +1438,14 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", t2_str) } - Type2::Array { + Type2::Array(Array { #[cfg(feature = "ast-comments")] comments_before_group, group, #[cfg(feature = "ast-comments")] comments_after_group, .. - } => { + }) => { let mut t2_str = String::from("["); #[cfg(feature = "ast-comments")] @@ -1383,13 +1502,13 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", t2_str) } - Type2::Unwrap { + Type2::Unwrap(Unwrap { #[cfg(feature = "ast-comments")] comments, ident, generic_args, .. - } => { + }) => { let mut t2_str = String::new(); #[cfg(feature = "ast-comments")] @@ -1405,7 +1524,7 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", t2_str) } - Type2::ChoiceFromInlineGroup { + Type2::ChoiceFromInlineGroup(ChoiceFromInlineGroup { #[cfg(feature = "ast-comments")] comments, #[cfg(feature = "ast-comments")] @@ -1414,7 +1533,7 @@ impl<'a> fmt::Display for Type2<'a> { #[cfg(feature = "ast-comments")] comments_after_group, .. - } => { + }) => { let mut t2_str = String::from("&"); #[cfg(feature = "ast-comments")] @@ -1444,13 +1563,13 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", t2_str) } - Type2::ChoiceFromGroup { + Type2::ChoiceFromGroup(ChoiceFromGroup { #[cfg(feature = "ast-comments")] comments, ident, generic_args, .. - } => { + }) => { let mut t2_str = String::from("&"); #[cfg(feature = "ast-comments")] @@ -1466,7 +1585,7 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", t2_str) } - Type2::TaggedData { + Type2::TaggedData(TaggedData { tag, #[cfg(feature = "ast-comments")] comments_before_type, @@ -1474,7 +1593,7 @@ impl<'a> fmt::Display for Type2<'a> { #[cfg(feature = "ast-comments")] comments_after_type, .. - } => { + }) => { let mut t2_str = String::from("#6"); if let Some(tag_uint) = tag { @@ -1503,7 +1622,7 @@ impl<'a> fmt::Display for Type2<'a> { write!(f, "{}", t2_str) } - Type2::DataMajorType { mt, constraint, .. } => { + Type2::DataMajorType(DataMajorType { mt, constraint, .. }) => { if let Some(c) = constraint { return write!(f, "{}.{}", mt, c); } @@ -1521,10 +1640,10 @@ impl<'a> fmt::Display for Type2<'a> { impl<'a> From> for Type2<'a> { fn from(rv: RangeValue<'a>) -> Self { #[cfg(feature = "ast-span")] - let span = (0, 0, 0); + let span = Span::default(); match rv { - RangeValue::IDENT(ident) => Type2::Typename { + RangeValue::IDENT(ident) => Type2::Typename(Typename { ident: Identifier { ident: ident.0, socket: ident.1, @@ -1534,268 +1653,271 @@ impl<'a> From> for Type2<'a> { generic_args: None, #[cfg(feature = "ast-span")] span, - }, - RangeValue::INT(value) => Type2::IntValue { + }), + RangeValue::INT(value) => Type2::IntValue(IntValue { value, #[cfg(feature = "ast-span")] span, - }, - RangeValue::UINT(value) => Type2::UintValue { + }), + RangeValue::UINT(value) => Type2::UintValue(UintValue { value, #[cfg(feature = "ast-span")] span, - }, - RangeValue::FLOAT(value) => Type2::FloatValue { + }), + RangeValue::FLOAT(value) => Type2::FloatValue(FloatValue { value, #[cfg(feature = "ast-span")] span, - }, + }), } } } impl<'a> From> for Type2<'a> { fn from(type1: Type1<'a>) -> Self { - Type2::ParenthesizedType { + Type2::ParenthesizedType(ParenthesizedType { pt: Type { type_choices: vec![TypeChoice { type1, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-comments")] - comments_before_type: None, + ..Default::default() }], #[cfg(feature = "ast-span")] span: Span::default(), }, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - } + ..Default::default() + }) } } impl<'a> From for Type2<'a> { fn from(value: usize) -> Self { - Type2::UintValue { + Type2::UintValue(UintValue { value, - #[cfg(feature = "ast-span")] - span: Span::default(), - } + ..Default::default() + }) } } impl<'a> From for Type2<'a> { fn from(value: isize) -> Self { - Type2::IntValue { + Type2::IntValue(IntValue { value, - #[cfg(feature = "ast-span")] - span: Span::default(), - } + ..Default::default() + }) } } impl<'a> From for Type2<'a> { fn from(value: f64) -> Self { - Type2::FloatValue { + Type2::FloatValue(FloatValue { value, - #[cfg(feature = "ast-span")] - span: Span::default(), - } + ..Default::default() + }) } } impl<'a> From for Type2<'a> { fn from(value: String) -> Self { - Type2::TextValue { + Type2::TextValue(TextValue { value: value.into(), - #[cfg(feature = "ast-span")] - span: Span::default(), - } + ..Default::default() + }) } } // Convenience method for testing impl<'a> From<&'a str> for Type2<'a> { fn from(value: &'a str) -> Self { - Type2::UTF8ByteString { + Type2::UTF8ByteString(Utf8ByteString { value: value.as_bytes().into(), - #[cfg(feature = "ast-span")] - span: Span::default(), - } + ..Default::default() + }) } } impl<'a> From> for Type2<'a> { fn from(value: ByteValue<'a>) -> Self { match value { - ByteValue::UTF8(value) => Type2::UTF8ByteString { + ByteValue::UTF8(value) => Type2::UTF8ByteString(Utf8ByteString { value, - #[cfg(feature = "ast-span")] - span: Span::default(), - }, - ByteValue::B16(value) => Type2::B16ByteString { + ..Default::default() + }), + ByteValue::B16(value) => Type2::B16ByteString(B16ByteString { value, - #[cfg(feature = "ast-span")] - span: Span::default(), - }, - ByteValue::B64(value) => Type2::B64ByteString { + ..Default::default() + }), + ByteValue::B64(value) => Type2::B64ByteString(B64ByteString { value, - #[cfg(feature = "ast-span")] - span: Span::default(), - }, + ..Default::default() + }), } } } +fn decfrac_type<'a>() -> Type<'a> { + Type { + type_choices: vec![TypeChoice { + type1: Type1 { + type2: Type2::Array(Array { + group: Group { + group_choices: vec![GroupChoice { + group_entries: vec![ + ( + GroupEntry::ValueMemberKey { + ge: Box::from(ValueMemberKeyEntry { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { + ident: "e10".into(), + ..Default::default() + })), + entry_type: Type { + type_choices: vec![TypeChoice { + type1: Type1 { + type2: Type2::Typename(Typename { + ident: Token::INT.into(), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }], + ..Default::default() + }, + occur: None, + }), + #[cfg(feature = "ast-comments")] + leading_comments: None, + #[cfg(feature = "ast-span")] + span: Span::default(), + #[cfg(feature = "ast-comments")] + trailing_comments: None, + }, + OptionalComma::default(), + ), + ( + GroupEntry::ValueMemberKey { + ge: Box::from(ValueMemberKeyEntry { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { + ident: "m".into(), + ..Default::default() + })), + entry_type: Type { + type_choices: vec![TypeChoice { + type1: Type1 { + type2: Type2::Typename(Typename { + ident: Token::INTEGER.into(), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }], + ..Default::default() + }, + ..Default::default() + }), + #[cfg(feature = "ast-comments")] + leading_comments: None, + #[cfg(feature = "ast-span")] + span: Span::default(), + #[cfg(feature = "ast-comments")] + trailing_comments: None, + }, + OptionalComma::default(), + ), + ], + ..Default::default() + }], + ..Default::default() + }, + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }], + ..Default::default() + } +} + /// Retrieve `Type2` from token if it is a tag type in the standard prelude pub fn tag_from_token<'a>(token: &Token) -> Option> { match token { - Token::TDATE => Some(Type2::TaggedData { + Token::TDATE => Some(Type2::TaggedData(TaggedData { tag: Some(0), t: type_from_token(Token::TSTR), - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::TIME => Some(Type2::TaggedData { + ..Default::default() + })), + Token::TIME => Some(Type2::TaggedData(TaggedData { tag: Some(1), t: type_from_token(Token::NUMBER), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::BIGUINT => Some(Type2::TaggedData { + ..Default::default() + })), + Token::BIGUINT => Some(Type2::TaggedData(TaggedData { tag: Some(2), t: type_from_token(Token::BSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::BIGNINT => Some(Type2::TaggedData { + ..Default::default() + })), + Token::BIGNINT => Some(Type2::TaggedData(TaggedData { tag: Some(3), t: type_from_token(Token::BSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::DECFRAC => unimplemented!(), + ..Default::default() + })), + Token::DECFRAC => Some(Type2::TaggedData(TaggedData { + tag: Some(4), + t: decfrac_type(), + ..Default::default() + })), Token::BIGFLOAT => unimplemented!(), - Token::EB64URL => Some(Type2::TaggedData { + Token::EB64URL => Some(Type2::TaggedData(TaggedData { tag: Some(21), t: type_from_token(Token::ANY), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::EB64LEGACY => Some(Type2::TaggedData { + ..Default::default() + })), + Token::EB64LEGACY => Some(Type2::TaggedData(TaggedData { tag: Some(22), t: type_from_token(Token::ANY), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::EB16 => Some(Type2::TaggedData { + ..Default::default() + })), + Token::EB16 => Some(Type2::TaggedData(TaggedData { tag: Some(23), t: type_from_token(Token::ANY), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::ENCODEDCBOR => Some(Type2::TaggedData { + ..Default::default() + })), + Token::ENCODEDCBOR => Some(Type2::TaggedData(TaggedData { tag: Some(24), t: type_from_token(Token::BSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::URI => Some(Type2::TaggedData { + ..Default::default() + })), + Token::URI => Some(Type2::TaggedData(TaggedData { tag: Some(32), t: type_from_token(Token::TSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::B64URL => Some(Type2::TaggedData { + ..Default::default() + })), + Token::B64URL => Some(Type2::TaggedData(TaggedData { tag: Some(33), t: type_from_token(Token::TSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::B64LEGACY => Some(Type2::TaggedData { + ..Default::default() + })), + Token::B64LEGACY => Some(Type2::TaggedData(TaggedData { tag: Some(34), t: type_from_token(Token::TSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::REGEXP => Some(Type2::TaggedData { + ..Default::default() + })), + Token::REGEXP => Some(Type2::TaggedData(TaggedData { tag: Some(35), t: type_from_token(Token::TSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::MIMEMESSAGE => Some(Type2::TaggedData { + ..Default::default() + })), + Token::MIMEMESSAGE => Some(Type2::TaggedData(TaggedData { tag: Some(36), t: type_from_token(Token::TSTR), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), - Token::CBORANY => Some(Type2::TaggedData { + ..Default::default() + })), + Token::CBORANY => Some(Type2::TaggedData(TaggedData { tag: Some(55799), t: type_from_token(Token::ANY), - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }), + ..Default::default() + })), _ => None, } } @@ -1805,25 +1927,15 @@ pub fn type_from_token(token: Token) -> Type { Type { type_choices: vec![TypeChoice { type1: Type1 { - #[cfg(feature = "ast-comments")] - comments_after_type: None, - operator: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier::from(token), - generic_args: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }, + ..Default::default() + }), + ..Default::default() }, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - #[cfg(feature = "ast-comments")] - comments_before_type: None, + ..Default::default() }], - #[cfg(feature = "ast-span")] - span: Span::default(), + ..Default::default() } } @@ -1833,7 +1945,7 @@ pub fn type_from_token(token: Token) -> Type { /// group = grpchoice * (S "//" S grpchoice) /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct Group<'a> { /// Group choices #[cfg_attr(target_arch = "wasm32", serde(borrow))] @@ -1927,7 +2039,7 @@ impl<'a> fmt::Display for Group<'a> { /// /// If tuple is true, then entry is marked by a trailing comma #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct GroupChoice<'a> { /// Group entries where the second item in the tuple indicates where or not a /// trailing comma is present @@ -2477,7 +2589,7 @@ impl<'a> fmt::Display for Occurrence<'a> { /// [occur S] [memberkey S] type /// ``` #[cfg_attr(target_arch = "wasm32", derive(Serialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct ValueMemberKeyEntry<'a> { /// Optional occurrence indicator pub occur: Option>, @@ -2547,90 +2659,110 @@ impl<'a> fmt::Display for TypeGroupnameEntry<'a> { #[derive(Debug, Clone, PartialEq)] pub enum MemberKey<'a> { /// Type expression - Type1 { - /// Type1 - #[cfg_attr(target_arch = "wasm32", serde(borrow))] - t1: Box>, - /// Is cut indicator present - is_cut: bool, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_before_cut: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_cut: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_arrowmap: Option>, - }, + Type1(Type1MemberKey<'a>), /// Bareword string type - Bareword { - /// Identifier - #[cfg_attr(target_arch = "wasm32", serde(borrow))] - ident: Identifier<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, - - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_colon: Option>, - }, + Bareword(BarewordMemberKey<'a>), /// Value type - Value { - /// Value - #[cfg_attr(target_arch = "wasm32", serde(borrow))] - value: Value<'a>, - /// Span - #[cfg(feature = "ast-span")] - span: Span, + Value(ValueMemberKey<'a>), - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments: Option>, - #[cfg(feature = "ast-comments")] - #[cfg_attr(target_arch = "wasm32", serde(skip))] - #[doc(hidden)] - comments_after_colon: Option>, - }, + // Used while parsing a parenthesized type that is not followed by a cut nor + // an arrow map + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + Parenthesized(ParenthesizedMemberKey<'a>), +} + +/// Type1 member key +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Clone, PartialEq, Default)] +pub struct Type1MemberKey<'a> { + /// Type1 + #[cfg_attr(target_arch = "wasm32", serde(borrow))] + pub t1: Box>, + /// Is cut indicator present + pub is_cut: bool, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_cut: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_cut: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_arrowmap: Option>, +} + +/// Bareword member key +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Clone, PartialEq, Default)] +pub struct BarewordMemberKey<'a> { + /// Identifier + #[cfg_attr(target_arch = "wasm32", serde(borrow))] + pub ident: Identifier<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + #[cfg(feature = "ast-comments")] #[cfg_attr(target_arch = "wasm32", serde(skip))] #[doc(hidden)] - NonMemberKey { - non_member_key: NonMemberKey<'a>, - #[cfg(feature = "ast-comments")] - comments_before_type_or_group: Option>, - #[cfg(feature = "ast-comments")] - comments_after_type_or_group: Option>, - }, + pub comments: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_colon: Option>, +} + +/// Value member key +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] +#[derive(Debug, Clone, PartialEq)] +pub struct ValueMemberKey<'a> { + /// Value + #[cfg_attr(target_arch = "wasm32", serde(borrow))] + pub value: Value<'a>, + /// Span + #[cfg(feature = "ast-span")] + pub span: Span, + + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_colon: Option>, } +/// Parenthesized member key +#[cfg_attr(target_arch = "wasm32", derive(Serialize))] #[derive(Debug, Clone, PartialEq)] #[doc(hidden)] -pub enum NonMemberKey<'a> { - Group(Group<'a>), - Type(Type<'a>), +pub struct ParenthesizedMemberKey<'a> { + pub non_member_key: ParenthesizedTypeOrGroup<'a>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_before_type_or_group: Option>, + #[cfg(feature = "ast-comments")] + #[cfg_attr(target_arch = "wasm32", serde(skip))] + #[doc(hidden)] + pub comments_after_type_or_group: Option>, } impl<'a> fmt::Display for MemberKey<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - MemberKey::Type1 { + MemberKey::Type1(Type1MemberKey { t1, #[cfg(feature = "ast-comments")] comments_before_cut, @@ -2640,7 +2772,7 @@ impl<'a> fmt::Display for MemberKey<'a> { #[cfg(feature = "ast-comments")] comments_after_arrowmap, .. - } => { + }) => { let mut mk_str = format!("{} ", t1); #[cfg(feature = "ast-comments")] @@ -2672,14 +2804,14 @@ impl<'a> fmt::Display for MemberKey<'a> { write!(f, "{}", mk_str) } - MemberKey::Bareword { + MemberKey::Bareword(BarewordMemberKey { ident, #[cfg(feature = "ast-comments")] comments, #[cfg(feature = "ast-comments")] comments_after_colon, .. - } => { + }) => { let mut mk_str = format!("{}", ident); #[cfg(feature = "ast-comments")] @@ -2700,14 +2832,14 @@ impl<'a> fmt::Display for MemberKey<'a> { write!(f, "{}", mk_str) } - MemberKey::Value { + MemberKey::Value(ValueMemberKey { value, #[cfg(feature = "ast-comments")] comments, #[cfg(feature = "ast-comments")] comments_after_colon, .. - } => { + }) => { let mut mk_str = format!("{}", value); #[cfg(feature = "ast-comments")] @@ -2728,13 +2860,13 @@ impl<'a> fmt::Display for MemberKey<'a> { write!(f, "{}", mk_str) } - MemberKey::NonMemberKey { - non_member_key: NonMemberKey::Group(g), + MemberKey::Parenthesized(ParenthesizedMemberKey { + non_member_key: ParenthesizedTypeOrGroup::Group(g), #[cfg(feature = "ast-comments")] comments_before_type_or_group, #[cfg(feature = "ast-comments")] comments_after_type_or_group, - } => { + }) => { let mut nmk_str = String::new(); #[cfg(feature = "ast-comments")] @@ -2751,13 +2883,13 @@ impl<'a> fmt::Display for MemberKey<'a> { write!(f, "{}", nmk_str) } - MemberKey::NonMemberKey { - non_member_key: NonMemberKey::Type(t), + MemberKey::Parenthesized(ParenthesizedMemberKey { + non_member_key: ParenthesizedTypeOrGroup::Type(ParenthesizedType { pt, .. }), #[cfg(feature = "ast-comments")] comments_before_type_or_group, #[cfg(feature = "ast-comments")] comments_after_type_or_group, - } => { + }) => { let mut nmk_str = String::new(); #[cfg(feature = "ast-comments")] @@ -2765,7 +2897,7 @@ impl<'a> fmt::Display for MemberKey<'a> { nmk_str.push_str(&comments.to_string()); } - nmk_str.push_str(&t.to_string()); + nmk_str.push_str(&pt.to_string()); #[cfg(feature = "ast-comments")] if let Some(comments) = comments_after_type_or_group { @@ -2877,7 +3009,7 @@ mod tests { leading_comments: None, trailing_comments: None, #[cfg(feature = "ast-span")] - span: (0, 0, 0), + span: Span::default(), } .to_string(), "entry1".to_string() @@ -2894,93 +3026,70 @@ mod tests { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: "key1".into(), - comments: None, - comments_after_colon: None, - #[cfg(feature = "ast-span")] - span: (0, 0, 0), - }), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "value1".into(), - #[cfg(feature = "ast-span")] - span: (0, 0, 0), - }, - operator: None, - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() + }), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() }, }), leading_comments: None, trailing_comments: None, #[cfg(feature = "ast-span")] - span: (0, 0, 0), + span: Span::default(), }, OptionalComma { optional_comma: true, - trailing_comments: None, - _a: PhantomData::default(), + ..Default::default() } ), ( GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: "key2".into(), - comments: None, - comments_after_colon: None, - #[cfg(feature = "ast-span")] - span: (0, 0, 0), - }), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "value2".into(), - #[cfg(feature = "ast-span")] - span: (0, 0, 0), - }, - operator: None, - comments_after_type: None, - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() + }), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() }, }), leading_comments: None, trailing_comments: None, #[cfg(feature = "ast-span")] - span: (0, 0, 0), + span: Span::default(), }, OptionalComma { optional_comma: true, - trailing_comments: None, - _a: PhantomData::default(), + ..Default::default() } ), ], - comments_before_grpchoice: None, - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() }], - #[cfg(feature = "ast-span")] - span: (0, 0, 0), + ..Default::default() } .to_string(), " key1: \"value1\", key2: \"value2\", ".to_string() diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 24fa02de..90fe595d 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -98,6 +98,14 @@ fn main() -> Result<(), Box> { .author(crate_authors!()) .about("Tool for verifying conformance of CDDL definitions against RFC 8610 and for validating JSON documents and CBOR binary files") .setting(AppSettings::SubcommandRequiredElseHelp) + .arg( + Arg::with_name("allow-missing-definitions") + .long("allow-missing-definitions") + .required(false) + .takes_value(false) + .global(true) + .help("Allow references to missing rule definitions") + ) .subcommand(SubCommand::with_name("compile-cddl") .about("Compile CDDL against RFC 8610") .arg_from_usage("-c --cddl= 'CDDL input file'")) @@ -108,8 +116,8 @@ fn main() -> Result<(), Box> { let matches = app.get_matches(); - if let Some(matches) = matches.subcommand_matches("compile-cddl") { - if let Some(c) = matches.value_of("cddl") { + if let Some(sub_matches) = matches.subcommand_matches("compile-cddl") { + if let Some(c) = sub_matches.value_of("cddl") { let p = Path::new(c); if !p.exists() { error!("CDDL document {:?} does not exist", p); @@ -126,14 +134,20 @@ fn main() -> Result<(), Box> { } let file_content = fs::read_to_string(c)?; - cddl_from_str(&mut lexer_from_str(&file_content), &file_content, true).map(|_| ())?; + cddl_from_str( + &mut lexer_from_str(&file_content), + &file_content, + true, + matches.is_present("allow-missing-definitions"), + ) + .map(|_| ())?; info!("{} is conformant", c); } } - if let Some(matches) = matches.subcommand_matches("compile-json") { - if let Some(j) = matches.value_of("json") { + if let Some(sub_matches) = matches.subcommand_matches("compile-json") { + if let Some(j) = sub_matches.value_of("json") { let p = Path::new(j); if !p.exists() { error!("CDDL document {:?} does not exist", p); @@ -157,10 +171,11 @@ fn main() -> Result<(), Box> { } } - if let Some(matches) = matches.subcommand_matches("validate") { - if let Some(cddl) = matches.value_of("cddl") { + if let Some(sub_matches) = matches.subcommand_matches("validate") { + if let Some(cddl) = sub_matches.value_of("cddl") { #[cfg(feature = "additional-controls")] - let enabled_features: Option> = matches.values_of("features").map(|f| f.collect()); + let enabled_features: Option> = + sub_matches.values_of("features").map(|f| f.collect()); #[cfg(feature = "additional-controls")] if let Some(enabled_features) = &enabled_features { let mut feature_str = String::from("enabled features: ["); @@ -193,7 +208,7 @@ fn main() -> Result<(), Box> { let cddl_str = fs::read_to_string(cddl)?; - if let Some(files) = matches.values_of("json") { + if let Some(files) = sub_matches.values_of("json") { for file in files { let p = Path::new(file); if !p.exists() { @@ -206,10 +221,15 @@ fn main() -> Result<(), Box> { let r = validate_json_from_str( &cddl_str, &fs::read_to_string(file)?, + matches.is_present("allow-missing-definitions"), enabled_features.as_deref(), ); #[cfg(not(feature = "additional-controls"))] - let r = validate_json_from_str(&cddl_str, &fs::read_to_string(file)?); + let r = validate_json_from_str( + &cddl_str, + &fs::read_to_string(file)?, + matches.is_present("allow-missing-definitions"), + ); match r { Ok(()) => { @@ -222,7 +242,7 @@ fn main() -> Result<(), Box> { } } - if let Some(files) = matches.values_of("cbor") { + if let Some(files) = sub_matches.values_of("cbor") { for file in files { let p = Path::new(file); if !p.exists() { @@ -235,9 +255,18 @@ fn main() -> Result<(), Box> { f.read_to_end(&mut data)?; #[cfg(feature = "additional-controls")] - let c = validate_cbor_from_slice(&cddl_str, &data, None); + let c = validate_cbor_from_slice( + &cddl_str, + &data, + matches.is_present("allow-missing-definitions"), + None, + ); #[cfg(not(feature = "additional-controls"))] - let c = validate_cbor_from_slice(&cddl_str, &data); + let c = validate_cbor_from_slice( + &cddl_str, + &data, + matches.is_present("allow-missing-definitions"), + ); match c { Ok(()) => { @@ -252,7 +281,7 @@ fn main() -> Result<(), Box> { return Ok(()); } - if matches.is_present("stdin") { + if sub_matches.is_present("stdin") { let stdin = io::stdin(); let mut reader = stdin.lock(); @@ -260,9 +289,18 @@ fn main() -> Result<(), Box> { reader.read_to_end(&mut data)?; if let Ok(json) = std::str::from_utf8(&data) { #[cfg(feature = "additional-controls")] - let r = validate_json_from_str(&cddl_str, json, None); + let r = validate_json_from_str( + &cddl_str, + json, + matches.is_present("allow-missing-definitions"), + None, + ); #[cfg(not(feature = "additional-controls"))] - let r = validate_json_from_str(&cddl_str, json); + let r = validate_json_from_str( + &cddl_str, + json, + matches.is_present("allow-missing-definitions"), + ); match r { Ok(()) => { @@ -274,9 +312,18 @@ fn main() -> Result<(), Box> { } } else { #[cfg(feature = "additional-controls")] - let c = validate_cbor_from_slice(&cddl_str, &data, enabled_features.as_deref()); + let c = validate_cbor_from_slice( + &cddl_str, + &data, + matches.is_present("allow-missing-definitions"), + enabled_features.as_deref(), + ); #[cfg(not(feature = "additional-controls"))] - let c = validate_cbor_from_slice(&cddl_str, &data); + let c = validate_cbor_from_slice( + &cddl_str, + &data, + matches.is_present("allow-missing-definitions"), + ); match c { Ok(()) => { diff --git a/src/error.rs b/src/error.rs index b6c2e88d..9feedb22 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,6 @@ +#[cfg(target_arch = "wasm32")] +use crate::lexer::Position; + use std::fmt; #[cfg(target_arch = "wasm32")] @@ -187,3 +190,10 @@ impl From for ErrorMsg { } } } + +#[cfg(target_arch = "wasm32")] +#[derive(Serialize)] +pub struct ParserError { + pub position: Position, + pub msg: ErrorMsg, +} diff --git a/src/faker.rs b/src/faker.rs new file mode 100644 index 00000000..a3e05b7f --- /dev/null +++ b/src/faker.rs @@ -0,0 +1,838 @@ +use crate::{ + ast::{self, *}, + token::{lookup_ident, Token}, + util::*, + visitor::{self, *}, +}; + +use std::collections::HashMap; + +use displaydoc::Display; +use fake::{faker::name::raw::*, locales::EN, Fake, Faker as FFaker}; +use rand::{self, seq::SliceRandom}; +use serde_json::{self, Map, Value}; + +#[cfg(not(target_arch = "wasm32"))] +use crate::{cddl_from_str, lexer_from_str}; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +/// Result type from fake data generation +pub type Result = std::result::Result; + +/// Fake data generation error type +#[derive(Debug, Display)] +pub enum Error { + /// Data cannot be generated from a CDDL document that has no type rules + /// defined + #[displaydoc("no type rules found in the cddl document")] + MissingTypeRules, + /// UTF8 parsing error + #[displaydoc("{0}")] + Utf8Error(std::str::Utf8Error), + /// CDDL parsing error + #[displaydoc("error parsing cddl: {0}")] + CDDLParsing(String), +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +/// JSON data faker for a given CDDL document +pub struct Faker<'a> { + cddl: &'a CDDL<'a>, + faked_json: Option, + entries: Entries<'a>, + ctrl: Option<(Identifier<'a>, Token<'a>)>, + generic_rules: Vec>, + eval_generic_rule: Option<&'a str>, + is_group_to_choice_enum: bool, +} + +impl<'a> Faker<'a> { + /// Construct a new faker from a given CDDL document + pub fn new(cddl: &'a CDDL) -> Self { + Faker { + cddl, + faked_json: None, + entries: Entries::None, + ctrl: None, + generic_rules: Vec::new(), + eval_generic_rule: None, + is_group_to_choice_enum: false, + } + } + + fn process_generic_args( + &mut self, + ident: &Identifier<'a>, + generic_args: &GenericArgs<'a>, + ) -> visitor::Result { + if let Some(rule) = rule_from_ident(self.cddl, ident) { + if let Some(gr) = self + .generic_rules + .iter_mut() + .find(|gr| gr.name == ident.ident) + { + for arg in generic_args.args.iter() { + gr.args.push((*arg.arg).clone()); + } + } else if let Some(params) = generic_params_from_rule(rule) { + self.generic_rules.push(GenericRule { + name: ident.ident, + params, + args: generic_args + .args + .iter() + .cloned() + .map(|arg| *arg.arg) + .collect(), + }); + } + + self.eval_generic_rule = Some(ident.ident); + + return self.visit_rule(rule); + } + + Ok(()) + } +} + +enum Entries<'a> { + None, + Map(HashMap, Type<'a>)>), + Array(Vec<(Option, Type<'a>)>), +} + +impl<'a> Visitor<'a, Error> for Faker<'a> { + fn visit_type_rule(&mut self, tr: &TypeRule<'a>) -> visitor::Result { + if let Some(gp) = &tr.generic_params { + if let Some(gr) = self + .generic_rules + .iter_mut() + .find(|r| r.name == tr.name.ident) + { + gr.params = gp.params.iter().map(|p| p.param.ident).collect(); + } else { + self.generic_rules.push(GenericRule { + name: tr.name.ident, + params: gp.params.iter().map(|p| p.param.ident).collect(), + args: vec![], + }); + } + } + + walk_type_rule(self, tr) + } + + fn visit_group_rule(&mut self, gr: &GroupRule<'a>) -> visitor::Result { + if let Some(gp) = &gr.generic_params { + if let Some(gr) = self + .generic_rules + .iter_mut() + .find(|r| r.name == gr.name.ident) + { + gr.params = gp.params.iter().map(|p| p.param.ident).collect(); + } else { + self.generic_rules.push(GenericRule { + name: gr.name.ident, + params: gp.params.iter().map(|p| p.param.ident).collect(), + args: vec![], + }); + } + } + + walk_group_rule(self, gr) + } + + fn visit_group(&mut self, g: &Group<'a>) -> visitor::Result { + if let Some(gc) = g.group_choices.choose(&mut rand::thread_rng()) { + return self.visit_group_choice(gc); + } + + walk_group(self, g) + } + + fn visit_group_choice(&mut self, gc: &GroupChoice<'a>) -> visitor::Result { + if self.is_group_to_choice_enum { + if let Some(tc) = + type_choices_from_group_choice(self.cddl, gc).choose(&mut rand::thread_rng()) + { + return self.visit_type_choice(tc); + } + } + + walk_group_choice(self, gc) + } + + fn visit_type(&mut self, t: &Type<'a>) -> visitor::Result { + if let Some(tc) = t.type_choices.choose(&mut rand::thread_rng()) { + return self.visit_type_choice(tc); + } + + walk_type(self, t) + } + + fn visit_type_groupname_entry( + &mut self, + entry: &TypeGroupnameEntry<'a>, + ) -> visitor::Result { + if let Entries::Array(entries) = &mut self.entries { + entries.push(( + entry.occur.clone().map(|o| o.occur), + Type::from(&entry.name), + )); + } else if let Entries::Map(_) = self.entries { + if let Some(rule) = group_rule_from_ident(self.cddl, &entry.name) { + return self.visit_group_rule(rule); + } + } + + Ok(()) + } + + fn visit_control_operator( + &mut self, + target: &Type2<'a>, + ctrl: &Token<'a>, + controller: &Type2<'a>, + ) -> visitor::Result { + if let Type2::Typename(Typename { + ident: target_ident, + .. + }) = target + { + if let Type2::Typename(Typename { + ident: controller_ident, + .. + }) = controller + { + if let Some(name) = self.eval_generic_rule { + if let Some(gr) = self + .generic_rules + .iter() + .cloned() + .find(|gr| gr.name == name) + { + for (idx, gp) in gr.params.iter().enumerate() { + if let Some(arg) = gr.args.get(idx) { + if *gp == target_ident.ident { + let t2 = Type2::from((*arg).clone()); + + if *gp == controller_ident.ident { + return self.visit_control_operator(&t2, ctrl, &t2); + } + + return self.visit_control_operator(&arg.type2, ctrl, controller); + } + } + } + } + } + } + + if let Some(name) = self.eval_generic_rule { + if let Some(gr) = self + .generic_rules + .iter() + .cloned() + .find(|gr| gr.name == name) + { + for (idx, gp) in gr.params.iter().enumerate() { + if let Some(arg) = gr.args.get(idx) { + if *gp == target_ident.ident { + let t2 = Type2::from((*arg).clone()); + return self.visit_control_operator(&t2, ctrl, controller); + } + } + } + } + } + } + + match ctrl { + Token::SIZE => match target { + Type2::Typename(Typename { ident, .. }) + if is_ident_string_data_type(self.cddl, ident) + || is_ident_uint_data_type(self.cddl, ident) => + { + self.ctrl = Some((ident.clone(), ctrl.clone())); + self.visit_type2(controller)?; + self.ctrl = None; + } + _ => return Ok(()), + }, + _ => return Ok(()), + } + + Ok(()) + } + + fn visit_range( + &mut self, + lower: &Type2<'a>, + upper: &Type2<'a>, + is_inclusive: bool, + ) -> visitor::Result { + match lower { + Type2::IntValue(IntValue { value: lower_v, .. }) => match upper { + Type2::IntValue(IntValue { value: upper, .. }) => { + if is_inclusive { + self.faked_json = Some((*lower_v..=*upper).fake::().into()); + } else { + self.faked_json = Some((*lower_v..*upper).fake::().into()); + } + } + Type2::UintValue(UintValue { value: upper, .. }) => { + if is_inclusive { + self.faked_json = Some((*lower_v..=*upper as isize).fake::().into()); + } else { + self.faked_json = Some((*lower_v..*upper as isize).fake::().into()); + } + } + Type2::Typename(Typename { + ident, + generic_args, + .. + }) => { + if let Some(ga) = generic_args { + return self.process_generic_args(ident, ga); + } + + if let Some(t2) = numeric_range_bound_from_ident(self.cddl, ident) { + return self.visit_range(lower, t2, is_inclusive); + } + } + _ => return Ok(()), + }, + Type2::UintValue(UintValue { value: lower_v, .. }) => match upper { + Type2::IntValue(IntValue { value: upper, .. }) => { + if is_inclusive { + self.faked_json = Some((*lower_v..=*upper as usize).fake::().into()); + } else { + self.faked_json = Some((*lower_v..*upper as usize).fake::().into()); + } + } + Type2::UintValue(UintValue { value: upper, .. }) => { + if is_inclusive { + self.faked_json = Some((*lower_v..=*upper).fake::().into()); + } else { + self.faked_json = Some((*lower_v..*upper).fake::().into()); + } + } + Type2::Typename(Typename { + ident, + generic_args, + .. + }) => { + if let Some(ga) = generic_args { + return self.process_generic_args(ident, ga); + } + + if let Some(t2) = numeric_range_bound_from_ident(self.cddl, ident) { + return self.visit_range(lower, t2, is_inclusive); + } + } + _ => return Ok(()), + }, + Type2::FloatValue(FloatValue { value: lower_v, .. }) => match upper { + Type2::FloatValue(FloatValue { value: upper, .. }) => { + if is_inclusive { + self.faked_json = Some((*lower_v..=*upper).fake::().into()); + } else { + self.faked_json = Some((*lower_v..*upper).fake::().into()); + } + } + Type2::Typename(Typename { + ident, + generic_args, + .. + }) => { + if let Some(ga) = generic_args { + return self.process_generic_args(ident, ga); + } + + if let Some(t2) = numeric_range_bound_from_ident(self.cddl, ident) { + return self.visit_range(lower, t2, is_inclusive); + } + } + _ => return Ok(()), + }, + Type2::Typename(Typename { + ident, + generic_args, + .. + }) => { + if let Some(ga) = generic_args { + return self.process_generic_args(ident, ga); + } + + if let Some(t2) = numeric_range_bound_from_ident(self.cddl, ident) { + return self.visit_range(t2, upper, is_inclusive); + } + } + _ => return Ok(()), + } + + Ok(()) + } + + fn visit_value_member_key_entry( + &mut self, + entry: &ValueMemberKeyEntry<'a>, + ) -> visitor::Result { + if let Entries::Map(entries) = &mut self.entries { + let occur = entry.occur.clone().map(|o| o.occur); + + if let Some(mk) = &entry.member_key { + match mk { + MemberKey::Bareword(BarewordMemberKey { ident, .. }) => { + entries.insert(ident.ident.to_string(), (occur, entry.entry_type.clone())); + } + MemberKey::Value(ValueMemberKey { value, .. }) => { + entries.insert(value.to_string(), (occur, entry.entry_type.clone())); + } + _ => return Ok(()), + } + } + } else if let Entries::Array(entries) = &mut self.entries { + entries.push(( + entry.occur.clone().map(|o| o.occur), + entry.entry_type.clone(), + )); + } + + Ok(()) + } + + fn visit_type2(&mut self, t2: &Type2<'a>) -> visitor::Result { + match t2 { + Type2::TextValue(TextValue { value, .. }) => { + if let Some(Value::Array(array)) = self.faked_json.as_mut() { + array.push(value.as_ref().into()); + } else { + self.faked_json = Some(value.as_ref().into()); + } + } + Type2::UTF8ByteString(Utf8ByteString { value, .. }) => { + let value = std::str::from_utf8(value.as_ref()).map_err(Error::Utf8Error)?; + + if let Some(Value::Array(array)) = self.faked_json.as_mut() { + array.push(value.into()); + } else { + self.faked_json = Some(value.into()); + } + } + Type2::UintValue(UintValue { value, .. }) => match &self.ctrl { + Some((target, Token::SIZE)) => { + if is_ident_string_data_type(self.cddl, target) { + self.faked_json = Some(value.fake::().into()) + } else if is_ident_uint_data_type(self.cddl, target) { + self.faked_json = Some((0..256u64.pow(*value as u32)).fake::().into()); + } + } + _ => self.faked_json = Some((*value).into()), + }, + Type2::IntValue(IntValue { value, .. }) => self.faked_json = Some((*value).into()), + Type2::FloatValue(FloatValue { value, .. }) => self.faked_json = Some((*value).into()), + Type2::Array(Array { group, .. }) => { + self.faked_json = Some(Value::Array(Vec::new())); + self.entries = Entries::Array(Vec::new()); + self.visit_group(group)?; + + if let Entries::Array(array_entries) = &mut self.entries { + if let Some(Value::Array(array)) = self.faked_json.as_mut() { + for (occur, entry) in array_entries.iter() { + if let Some(occur) = occur { + match occur { + #[cfg(feature = "ast-span")] + Occur::Optional(_) => { + if FFaker.fake::() { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + + continue; + } + } + #[cfg(not(feature = "ast-span"))] + Occur::Optional => { + if FFaker.fake::() { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + + continue; + } + } + #[cfg(feature = "ast-span")] + Occur::ZeroOrMore(_) => { + let lower = (0..2).fake::(); + let upper = (0..5).fake::(); + + // If the random lower >= random upper, the random array + // will be empty. + for _ in lower..upper { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + + // Break due to ambiguity + break; + } + #[cfg(not(feature = "ast-span"))] + Occur::ZeroOrMore => { + let lower = (0..2).fake::(); + let upper = (0..5).fake::(); + + // If the random lower >= random upper, the random array + // will be empty. + for _ in lower..upper { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + + // Break due to ambiguity + break; + } + #[cfg(feature = "ast-span")] + Occur::OneOrMore(_) => { + for _ in 0..(1..5).fake::() { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + + // Break due to ambiguity + break; + } + #[cfg(not(feature = "ast-span"))] + Occur::OneOrMore => { + for _ in 0..(1..5).fake::() { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + + // Break due to ambiguity + break; + } + Occur::Exact { lower, upper, .. } => { + if let Some(lower) = lower { + if let Some(upper) = upper { + for _ in *lower..*upper { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + } else { + for _ in *lower..(lower + (0..5).fake::()) { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + } + } else if let Some(upper) = upper { + for _ in 0..(upper - (0..=*upper).fake::()) { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + } + } + } + } else { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(entry)?; + + if let Some(value) = entry_f.faked_json { + array.push(value); + } + } + } + } + } + + self.entries = Entries::None; + } + Type2::Map(ast::Map { group, .. }) => { + self.faked_json = Some(Value::Object(Map::default())); + self.entries = Entries::Map(HashMap::new()); + self.visit_group(group)?; + + if let Entries::Map(map_entries) = &mut self.entries { + if let Some(Value::Object(map)) = self.faked_json.as_mut() { + for (k, (occur, v)) in map_entries.iter() { + #[cfg(feature = "ast-span")] + let generate = if let Some(Occur::Optional(_)) = occur { + FFaker.fake::() + } else { + true + }; + #[cfg(not(feature = "ast-span"))] + let generate = if let Some(Occur::Optional) = occur { + FFaker.fake::() + } else { + true + }; + + if generate { + let mut entry_f = Faker::new(self.cddl); + entry_f.eval_generic_rule = self.eval_generic_rule; + entry_f.generic_rules = self.generic_rules.clone(); + entry_f.ctrl = self.ctrl.clone(); + entry_f.visit_type(v)?; + + if let Some(value) = entry_f.faked_json { + map.insert(k.to_string(), value); + } + } + } + } + } + + self.entries = Entries::None; + } + Type2::Typename(Typename { + ident, + generic_args, + .. + }) => { + if let Some(ga) = generic_args { + return self.process_generic_args(ident, ga); + } + + return self.visit_identifier(ident); + } + Type2::ChoiceFromGroup(ChoiceFromGroup { + ident, + generic_args, + .. + }) => { + if let Some(ga) = generic_args { + self.is_group_to_choice_enum = true; + + return self.process_generic_args(ident, ga); + } + + self.is_group_to_choice_enum = true; + self.visit_identifier(ident)?; + self.is_group_to_choice_enum = false; + } + _ => walk_type2(self, t2)?, + } + + Ok(()) + } + + fn visit_identifier(&mut self, ident: &Identifier<'a>) -> visitor::Result { + if let Some(name) = self.eval_generic_rule { + if let Some(gr) = self + .generic_rules + .iter() + .cloned() + .find(|gr| gr.name == name) + { + for (idx, gp) in gr.params.iter().enumerate() { + if *gp == ident.ident { + if let Some(arg) = gr.args.get(idx) { + return self.visit_type1(arg); + } + } + } + } + } + + if is_ident_string_data_type(self.cddl, ident) { + self.faked_json = Some(Name(EN).fake::().into()); + } else if let Token::NUMBER = lookup_ident(ident.ident) { + if FFaker.fake::() { + self.faked_json = Some(FFaker.fake::().into()); + } else { + self.faked_json = Some(FFaker.fake::().into()); + } + } else if is_ident_float_data_type(self.cddl, ident) { + self.faked_json = Some(FFaker.fake::().into()); + } else if is_ident_uint_data_type(self.cddl, ident) { + self.faked_json = Some(FFaker.fake::().into()); + } else if is_ident_nint_data_type(self.cddl, ident) { + self.faked_json = Some((..0).fake::().into()); + } else if is_ident_integer_data_type(self.cddl, ident) { + self.faked_json = Some(FFaker.fake::().into()); + } else if is_ident_bool_data_type(self.cddl, ident) { + self.faked_json = Some(FFaker.fake::().into()); + } else if is_ident_null_data_type(self.cddl, ident) { + self.faked_json = Some(Value::Null); + } else if let Some(rule) = rule_from_ident(self.cddl, ident) { + self.visit_rule(rule)?; + } else { + self.faked_json = Some(ident.ident.into()); + } + + Ok(()) + } +} + +#[cfg(not(target_arch = "wasm32"))] +/// Generate fake JSON from a given CDDL document string +pub fn fake_json_from_cddl_str(cddl_str: &str) -> Result { + let mut lexer = lexer_from_str(cddl_str); + let cddl = cddl_from_str(&mut lexer, cddl_str, true, false).map_err(Error::CDDLParsing)?; + let mut faker = Faker::new(&cddl); + + for rule in faker.cddl.rules.iter() { + if let Rule::Type { rule, .. } = rule { + faker.visit_type_rule(rule)?; + break; + } + } + + if let Some(faked_json) = faker.faked_json { + return Ok(faked_json.to_string()); + } + + Err(Error::MissingTypeRules) +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +/// Generate fake JSON from a given CDDL document string +pub fn fake_json_from_cddl_str( + cddl_str: &str, + allow_missing_definitions: bool, +) -> std::result::Result { + use crate::{ + error::ParserError, + lexer::Lexer, + parser::{self, Parser}, + }; + + let mut l = Lexer::new(cddl_str); + let mut p = Parser::new((&mut l).iter(), cddl_str, allow_missing_definitions) + .map_err(|e| JsValue::from(e.to_string()))?; + let c = p.parse_cddl().map_err(|e| JsValue::from(e.to_string()))?; + if !p.errors.is_empty() { + return Err( + JsValue::from_serde( + &p.errors + .iter() + .filter_map(|e| { + if let parser::Error::PARSER { position, msg } = e { + Some(ParserError { + position: *position, + msg: msg.clone(), + }) + } else { + None + } + }) + .collect::>(), + ) + .map_err(|e| JsValue::from(e.to_string()))?, + ); + } + let mut faker = Faker::new(&c); + + for rule in faker.cddl.rules.iter() { + if let Rule::Type { rule, .. } = rule { + faker + .visit_type_rule(rule) + .map_err(|e| JsValue::from(e.to_string()))?; + break; + } + } + + if let Some(faked_json) = faker.faked_json { + return Ok(faked_json.to_string().into()); + } + + Err(Error::MissingTypeRules.to_string().into()) +} + +#[cfg(test)] +#[cfg(not(target_arch = "wasm32"))] +mod tests { + use super::*; + + use indoc::indoc; + + #[test] + fn test_faker() -> Result<()> { + let cddl = indoc!( + r#" + messages = message<"reboot", "now"> / message<"sleep", a .. 5> + message = {type: t, value: v} + a = 1 / 2 / 3 + "# + ); + + println!("{}", fake_json_from_cddl_str(&cddl)?); + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 856931c9..3a3c7065 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ //! [![crates.io](https://img.shields.io/crates/v/cddl.svg)](https://crates.io/crates/cddl) //! [![docs.rs](https://docs.rs/cddl/badge.svg)](https://docs.rs/cddl) //! [![Publish -//! packages](https://github.com/anweiss/cddl/workflows/Publish%20packages/badge.svg?branch=0.9.0-beta.0&event=release)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Publish+packages%22) +//! packages](https://github.com/anweiss/cddl/workflows/Publish%20packages/badge.svg?branch=0.9.0-beta.1&event=release)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Publish+packages%22) //! [![Build and //! Test](https://github.com/anweiss/cddl/workflows/Build%20and%20Test/badge.svg)](https://github.com/anweiss/cddl/actions?query=workflow%3A%22Build+and+Test%22) //! [![Active @@ -227,7 +227,7 @@ //! use cddl::{lexer_from_str, parser::cddl_from_str}; //! //! let input = r#"myrule = int"#; -//! assert!(cddl_from_str(&mut lexer_from_str(input), input, true).is_ok()) +//! assert!(cddl_from_str(&mut lexer_from_str(input), input, true, false).is_ok()) //! ``` //! //! ### Validating JSON @@ -407,7 +407,7 @@ //! let json = r#""v""#; //! //! #[cfg(feature = "additional-controls")] -//! assert!(validate_json_from_str(cddl, json, Some(&["json"])).is_ok()) +//! assert!(validate_json_from_str(cddl, json, false, Some(&["json"])).is_ok()) //! ``` //! //! #### Comparing with JSON schema and JSON schema language @@ -493,7 +493,7 @@ //! let cbor = b"\x02"; //! //! #[cfg(feature = "additional-controls")] -//! assert!(validate_cbor_from_slice(cddl, cbor, Some(&["cbor"])).is_ok()) +//! assert!(validate_cbor_from_slice(cddl, cbor, false, Some(&["cbor"])).is_ok()) //! ``` //! //! ## `no_std` support @@ -533,15 +533,6 @@ extern crate alloc; #[cfg(not(feature = "std"))] extern crate core as std; -#[cfg(feature = "std")] -extern crate serde_json; - -#[cfg(feature = "std")] -extern crate uriparse; - -#[cfg(feature = "std")] -extern crate base64_url; - /// Abstract syntax tree representing a CDDL definition pub mod ast; /// Static error messages @@ -555,10 +546,16 @@ pub mod parser; pub mod repl; /// CDDL tokens for lexing pub mod token; +/// CDDL helper functions +pub mod util; /// Validators for JSON and CBOR data structures #[cfg(feature = "std")] pub mod validator; +/// Generate fake JSON data from CDDL +#[cfg(feature = "std")] +pub mod faker; + /// CDDL AST visitor pub mod visitor; diff --git a/src/parser.rs b/src/parser.rs index 90c2135c..6c51e61e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -44,6 +44,9 @@ pub struct Parser<'a, I> where I: Iterator>, { + /// Vec of collected parsing errors + pub errors: Vec, + tokens: I, str_input: &'a str, cur_token: Token<'a>, @@ -52,13 +55,12 @@ where peek_lexer_position: Position, #[cfg(feature = "ast-span")] parser_position: Position, - /// Vec of collected parsing errors - pub errors: Vec, #[cfg(feature = "ast-span")] visited_rule_idents: Vec<(&'a str, Span)>, #[cfg(not(feature = "ast-span"))] visited_rule_idents: Vec<&'a str>, current_rule_generic_param_idents: Option>, + allow_missing_definitions: bool, } /// Parsing error types @@ -108,9 +110,9 @@ where /// use cddl::lexer::Lexer; /// /// let input = r#"mycddl = ( int / float )"#; - /// let p = Parser::new(Lexer::new(input).iter(), input); + /// let p = Parser::new(Lexer::new(input).iter(), input, false); /// ``` - pub fn new(tokens: I, str_input: &'a str) -> Result> { + pub fn new(tokens: I, str_input: &'a str, allow_missing_definitions: bool) -> Result> { let mut p = Parser { tokens, str_input, @@ -123,6 +125,7 @@ where parser_position: Position::default(), visited_rule_idents: Vec::default(), current_rule_generic_param_idents: None, + allow_missing_definitions, }; p.next_token()?; @@ -145,7 +148,7 @@ where /// use cddl::lexer::Lexer; /// /// let input = r#"mycddl = ( int / float )"#; - /// if let Ok(mut p) = Parser::new(Lexer::new(input).iter(), input) { + /// if let Ok(mut p) = Parser::new(Lexer::new(input).iter(), input, false) { /// if let Err(Error::INCREMENTAL) = p.parse_cddl() { /// let _ = p.report_errors(true); /// } @@ -379,33 +382,35 @@ where } } - #[cfg(feature = "ast-span")] - for (rule, span) in self.visited_rule_idents.iter() { - if !c.rules.iter().any(|r| r.name() == *rule) { - self.errors.push(Error::PARSER { - position: Position { - column: 0, - index: span.0, - line: span.2, - range: (span.0, span.1), - }, - msg: ErrorMsg { - short: format!("missing definition for rule {}", rule), - extended: None, - }, - }) + if !self.allow_missing_definitions { + #[cfg(feature = "ast-span")] + for (rule, span) in self.visited_rule_idents.iter() { + if !c.rules.iter().any(|r| r.name() == *rule) { + self.errors.push(Error::PARSER { + position: Position { + column: 0, + index: span.0, + line: span.2, + range: (span.0, span.1), + }, + msg: ErrorMsg { + short: format!("missing definition for \"{}\"", rule), + extended: None, + }, + }) + } } - } - #[cfg(not(feature = "ast-span"))] - for rule in self.visited_rule_idents.iter() { - if !c.rules.iter().any(|r| r.name() == *rule) { - self.errors.push(Error::PARSER { - msg: ErrorMsg { - short: format!("missing definition for rule {}", rule), - extended: None, - }, - }) + #[cfg(not(feature = "ast-span"))] + for rule in self.visited_rule_idents.iter() { + if !c.rules.iter().any(|r| r.name() == *rule) { + self.errors.push(Error::PARSER { + msg: ErrorMsg { + short: format!("missing definition for \"{}\"", rule), + extended: None, + }, + }) + } } } @@ -573,7 +578,7 @@ where self.advance_newline()?; #[cfg(feature = "ast-span")] - let span = ( + let span = Span( begin_rule_range, self.parser_position.range.1, begin_rule_line, @@ -604,7 +609,7 @@ where let t = self.parse_type(None)?; #[cfg(feature = "ast-span")] - let span = ( + let span = Span( begin_rule_range, self.parser_position.range.1, begin_rule_line, @@ -679,19 +684,20 @@ where // TODO: Replace with box pattern destructuring once supported in stable if let GroupEntry::ValueMemberKey { ge, .. } = &group_entry.0 { if ge.occur.is_none() && ge.member_key.is_none() { - let value = self.parse_type(Some(Type2::ParenthesizedType { - #[cfg(feature = "ast-comments")] - comments_before_type: comments_before_group.clone(), - pt: ge.entry_type.clone(), - #[cfg(feature = "ast-comments")] - comments_after_type: comments_after_group.clone(), - #[cfg(feature = "ast-span")] - span: ( - begin_pt_range, - self.parser_position.range.1, - begin_rule_line, - ), - }))?; + let value = + self.parse_type(Some(Type2::ParenthesizedType(ParenthesizedType { + #[cfg(feature = "ast-comments")] + comments_before_type: comments_before_group.clone(), + pt: ge.entry_type.clone(), + #[cfg(feature = "ast-comments")] + comments_after_type: comments_after_group.clone(), + #[cfg(feature = "ast-span")] + span: Span( + begin_pt_range, + self.parser_position.range.1, + begin_rule_line, + ), + })))?; #[cfg(feature = "ast-span")] { @@ -714,7 +720,7 @@ where #[cfg(feature = "ast-comments")] comments_after_rule, #[cfg(feature = "ast-span")] - span: (begin_rule_range, end_rule_range, begin_rule_line), + span: Span(begin_rule_range, end_rule_range, begin_rule_line), }); } } @@ -741,7 +747,7 @@ where #[cfg(feature = "ast-comments")] comments_after_rule, #[cfg(feature = "ast-span")] - span: (begin_rule_range, end_rule_range, begin_rule_line), + span: Span(begin_rule_range, end_rule_range, begin_rule_line), }) } _ => { @@ -782,7 +788,7 @@ where } #[cfg(feature = "ast-span")] - let span = ( + let span = Span( begin_rule_range, self.parser_position.range.1, begin_rule_line, @@ -903,7 +909,7 @@ where #[cfg(feature = "ast-span")] { let end_range = self.lexer_position.range.1; - generic_params.span = (begin_range, end_range, self.lexer_position.line); + generic_params.span = Span(begin_range, end_range, self.lexer_position.line); } Ok(generic_params) @@ -973,7 +979,7 @@ where #[cfg(feature = "ast-span")] { - generic_args.span = ( + generic_args.span = Span( begin_generic_arg_range, self.parser_position.range.1, begin_generic_arg_line, @@ -992,18 +998,19 @@ where } #[cfg(feature = "ast-span")] - let begin_type_range = if let Some(Type2::ParenthesizedType { span, .. }) = parenthesized_type { - self.parser_position.line = span.2; + let begin_type_range = + if let Some(Type2::ParenthesizedType(ParenthesizedType { span, .. })) = parenthesized_type { + self.parser_position.line = span.2; - span.0 - } else { - self.parser_position.range.0 - }; + span.0 + } else { + self.parser_position.range.0 + }; let mut t = Type { type_choices: Vec::new(), #[cfg(feature = "ast-span")] - span: (begin_type_range, 0, self.parser_position.line), + span: Span(begin_type_range, 0, self.parser_position.line), }; #[cfg(feature = "ast-comments")] @@ -1072,7 +1079,7 @@ where #[cfg(feature = "ast-span")] let mut begin_type1_range = self.lexer_position.range.0; - let t2_1 = if let Some(Type2::ParenthesizedType { + let t2_1 = if let Some(Type2::ParenthesizedType(ParenthesizedType { #[cfg(feature = "ast-comments")] comments_before_type, pt, @@ -1080,7 +1087,7 @@ where comments_after_type, #[cfg(feature = "ast-span")] span, - }) = parenthesized_type + })) = parenthesized_type { #[cfg(feature = "ast-span")] { @@ -1088,7 +1095,7 @@ where begin_type1_range = span.0; } - Type2::ParenthesizedType { + Type2::ParenthesizedType(ParenthesizedType { #[cfg(feature = "ast-comments")] comments_before_type, pt, @@ -1096,13 +1103,13 @@ where comments_after_type, #[cfg(feature = "ast-span")] span, - } + }) } else { self.parse_type2()? }; #[cfg(feature = "ast-span")] - let mut span = ( + let mut span = Span( begin_type1_range, self.lexer_position.range.1, begin_type1_line, @@ -1142,7 +1149,7 @@ where #[cfg(feature = "ast-span")] { - span = ( + span = Span( begin_type1_range, self.parser_position.range.1, begin_type1_line, @@ -1204,71 +1211,75 @@ where } #[cfg(feature = "ast-span")] - let span = ( + let span = Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, ); match value { - token::Value::TEXT(t) => Ok(Type2::TextValue { + token::Value::TEXT(t) => Ok(Type2::TextValue(TextValue { value: t.clone(), #[cfg(feature = "ast-span")] span, - }), - token::Value::INT(i) => Ok(Type2::IntValue { + })), + token::Value::INT(i) => Ok(Type2::IntValue(IntValue { value: *i, #[cfg(feature = "ast-span")] span, - }), - token::Value::UINT(ui) => Ok(Type2::UintValue { + })), + token::Value::UINT(ui) => Ok(Type2::UintValue(UintValue { value: *ui, #[cfg(feature = "ast-span")] span, - }), - token::Value::FLOAT(f) => Ok(Type2::FloatValue { + })), + token::Value::FLOAT(f) => Ok(Type2::FloatValue(FloatValue { value: *f, #[cfg(feature = "ast-span")] span, - }), + })), token::Value::BYTE(token::ByteValue::UTF8(Cow::Borrowed(utf8))) => { - Ok(Type2::UTF8ByteString { + Ok(Type2::UTF8ByteString(Utf8ByteString { value: Cow::Borrowed(utf8), #[cfg(feature = "ast-span")] span, - }) + })) } token::Value::BYTE(token::ByteValue::UTF8(Cow::Owned(utf8))) => { - Ok(Type2::UTF8ByteString { + Ok(Type2::UTF8ByteString(Utf8ByteString { value: Cow::Owned(utf8.to_owned()), #[cfg(feature = "ast-span")] span, - }) + })) } token::Value::BYTE(token::ByteValue::B16(Cow::Borrowed(b16))) => { - Ok(Type2::B16ByteString { + Ok(Type2::B16ByteString(B16ByteString { value: Cow::Borrowed(b16), #[cfg(feature = "ast-span")] span, - }) + })) + } + token::Value::BYTE(token::ByteValue::B16(Cow::Owned(b16))) => { + Ok(Type2::B16ByteString(B16ByteString { + value: Cow::Owned(b16.to_owned()), + #[cfg(feature = "ast-span")] + span, + })) } - token::Value::BYTE(token::ByteValue::B16(Cow::Owned(b16))) => Ok(Type2::B16ByteString { - value: Cow::Owned(b16.to_owned()), - #[cfg(feature = "ast-span")] - span, - }), token::Value::BYTE(token::ByteValue::B64(Cow::Borrowed(b64))) => { - Ok(Type2::B64ByteString { + Ok(Type2::B64ByteString(B64ByteString { value: Cow::Borrowed(b64), #[cfg(feature = "ast-span")] span, - }) + })) + } + token::Value::BYTE(token::ByteValue::B64(Cow::Owned(b64))) => { + Ok(Type2::B64ByteString(B64ByteString { + value: Cow::Owned(b64.to_owned()), + #[cfg(feature = "ast-span")] + span, + })) } - token::Value::BYTE(token::ByteValue::B64(Cow::Owned(b64))) => Ok(Type2::B64ByteString { - value: Cow::Owned(b64.to_owned()), - #[cfg(feature = "ast-span")] - span, - }), } } @@ -1287,12 +1298,39 @@ where #[cfg(feature = "ast-span")] let end_type2_range = self.parser_position.range.1; - return Ok(Type2::Typename { + #[cfg(feature = "ast-span")] + if let Some(params) = &self.current_rule_generic_param_idents { + if !params.iter().any(|&p| p == ident.ident) + && !self + .visited_rule_idents + .contains(&(ident.ident, ident.span)) + { + self.visited_rule_idents.push((ident.ident, ident.span)); + } + } else if !self + .visited_rule_idents + .contains(&(ident.ident, ident.span)) + { + self.visited_rule_idents.push((ident.ident, ident.span)); + } + + #[cfg(not(feature = "ast-span"))] + if let Some(params) = &self.current_rule_generic_param_idents { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.contains(&ident.ident) + { + self.visited_rule_idents.push(ident.ident); + } + } else if !self.visited_rule_idents.contains(&ident.ident) { + self.visited_rule_idents.push(ident.ident); + } + + return Ok(Type2::Typename(Typename { ident, generic_args: Some(ga), #[cfg(feature = "ast-span")] - span: (begin_type2_range, end_type2_range, begin_type2_line), - }); + span: Span(begin_type2_range, end_type2_range, begin_type2_line), + })); } #[cfg(feature = "ast-span")] @@ -1301,16 +1339,39 @@ where self.parser_position.line = self.lexer_position.line; } - Ok(Type2::Typename { + #[cfg(feature = "ast-span")] + let span = Span( + self.parser_position.range.0, + self.parser_position.range.1, + self.parser_position.line, + ); + + #[cfg(feature = "ast-span")] + if let Some(params) = &self.current_rule_generic_param_idents { + if !params.iter().any(|&p| p == ident.0) + && !self.visited_rule_idents.contains(&(ident.0, span)) + { + self.visited_rule_idents.push((ident.0, span)); + } + } else if ident.1.is_none() && !self.visited_rule_idents.iter().any(|r| r.0 == ident.0) { + self.visited_rule_idents.push((ident.0, span)); + } + + #[cfg(not(feature = "ast-span"))] + if let Some(params) = &self.current_rule_generic_param_idents { + if !params.iter().any(|&p| p == ident.0) && !self.visited_rule_idents.contains(&ident.0) { + self.visited_rule_idents.push(ident.0); + } + } else if ident.1.is_none() && !self.visited_rule_idents.iter().any(|r| *r == ident.0) { + self.visited_rule_idents.push(ident.0); + } + + Ok(Type2::Typename(Typename { ident: self.identifier_from_ident_token(*ident), generic_args: None, #[cfg(feature = "ast-span")] - span: ( - self.parser_position.range.0, - self.parser_position.range.1, - self.parser_position.line, - ), - }) + span, + })) } // ( type ) @@ -1341,19 +1402,19 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Ok(Type2::ParenthesizedType { + Ok(Type2::ParenthesizedType(ParenthesizedType { #[cfg(feature = "ast-comments")] comments_before_type, #[cfg(feature = "ast-comments")] comments_after_type, pt, #[cfg(feature = "ast-span")] - span: ( + span: Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, ), - }) + })) } // { group } @@ -1380,7 +1441,7 @@ where }; #[cfg(feature = "ast-span")] - let span = ( + let span = Span( begin_type2_range, self.lexer_position.range.1, begin_type2_line, @@ -1391,7 +1452,7 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Ok(Type2::Map { + Ok(Type2::Map(Map { #[cfg(feature = "ast-comments")] comments_before_group, group, @@ -1399,7 +1460,7 @@ where span, #[cfg(feature = "ast-comments")] comments_after_group, - }) + })) } // [ group ] @@ -1430,7 +1491,7 @@ where }; #[cfg(feature = "ast-span")] - let span = ( + let span = Span( begin_type2_range, self.lexer_position.range.1, begin_type2_line, @@ -1441,7 +1502,7 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Ok(Type2::Array { + Ok(Type2::Array(Array { #[cfg(feature = "ast-comments")] comments_before_group, group, @@ -1449,7 +1510,7 @@ where comments_after_group, #[cfg(feature = "ast-span")] span, - }) + })) } // ~ typename [genericarg] @@ -1473,24 +1534,24 @@ where if self.peek_token_is(&Token::LANGLEBRACKET) { self.next_token()?; - return Ok(Type2::Unwrap { + return Ok(Type2::Unwrap(Unwrap { #[cfg(feature = "ast-comments")] comments, ident, generic_args: Some(self.parse_genericargs()?), #[cfg(feature = "ast-span")] - span: (0, 0, 0), - }); + span: Span(0, 0, 0), + })); } - return Ok(Type2::Unwrap { + return Ok(Type2::Unwrap(Unwrap { #[cfg(feature = "ast-comments")] comments, ident, generic_args: None, #[cfg(feature = "ast-span")] - span: (0, 0, 0), - }); + span: Span(0, 0, 0), + })); } self.errors.push(Error::PARSER { @@ -1533,7 +1594,7 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Ok(Type2::ChoiceFromInlineGroup { + Ok(Type2::ChoiceFromInlineGroup(ChoiceFromInlineGroup { #[cfg(feature = "ast-comments")] comments, #[cfg(feature = "ast-comments")] @@ -1542,12 +1603,12 @@ where #[cfg(feature = "ast-comments")] comments_after_group, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_type2_range, self.parser_position.range.1, begin_type2_line, ), - }) + })) } Token::IDENT(ident) => { let ident = self.identifier_from_ident_token(*ident); @@ -1556,18 +1617,18 @@ where let generic_args = Some(self.parse_genericargs()?); - return Ok(Type2::ChoiceFromGroup { + return Ok(Type2::ChoiceFromGroup(ChoiceFromGroup { #[cfg(feature = "ast-comments")] comments, ident, generic_args, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_type2_range, self.parser_position.range.1, begin_type2_line, ), - }); + })); } #[cfg(feature = "ast-span")] @@ -1575,18 +1636,18 @@ where self.parser_position.range.1 = self.lexer_position.range.1; } - Ok(Type2::ChoiceFromGroup { + Ok(Type2::ChoiceFromGroup(ChoiceFromGroup { #[cfg(feature = "ast-comments")] comments, ident, generic_args: None, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_type2_range, self.parser_position.range.1, begin_type2_line, ), - }) + })) } _ => { self.errors.push(Error::PARSER { @@ -1651,7 +1712,7 @@ where return Err(Error::INCREMENTAL); } - Ok(Type2::TaggedData { + Ok(Type2::TaggedData(TaggedData { tag, #[cfg(feature = "ast-comments")] comments_before_type, @@ -1659,26 +1720,26 @@ where #[cfg(feature = "ast-comments")] comments_after_type, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_type2_range, self.parser_position.range.1, begin_type2_line, ), - }) + })) } // Tagged data of a major type - (Some(mt), constraint) => Ok(Type2::DataMajorType { + (Some(mt), constraint) => Ok(Type2::DataMajorType(DataMajorType { mt, constraint, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_type2_range, self.lexer_position.range.1, begin_type2_line, ), - }), + })), #[cfg(feature = "ast-span")] - _ => Ok(Type2::Any(( + _ => Ok(Type2::Any(Span( begin_type2_range, self.lexer_position.range.1, begin_type2_line, @@ -1702,16 +1763,16 @@ where self.parser_position.line = self.lexer_position.line; } - Ok(Type2::Typename { + Ok(Type2::Typename(Typename { ident, generic_args: None, #[cfg(feature = "ast-span")] - span: ( + span: Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, ), - }) + })) } None => { #[cfg(feature = "ast-span")] @@ -1777,7 +1838,7 @@ where let mut group = Group { group_choices: Vec::new(), #[cfg(feature = "ast-span")] - span: (begin_group_range, 0, self.lexer_position.line), + span: Span(begin_group_range, 0, self.lexer_position.line), }; group.group_choices.push(self.parse_grpchoice()?); @@ -1813,7 +1874,7 @@ where #[cfg(feature = "ast-comments")] comments_before_grpchoice: None, #[cfg(feature = "ast-span")] - span: (self.lexer_position.range.0, 0, self.lexer_position.line), + span: Span(self.lexer_position.range.0, 0, self.lexer_position.line), }; if let Token::GCHOICE = &self.cur_token { @@ -1962,7 +2023,7 @@ where let group = self.parse_group()?; #[cfg(feature = "ast-span")] - let mut span = ( + let mut span = Span( begin_grpent_range, self.parser_position.range.1, begin_grpent_line, @@ -2007,43 +2068,44 @@ where } #[cfg(feature = "ast-span")] - let mut span = ( + let mut span = Span( begin_grpent_range, self.parser_position.range.1, begin_grpent_line, ); match member_key { - Some(MemberKey::NonMemberKey { + Some(MemberKey::Parenthesized(ParenthesizedMemberKey { #[cfg(feature = "ast-comments")] - non_member_key: NonMemberKey::Type(mut entry_type), + non_member_key: ParenthesizedTypeOrGroup::Type(entry_type), #[cfg(not(feature = "ast-comments"))] - non_member_key: NonMemberKey::Type(entry_type), + non_member_key: ParenthesizedTypeOrGroup::Type(entry_type), #[cfg(feature = "ast-comments")] comments_before_type_or_group, - #[cfg(feature = "ast-comments")] - comments_after_type_or_group, - }) => { + .. + })) => { #[cfg(feature = "ast-span")] if let Token::COMMA = &self.cur_token { span.1 = self.lexer_position.range.1; } #[cfg(feature = "ast-comments")] - let trailing_comments = entry_type.comments_after_type(); + let trailing_comments = entry_type.comments_after_type; #[cfg(feature = "ast-span")] - if let Some((name, generic_args, _)) = entry_type.groupname_entry() { + if let Some((name, generic_args, _)) = entry_type.pt.groupname_entry() { if name.socket.is_none() && token::lookup_ident(name.ident) .in_standard_prelude() .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == name.ident) { + if !params.iter().any(|&p| p == name.ident) + && !self.visited_rule_idents.iter().any(|r| r.0 == name.ident) + { self.visited_rule_idents.push((name.ident, name.span)); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| r.0 == name.ident) { self.visited_rule_idents.push((name.ident, name.span)); } } @@ -2063,17 +2125,19 @@ where } #[cfg(not(feature = "ast-span"))] - if let Some((name, generic_args)) = entry_type.groupname_entry() { + if let Some((name, generic_args)) = entry_type.pt.groupname_entry() { if name.socket.is_none() && token::lookup_ident(name.ident) .in_standard_prelude() .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == name.ident) { + if !params.iter().any(|&p| p == name.ident) + && !self.visited_rule_idents.iter().any(|r| *r == name.ident) + { self.visited_rule_idents.push(name.ident); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| *r == name.ident) { self.visited_rule_idents.push(name.ident); } } @@ -2098,42 +2162,39 @@ where self.next_token()?; } - #[cfg(feature = "ast-comments")] - let trailing_comments = if let Some(comments) = entry_type.comments_after_type() { - Some(comments) - } else { - comments_after_type_or_group - }; - #[cfg(feature = "ast-span")] - if let Some((ident, _, _)) = entry_type.groupname_entry() { + if let Some((ident, _, _)) = entry_type.pt.groupname_entry() { if ident.socket.is_none() && token::lookup_ident(ident.ident) .in_standard_prelude() .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == ident.ident) { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.iter().any(|r| r.0 == ident.ident) + { self.visited_rule_idents.push((ident.ident, ident.span)); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| r.0 == ident.ident) { self.visited_rule_idents.push((ident.ident, ident.span)); } } } #[cfg(not(feature = "ast-span"))] - if let Some((ident, _)) = entry_type.groupname_entry() { + if let Some((ident, _)) = entry_type.pt.groupname_entry() { if ident.socket.is_none() && token::lookup_ident(ident.ident) .in_standard_prelude() .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == ident.ident) { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.iter().any(|r| *r == ident.ident) + { self.visited_rule_idents.push(ident.ident); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| *r == ident.ident) { self.visited_rule_idents.push(ident.ident); } } @@ -2143,7 +2204,7 @@ where ge: Box::from(ValueMemberKeyEntry { occur, member_key: None, - entry_type, + entry_type: entry_type.pt, }), #[cfg(feature = "ast-comments")] leading_comments: comments_before_type_or_group, @@ -2153,13 +2214,13 @@ where span, }) } - Some(MemberKey::NonMemberKey { - non_member_key: NonMemberKey::Group(group), + Some(MemberKey::Parenthesized(ParenthesizedMemberKey { + non_member_key: ParenthesizedTypeOrGroup::Group(group), #[cfg(feature = "ast-comments")] comments_before_type_or_group, #[cfg(feature = "ast-comments")] comments_after_type_or_group, - }) => { + })) => { #[cfg(feature = "ast-span")] if let Token::COMMA = &self.cur_token { span.1 = self.lexer_position.range.1; @@ -2203,10 +2264,12 @@ where .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == ident.ident) { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.iter().any(|r| r.0 == ident.ident) + { self.visited_rule_idents.push((ident.ident, ident.span)); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| r.0 == ident.ident) { self.visited_rule_idents.push((ident.ident, ident.span)); } } @@ -2220,10 +2283,12 @@ where .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == ident.ident) { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.iter().any(|r| *r == ident.ident) + { self.visited_rule_idents.push(ident.ident); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| *r == ident.ident) { self.visited_rule_idents.push(ident.ident); } } @@ -2284,10 +2349,12 @@ where .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == name.ident) { + if !params.iter().any(|&p| p == name.ident) + && !self.visited_rule_idents.iter().any(|r| r.0 == name.ident) + { self.visited_rule_idents.push((name.ident, name.span)); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| r.0 == name.ident) { self.visited_rule_idents.push((name.ident, name.span)); } } @@ -2322,10 +2389,12 @@ where .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == name.ident) { + if !params.iter().any(|&p| p == name.ident) + && !self.visited_rule_idents.iter().any(|r| *r == name.ident) + { self.visited_rule_idents.push(name.ident); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| *r == name.ident) { self.visited_rule_idents.push(name.ident); } } @@ -2351,10 +2420,12 @@ where .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == ident.ident) { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.iter().any(|r| r.0 == ident.ident) + { self.visited_rule_idents.push((ident.ident, ident.span)); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| r.0 == ident.ident) { self.visited_rule_idents.push((ident.ident, ident.span)); } } @@ -2368,10 +2439,12 @@ where .is_none() { if let Some(params) = &self.current_rule_generic_param_idents { - if !params.iter().any(|&p| p == ident.ident) { + if !params.iter().any(|&p| p == ident.ident) + && !self.visited_rule_idents.iter().any(|r| *r == ident.ident) + { self.visited_rule_idents.push(ident.ident); } - } else { + } else if !self.visited_rule_idents.iter().any(|r| *r == ident.ident) { self.visited_rule_idents.push(ident.ident); } } @@ -2426,7 +2499,7 @@ where let ident = self.identifier_from_ident_token((ident.0, ident.1)); #[cfg(feature = "ast-span")] { - ident.span = (begin_memberkey_range, end_t1_range, begin_memberkey_line); + ident.span = Span(begin_memberkey_range, end_t1_range, begin_memberkey_line); } self.next_token()?; @@ -2468,19 +2541,19 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - let t1 = MemberKey::Type1 { + let t1 = MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident, generic_args: None, #[cfg(feature = "ast-span")] - span: (begin_memberkey_range, end_t1_range, begin_memberkey_line), - }, + span: Span(begin_memberkey_range, end_t1_range, begin_memberkey_line), + }), operator: None, #[cfg(feature = "ast-comments")] comments_after_type: None, #[cfg(feature = "ast-span")] - span: (begin_memberkey_range, end_t1_range, begin_memberkey_line), + span: Span(begin_memberkey_range, end_t1_range, begin_memberkey_line), }), #[cfg(feature = "ast-comments")] comments_before_cut, @@ -2490,12 +2563,12 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, end_memberkey_range, begin_memberkey_line, ), - }; + }); self.next_token()?; @@ -2516,19 +2589,19 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - let t1 = MemberKey::Type1 { + let t1 = MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident, generic_args: None, #[cfg(feature = "ast-span")] - span: (begin_memberkey_range, end_t1_range, begin_memberkey_line), - }, + span: Span(begin_memberkey_range, end_t1_range, begin_memberkey_line), + }), operator: None, #[cfg(feature = "ast-comments")] comments_after_type: None, #[cfg(feature = "ast-span")] - span: (begin_memberkey_range, end_t1_range, begin_memberkey_line), + span: Span(begin_memberkey_range, end_t1_range, begin_memberkey_line), }), #[cfg(feature = "ast-comments")] comments_before_cut, @@ -2538,12 +2611,12 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, end_memberkey_range, begin_memberkey_line, ), - }; + }); self.next_token()?; @@ -2558,19 +2631,19 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Some(MemberKey::Bareword { + Some(MemberKey::Bareword(BarewordMemberKey { ident, #[cfg(feature = "ast-comments")] comments: comments_before_cut, #[cfg(feature = "ast-comments")] comments_after_colon, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, self.parser_position.range.1, begin_memberkey_line, ), - }) + })) }; Ok(mk) @@ -2657,7 +2730,7 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Some(MemberKey::Type1 { + Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(t1), #[cfg(feature = "ast-comments")] comments_before_cut, @@ -2667,12 +2740,12 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap: memberkey_comments, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, end_memberkey_range, begin_memberkey_line, ), - }) + })) } else { #[cfg(feature = "ast-comments")] let comments = self.collect_comments()?; @@ -2700,19 +2773,19 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Some(MemberKey::Value { + Some(MemberKey::Value(ValueMemberKey { value, #[cfg(feature = "ast-comments")] comments, #[cfg(feature = "ast-comments")] comments_after_colon: memberkey_comments, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, self.parser_position.range.1, begin_memberkey_line, ), - }) + })) }; if let Token::COLON = &self.cur_token { @@ -2804,7 +2877,11 @@ where // Parse tokens vec as group if has_group_entries { - let mut p = Parser::new(tokens.into_iter(), self.str_input)?; + let mut p = Parser::new( + tokens.into_iter(), + self.str_input, + self.allow_missing_definitions, + )?; let group = match p.parse_group() { Ok(g) => g, Err(Error::INCREMENTAL) => { @@ -2817,17 +2894,23 @@ where Err(e) => return Err(e), }; - return Ok(Some(MemberKey::NonMemberKey { - non_member_key: NonMemberKey::Group(group), + self.visited_rule_idents.append(&mut p.visited_rule_idents); + + return Ok(Some(MemberKey::Parenthesized(ParenthesizedMemberKey { + non_member_key: ParenthesizedTypeOrGroup::Group(group), #[cfg(feature = "ast-comments")] comments_before_type_or_group, #[cfg(feature = "ast-comments")] comments_after_type_or_group, - })); + }))); } // Parse tokens vec as type - let mut p = Parser::new(tokens.into_iter(), self.str_input)?; + let mut p = Parser::new( + tokens.into_iter(), + self.str_input, + self.allow_missing_definitions, + )?; let t = match p.parse_type(None) { Ok(t) => t, Err(Error::INCREMENTAL) => { @@ -2840,6 +2923,8 @@ where Err(e) => return Err(e), }; + self.visited_rule_idents.append(&mut p.visited_rule_idents); + #[cfg(feature = "ast-comments")] let comments_before_cut = self.collect_comments()?; #[cfg(not(feature = "ast-comments"))] @@ -2865,26 +2950,26 @@ where #[cfg(feature = "ast-span")] let end_memberkey_range = self.lexer_position.range.1; - let t1 = Some(MemberKey::Type1 { + let t1 = Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::ParenthesizedType { + type2: Type2::ParenthesizedType(ParenthesizedType { pt: t, #[cfg(feature = "ast-comments")] comments_before_type: comments_before_type_or_group, #[cfg(feature = "ast-comments")] comments_after_type: comments_after_type_or_group, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, closing_parend_index, begin_memberkey_line, ), - }, + }), #[cfg(feature = "ast-comments")] comments_after_type: comments_before_cut.clone(), operator: None, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, closing_parend_index, begin_memberkey_line, @@ -2898,12 +2983,12 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap: None, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, end_memberkey_range, begin_memberkey_line, ), - }); + })); return Ok(t1); } @@ -2921,26 +3006,26 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Some(MemberKey::Type1 { + Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::ParenthesizedType { + type2: Type2::ParenthesizedType(ParenthesizedType { pt: t, #[cfg(feature = "ast-comments")] comments_before_type: comments_before_type_or_group, #[cfg(feature = "ast-comments")] comments_after_type: comments_after_type_or_group, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, closing_parend_index, begin_memberkey_line, ), - }, + }), #[cfg(feature = "ast-comments")] comments_after_type: comments_before_cut.clone(), operator: None, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, closing_parend_index, begin_memberkey_line, @@ -2954,28 +3039,32 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap: memberkey_comments, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, self.lexer_position.range.0, begin_memberkey_line, ), - }) + })) } else { - Some(MemberKey::NonMemberKey { - non_member_key: NonMemberKey::Type(Type { - type_choices: t.type_choices, + Some(MemberKey::Parenthesized(ParenthesizedMemberKey { + non_member_key: ParenthesizedTypeOrGroup::Type(ParenthesizedType { + pt: t, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, self.parser_position.range.1, begin_memberkey_line, ), + #[cfg(feature = "ast-comments")] + comments_before_type: comments_before_type_or_group.clone(), + #[cfg(feature = "ast-comments")] + comments_after_type: comments_after_type_or_group.clone(), }), #[cfg(feature = "ast-comments")] - comments_before_type_or_group, - #[cfg(feature = "ast-comments")] comments_after_type_or_group, - }) + #[cfg(feature = "ast-comments")] + comments_before_type_or_group, + })) }; Ok(t1) @@ -3015,7 +3104,7 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - return Ok(Some(MemberKey::Type1 { + return Ok(Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(t1), #[cfg(feature = "ast-comments")] comments_before_cut, @@ -3025,12 +3114,12 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap: memberkey_comments, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, end_memberkey_range, begin_memberkey_line, ), - })); + }))); } let t1 = if let Token::ARROWMAP = &self.cur_token { @@ -3046,7 +3135,7 @@ where #[cfg(not(feature = "ast-comments"))] self.advance_newline()?; - Some(MemberKey::Type1 { + Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(t1), #[cfg(feature = "ast-comments")] comments_before_cut, @@ -3056,24 +3145,36 @@ where #[cfg(feature = "ast-comments")] comments_after_arrowmap: memberkey_comments, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, self.parser_position.range.1, begin_memberkey_line, ), - }) + })) } else { - Some(MemberKey::NonMemberKey { - non_member_key: NonMemberKey::Type(Type { - type_choices: vec![TypeChoice { - #[cfg(feature = "ast-comments")] - comments_before_type: None, - #[cfg(feature = "ast-comments")] - comments_after_type: None, - type1: t1, - }], + Some(MemberKey::Parenthesized(ParenthesizedMemberKey { + non_member_key: ParenthesizedTypeOrGroup::Type(ParenthesizedType { + pt: Type { + type_choices: vec![TypeChoice { + #[cfg(feature = "ast-comments")] + comments_before_type: None, + #[cfg(feature = "ast-comments")] + comments_after_type: None, + type1: t1, + }], + #[cfg(feature = "ast-span")] + span: Span( + begin_memberkey_range, + self.parser_position.range.1, + begin_memberkey_line, + ), + }, + #[cfg(feature = "ast-comments")] + comments_before_type: None, + #[cfg(feature = "ast-comments")] + comments_after_type: None, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_memberkey_range, self.parser_position.range.1, begin_memberkey_line, @@ -3083,7 +3184,7 @@ where comments_before_type_or_group: None, #[cfg(feature = "ast-comments")] comments_after_type_or_group: comments_before_cut, - }) + })) }; Ok(t1) @@ -3118,7 +3219,7 @@ where Ok(Some(Occurrence { #[cfg(feature = "ast-span")] - occur: Occur::Optional(( + occur: Occur::Optional(Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, @@ -3145,7 +3246,7 @@ where Ok(Some(Occurrence { #[cfg(feature = "ast-span")] - occur: Occur::OneOrMore(( + occur: Occur::OneOrMore(Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, @@ -3169,7 +3270,7 @@ where lower: None, upper: Some(*u), #[cfg(feature = "ast-span")] - span: ( + span: Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, @@ -3179,7 +3280,7 @@ where #[cfg(feature = "ast-span")] { self.parser_position.range = self.lexer_position.range; - Occur::ZeroOrMore(( + Occur::ZeroOrMore(Span( self.parser_position.range.0, self.parser_position.range.1, self.parser_position.line, @@ -3263,7 +3364,7 @@ where lower, upper, #[cfg(feature = "ast-span")] - span: ( + span: Span( begin_occur_range, self.parser_position.range.1, begin_occur_line, @@ -3303,7 +3404,7 @@ where ident: ident.0, socket: ident.1, #[cfg(feature = "ast-span")] - span: ( + span: Span( self.lexer_position.range.0, self.lexer_position.range.1, self.lexer_position.line, @@ -3320,6 +3421,7 @@ where /// `cddl::lexer_from_str()` /// * `input` - A string slice with the CDDL text input /// * `print_stderr` - When true, print any errors to stderr +/// * `allow_missing_definitions` - When true, allow references to missing rule definitions /// /// # Example /// @@ -3327,15 +3429,16 @@ where /// use cddl::{lexer_from_str, parser::cddl_from_str}; /// /// let input = r#"myrule = int"#; -/// let _ = cddl_from_str(&mut lexer_from_str(input), input, true); +/// let _ = cddl_from_str(&mut lexer_from_str(input), input, true, false); #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "std")] pub fn cddl_from_str<'a>( lexer: &'a mut Lexer<'a>, input: &'a str, print_stderr: bool, + allow_missing_definitions: bool, ) -> std::result::Result, String> { - match Parser::new(lexer.iter(), input).map_err(|e| e.to_string()) { + match Parser::new(lexer.iter(), input, allow_missing_definitions).map_err(|e| e.to_string()) { Ok(mut p) => match p.parse_cddl() { Ok(c) => Ok(c), Err(Error::INCREMENTAL) => { @@ -3364,6 +3467,8 @@ pub fn cddl_from_str<'a>( /// * `lexer` - A mutable reference to a `lexer::Lexer`. Can be created from /// `cddl::lexer_from_str()` /// * `input` - A string slice with the CDDL text input +/// * `allow_missing_definitions` - When true, allow references to missing rule +/// definitions /// /// # Example /// @@ -3379,8 +3484,9 @@ pub fn cddl_from_str<'a>( pub fn cddl_from_str<'a>( lexer: &'a mut Lexer<'a>, input: &'a str, + allow_missing_definitions: bool, ) -> std::result::Result, String> { - match Parser::new(lexer.iter(), input).map_err(|e| e.to_string()) { + match Parser::new(lexer.iter(), input, allow_missing_definitions).map_err(|e| e.to_string()) { Ok(mut p) => match p.parse_cddl() { Ok(c) => Ok(c), Err(Error::INCREMENTAL) => { @@ -3401,6 +3507,7 @@ pub fn cddl_from_str<'a>( /// # Arguments /// /// * `input` - A string slice with the CDDL text input +/// * `allow_missing_definitions` - When true, allow references to missing rule definitions /// /// # Example /// @@ -3416,14 +3523,17 @@ pub fn cddl_from_str<'a>( /// ``` #[cfg(target_arch = "wasm32")] #[wasm_bindgen] -pub fn cddl_from_str(input: &str) -> result::Result { +pub fn cddl_from_str( + input: &str, + allow_missing_definitions: bool, +) -> result::Result { #[derive(Serialize)] struct ParserError { position: Position, msg: ErrorMsg, } - match Parser::new(Lexer::new(input).iter(), input) { + match Parser::new(Lexer::new(input).iter(), input, allow_missing_definitions) { Ok(mut p) => match p.parse_cddl() { Ok(c) => JsValue::from_serde(&c).map_err(|e| JsValue::from(e.to_string())), Err(Error::INCREMENTAL) => { @@ -3460,14 +3570,17 @@ pub fn cddl_from_str(input: &str) -> result::Result { #[cfg(target_arch = "wasm32")] #[wasm_bindgen] /// Formats cddl from input string -pub fn format_cddl_from_str(input: &str) -> result::Result { +pub fn format_cddl_from_str( + input: &str, + allow_missing_definitions: bool, +) -> result::Result { #[derive(Serialize)] struct ParserError { position: Position, msg: ErrorMsg, } - match Parser::new(Lexer::new(input).iter(), input) { + match Parser::new(Lexer::new(input).iter(), input, allow_missing_definitions) { Ok(mut p) => match p.parse_cddl() { Ok(c) => Ok(c.to_string()), Err(Error::INCREMENTAL) => { diff --git a/src/parser_tests.rs b/src/parser_tests.rs index 505956c0..eaec6030 100644 --- a/src/parser_tests.rs +++ b/src/parser_tests.rs @@ -20,11 +20,11 @@ mod tests { let input = indoc!( r#" a = 1234 - a = b + a = 5678 "# ); - match Parser::new(Lexer::new(input).iter(), input) { + match Parser::new(Lexer::new(input).iter(), input, false) { Ok(mut p) => match p.parse_cddl() { Ok(_) => Ok(()), #[cfg(feature = "std")] @@ -41,8 +41,8 @@ mod tests { error: parser errors ┌─ input:2:1 │ - 2 │ a = b - │ ^^^^^ rule with the same identifier is already defined + 2 │ a = 5678 + │ ^^^^^^^^ rule with the same identifier is already defined "# ) @@ -59,8 +59,8 @@ mod tests { ┌── input:2:1 ─── │ - 2 │ a = b - │ ^^^^^ rule with the same identifier is already defined + 2 │ a = 5678 + │ ^^^^^^^^ rule with the same identifier is already defined "# ) @@ -78,7 +78,7 @@ mod tests { let input = r#""#; let mut l = Lexer::new(input); - let gps = Parser::new(&mut l.iter(), input)?.parse_genericparm()?; + let gps = Parser::new(&mut l.iter(), input, false)?.parse_genericparm()?; let expected_output = GenericParams { params: vec![ @@ -86,7 +86,7 @@ mod tests { param: Identifier { ident: "t".into(), socket: None, - span: (1, 2, 1), + span: Span(1, 2, 1), }, comments_before_ident: None, comments_after_ident: None, @@ -95,13 +95,13 @@ mod tests { param: Identifier { ident: "v".into(), socket: None, - span: (4, 5, 1), + span: Span(4, 5, 1), }, comments_before_ident: None, comments_after_ident: None, }, ], - span: (0, 6, 1), + span: Span(0, 6, 1), }; assert_eq!(gps, expected_output); @@ -114,7 +114,7 @@ mod tests { fn verify_genericparm_diagnostic() -> Result<()> { let input = r#"<1, 2>"#; - match Parser::new(Lexer::new(input).iter(), input) { + match Parser::new(Lexer::new(input).iter(), input, false) { Ok(mut p) => match p.parse_genericparm() { Ok(_) => Ok(()), #[cfg(feature = "std")] @@ -175,7 +175,7 @@ mod tests { "# ); - match Parser::new(Lexer::new(input).iter(), input) { + match Parser::new(Lexer::new(input).iter(), input, false) { Ok(mut p) => match p.parse_cddl() { Ok(_) => Ok(()), #[cfg(feature = "std")] @@ -195,10 +195,17 @@ mod tests { │ ^^^^^^^^^^^^^ generic parameters should be between angle brackets '<' and '>' and separated by a comma ',' 2 │ ruleb = rulec 3 │ ruleb = ruled - │ ^^^^^^^^^^^^^ rule with the same identifier is already defined + │ ^^^^^^^^^^^^^ + │ │ │ + │ │ missing definition for "ruled" + │ rule with the same identifier is already defined 4 │ rulec = rulee + │ ^^^^^ missing definition for "rulee" 5 │ rulec = rulee2 - │ ^^^^^^^^^^^^^^ rule with the same identifier is already defined + │ ^^^^^^^^^^^^^^ + │ │ │ + │ │ missing definition for "rulee2" + │ rule with the same identifier is already defined "# ) @@ -241,38 +248,34 @@ mod tests { let mut l = Lexer::new(input); - let generic_args = Parser::new(l.iter(), input)?.parse_genericargs()?; + let generic_args = Parser::new(l.iter(), input, false)?.parse_genericargs()?; let expected_output = GenericArgs { args: vec![ GenericArg { arg: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "reboot".into(), - span: (1, 9, 1), - }, - operator: None, - comments_after_type: None, - span: (1, 9, 1), + span: Span(1, 9, 1), + }), + span: Span(1, 9, 1), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, GenericArg { arg: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "now".into(), - span: (11, 16, 1), - }, - operator: None, - comments_after_type: None, - span: (11, 16, 1), + span: Span(11, 16, 1), + }), + span: Span(11, 16, 1), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (0, 17, 1), + span: Span(0, 17, 1), }; assert_eq!(generic_args, expected_output); @@ -287,65 +290,58 @@ mod tests { let mut l = Lexer::new(input); - let t = Parser::new(l.iter(), input)?.parse_type(None)?; + let t = Parser::new(l.iter(), input, false)?.parse_type(None)?; let expected_output = Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::ParenthesizedType { + type2: Type2::ParenthesizedType(ParenthesizedType { pt: Type { type_choices: vec![ TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "tchoice1".into(), socket: None, - span: (2, 10, 1), + span: Span(2, 10, 1), }, generic_args: None, - span: (2, 10, 1), - }, - operator: None, - comments_after_type: None, - span: (2, 10, 1), + span: Span(2, 10, 1), + }), + span: Span(2, 10, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "tchoice2".into(), socket: None, - span: (13, 21, 1), + span: Span(13, 21, 1), }, generic_args: None, - span: (13, 21, 1), - }, - operator: None, - comments_after_type: None, - span: (13, 21, 1), + span: Span(13, 21, 1), + }), + span: Span(13, 21, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (2, 21, 1), + span: Span(2, 21, 1), }, - comments_before_type: None, - comments_after_type: None, - span: (0, 23, 1), - }, - operator: None, - comments_after_type: None, - span: (0, 23, 1), + span: Span(0, 23, 1), + ..Default::default() + }), + span: Span(0, 23, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (0, 23, 1), + span: Span(0, 23, 1), }; assert_eq!(t, expected_output); @@ -367,193 +363,188 @@ mod tests { let expected_outputs = [ Type1 { - type2: Type2::UintValue { + type2: Type2::UintValue(UintValue { value: 5, - span: (0, 1, 1), - }, + span: Span(0, 1, 1), + }), operator: Some(Operator { operator: RangeCtlOp::RangeOp { is_inclusive: true, - span: (1, 3, 1), + span: Span(1, 3, 1), }, - type2: Type2::UintValue { + type2: Type2::UintValue(UintValue { value: 10, - span: (3, 5, 1), - }, + span: Span(3, 5, 1), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (0, 5, 1), + span: Span(0, 5, 1), }, Type1 { - type2: Type2::FloatValue { + type2: Type2::FloatValue(FloatValue { value: -10.5, - span: (0, 5, 1), - }, + span: Span(0, 5, 1), + }), operator: Some(Operator { operator: RangeCtlOp::RangeOp { is_inclusive: false, - span: (5, 8, 1), + span: Span(5, 8, 1), }, - type2: Type2::FloatValue { + type2: Type2::FloatValue(FloatValue { value: 10.1, - span: (8, 12, 1), - }, + span: Span(8, 12, 1), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (0, 12, 1), + span: Span(0, 12, 1), }, Type1 { - type2: Type2::FloatValue { + type2: Type2::FloatValue(FloatValue { value: 1.5, - span: (0, 3, 1), - }, + span: Span(0, 3, 1), + }), operator: Some(Operator { operator: RangeCtlOp::RangeOp { is_inclusive: true, - span: (3, 5, 1), + span: Span(3, 5, 1), }, - type2: Type2::FloatValue { + type2: Type2::FloatValue(FloatValue { value: 4.5, - span: (5, 8, 1), - }, + span: Span(5, 8, 1), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (0, 8, 1), + span: Span(0, 8, 1), }, Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "my..lower".into(), socket: None, - span: (0, 9, 1), + span: Span(0, 9, 1), }, generic_args: None, - span: (0, 9, 1), - }, + span: Span(0, 9, 1), + }), operator: Some(Operator { operator: RangeCtlOp::RangeOp { is_inclusive: false, - span: (10, 13, 1), + span: Span(10, 13, 1), }, - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "upper".into(), socket: None, - span: (14, 19, 1), + span: Span(14, 19, 1), }, generic_args: None, - span: (14, 19, 1), - }, + span: Span(14, 19, 1), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (0, 19, 1), + span: Span(0, 19, 1), }, Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "target".into(), socket: None, - span: (0, 6, 1), + span: Span(0, 6, 1), }, generic_args: None, - span: (0, 6, 1), - }, + span: Span(0, 6, 1), + }), operator: Some(Operator { operator: RangeCtlOp::CtlOp { ctrl: ".lt", - span: (7, 10, 1), + span: Span(7, 10, 1), }, - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "controller".into(), socket: None, - span: (11, 21, 1), + span: Span(11, 21, 1), }, generic_args: None, - span: (11, 21, 1), - }, + span: Span(11, 21, 1), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (0, 21, 1), + span: Span(0, 21, 1), }, Type1 { - type2: Type2::ParenthesizedType { + type2: Type2::ParenthesizedType(ParenthesizedType { pt: Type { type_choices: vec![ TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "text".into(), socket: None, - span: (2, 6, 1), + span: Span(2, 6, 1), }, generic_args: None, - span: (2, 6, 1), - }, - operator: None, - comments_after_type: None, - span: (2, 6, 1), + span: Span(2, 6, 1), + }), + span: Span(2, 6, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "tstr".into(), socket: None, - span: (9, 13, 1), + span: Span(9, 13, 1), }, generic_args: None, - span: (9, 13, 1), - }, - operator: None, - comments_after_type: None, - span: (9, 13, 1), + span: Span(9, 13, 1), + }), + span: Span(9, 13, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (2, 13, 1), + span: Span(2, 13, 1), }, - comments_before_type: None, - comments_after_type: None, - span: (0, 15, 1), - }, + span: Span(0, 15, 1), + ..Default::default() + }), operator: Some(Operator { operator: RangeCtlOp::CtlOp { ctrl: ".eq", - span: (16, 19, 1), + span: Span(16, 19, 1), }, - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "hello".into(), - span: (20, 27, 1), - }, + span: Span(20, 27, 1), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (0, 27, 1), + span: Span(0, 27, 1), }, ]; for (idx, expected_output) in expected_outputs.iter().enumerate() { let mut l = Lexer::new(inputs[idx]); - let t1 = Parser::new(l.iter(), inputs[idx])?.parse_type1(None)?; + let t1 = Parser::new(l.iter(), inputs[idx], false)?.parse_type1(None)?; assert_eq!(&t1, expected_output); assert_eq!(t1.to_string(), expected_output.to_string()); @@ -581,101 +572,93 @@ mod tests { ]; let expected_outputs = [ - Type2::TextValue { + Type2::TextValue(TextValue { value: "myvalue".into(), - span: (0, 9, 1), - }, - Type2::Typename { + span: Span(0, 9, 1), + }), + Type2::Typename(Typename { ident: Identifier { ident: "message".into(), socket: None, - span: (0, 7, 1), + span: Span(0, 7, 1), }, generic_args: Some(GenericArgs { args: vec![ GenericArg { arg: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "reboot".into(), - span: (8, 16, 1), - }, - operator: None, - comments_after_type: None, - span: (8, 16, 1), + span: Span(8, 16, 1), + }), + span: Span(8, 16, 1), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, GenericArg { arg: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "now".into(), - span: (18, 23, 1), - }, - operator: None, - comments_after_type: None, - span: (18, 23, 1), + span: Span(18, 23, 1), + }), + span: Span(18, 23, 1), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (7, 24, 1), + span: Span(7, 24, 1), }), - span: (0, 24, 1), - }, - Type2::Typename { + span: Span(0, 24, 1), + }), + Type2::Typename(Typename { ident: Identifier { ident: "tcp-option".into(), socket: Some(SocketPlug::GROUP), - span: (0, 12, 1), + span: Span(0, 12, 1), }, generic_args: None, - span: (0, 12, 1), - }, - Type2::Unwrap { + span: Span(0, 12, 1), + }), + Type2::Unwrap(Unwrap { ident: Identifier { ident: "group1".into(), socket: None, - span: (1, 7, 1), + span: Span(1, 7, 1), }, - generic_args: None, - comments: None, - span: (0, 0, 0), - }, - Type2::TaggedData { + span: Span(0, 0, 0), + ..Default::default() + }), + Type2::TaggedData(TaggedData { tag: Some(997), t: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "tstr".into(), socket: None, - span: (7, 11, 1), + span: Span(7, 11, 1), }, generic_args: None, - span: (7, 11, 1), - }, - operator: None, - comments_after_type: None, - span: (7, 11, 1), + span: Span(7, 11, 1), + }), + span: Span(7, 11, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (7, 11, 1), + span: Span(7, 11, 1), }, - comments_before_type: None, - comments_after_type: None, - span: (0, 11, 1), - }, - Type2::FloatValue { + span: Span(0, 11, 1), + ..Default::default() + }), + Type2::FloatValue(FloatValue { value: 9.9, - span: (0, 3, 1), - }, - Type2::Any((0, 1, 1)), - Type2::Array { + span: Span(0, 3, 1), + }), + Type2::Any(Span(0, 1, 1)), + Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( @@ -685,7 +668,7 @@ mod tests { occur: Occur::Exact { lower: None, upper: Some(3), - span: (1, 3, 1), + span: Span(1, 3, 1), }, comments: None, _a: PhantomData::default(), @@ -693,13 +676,13 @@ mod tests { name: Identifier { ident: "reputon".into(), socket: None, - span: (4, 11, 1), + span: Span(4, 11, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (1, 11, 1), + span: Span(1, 11, 1), }, OptionalComma { optional_comma: false, @@ -708,35 +691,34 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (1, 11, 1), + span: Span(1, 11, 1), }], - span: (1, 11, 1), + span: Span(1, 11, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 12, 1), - }, - Type2::Array { + span: Span(0, 12, 1), + ..Default::default() + }), + Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( GroupEntry::TypeGroupname { ge: TypeGroupnameEntry { occur: Some(Occurrence { - occur: Occur::OneOrMore((1, 2, 1)), + occur: Occur::OneOrMore(Span(1, 2, 1)), comments: None, _a: PhantomData::default(), }), name: Identifier { ident: "reputon".into(), socket: None, - span: (3, 10, 1), + span: Span(3, 10, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (1, 10, 1), + span: Span(1, 10, 1), }, OptionalComma { optional_comma: false, @@ -745,25 +727,23 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (1, 10, 1), + span: Span(1, 10, 1), }], - span: (1, 10, 1), + span: Span(1, 10, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 11, 1), - }, - Type2::ChoiceFromGroup { + span: Span(0, 11, 1), + ..Default::default() + }), + Type2::ChoiceFromGroup(ChoiceFromGroup { ident: Identifier { ident: "groupname".into(), socket: None, - span: (1, 10, 1), + span: Span(1, 10, 1), }, - generic_args: None, - comments: None, - span: (0, 10, 1), - }, - Type2::ChoiceFromInlineGroup { + span: Span(0, 10, 1), + ..Default::default() + }), + Type2::ChoiceFromInlineGroup(ChoiceFromInlineGroup { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( @@ -773,13 +753,13 @@ mod tests { name: Identifier { ident: "inlinegroup".into(), socket: None, - span: (3, 14, 1), + span: Span(3, 14, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (3, 14, 1), + span: Span(3, 14, 1), }, OptionalComma { optional_comma: false, @@ -788,67 +768,61 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (3, 14, 1), + span: Span(3, 14, 1), }], - span: (3, 14, 1), + span: Span(3, 14, 1), }, - comments: None, - comments_before_group: None, - comments_after_group: None, - span: (0, 14, 1), - }, - Type2::Map { + span: Span(0, 14, 1), + ..Default::default() + }), + Type2::Map(Map { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: Some(Occurrence { - occur: Occur::Optional((2, 3, 1)), + occur: Occur::Optional(Span(2, 3, 1)), comments: None, _a: PhantomData::default(), }), - member_key: Some(MemberKey::Type1 { + member_key: Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "optional-key".into(), - span: (4, 18, 1), - }, + span: Span(4, 18, 1), + }), operator: None, comments_after_type: None, - span: (4, 18, 1), + span: Span(4, 18, 1), }), is_cut: true, - comments_before_cut: None, - comments_after_cut: None, - comments_after_arrowmap: None, - span: (4, 23, 1), - }), + span: Span(4, 23, 1), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "int".into(), socket: None, - span: (24, 27, 1), + span: Span(24, 27, 1), }, generic_args: None, - span: (24, 27, 1), - }, - operator: None, - comments_after_type: None, - span: (24, 27, 1), + span: Span(24, 27, 1), + }), + span: Span(24, 27, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (24, 27, 1), + span: Span(24, 27, 1), }, }), leading_comments: None, trailing_comments: None, - span: (2, 28, 1), + span: Span(2, 28, 1), }, OptionalComma { optional_comma: true, @@ -857,15 +831,14 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (2, 28, 1), + span: Span(2, 28, 1), }], - span: (2, 28, 1), + span: Span(2, 28, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 30, 1), - }, - Type2::Array { + span: Span(0, 30, 1), + ..Default::default() + }), + Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( @@ -877,41 +850,39 @@ mod tests { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "a".into(), socket: None, - span: (4, 5, 1), + span: Span(4, 5, 1), }, comments: None, comments_after_colon: None, - span: (4, 6, 1), - }), + span: Span(4, 6, 1), + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "int".into(), socket: None, - span: (7, 10, 1), + span: Span(7, 10, 1), }, generic_args: None, - span: (7, 10, 1), - }, - operator: None, - comments_after_type: None, - span: (7, 10, 1), + span: Span(7, 10, 1), + }), + span: Span(7, 10, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (7, 10, 1), + span: Span(7, 10, 1), }, }), leading_comments: None, trailing_comments: None, - span: (4, 11, 1), + span: Span(4, 11, 1), }, OptionalComma { optional_comma: true, @@ -923,41 +894,38 @@ mod tests { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "b".into(), socket: None, - span: (12, 13, 1), + span: Span(12, 13, 1), }, - comments: None, - comments_after_colon: None, - span: (12, 14, 1), - }), + span: Span(12, 14, 1), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "tstr".into(), socket: None, - span: (15, 19, 1), + span: Span(15, 19, 1), }, generic_args: None, - span: (15, 19, 1), - }, - operator: None, - comments_after_type: None, - span: (15, 19, 1), + span: Span(15, 19, 1), + }), + span: Span(15, 19, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (15, 19, 1), + span: Span(15, 19, 1), }, }), leading_comments: None, trailing_comments: None, - span: (12, 19, 1), + span: Span(12, 19, 1), }, OptionalComma { optional_comma: false, @@ -967,14 +935,14 @@ mod tests { ), ], comments_before_grpchoice: None, - span: (4, 19, 1), + span: Span(4, 19, 1), }], - span: (4, 19, 1), + span: Span(4, 19, 1), }, occur: None, comments_before_group: None, comments_after_group: None, - span: (2, 21, 1), + span: Span(2, 21, 1), }, OptionalComma { optional_comma: false, @@ -983,19 +951,18 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (2, 21, 1), + span: Span(2, 21, 1), }], - span: (2, 21, 1), + span: Span(2, 21, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 23, 1), - }, + span: Span(0, 23, 1), + ..Default::default() + }), ]; for (idx, expected_output) in expected_outputs.iter().enumerate() { let mut l = Lexer::new(inputs[idx]); - let t2 = Parser::new(l.iter(), inputs[idx])?.parse_type2()?; + let t2 = Parser::new(l.iter(), inputs[idx], false)?.parse_type2()?; assert_eq!(&t2, expected_output); assert_eq!(t2.to_string(), expected_output.to_string()); @@ -1013,7 +980,7 @@ mod tests { ]; let expected_ouputs = [ - Type2::Array { + Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![ @@ -1025,27 +992,27 @@ mod tests { entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Array { + type2: Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( GroupEntry::TypeGroupname { ge: TypeGroupnameEntry { occur: Some(Occurrence { - occur: Occur::ZeroOrMore((3, 4, 1)), + occur: Occur::ZeroOrMore(Span(3, 4, 1)), comments: None, _a: PhantomData::default(), }), name: Identifier { ident: "file-entry".into(), socket: None, - span: (5, 15, 1), + span: Span(5, 15, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (3, 15, 1), + span: Span(3, 15, 1), }, OptionalComma { optional_comma: false, @@ -1054,27 +1021,24 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (3, 15, 1), + span: Span(3, 15, 1), }], - span: (3, 15, 1), + span: Span(3, 15, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (2, 16, 1), - }, - operator: None, - comments_after_type: None, - span: (2, 16, 1), + span: Span(2, 16, 1), + ..Default::default() + }), + span: Span(2, 16, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (2, 16, 1), + span: Span(2, 16, 1), }, }), leading_comments: None, trailing_comments: None, - span: (2, 17, 1), + span: Span(2, 17, 1), }, OptionalComma { optional_comma: true, @@ -1090,27 +1054,27 @@ mod tests { entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Array { + type2: Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( GroupEntry::TypeGroupname { ge: TypeGroupnameEntry { occur: Some(Occurrence { - occur: Occur::ZeroOrMore((19, 20, 1)), + occur: Occur::ZeroOrMore(Span(19, 20, 1)), comments: None, _a: PhantomData::default(), }), name: Identifier { ident: "directory-entry".into(), socket: None, - span: (21, 36, 1), + span: Span(21, 36, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (19, 36, 1), + span: Span(19, 36, 1), }, OptionalComma { optional_comma: false, @@ -1119,27 +1083,25 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (19, 36, 1), + span: Span(19, 36, 1), }], - span: (19, 36, 1), + span: Span(19, 36, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (18, 37, 1), - }, + span: Span(18, 37, 1), + ..Default::default() + }), operator: None, comments_after_type: None, - span: (18, 37, 1), + span: Span(18, 37, 1), }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (18, 37, 1), + span: Span(18, 37, 1), }, }), leading_comments: None, trailing_comments: None, - span: (18, 37, 1), + span: Span(18, 37, 1), }, OptionalComma { optional_comma: false, @@ -1149,15 +1111,14 @@ mod tests { ), ], comments_before_grpchoice: None, - span: (2, 37, 1), + span: Span(2, 37, 1), }], - span: (2, 37, 1), + span: Span(2, 37, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 39, 1), - }, - Type2::Map { + span: Span(0, 39, 1), + ..Default::default() + }), + Type2::Map(Map { group: Group { group_choices: vec![ GroupChoice { @@ -1169,13 +1130,13 @@ mod tests { name: Identifier { ident: "int".into(), socket: None, - span: (2, 5, 1), + span: Span(2, 5, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (2, 6, 1), + span: Span(2, 6, 1), }, OptionalComma { optional_comma: true, @@ -1190,13 +1151,13 @@ mod tests { name: Identifier { ident: "int".into(), socket: None, - span: (7, 10, 1), + span: Span(7, 10, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (7, 10, 1), + span: Span(7, 10, 1), }, OptionalComma { optional_comma: false, @@ -1206,7 +1167,7 @@ mod tests { ), ], comments_before_grpchoice: None, - span: (2, 10, 1), + span: Span(2, 10, 1), }, GroupChoice { group_entries: vec![ @@ -1217,13 +1178,13 @@ mod tests { name: Identifier { ident: "int".into(), socket: None, - span: (14, 17, 1), + span: Span(14, 17, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (14, 18, 1), + span: Span(14, 18, 1), }, OptionalComma { optional_comma: true, @@ -1238,13 +1199,13 @@ mod tests { name: Identifier { ident: "tstr".into(), socket: None, - span: (19, 23, 1), + span: Span(19, 23, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (19, 23, 1), + span: Span(19, 23, 1), }, OptionalComma { optional_comma: false, @@ -1254,16 +1215,15 @@ mod tests { ), ], comments_before_grpchoice: None, - span: (14, 23, 1), + span: Span(14, 23, 1), }, ], - span: (2, 23, 1), + span: Span(2, 23, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 25, 1), - }, - Type2::Map { + span: Span(0, 25, 1), + ..Default::default() + }), + Type2::Map(Map { group: Group { group_choices: vec![GroupChoice { group_entries: vec![ @@ -1274,13 +1234,13 @@ mod tests { name: Identifier { ident: "int".into(), socket: None, - span: (2, 5, 1), + span: Span(2, 5, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (2, 6, 1), + span: Span(2, 6, 1), }, OptionalComma { optional_comma: true, @@ -1295,13 +1255,13 @@ mod tests { name: Identifier { ident: "int".into(), socket: None, - span: (7, 10, 1), + span: Span(7, 10, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (7, 11, 1), + span: Span(7, 11, 1), }, OptionalComma { optional_comma: true, @@ -1316,13 +1276,13 @@ mod tests { name: Identifier { ident: "int".into(), socket: None, - span: (12, 15, 1), + span: Span(12, 15, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (12, 16, 1), + span: Span(12, 16, 1), }, OptionalComma { optional_comma: true, @@ -1337,13 +1297,13 @@ mod tests { name: Identifier { ident: "tstr".into(), socket: None, - span: (17, 21, 1), + span: Span(17, 21, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (17, 21, 1), + span: Span(17, 21, 1), }, OptionalComma { optional_comma: false, @@ -1353,19 +1313,18 @@ mod tests { ), ], comments_before_grpchoice: None, - span: (2, 21, 1), + span: Span(2, 21, 1), }], - span: (2, 21, 1), + span: Span(2, 21, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (0, 23, 1), - }, + span: Span(0, 23, 1), + ..Default::default() + }), ]; for (idx, expected_output) in expected_ouputs.iter().enumerate() { let mut l = Lexer::new(inputs[idx]); - let t2 = Parser::new(l.iter(), inputs[idx])?.parse_type2()?; + let t2 = Parser::new(l.iter(), inputs[idx], false)?.parse_type2()?; assert_eq!(&t2, expected_output); assert_eq!(t2.to_string(), expected_output.to_string()); @@ -1389,90 +1348,82 @@ mod tests { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: Some(Occurrence { - occur: Occur::ZeroOrMore((0, 1, 1)), + occur: Occur::ZeroOrMore(Span(0, 1, 1)), comments: None, _a: PhantomData::default(), }), - member_key: Some(MemberKey::Type1 { + member_key: Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "type1".into(), socket: None, - span: (2, 7, 1), + span: Span(2, 7, 1), }, generic_args: None, - span: (2, 7, 1), - }, - operator: None, - comments_after_type: None, - span: (2, 7, 1), + span: Span(2, 7, 1), + }), + span: Span(2, 7, 1), + ..Default::default() }), is_cut: true, - comments_before_cut: None, - comments_after_cut: None, - comments_after_arrowmap: None, - span: (2, 12, 1), - }), + span: Span(2, 12, 1), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "value".into(), - span: (13, 20, 1), - }, - operator: None, - comments_after_type: None, - span: (13, 20, 1), + span: Span(13, 20, 1), + }), + span: Span(13, 20, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (13, 20, 1), + span: Span(13, 20, 1), }, }), leading_comments: None, trailing_comments: None, - span: (0, 20, 1), + span: Span(0, 20, 1), }, GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "type1".into(), socket: None, - span: (0, 5, 1), + span: Span(0, 5, 1), }, - comments: None, - comments_after_colon: None, - span: (0, 6, 1), - }), + span: Span(0, 6, 1), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "type2".into(), socket: None, - span: (7, 12, 1), + span: Span(7, 12, 1), }, generic_args: None, - span: (7, 12, 1), - }, - operator: None, - comments_after_type: None, - span: (7, 12, 1), + span: Span(7, 12, 1), + }), + span: Span(7, 12, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (7, 12, 1), + span: Span(7, 12, 1), }, }), leading_comments: None, trailing_comments: None, - span: (0, 12, 1), + span: Span(0, 12, 1), }, GroupEntry::TypeGroupname { ge: TypeGroupnameEntry { @@ -1480,119 +1431,113 @@ mod tests { name: Identifier { ident: "typename".into(), socket: None, - span: (0, 8, 1), + span: Span(0, 8, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (0, 8, 1), + span: Span(0, 8, 1), }, GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: Some(Occurrence { - occur: Occur::Optional((0, 1, 1)), + occur: Occur::Optional(Span(0, 1, 1)), comments: None, _a: PhantomData::default(), }), - member_key: Some(MemberKey::Value { + member_key: Some(MemberKey::Value(ValueMemberKey { value: token::Value::UINT(0), comments: None, comments_after_colon: None, - span: (2, 4, 1), - }), + span: Span(2, 4, 1), + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "addrdistr".into(), socket: None, - span: (5, 14, 1), + span: Span(5, 14, 1), }, generic_args: None, - span: (5, 14, 1), - }, - operator: None, - comments_after_type: None, - span: (5, 14, 1), + span: Span(5, 14, 1), + }), + span: Span(5, 14, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (5, 14, 1), + span: Span(5, 14, 1), }, }), leading_comments: None, trailing_comments: None, - span: (0, 14, 1), + span: Span(0, 14, 1), }, GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Value { + member_key: Some(MemberKey::Value(ValueMemberKey { value: token::Value::UINT(0), comments: None, comments_after_colon: None, - span: (0, 2, 1), - }), + span: Span(0, 2, 1), + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "finite_set".into(), socket: None, - span: (3, 13, 1), + span: Span(3, 13, 1), }, generic_args: Some(GenericArgs { args: vec![GenericArg { arg: Box::from(Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "transaction_input".into(), socket: None, - span: (14, 31, 1), + span: Span(14, 31, 1), }, generic_args: None, - span: (14, 31, 1), - }, - operator: None, - comments_after_type: None, - span: (14, 31, 1), + span: Span(14, 31, 1), + }), + span: Span(14, 31, 1), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (13, 32, 1), + span: Span(13, 32, 1), }), - span: (3, 32, 1), - }, - operator: None, - comments_after_type: None, - span: (3, 32, 1), + span: Span(3, 32, 1), + }), + span: Span(3, 32, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (3, 32, 1), + span: Span(3, 32, 1), }, }), leading_comments: None, trailing_comments: None, - span: (0, 32, 1), + span: Span(0, 32, 1), }, GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: Some(Occurrence { - occur: Occur::ZeroOrMore((0, 1, 1)), + occur: Occur::ZeroOrMore(Span(0, 1, 1)), comments: None, _a: PhantomData::default(), }), - member_key: Some(MemberKey::Type1 { + member_key: Some(MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::Array { + type2: Type2::Array(Array { group: Group { group_choices: vec![GroupChoice { group_entries: vec![( @@ -1602,13 +1547,13 @@ mod tests { name: Identifier { ident: "credential".into(), socket: None, - span: (3, 13, 1), + span: Span(3, 13, 1), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (3, 13, 1), + span: Span(3, 13, 1), }, OptionalComma { optional_comma: false, @@ -1617,55 +1562,49 @@ mod tests { }, )], comments_before_grpchoice: None, - span: (3, 13, 1), + span: Span(3, 13, 1), }], - span: (3, 13, 1), + span: Span(3, 13, 1), }, - comments_before_group: None, - comments_after_group: None, - span: (2, 14, 1), - }, - operator: None, - comments_after_type: None, - span: (2, 14, 1), + span: Span(2, 14, 1), + ..Default::default() + }), + span: Span(2, 14, 1), + ..Default::default() }), is_cut: false, - comments_before_cut: None, - comments_after_cut: None, - comments_after_arrowmap: None, - span: (2, 22, 1), - }), + span: Span(2, 22, 1), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "coin".into(), socket: None, - span: (18, 22, 1), + span: Span(18, 22, 1), }, generic_args: None, - span: (18, 22, 1), - }, - operator: None, - comments_after_type: None, - span: (18, 22, 1), + span: Span(18, 22, 1), + }), + span: Span(18, 22, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (18, 22, 1), + span: Span(18, 22, 1), }, }), leading_comments: None, trailing_comments: None, - span: (0, 22, 1), + span: Span(0, 22, 1), }, ]; for (idx, expected_output) in expected_outputs.iter().enumerate() { let mut l = Lexer::new(inputs[idx]); - let grpent = Parser::new(l.iter(), inputs[idx])?.parse_grpent(false)?; + let grpent = Parser::new(l.iter(), inputs[idx], false)?.parse_grpent(false)?; assert_eq!(&grpent, expected_output); assert_eq!(grpent.to_string(), expected_output.to_string()); @@ -1686,117 +1625,103 @@ mod tests { ]; let expected_outputs = [ - MemberKey::Type1 { + MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "type1".into(), socket: None, - span: (0, 5, 1), + span: Span(0, 5, 1), }, generic_args: None, - span: (0, 5, 1), - }, - operator: None, - comments_after_type: None, - span: (0, 5, 1), + span: Span(0, 5, 1), + }), + span: Span(0, 5, 1), + ..Default::default() }), - is_cut: false, - comments_before_cut: None, - comments_after_cut: None, - comments_after_arrowmap: None, - span: (0, 8, 1), - }, - MemberKey::Type1 { + span: Span(0, 8, 1), + ..Default::default() + }), + MemberKey::Type1(Type1MemberKey { t1: Box::from(Type1 { - type2: Type2::ParenthesizedType { + type2: Type2::ParenthesizedType(ParenthesizedType { pt: Type { type_choices: vec![ TypeChoice { type1: Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "mytype1".into(), - span: (2, 11, 1), - }, - operator: None, - span: (2, 11, 1), - comments_after_type: None, + span: Span(2, 11, 1), + }), + span: Span(2, 11, 1), + ..Default::default() }, - comments_after_type: None, - comments_before_type: None, + ..Default::default() }, TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "int", - span: (14, 17, 1), + span: Span(14, 17, 1), socket: None, }, - span: (14, 17, 1), + span: Span(14, 17, 1), generic_args: None, - }, - span: (14, 17, 1), - comments_after_type: None, - operator: None, + }), + span: Span(14, 17, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (2, 17, 1), + span: Span(2, 17, 1), }, - span: (0, 19, 1), - comments_before_type: None, - comments_after_type: None, - }, - operator: None, - comments_after_type: None, - span: (0, 19, 1), + span: Span(0, 19, 1), + ..Default::default() + }), + span: Span(0, 19, 1), + ..Default::default() }), is_cut: true, - comments_before_cut: None, - comments_after_cut: None, - comments_after_arrowmap: None, - span: (0, 24, 1), - }, - MemberKey::Bareword { + span: Span(0, 24, 1), + ..Default::default() + }), + MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "mybareword".into(), socket: None, - span: (0, 10, 1), + span: Span(0, 10, 1), }, - comments: None, - comments_after_colon: None, - span: (0, 11, 1), - }, - MemberKey::Bareword { + span: Span(0, 11, 1), + ..Default::default() + }), + MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "my..bareword".into(), socket: None, - span: (0, 12, 1), + span: Span(0, 12, 1), }, - comments: None, - comments_after_colon: None, - span: (0, 13, 1), - }, - MemberKey::Value { + span: Span(0, 13, 1), + ..Default::default() + }), + MemberKey::Value(ValueMemberKey { value: token::Value::TEXT("myvalue".into()), comments: None, comments_after_colon: None, - span: (0, 10, 1), - }, - MemberKey::Value { + span: Span(0, 10, 1), + }), + MemberKey::Value(ValueMemberKey { value: token::Value::UINT(0), comments: None, comments_after_colon: None, - span: (0, 2, 1), - }, + span: Span(0, 2, 1), + }), ]; for (idx, expected_output) in expected_outputs.iter().enumerate() { let mut l = Lexer::new(inputs[idx]); - let mk = Parser::new(l.iter(), inputs[idx])?.parse_memberkey(false)?; + let mk = Parser::new(l.iter(), inputs[idx], false)?.parse_memberkey(false)?; if let Some(mk) = mk { assert_eq!(&mk, expected_output); @@ -1816,18 +1741,18 @@ mod tests { occur: Occur::Exact { lower: Some(1), upper: Some(3), - span: (0, 3, 1), + span: Span(0, 3, 1), }, comments: None, _a: PhantomData::default(), }, Occurrence { - occur: Occur::ZeroOrMore((0, 1, 1)), + occur: Occur::ZeroOrMore(Span(0, 1, 1)), comments: None, _a: PhantomData::default(), }, Occurrence { - occur: Occur::OneOrMore((0, 1, 1)), + occur: Occur::OneOrMore(Span(0, 1, 1)), comments: None, _a: PhantomData::default(), }, @@ -1835,7 +1760,7 @@ mod tests { occur: Occur::Exact { lower: Some(5), upper: None, - span: (0, 2, 1), + span: Span(0, 2, 1), }, comments: None, _a: PhantomData::default(), @@ -1844,13 +1769,13 @@ mod tests { occur: Occur::Exact { lower: None, upper: Some(3), - span: (0, 2, 1), + span: Span(0, 2, 1), }, comments: None, _a: PhantomData::default(), }, Occurrence { - occur: Occur::Optional((0, 1, 1)), + occur: Occur::Optional(Span(0, 1, 1)), comments: None, _a: PhantomData::default(), }, @@ -1858,7 +1783,7 @@ mod tests { for (idx, expected_output) in expected_outputs.iter().enumerate() { let mut l = Lexer::new(inputs[idx]); - let o = Parser::new(l.iter(), inputs[idx])?.parse_occur(false)?; + let o = Parser::new(l.iter(), inputs[idx], false)?.parse_occur(false)?; if let Some(o) = o { assert_eq!(&o, expected_output); diff --git a/src/repl.rs b/src/repl.rs index adf2bbbf..46decc31 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -28,7 +28,7 @@ pub fn start(mut reader: R, mut writer: W) -> Result<()> { if let Ok(Some(_)) = control(&line) { writer.flush()?; } else { - if let Ok(c) = parser::cddl_from_str(&mut lexer::Lexer::new(&line), &line, true) { + if let Ok(c) = parser::cddl_from_str(&mut lexer::Lexer::new(&line), &line, true, false) { writer.write_all(format!("{:#?}\n", c).as_bytes())?; } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 00000000..c8f5ffd3 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,645 @@ +use crate::{ast::*, token::*}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +#[cfg(feature = "std")] +use regex_syntax; + +/// Find non-choice alternate rule from a given identifier +pub fn rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a Rule<'a>> { + cddl.rules.iter().find_map(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident && !rule.is_type_choice_alternate => Some(r), + Rule::Group { rule, .. } if rule.name == *ident && !rule.is_group_choice_alternate => Some(r), + _ => None, + }) +} + +/// Find text values from a given identifier +pub fn text_value_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a Type2<'a>> { + cddl.rules.iter().find_map(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => { + rule.value.type_choices.iter().find_map(|tc| { + if tc.type1.operator.is_none() { + match &tc.type1.type2 { + Type2::TextValue { .. } | Type2::UTF8ByteString { .. } => Some(&tc.type1.type2), + Type2::Typename(Typename { ident, .. }) => text_value_from_ident(cddl, ident), + Type2::ParenthesizedType(ParenthesizedType { pt, .. }) => { + pt.type_choices.iter().find_map(|tc| { + if tc.type1.operator.is_none() { + text_value_from_type2(cddl, &tc.type1.type2) + } else { + None + } + }) + } + _ => None, + } + } else { + None + } + }) + } + _ => None, + }) +} + +/// Find text values from a given Type2 +pub fn text_value_from_type2<'a>(cddl: &'a CDDL, t2: &'a Type2<'a>) -> Option<&'a Type2<'a>> { + match t2 { + Type2::TextValue(_) | Type2::UTF8ByteString(_) => Some(t2), + Type2::Typename(Typename { ident, .. }) => text_value_from_ident(cddl, ident), + Type2::Array(Array { group, .. }) => group.group_choices.iter().find_map(|gc| { + if gc.group_entries.len() == 2 { + if let Some(ge) = gc.group_entries.first() { + if let GroupEntry::ValueMemberKey { ge, .. } = &ge.0 { + if ge.member_key.is_none() { + ge.entry_type.type_choices.iter().find_map(|tc| { + if tc.type1.operator.is_none() { + text_value_from_type2(cddl, &tc.type1.type2) + } else { + None + } + }) + } else { + None + } + } else { + None + } + } else { + None + } + } else { + None + } + }), + Type2::ParenthesizedType(ParenthesizedType { pt, .. }) => { + pt.type_choices.iter().find_map(|tc| { + if tc.type1.operator.is_none() { + text_value_from_type2(cddl, &tc.type1.type2) + } else { + None + } + }) + } + _ => None, + } +} + +/// Unwrap array, map or tag type rule from ident +pub fn unwrap_rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a Rule<'a>> { + cddl.rules.iter().find_map(|r| match r { + Rule::Type { + rule: + TypeRule { + name, + is_type_choice_alternate, + value: Type { type_choices, .. }, + .. + }, + .. + } if name == ident && !is_type_choice_alternate => { + let match_fn = |tc: &TypeChoice| { + matches!( + tc.type1.type2, + Type2::Map { .. } | Type2::Array { .. } | Type2::TaggedData { .. } + ) + }; + + if type_choices.iter().any(match_fn) { + Some(r) + } else if let Some(ident) = type_choices.iter().find_map(|tc| { + if let Type2::Typename(Typename { + ident, + generic_args: None, + .. + }) = &tc.type1.type2 + { + Some(ident) + } else { + None + } + }) { + unwrap_rule_from_ident(cddl, ident) + } else { + None + } + } + _ => None, + }) +} + +/// Find non-group choice alternate rule from a given identifier +pub fn group_rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a GroupRule<'a>> { + cddl.rules.iter().find_map(|r| match r { + Rule::Group { rule, .. } if rule.name == *ident && !rule.is_group_choice_alternate => { + Some(rule.as_ref()) + } + _ => None, + }) +} + +/// Find non-group choice alternate rule from a given identifier +pub fn type_rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a TypeRule<'a>> { + cddl.rules.iter().find_map(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident && !rule.is_type_choice_alternate => Some(rule), + _ => None, + }) +} + +/// Retrieve a numeric value range bound from a given identifier +pub fn numeric_range_bound_from_ident<'a>( + cddl: &'a CDDL, + ident: &Identifier, +) -> Option<&'a Type2<'a>> { + cddl.rules.iter().find_map(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident && !rule.is_type_choice_alternate => { + match rule.value.type_choices.first() { + Some(TypeChoice { + type1: Type1 { + type2, operator, .. + }, + .. + }) if operator.is_none() => match type2 { + Type2::IntValue { .. } | Type2::UintValue { .. } | Type2::FloatValue { .. } => { + Some(type2) + } + Type2::Typename(Typename { ident, .. }) => numeric_range_bound_from_ident(cddl, ident), + _ => None, + }, + _ => None, + } + } + _ => None, + }) +} + +/// Retrieve the list of generic parameters for a given rule +pub fn generic_params_from_rule<'a>(rule: &Rule<'a>) -> Option> { + match rule { + Rule::Type { rule, .. } => rule + .generic_params + .as_ref() + .map(|gp| gp.params.iter().map(|gp| gp.param.ident).collect()), + Rule::Group { rule, .. } => rule + .generic_params + .as_ref() + .map(|gp| gp.params.iter().map(|gp| gp.param.ident).collect()), + } +} + +/// Find all type choice alternate rules from a given identifier +pub fn type_choice_alternates_from_ident<'a>( + cddl: &'a CDDL, + ident: &Identifier, +) -> Vec<&'a Type<'a>> { + cddl + .rules + .iter() + .filter_map(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => Some(&rule.value), + _ => None, + }) + .collect::>() +} + +/// Find all group choice alternate rules from a given identifier +pub fn group_choice_alternates_from_ident<'a>( + cddl: &'a CDDL, + ident: &Identifier, +) -> Vec<&'a GroupEntry<'a>> { + cddl + .rules + .iter() + .filter_map(|r| match r { + Rule::Group { rule, .. } if &rule.name == ident => Some(&rule.entry), + _ => None, + }) + .collect::>() +} + +/// Convert a given group choice to a list of type choices +pub fn type_choices_from_group_choice<'a>( + cddl: &'a CDDL, + grpchoice: &GroupChoice<'a>, +) -> Vec> { + let mut type_choices = Vec::new(); + for ge in grpchoice.group_entries.iter() { + match &ge.0 { + GroupEntry::ValueMemberKey { ge, .. } => { + type_choices.append(&mut ge.entry_type.type_choices.clone()); + } + GroupEntry::TypeGroupname { ge, .. } => { + // TODO: parse generic args + if let Some(r) = rule_from_ident(cddl, &ge.name) { + match r { + Rule::Type { rule, .. } => type_choices.append(&mut rule.value.type_choices.clone()), + Rule::Group { rule, .. } => type_choices.append(&mut type_choices_from_group_choice( + cddl, + &GroupChoice::new(vec![rule.entry.clone()]), + )), + } + } + } + GroupEntry::InlineGroup { group, .. } => { + for gc in group.group_choices.iter() { + type_choices.append(&mut type_choices_from_group_choice(cddl, gc)); + } + } + } + } + + type_choices +} + +/// Is the given identifier associated with a null data type +pub fn is_ident_null_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::NULL | Token::NIL = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_null_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a boolean data type +pub fn is_ident_bool_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::BOOL = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_bool_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Does the given boolean identifier match the boolean value +pub fn ident_matches_bool_value(cddl: &CDDL, ident: &Identifier, value: bool) -> bool { + if let Token::TRUE = lookup_ident(ident.ident) { + if value { + return true; + } + } + + if let Token::FALSE = lookup_ident(ident.ident) { + if !value { + return true; + } + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + ident_matches_bool_value(cddl, ident, value) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a URI data type +pub fn is_ident_uri_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::URI = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_uri_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a b64url data type +pub fn is_ident_b64url_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::B64URL = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_b64url_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a tdate data type +pub fn is_ident_tdate_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::TDATE = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_tdate_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a time data type +pub fn is_ident_time_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::TIME = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_time_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a numeric data type +pub fn is_ident_numeric_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::UINT + | Token::NINT + | Token::INTEGER + | Token::INT + | Token::NUMBER + | Token::FLOAT + | Token::FLOAT16 + | Token::FLOAT32 + | Token::FLOAT64 + | Token::FLOAT1632 + | Token::FLOAT3264 + | Token::UNSIGNED = lookup_ident(ident.ident) + { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_numeric_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a uint data type +pub fn is_ident_uint_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::UINT = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_uint_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a nint data type +pub fn is_ident_nint_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::NINT = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_nint_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with an integer data type +pub fn is_ident_integer_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::INT | Token::INTEGER | Token::NINT | Token::UINT | Token::NUMBER | Token::UNSIGNED = + lookup_ident(ident.ident) + { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_integer_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a float data type +pub fn is_ident_float_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::FLOAT + | Token::FLOAT16 + | Token::FLOAT1632 + | Token::FLOAT32 + | Token::FLOAT3264 + | Token::FLOAT64 = lookup_ident(ident.ident) + { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_float_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a string data type +pub fn is_ident_string_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::TEXT | Token::TSTR = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_string_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with the any type +pub fn is_ident_any_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::ANY = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_any_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Is the given identifier associated with a byte string data type +pub fn is_ident_byte_string_data_type(cddl: &CDDL, ident: &Identifier) -> bool { + if let Token::BSTR | Token::BYTES = lookup_ident(ident.ident) { + return true; + } + + cddl.rules.iter().any(|r| match r { + Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { + if let Type2::Typename(Typename { ident, .. }) = &tc.type1.type2 { + is_ident_byte_string_data_type(cddl, ident) + } else { + false + } + }), + _ => false, + }) +} + +/// Retrieve number of group entries from a group choice. This is currently only +/// used for determining map equality/inequality and for validating the number +/// of entries in arrays, but may be useful in other contexts. The occurrence is +/// only captured for the second element of the CDDL array to avoid ambiguity in +/// non-homogenous array definitions +pub fn entry_counts_from_group_choice(cddl: &CDDL, group_choice: &GroupChoice) -> EntryCount { + let mut count = 0; + let mut entry_occurrence = None; + + for (idx, ge) in group_choice.group_entries.iter().enumerate() { + match &ge.0 { + GroupEntry::ValueMemberKey { ge, .. } => { + if idx == 1 { + if let Some(occur) = &ge.occur { + entry_occurrence = Some(occur.occur.clone()) + } + } + + count += 1; + } + GroupEntry::InlineGroup { group, occur, .. } => { + if idx == 1 { + if let Some(occur) = occur { + entry_occurrence = Some(occur.occur.clone()) + } + } + for gc in group.group_choices.iter() { + count += entry_counts_from_group_choice(cddl, gc).count; + } + } + GroupEntry::TypeGroupname { ge, .. } => { + if idx == 1 { + if let Some(occur) = &ge.occur { + entry_occurrence = Some(occur.occur.clone()) + } + } + if let Some(gr) = group_rule_from_ident(cddl, &ge.name) { + count += + entry_counts_from_group_choice(cddl, &GroupChoice::new(vec![gr.entry.clone()])).count; + } else { + count += 1; + } + } + } + } + + EntryCount { + count, + entry_occurrence, + } +} + +/// Entry count +#[derive(Clone, Debug)] +pub struct EntryCount { + /// Count + pub count: u64, + /// Optional occurrence + pub entry_occurrence: Option, +} + +/// Regex needs to be formatted in a certain way so it can be parsed. See +/// +#[cfg(feature = "std")] +pub fn format_regex(input: &str) -> Option { + let mut formatted_regex = String::from(input); + let mut unescape = Vec::new(); + for (idx, c) in formatted_regex.char_indices() { + if c == '\\' { + if let Some(c) = formatted_regex.chars().nth(idx + 1) { + if !regex_syntax::is_meta_character(c) && c != 'd' { + unescape.push(format!("\\{}", c)); + } + } + } + } + + for replace in unescape.iter() { + formatted_regex = + formatted_regex.replace(replace, &replace.chars().nth(1).unwrap().to_string()); + } + + for find in ["?=", "?!", "?<=", "? { disabled_features: Option>, } -#[derive(Clone, Debug)] -struct GenericRule<'a> { - name: &'a str, - params: Vec<&'a str>, - args: Vec>, -} - impl<'a> CBORValidator<'a> { #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "additional-controls")] @@ -749,8 +743,8 @@ where } match lower { - Type2::IntValue { value: l, .. } => match upper { - Type2::IntValue { value: u, .. } => { + Type2::IntValue(IntValue { value: l, .. }) => match upper { + Type2::IntValue(IntValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected integer to be in range {} <= value <= {}, got {:?}", @@ -784,7 +778,7 @@ where } } } - Type2::UintValue { value: u, .. } => { + Type2::UintValue(UintValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected integer to be in range {} <= value <= {}, got {:?}", @@ -826,8 +820,8 @@ where return Ok(()); } }, - Type2::UintValue { value: l, .. } => match upper { - Type2::UintValue { value: u, .. } => { + Type2::UintValue(UintValue { value: l, .. }) => match upper { + Type2::UintValue(UintValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected uint to be in range {} <= value <= {}, got {:?}", @@ -895,8 +889,8 @@ where return Ok(()); } }, - Type2::FloatValue { value: l, .. } => match upper { - Type2::FloatValue { value: u, .. } => { + Type2::FloatValue(FloatValue { value: l, .. }) => match upper { + Type2::FloatValue(FloatValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected float to be in range {} <= value <= {}, got {:?}", @@ -954,18 +948,18 @@ where fn visit_control_operator( &mut self, target: &Type2<'a>, - ctrl: &str, + ctrl: &Token<'a>, controller: &Type2<'a>, ) -> visitor::Result> { - if let Type2::Typename { + if let Type2::Typename(Typename { ident: target_ident, .. - } = target + }) = target { - if let Type2::Typename { + if let Type2::Typename(Typename { ident: controller_ident, .. - } = controller + }) = controller { if let Some(name) = self.eval_generic_rule { if let Some(gr) = self @@ -1010,17 +1004,17 @@ where } } - match lookup_control_from_str(ctrl) { - t @ Some(Token::EQ) => { + match ctrl { + Token::EQ => { match target { - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { if is_ident_string_data_type(self.cddl, ident) || is_ident_numeric_data_type(self.cddl, ident) { return self.visit_type2(controller); } } - Type2::Array { group, .. } => { + Type2::Array(Array { group, .. }) => { if let Value::Array(_) = &self.cbor { let mut entry_counts = Vec::new(); for gc in group.group_choices.iter() { @@ -1033,9 +1027,9 @@ where return Ok(()); } } - Type2::Map { .. } => { + Type2::Map(Map { .. }) => { if let Value::Map(_) = &self.cbor { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.is_ctrl_map_equality = true; self.visit_type2(controller)?; self.ctrl = None; @@ -1050,29 +1044,29 @@ where } Ok(()) } - t @ Some(Token::NE) => { + Token::NE => { match target { - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { if is_ident_string_data_type(self.cddl, ident) || is_ident_numeric_data_type(self.cddl, ident) { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.visit_type2(controller)?; self.ctrl = None; return Ok(()); } } - Type2::Array { .. } => { + Type2::Array(Array { .. }) => { if let Value::Array(_) = &self.cbor { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.visit_type2(controller)?; self.ctrl = None; return Ok(()); } } - Type2::Map { .. } => { + Type2::Map(Map { .. }) => { if let Value::Map(_) = &self.cbor { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.is_ctrl_map_equality = true; self.visit_type2(controller)?; self.ctrl = None; @@ -1087,30 +1081,28 @@ where } Ok(()) } - t @ Some(Token::LT) | t @ Some(Token::GT) | t @ Some(Token::GE) | t @ Some(Token::LE) => { - match target { - Type2::Typename { ident, .. } if is_ident_numeric_data_type(self.cddl, ident) => { - self.ctrl = t; - self.visit_type2(controller)?; - self.ctrl = None; - Ok(()) - } - _ => { - self.add_error(format!( - "target for .lt, .gt, .ge or .le operator must be a numerical data type, got {}", - target - )); - Ok(()) - } + Token::LT | Token::GT | Token::GE | Token::LE => match target { + Type2::Typename(Typename { ident, .. }) if is_ident_numeric_data_type(self.cddl, ident) => { + self.ctrl = Some(ctrl.clone()); + self.visit_type2(controller)?; + self.ctrl = None; + Ok(()) } - } - t @ Some(Token::SIZE) => match target { - Type2::Typename { ident, .. } + _ => { + self.add_error(format!( + "target for .lt, .gt, .ge or .le operator must be a numerical data type, got {}", + target + )); + Ok(()) + } + }, + Token::SIZE => match target { + Type2::Typename(Typename { ident, .. }) if is_ident_string_data_type(self.cddl, ident) || is_ident_uint_data_type(self.cddl, ident) || is_ident_byte_string_data_type(self.cddl, ident) => { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.visit_type2(controller)?; self.ctrl = None; Ok(()) @@ -1123,15 +1115,15 @@ where Ok(()) } }, - t @ Some(Token::AND) => { - self.ctrl = t; + Token::AND => { + self.ctrl = Some(ctrl.clone()); self.visit_type2(target)?; self.visit_type2(controller)?; self.ctrl = None; Ok(()) } - t @ Some(Token::WITHIN) => { - self.ctrl = t; + Token::WITHIN => { + self.ctrl = Some(ctrl.clone()); let error_count = self.errors.len(); self.visit_type2(target)?; let no_errors = self.errors.len() == error_count; @@ -1151,8 +1143,8 @@ where Ok(()) } - t @ Some(Token::DEFAULT) => { - self.ctrl = t; + Token::DEFAULT => { + self.ctrl = Some(ctrl.clone()); let error_count = self.errors.len(); self.visit_type2(target)?; if self.errors.len() != error_count { @@ -1174,10 +1166,12 @@ where self.ctrl = None; Ok(()) } - t @ Some(Token::REGEXP) | t @ Some(Token::PCRE) => { - self.ctrl = t; + Token::REGEXP | Token::PCRE => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => { + Type2::Typename(Typename { ident, .. }) + if is_ident_string_data_type(self.cddl, ident) => + { match self.cbor { Value::Text(_) | Value::Array(_) => self.visit_type2(controller)?, _ => self.add_error(format!( @@ -1195,10 +1189,12 @@ where Ok(()) } - t @ Some(Token::CBOR) | t @ Some(Token::CBORSEQ) => { - self.ctrl = t; + Token::CBOR | Token::CBORSEQ => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } if is_ident_byte_string_data_type(self.cddl, ident) => { + Type2::Typename(Typename { ident, .. }) + if is_ident_byte_string_data_type(self.cddl, ident) => + { match &self.cbor { Value::Bytes(_) | Value::Array(_) => self.visit_type2(controller)?, _ => self.add_error(format!( @@ -1216,10 +1212,10 @@ where Ok(()) } - t @ Some(Token::BITS) => { - self.ctrl = t; + Token::BITS => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } + Type2::Typename(Typename { ident, .. }) if is_ident_byte_string_data_type(self.cddl, ident) || is_ident_uint_data_type(self.cddl, ident) => { @@ -1242,8 +1238,8 @@ where Ok(()) } #[cfg(feature = "additional-controls")] - t @ Some(Token::CAT) => { - self.ctrl = t; + Token::CAT => { + self.ctrl = Some(ctrl.clone()); match cat_operation(self.cddl, target, controller, false) { Ok(values) => { @@ -1270,8 +1266,8 @@ where Ok(()) } #[cfg(feature = "additional-controls")] - t @ Some(Token::DET) => { - self.ctrl = t; + Token::DET => { + self.ctrl = Some(ctrl.clone()); match cat_operation(self.cddl, target, controller, true) { Ok(values) => { @@ -1298,8 +1294,8 @@ where Ok(()) } #[cfg(feature = "additional-controls")] - t @ Some(Token::PLUS) => { - self.ctrl = t; + Token::PLUS => { + self.ctrl = Some(ctrl.clone()); match plus_operation(self.cddl, target, controller) { Ok(values) => { @@ -1328,14 +1324,16 @@ where Ok(()) } #[cfg(feature = "additional-controls")] - t @ Some(Token::ABNF) => { - self.ctrl = t; + Token::ABNF => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => { + Type2::Typename(Typename { ident, .. }) + if is_ident_string_data_type(self.cddl, ident) => + { match self.cbor { Value::Text(_) | Value::Array(_) => { - if let Type2::ParenthesizedType { pt, .. } = controller { + if let Type2::ParenthesizedType(ParenthesizedType { pt, .. }) = controller { match abnf_from_complex_controller(self.cddl, pt) { Ok(values) => { let error_count = self.errors.len(); @@ -1376,14 +1374,16 @@ where Ok(()) } #[cfg(feature = "additional-controls")] - t @ Some(Token::ABNFB) => { - self.ctrl = t; + Token::ABNFB => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } if is_ident_byte_string_data_type(self.cddl, ident) => { + Type2::Typename(Typename { ident, .. }) + if is_ident_byte_string_data_type(self.cddl, ident) => + { match self.cbor { Value::Bytes(_) | Value::Array(_) => { - if let Type2::ParenthesizedType { pt, .. } = controller { + if let Type2::ParenthesizedType(ParenthesizedType { pt, .. }) = controller { match abnf_from_complex_controller(self.cddl, pt) { Ok(values) => { let error_count = self.errors.len(); @@ -1425,12 +1425,12 @@ where } #[cfg(feature = "additional-controls")] #[cfg(not(target_arch = "wasm32"))] - t @ Some(Token::FEATURE) => { - self.ctrl = t; + Token::FEATURE => { + self.ctrl = Some(ctrl.clone()); if let Some(ef) = self.enabled_features { let tv = text_value_from_type2(self.cddl, controller); - if let Some(Type2::TextValue { value, .. }) = tv { + if let Some(Type2::TextValue(TextValue { value, .. })) = tv { if ef.contains(&&**value) { let err_count = self.errors.len(); self.visit_type2(target)?; @@ -1444,7 +1444,7 @@ where .get_or_insert(vec![value.to_string()]) .push(value.to_string()); } - } else if let Some(Type2::UTF8ByteString { value, .. }) = tv { + } else if let Some(Type2::UTF8ByteString(Utf8ByteString { value, .. })) = tv { let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?; if ef.contains(&value) { let err_count = self.errors.len(); @@ -1468,12 +1468,12 @@ where } #[cfg(feature = "additional-controls")] #[cfg(target_arch = "wasm32")] - t @ Some(Token::FEATURE) => { - self.ctrl = t; + Token::FEATURE => { + self.ctrl = Some(ctrl.clone()); if let Some(ef) = &self.enabled_features { let tv = text_value_from_type2(self.cddl, controller); - if let Some(Type2::TextValue { value, .. }) = tv { + if let Some(Type2::TextValue(TextValue { value, .. })) = tv { if ef.contains(&JsValue::from(value.as_ref())) { let err_count = self.errors.len(); self.visit_type2(target)?; @@ -1487,7 +1487,7 @@ where .get_or_insert(vec![value.to_string()]) .push(value.to_string()); } - } else if let Some(Type2::UTF8ByteString { value, .. }) = tv { + } else if let Some(Type2::UTF8ByteString(Utf8ByteString { value, .. })) = tv { let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?; if ef.contains(&JsValue::from(value)) { let err_count = self.errors.len(); @@ -1606,8 +1606,10 @@ where } match t2 { - Type2::TextValue { value, .. } => self.visit_value(&token::Value::TEXT(value.clone())), - Type2::Map { group, .. } => match &self.cbor { + Type2::TextValue(TextValue { value, .. }) => { + self.visit_value(&token::Value::TEXT(value.clone())) + } + Type2::Map(Map { group, .. }) => match &self.cbor { Value::Map(m) => { if self.is_member_key { let current_location = self.cbor_location.clone(); @@ -1645,6 +1647,7 @@ where return Ok(()); } + #[allow(clippy::needless_collect)] let m = m.iter().map(|entry| entry.0.clone()).collect::>(); self.visit_group(group)?; @@ -1766,7 +1769,7 @@ where Ok(()) } }, - Type2::Array { group, .. } => match &self.cbor { + Type2::Array(Array { group, .. }) => match &self.cbor { Value::Array(a) => { if group.group_choices.len() == 1 && group.group_choices[0].group_entries.is_empty() @@ -1854,11 +1857,11 @@ where Ok(()) } }, - Type2::ChoiceFromGroup { + Type2::ChoiceFromGroup(ChoiceFromGroup { ident, generic_args, .. - } => { + }) => { if let Some(ga) = generic_args { if let Some(rule) = rule_from_ident(self.cddl, ident) { if let Some(gr) = self @@ -1911,17 +1914,17 @@ where Ok(()) } - Type2::ChoiceFromInlineGroup { group, .. } => { + Type2::ChoiceFromInlineGroup(ChoiceFromInlineGroup { group, .. }) => { self.is_group_to_choice_enum = true; self.visit_group(group)?; self.is_group_to_choice_enum = false; Ok(()) } - Type2::Typename { + Type2::Typename(Typename { ident, generic_args, .. - } => { + }) => { if let Some(ga) = generic_args { if let Some(rule) = rule_from_ident(self.cddl, ident) { if let Some(gr) = self @@ -1961,19 +1964,21 @@ where self.visit_identifier(ident) } - Type2::IntValue { value, .. } => self.visit_value(&token::Value::INT(*value)), - Type2::UintValue { value, .. } => self.visit_value(&token::Value::UINT(*value)), - Type2::FloatValue { value, .. } => self.visit_value(&token::Value::FLOAT(*value)), - Type2::ParenthesizedType { pt, .. } => self.visit_type(pt), - Type2::Unwrap { + Type2::IntValue(IntValue { value, .. }) => self.visit_value(&token::Value::INT(*value)), + Type2::UintValue(UintValue { value, .. }) => self.visit_value(&token::Value::UINT(*value)), + Type2::FloatValue(FloatValue { value, .. }) => self.visit_value(&token::Value::FLOAT(*value)), + Type2::ParenthesizedType(ParenthesizedType { pt, .. }) => self.visit_type(pt), + Type2::Unwrap(Unwrap { ident, generic_args, .. - } => { + }) => { // Per // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719739215, // strip tag and validate underlying type - if let Some(Type2::TaggedData { t, .. }) = tag_from_token(&lookup_ident(ident.ident)) { + if let Some(Type2::TaggedData(TaggedData { t, .. })) = + tag_from_token(&lookup_ident(ident.ident)) + { return self.visit_type(&t); } @@ -2025,7 +2030,7 @@ where Ok(()) } - Type2::TaggedData { tag, t, .. } => match &self.cbor { + Type2::TaggedData(TaggedData { tag, t, .. }) => match &self.cbor { Value::Tag(actual_tag, value) => { if let Some(tag) = tag { if *tag as u64 != *actual_tag { @@ -2081,7 +2086,7 @@ where Ok(()) } }, - Type2::DataMajorType { mt, constraint, .. } => match &self.cbor { + Type2::DataMajorType(DataMajorType { mt, constraint, .. }) => match &self.cbor { Value::Integer(i) => { match mt { 0u8 => match constraint { @@ -3123,7 +3128,7 @@ where fn visit_memberkey(&mut self, mk: &MemberKey<'a>) -> visitor::Result> { match mk { - MemberKey::Type1 { is_cut, .. } => { + MemberKey::Type1(Type1MemberKey { is_cut, .. }) => { self.is_cut_present = *is_cut; walk_memberkey(self, mk)?; self.is_cut_present = false; @@ -3662,7 +3667,7 @@ mod tests { let cbor = ciborium::value::Value::Bytes(sha256_oid.as_bytes().to_vec()); let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true)?; + let cddl = cddl_from_str(&mut lexer, cddl, true, false)?; let mut cv = CBORValidator::new(&cddl, cbor, None); cv.validate()?; @@ -3681,7 +3686,7 @@ mod tests { ); let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing); + let cddl = cddl_from_str(&mut lexer, cddl, true, false).map_err(json::Error::CDDLParsing); if let Err(e) = &cddl { println!("{}", e); } diff --git a/src/validator/control.rs b/src/validator/control.rs index cffe9c91..c13b0703 100644 --- a/src/validator/control.rs +++ b/src/validator/control.rs @@ -1,7 +1,7 @@ #![cfg(any(feature = "json", feature = "cbor"))] #![cfg(not(feature = "lsp"))] -use crate::ast::{Identifier, Operator, RangeCtlOp, Rule, Type2, CDDL}; +use crate::ast::*; #[cfg(feature = "additional-controls")] use crate::{ast::Type, token::lookup_control_from_str, validator::ByteValue, Token}; @@ -23,7 +23,7 @@ pub fn string_literals_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Vec | t @ Type2::UTF8ByteString { .. } | t @ Type2::B16ByteString { .. } | t @ Type2::B64ByteString { .. } => literals.push(t), - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { literals.append(&mut string_literals_from_ident(cddl, ident)) } _ => continue, @@ -48,7 +48,7 @@ pub fn numeric_values_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Vec< t @ Type2::IntValue { .. } | t @ Type2::UintValue { .. } | t @ Type2::FloatValue { .. } => literals.push(t), - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { literals.append(&mut numeric_values_from_ident(cddl, ident)) } _ => continue, @@ -74,11 +74,11 @@ pub fn cat_operation<'a>( let ctrl = if is_dedent { Token::DET } else { Token::CAT }; match target { - Type2::TextValue { value, .. } => match controller { + Type2::TextValue(TextValue { value, .. }) => match controller { // "testing" .cat "123" - Type2::TextValue { + Type2::TextValue(TextValue { value: controller, .. - } => { + }) => { if is_dedent { literals.push(format!("{}{}", dedent_str(value), dedent_str(controller)).into()) } else { @@ -86,7 +86,7 @@ pub fn cat_operation<'a>( } } // "testing" .cat a - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { let sl = string_literals_from_ident(cddl, ident); if sl.is_empty() { return Err(format!( @@ -99,9 +99,9 @@ pub fn cat_operation<'a>( } } // "testing" .cat '123' - Type2::UTF8ByteString { + Type2::UTF8ByteString(Utf8ByteString { value: controller, .. - } => match std::str::from_utf8(controller) { + }) => match std::str::from_utf8(controller) { Ok(controller) => { let controller = controller.trim_start_matches('\'').trim_end_matches('\''); @@ -114,9 +114,9 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("error parsing byte string: {}", e)), }, // "testing" .cat h'313233' - Type2::B16ByteString { + Type2::B16ByteString(B16ByteString { value: controller, .. - } => match base16::decode(controller) { + }) => match base16::decode(controller) { Ok(controller) => match String::from_utf8(controller) { Ok(controller) => { if is_dedent { @@ -130,9 +130,9 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("error decoding base16 byte string literal: {}", e)), }, // "testing" .cat b64'MTIz' - Type2::B64ByteString { + Type2::B64ByteString(B64ByteString { value: controller, .. - } => match base64::decode_config(controller, base64::URL_SAFE) { + }) => match base64::decode_config(controller, base64::URL_SAFE) { Ok(controller) => match String::from_utf8(controller) { Ok(controller) => { if is_dedent { @@ -151,7 +151,7 @@ pub fn cat_operation<'a>( } }, // "testing" .cat ( "123" / "1234" ) - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { if controller.type1.operator.is_none() { literals.append(&mut cat_operation( @@ -166,7 +166,7 @@ pub fn cat_operation<'a>( _ => return Err(format!("invalid controller used for {} operation", ctrl)), }, // a .cat "123" - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { // Only grab the first type choice literal from the target per // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 if let Some(value) = string_literals_from_ident(cddl, ident).first() { @@ -176,7 +176,7 @@ pub fn cat_operation<'a>( } } // ( "test" / "testing" ) .cat "123" - Type2::ParenthesizedType { pt: target, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: target, .. }) => { // Only grab the first type choice literal from the target per // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 if let Some(tc) = target.type_choices.first() { @@ -193,12 +193,12 @@ pub fn cat_operation<'a>( return Err(format!("invalid target type in {} control operator", ctrl)); } - Type2::UTF8ByteString { value, .. } => match std::str::from_utf8(value) { + Type2::UTF8ByteString(Utf8ByteString { value, .. }) => match std::str::from_utf8(value) { Ok(value) => match controller { // 'testing' .cat "123" - Type2::TextValue { + Type2::TextValue(TextValue { value: controller, .. - } => { + }) => { let value = value.trim_start_matches('\'').trim_end_matches('\''); if is_dedent { @@ -207,7 +207,7 @@ pub fn cat_operation<'a>( literals.push(format!("{}{}", value, controller).into()) } } - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { let sl = string_literals_from_ident(cddl, ident); if sl.is_empty() { return Err(format!( @@ -220,9 +220,9 @@ pub fn cat_operation<'a>( } } // 'testing' .cat '123 - Type2::UTF8ByteString { + Type2::UTF8ByteString(Utf8ByteString { value: controller, .. - } => match std::str::from_utf8(controller) { + }) => match std::str::from_utf8(controller) { Ok(controller) => { let value = value.trim_start_matches('\'').trim_end_matches('\''); let controller = controller.trim_start_matches('\'').trim_end_matches('\''); @@ -236,9 +236,9 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("error parsing byte string: {}", e)), }, // 'testing' .cat h'313233' - Type2::B16ByteString { + Type2::B16ByteString(B16ByteString { value: controller, .. - } => match base16::decode(controller) { + }) => match base16::decode(controller) { Ok(controller) => match String::from_utf8(controller) { Ok(controller) => { let value = value.trim_start_matches('\'').trim_end_matches('\''); @@ -254,9 +254,9 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("error decoding base16 byte string literal: {}", e)), }, // 'testing' .cat b64'MTIz' - Type2::B64ByteString { + Type2::B64ByteString(B64ByteString { value: controller, .. - } => match base64::decode_config(controller, base64::URL_SAFE) { + }) => match base64::decode_config(controller, base64::URL_SAFE) { Ok(controller) => match String::from_utf8(controller) { Ok(controller) => { let value = value.trim_start_matches('\'').trim_end_matches('\''); @@ -277,7 +277,7 @@ pub fn cat_operation<'a>( } }, // 'testing' .cat ( "123" / "1234" ) - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { if controller.type1.operator.is_none() { literals.append(&mut cat_operation( @@ -293,11 +293,11 @@ pub fn cat_operation<'a>( }, Err(e) => return Err(format!("error parsing byte string: {}", e)), }, - Type2::B16ByteString { value, .. } => match controller { + Type2::B16ByteString(B16ByteString { value, .. }) => match controller { // h'74657374696E67' .cat "123" - Type2::TextValue { + Type2::TextValue(TextValue { value: controller, .. - } => { + }) => { let controller = if is_dedent { base16::encode_lower(dedent_str(controller).as_bytes()) } else { @@ -316,7 +316,7 @@ pub fn cat_operation<'a>( } } // h'74657374696E67' .cat b - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { let sl = string_literals_from_ident(cddl, ident); if sl.is_empty() { return Err(format!( @@ -329,9 +329,9 @@ pub fn cat_operation<'a>( } } // h'74657374696E67' .cat '123' - Type2::UTF8ByteString { + Type2::UTF8ByteString(Utf8ByteString { value: controller, .. - } => { + }) => { let controller = if is_dedent { base16::encode_lower(&dedent_bytes(controller, true)?) } else { @@ -351,9 +351,9 @@ pub fn cat_operation<'a>( } } // h'74657374696E67' .cat h'313233' - Type2::B16ByteString { + Type2::B16ByteString(B16ByteString { value: controller, .. - } => { + }) => { let concat = if is_dedent { [ &dedent_bytes(value, false)?[..], @@ -370,9 +370,9 @@ pub fn cat_operation<'a>( } } // h'74657374696E67' .cat b64'MTIz' - Type2::B64ByteString { + Type2::B64ByteString(B64ByteString { value: controller, .. - } => match base64::decode_config(controller, base64::URL_SAFE) { + }) => match base64::decode_config(controller, base64::URL_SAFE) { Ok(controller) => { let controller = base16::encode_lower(&controller); let concat = if is_dedent { @@ -393,7 +393,7 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("controller is invalid base64: {}", e)), }, // h'74657374696E67' .cat ( "123" / "1234" ) - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { if controller.type1.operator.is_none() { literals.append(&mut cat_operation( @@ -407,11 +407,11 @@ pub fn cat_operation<'a>( } _ => return Err(format!("invalid controller used for {} operation", ctrl)), }, - Type2::B64ByteString { value, .. } => match controller { + Type2::B64ByteString(B64ByteString { value, .. }) => match controller { // b64'dGVzdGluZw==' .cat "123" - Type2::TextValue { + Type2::TextValue(TextValue { value: controller, .. - } => match base64::decode_config(value, base64::URL_SAFE) { + }) => match base64::decode_config(value, base64::URL_SAFE) { Ok(value) => { let concat = if is_dedent { [ @@ -435,7 +435,7 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("target is invalid base64: {}", e)), }, // b64'dGVzdGluZw==' .cat b - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { let sl = string_literals_from_ident(cddl, ident); if sl.is_empty() { return Err(format!( @@ -448,9 +448,9 @@ pub fn cat_operation<'a>( } } // b64'dGVzdGluZw==' .cat '123' - Type2::UTF8ByteString { + Type2::UTF8ByteString(Utf8ByteString { value: controller, .. - } => match base64::decode_config(value, base64::URL_SAFE) { + }) => match base64::decode_config(value, base64::URL_SAFE) { Ok(value) => { let concat = if is_dedent { [ @@ -474,9 +474,9 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("target is invalid base64: {}", e)), }, // b64'dGVzdGluZw==' .cat h'313233' - Type2::B16ByteString { + Type2::B16ByteString(B16ByteString { value: controller, .. - } => match base64::decode_config(value, base64::URL_SAFE) { + }) => match base64::decode_config(value, base64::URL_SAFE) { Ok(value) => match base16::decode(controller) { Ok(controller) => { let concat = if is_dedent { @@ -502,9 +502,9 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("target is invalid base64: {}", e)), }, // b64'dGVzdGluZw==' .cat b64'MTIz' - Type2::B64ByteString { + Type2::B64ByteString(B64ByteString { value: controller, .. - } => match base64::decode_config(value, base64::URL_SAFE) { + }) => match base64::decode_config(value, base64::URL_SAFE) { Ok(value) => match base64::decode_config(controller, base64::URL_SAFE) { Ok(controller) => { let concat = if is_dedent { @@ -530,7 +530,7 @@ pub fn cat_operation<'a>( Err(e) => return Err(format!("target is invalid base64: {}", e)), }, // b64'dGVzdGluZw==' .cat ( "123" / "1234" ) - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { if controller.type1.operator.is_none() { literals.append(&mut cat_operation( @@ -607,17 +607,17 @@ pub fn plus_operation<'a>( ) -> Result>, String> { let mut values = Vec::new(); match target { - Type2::UintValue { value, .. } => match controller { - Type2::UintValue { + Type2::UintValue(UintValue { value, .. }) => match controller { + Type2::UintValue(UintValue { value: controller, .. - } => values.push((value + controller).into()), - Type2::IntValue { + }) => values.push((value + controller).into()), + Type2::IntValue(IntValue { value: controller, .. - } => values.push(((*value as isize + controller) as usize).into()), - Type2::FloatValue { + }) => values.push(((*value as isize + controller) as usize).into()), + Type2::FloatValue(FloatValue { value: controller, .. - } => values.push(((*value as isize + *controller as isize) as usize).into()), - Type2::Typename { ident, .. } => { + }) => values.push(((*value as isize + *controller as isize) as usize).into()), + Type2::Typename(Typename { ident, .. }) => { let nv = numeric_values_from_ident(cddl, ident); if nv.is_empty() { return Err(format!( @@ -630,7 +630,7 @@ pub fn plus_operation<'a>( values.append(&mut plus_operation(cddl, target, controller)?) } } - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { match &controller.type1.operator { Some(Operator { @@ -649,17 +649,17 @@ pub fn plus_operation<'a>( } _ => return Err("invalid controller used for .plus operation".to_string()), }, - Type2::IntValue { value, .. } => match controller { - Type2::IntValue { + Type2::IntValue(IntValue { value, .. }) => match controller { + Type2::IntValue(IntValue { value: controller, .. - } => values.push((value + controller).into()), - Type2::UintValue { + }) => values.push((value + controller).into()), + Type2::UintValue(UintValue { value: controller, .. - } => values.push((value + *controller as isize).into()), - Type2::FloatValue { + }) => values.push((value + *controller as isize).into()), + Type2::FloatValue(FloatValue { value: controller, .. - } => values.push((value + *controller as isize).into()), - Type2::Typename { ident, .. } => { + }) => values.push((value + *controller as isize).into()), + Type2::Typename(Typename { ident, .. }) => { let nv = numeric_values_from_ident(cddl, ident); if nv.is_empty() { return Err(format!( @@ -672,7 +672,7 @@ pub fn plus_operation<'a>( values.append(&mut plus_operation(cddl, target, controller)?) } } - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { match &controller.type1.operator { Some(Operator { @@ -691,14 +691,14 @@ pub fn plus_operation<'a>( } _ => return Err("invalid controller used for .plus operation".to_string()), }, - Type2::FloatValue { value, .. } => match controller { - Type2::IntValue { + Type2::FloatValue(FloatValue { value, .. }) => match controller { + Type2::IntValue(IntValue { value: controller, .. - } => values.push((value + *controller as f64).into()), - Type2::FloatValue { + }) => values.push((value + *controller as f64).into()), + Type2::FloatValue(FloatValue { value: controller, .. - } => values.push((value + controller).into()), - Type2::Typename { ident, .. } => { + }) => values.push((value + controller).into()), + Type2::Typename(Typename { ident, .. }) => { let nv = numeric_values_from_ident(cddl, ident); if nv.is_empty() { return Err(format!( @@ -711,7 +711,7 @@ pub fn plus_operation<'a>( values.append(&mut plus_operation(cddl, target, controller)?) } } - Type2::ParenthesizedType { pt: controller, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: controller, .. }) => { for controller in controller.type_choices.iter() { match &controller.type1.operator { Some(Operator { @@ -730,7 +730,7 @@ pub fn plus_operation<'a>( } _ => return Err("invalid controller used for .plus operation".to_string()), }, - Type2::Typename { ident, .. } => { + Type2::Typename(Typename { ident, .. }) => { // Only grab the first type choice value from the target per // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 if let Some(value) = numeric_values_from_ident(cddl, ident).first() { @@ -739,7 +739,7 @@ pub fn plus_operation<'a>( return Err("invalid controller used for .plus operation".to_string()); } } - Type2::ParenthesizedType { pt: target, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt: target, .. }) => { // Only grab the first type choice value from the target per // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 if let Some(tc) = target.type_choices.first() { @@ -845,7 +845,7 @@ mod tests { ); let mut l = lexer_from_str(cddl_str); - let cddl = cddl_from_str(&mut l, cddl_str, true)?; + let cddl = cddl_from_str(&mut l, cddl_str, true, false)?; assert_eq!( cat_operation( @@ -859,11 +859,10 @@ mod tests { )), false, )?, - vec![Type2::TextValue { + vec![Type2::TextValue(TextValue { value: "foo\n bar\n baz\n".into(), - #[cfg(feature = "ast-span")] - span: Span::default(), - }], + ..Default::default() + })], ); Ok(()) @@ -883,25 +882,22 @@ mod tests { ); let mut l = lexer_from_str(cddl_str); - let cddl = cddl_from_str(&mut l, cddl_str, true)?; + let cddl = cddl_from_str(&mut l, cddl_str, true, false)?; assert_eq!( cat_operation( &cddl, &Type2::from("foo".to_string()), - &Type2::Typename { + &Type2::Typename(Typename { ident: "b".into(), - generic_args: None, - #[cfg(feature = "ast-span")] - span: Span::default(), - }, + ..Default::default() + }), true, )?, - vec![Type2::TextValue { + vec![Type2::TextValue(TextValue { value: "foo\nbar\nbaz\n".into(), - #[cfg(feature = "ast-span")] - span: Span::default(), - }] + ..Default::default() + })] ); Ok(()) diff --git a/src/validator/json.rs b/src/validator/json.rs index 6a04c40c..a0359e44 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -2,10 +2,11 @@ #![cfg(feature = "json")] #![cfg(not(feature = "lsp"))] -use super::*; use crate::{ ast::*, - token::{self, Token}, + token::{self, lookup_ident, Token}, + util::*, + validator::*, visitor::{self, *}, }; @@ -15,7 +16,7 @@ use chrono::{TimeZone, Utc}; use serde_json::Value; #[cfg(feature = "additional-controls")] -use control::{abnf_from_complex_controller, cat_operation, plus_operation, validate_abnf}; +use super::control::{abnf_from_complex_controller, cat_operation, plus_operation, validate_abnf}; /// JSON validation Result pub type Result = std::result::Result<(), Error>; @@ -212,13 +213,6 @@ pub struct JSONValidator<'a> { disabled_features: Option>, } -#[derive(Clone, Debug)] -struct GenericRule<'a> { - name: &'a str, - params: Vec<&'a str>, - args: Vec>, -} - impl<'a> JSONValidator<'a> { #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "additional-controls")] @@ -917,8 +911,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { } match lower { - Type2::IntValue { value: l, .. } => match upper { - Type2::IntValue { value: u, .. } => { + Type2::IntValue(IntValue { value: l, .. }) => match upper { + Type2::IntValue(IntValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected integer to be in range {} <= value <= {}, got {}", @@ -957,7 +951,7 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { } } } - Type2::UintValue { value: u, .. } => { + Type2::UintValue(UintValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected integer to be in range {} <= value <= {}, got {}", @@ -1004,8 +998,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { return Ok(()); } }, - Type2::UintValue { value: l, .. } => match upper { - Type2::UintValue { value: u, .. } => { + Type2::UintValue(UintValue { value: l, .. }) => match upper { + Type2::UintValue(UintValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected uint to be in range {} <= value <= {}, got {}", @@ -1078,8 +1072,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { return Ok(()); } }, - Type2::FloatValue { value: l, .. } => match upper { - Type2::FloatValue { value: u, .. } => { + Type2::FloatValue(FloatValue { value: l, .. }) => match upper { + Type2::FloatValue(FloatValue { value: u, .. }) => { let error_str = if is_inclusive { format!( "expected float to be in range {} <= value <= {}, got {}", @@ -1142,18 +1136,18 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { fn visit_control_operator( &mut self, target: &Type2<'a>, - ctrl: &str, + ctrl: &Token<'a>, controller: &Type2<'a>, ) -> visitor::Result { - if let Type2::Typename { + if let Type2::Typename(Typename { ident: target_ident, .. - } = target + }) = target { - if let Type2::Typename { + if let Type2::Typename(Typename { ident: controller_ident, .. - } = controller + }) = controller { if let Some(name) = self.eval_generic_rule { if let Some(gr) = self @@ -1198,16 +1192,16 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { } } - match lookup_control_from_str(ctrl) { - t @ Some(Token::EQ) => match target { - Type2::Typename { ident, .. } => { + match ctrl { + Token::EQ => match target { + Type2::Typename(Typename { ident, .. }) => { if is_ident_string_data_type(self.cddl, ident) || is_ident_numeric_data_type(self.cddl, ident) { return self.visit_type2(controller); } } - Type2::Array { group, .. } => { + Type2::Array(Array { group, .. }) => { if let Value::Array(_) = &self.json { let mut entry_counts = Vec::new(); for gc in group.group_choices.iter() { @@ -1220,9 +1214,9 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { return Ok(()); } } - Type2::Map { .. } => { + Type2::Map(Map { .. }) => { if let Value::Object(_) = &self.json { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.is_ctrl_map_equality = true; self.visit_type2(controller)?; self.ctrl = None; @@ -1235,28 +1229,28 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { target )), }, - t @ Some(Token::NE) => match target { - Type2::Typename { ident, .. } => { + Token::NE => match target { + Type2::Typename(Typename { ident, .. }) => { if is_ident_string_data_type(self.cddl, ident) || is_ident_numeric_data_type(self.cddl, ident) { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.visit_type2(controller)?; self.ctrl = None; return Ok(()); } } - Type2::Array { .. } => { + Type2::Array(Array { .. }) => { if let Value::Array(_) = &self.json { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.visit_type2(controller)?; self.ctrl = None; return Ok(()); } } - Type2::Map { .. } => { + Type2::Map(Map { .. }) => { if let Value::Object(_) = &self.json { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.is_ctrl_map_equality = true; self.visit_type2(controller)?; self.ctrl = None; @@ -1269,27 +1263,25 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { target )), }, - t @ Some(Token::LT) | t @ Some(Token::GT) | t @ Some(Token::GE) | t @ Some(Token::LE) => { - match target { - Type2::Typename { ident, .. } if is_ident_numeric_data_type(self.cddl, ident) => { - self.ctrl = t; - self.visit_type2(controller)?; - self.ctrl = None; - } - _ => { - self.add_error(format!( - "target for .lt, .gt, .ge or .le operator must be a numerical data type, got {}", - target - )); - } + Token::LT | Token::GT | Token::GE | Token::LE => match target { + Type2::Typename(Typename { ident, .. }) if is_ident_numeric_data_type(self.cddl, ident) => { + self.ctrl = Some(ctrl.clone()); + self.visit_type2(controller)?; + self.ctrl = None; } - } - t @ Some(Token::SIZE) => match target { - Type2::Typename { ident, .. } + _ => { + self.add_error(format!( + "target for .lt, .gt, .ge or .le operator must be a numerical data type, got {}", + target + )); + } + }, + Token::SIZE => match target { + Type2::Typename(Typename { ident, .. }) if is_ident_string_data_type(self.cddl, ident) || is_ident_uint_data_type(self.cddl, ident) => { - self.ctrl = t; + self.ctrl = Some(ctrl.clone()); self.visit_type2(controller)?; self.ctrl = None; } @@ -1300,14 +1292,14 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { )); } }, - t @ Some(Token::AND) => { - self.ctrl = t; + Token::AND => { + self.ctrl = Some(ctrl.clone()); self.visit_type2(target)?; self.visit_type2(controller)?; self.ctrl = None; } - t @ Some(Token::WITHIN) => { - self.ctrl = t; + Token::WITHIN => { + self.ctrl = Some(ctrl.clone()); let error_count = self.errors.len(); self.visit_type2(target)?; let no_errors = self.errors.len() == error_count; @@ -1325,8 +1317,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { self.ctrl = None; } - t @ Some(Token::DEFAULT) => { - self.ctrl = t; + Token::DEFAULT => { + self.ctrl = Some(ctrl.clone()); let error_count = self.errors.len(); self.visit_type2(target)?; if self.errors.len() != error_count { @@ -1347,10 +1339,12 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { } self.ctrl = None; } - t @ Some(Token::REGEXP) | t @ Some(Token::PCRE) => { - self.ctrl = t; + Token::REGEXP | Token::PCRE => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => { + Type2::Typename(Typename { ident, .. }) + if is_ident_string_data_type(self.cddl, ident) => + { match self.json { Value::String(_) | Value::Array(_) => self.visit_type2(controller)?, _ => self.add_error(format!( @@ -1367,8 +1361,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { self.ctrl = None; } #[cfg(feature = "additional-controls")] - t @ Some(Token::CAT) => { - self.ctrl = t; + Token::CAT => { + self.ctrl = Some(ctrl.clone()); match cat_operation(self.cddl, target, controller, false) { Ok(values) => { @@ -1394,8 +1388,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { self.ctrl = None; } #[cfg(feature = "additional-controls")] - t @ Some(Token::DET) => { - self.ctrl = t; + Token::DET => { + self.ctrl = Some(ctrl.clone()); match cat_operation(self.cddl, target, controller, true) { Ok(values) => { @@ -1421,8 +1415,8 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { self.ctrl = None; } #[cfg(feature = "additional-controls")] - t @ Some(Token::PLUS) => { - self.ctrl = t; + Token::PLUS => { + self.ctrl = Some(ctrl.clone()); match plus_operation(self.cddl, target, controller) { Ok(values) => { @@ -1447,14 +1441,16 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { self.ctrl = None; } #[cfg(feature = "additional-controls")] - t @ Some(Token::ABNF) => { - self.ctrl = t; + Token::ABNF => { + self.ctrl = Some(ctrl.clone()); match target { - Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => { + Type2::Typename(Typename { ident, .. }) + if is_ident_string_data_type(self.cddl, ident) => + { match self.json { Value::String(_) | Value::Array(_) => { - if let Type2::ParenthesizedType { pt, .. } = controller { + if let Type2::ParenthesizedType(ParenthesizedType { pt, .. }) = controller { match abnf_from_complex_controller(self.cddl, pt) { Ok(values) => { let error_count = self.errors.len(); @@ -1494,12 +1490,12 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { } #[cfg(feature = "additional-controls")] #[cfg(not(target_arch = "wasm32"))] - t @ Some(Token::FEATURE) => { - self.ctrl = t; + Token::FEATURE => { + self.ctrl = Some(ctrl.clone()); if let Some(ef) = self.enabled_features { let tv = text_value_from_type2(self.cddl, controller); - if let Some(Type2::TextValue { value, .. }) = tv { + if let Some(Type2::TextValue(TextValue { value, .. })) = tv { if ef.contains(&&**value) { let err_count = self.errors.len(); self.visit_type2(target)?; @@ -1513,7 +1509,7 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { .get_or_insert(vec![value.to_string()]) .push(value.to_string()); } - } else if let Some(Type2::UTF8ByteString { value, .. }) = tv { + } else if let Some(Type2::UTF8ByteString(Utf8ByteString { value, .. })) = tv { let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?; if ef.contains(&value) { let err_count = self.errors.len(); @@ -1535,12 +1531,12 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { } #[cfg(feature = "additional-controls")] #[cfg(target_arch = "wasm32")] - t @ Some(Token::FEATURE) => { - self.ctrl = t; + Token::FEATURE => { + self.ctrl = Some(ctrl.clone()); if let Some(ef) = &self.enabled_features { let tv = text_value_from_type2(self.cddl, controller); - if let Some(Type2::TextValue { value, .. }) = tv { + if let Some(Type2::TextValue(TextValue { value, .. })) = tv { if ef.contains(&JsValue::from(value.as_ref())) { let err_count = self.errors.len(); self.visit_type2(target)?; @@ -1554,7 +1550,7 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { .get_or_insert(vec![value.to_string()]) .push(value.to_string()); } - } else if let Some(Type2::UTF8ByteString { value, .. }) = tv { + } else if let Some(Type2::UTF8ByteString(Utf8ByteString { value, .. })) = tv { let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?; if ef.contains(&JsValue::from(value)) { let err_count = self.errors.len(); @@ -1584,9 +1580,12 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { fn visit_type2(&mut self, t2: &Type2<'a>) -> visitor::Result { match t2 { - Type2::TextValue { value, .. } => self.visit_value(&token::Value::TEXT(value.clone())), - Type2::Map { group, .. } => match &self.json { + Type2::TextValue(TextValue { value, .. }) => { + self.visit_value(&token::Value::TEXT(value.clone())) + } + Type2::Map(Map { group, .. }) => match &self.json { Value::Object(o) => { + #[allow(clippy::needless_collect)] let o = o.keys().cloned().collect::>(); self.visit_group(group)?; @@ -1704,7 +1703,7 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { Ok(()) } }, - Type2::Array { group, .. } => match &self.json { + Type2::Array(Array { group, .. }) => match &self.json { Value::Array(a) => { if group.group_choices.len() == 1 && group.group_choices[0].group_entries.is_empty() @@ -1747,11 +1746,11 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { Ok(()) } }, - Type2::ChoiceFromGroup { + Type2::ChoiceFromGroup(ChoiceFromGroup { ident, generic_args, .. - } => { + }) => { if let Some(ga) = generic_args { if let Some(rule) = rule_from_ident(self.cddl, ident) { if let Some(gr) = self @@ -1804,17 +1803,17 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { Ok(()) } - Type2::ChoiceFromInlineGroup { group, .. } => { + Type2::ChoiceFromInlineGroup(ChoiceFromInlineGroup { group, .. }) => { self.is_group_to_choice_enum = true; self.visit_group(group)?; self.is_group_to_choice_enum = false; Ok(()) } - Type2::Typename { + Type2::Typename(Typename { ident, generic_args, .. - } => { + }) => { if let Some(ga) = generic_args { if let Some(rule) = rule_from_ident(self.cddl, ident) { if let Some(gr) = self @@ -1854,19 +1853,21 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { self.visit_identifier(ident) } - Type2::IntValue { value, .. } => self.visit_value(&token::Value::INT(*value)), - Type2::UintValue { value, .. } => self.visit_value(&token::Value::UINT(*value)), - Type2::FloatValue { value, .. } => self.visit_value(&token::Value::FLOAT(*value)), - Type2::ParenthesizedType { pt, .. } => self.visit_type(pt), - Type2::Unwrap { + Type2::IntValue(IntValue { value, .. }) => self.visit_value(&token::Value::INT(*value)), + Type2::UintValue(UintValue { value, .. }) => self.visit_value(&token::Value::UINT(*value)), + Type2::FloatValue(FloatValue { value, .. }) => self.visit_value(&token::Value::FLOAT(*value)), + Type2::ParenthesizedType(ParenthesizedType { pt, .. }) => self.visit_type(pt), + Type2::Unwrap(Unwrap { ident, generic_args, .. - } => { + }) => { // Per // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719739215, // strip tag and validate underlying type - if let Some(Type2::TaggedData { t, .. }) = tag_from_token(&lookup_ident(ident.ident)) { + if let Some(Type2::TaggedData(TaggedData { t, .. })) = + tag_from_token(&lookup_ident(ident.ident)) + { return self.visit_type(&t); } @@ -2350,7 +2351,7 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> { fn visit_memberkey(&mut self, mk: &MemberKey<'a>) -> visitor::Result { match mk { - MemberKey::Type1 { is_cut, .. } => { + MemberKey::Type1(Type1MemberKey { is_cut, .. }) => { self.is_cut_present = *is_cut; walk_memberkey(self, mk)?; self.is_cut_present = false; @@ -2613,6 +2614,9 @@ mod tests { #![allow(unused_imports)] use super::*; + + use crate::{cddl_from_str, lexer_from_str, validator::json}; + use indoc::indoc; #[cfg(feature = "additional-controls")] @@ -2634,7 +2638,7 @@ mod tests { let json = r#"{ "test": 10 }"#; let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing); + let cddl = cddl_from_str(&mut lexer, cddl, true, false).map_err(json::Error::CDDLParsing); if let Err(e) = &cddl { println!("{}", e); } @@ -2661,7 +2665,7 @@ mod tests { let json = r#""v""#; let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing); + let cddl = cddl_from_str(&mut lexer, cddl, true, false).map_err(json::Error::CDDLParsing); if let Err(e) = &cddl { println!("{}", e); } diff --git a/src/validator/mod.rs b/src/validator/mod.rs index ac45027c..a9923fe8 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -7,14 +7,7 @@ pub mod json; mod control; -use crate::{ - ast::{ - GroupChoice, GroupEntry, GroupRule, Identifier, Occur, Rule, Type, Type2, TypeChoice, TypeRule, - CDDL, - }, - token::*, - visitor::Visitor, -}; +use crate::{ast::Occur, token::*, util::EntryCount, visitor::Visitor}; use std::error::Error; @@ -24,29 +17,22 @@ use cbor::CBORValidator; use ciborium; #[cfg(feature = "json")] use json::JSONValidator; + use serde::de::Deserialize; #[cfg(target_arch = "wasm32")] use crate::{ - error::ErrorMsg, - lexer::{Lexer, Position}, + error::ParserError, + lexer::Lexer, parser::{self, Parser}, }; -#[cfg(target_arch = "wasm32")] -use serde::Serialize; + #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; #[cfg(not(target_arch = "wasm32"))] use crate::{cddl_from_str, lexer_from_str}; -#[cfg(target_arch = "wasm32")] -#[derive(Serialize)] -struct ParserError { - position: Position, - msg: ErrorMsg, -} - trait Validator<'a, E: Error>: Visitor<'a, E> { fn validate(&mut self) -> std::result::Result<(), E>; fn add_error(&mut self, reason: String); @@ -58,10 +44,12 @@ trait Validator<'a, E: Error>: Visitor<'a, E> { pub fn validate_json_from_str( cddl: &str, json: &str, + allow_missing_definitions: bool, #[cfg(feature = "additional-controls")] enabled_features: Option<&[&str]>, ) -> json::Result { let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing)?; + let cddl = cddl_from_str(&mut lexer, cddl, true, allow_missing_definitions) + .map_err(json::Error::CDDLParsing)?; let json = serde_json::from_str::(json).map_err(json::Error::JSONParsing)?; #[cfg(feature = "additional-controls")] @@ -80,10 +68,12 @@ pub fn validate_json_from_str( pub fn validate_json_from_str( cddl: &str, json: &str, + allow_missing_definitions: bool, enabled_features: Option>, ) -> std::result::Result { let mut l = Lexer::new(cddl); - let mut p = Parser::new((&mut l).iter(), cddl).map_err(|e| JsValue::from(e.to_string()))?; + let mut p = Parser::new((&mut l).iter(), cddl, allow_missing_definitions) + .map_err(|e| JsValue::from(e.to_string()))?; let c = p.parse_cddl().map_err(|e| JsValue::from(e.to_string()))?; if !p.errors.is_empty() { return Err( @@ -120,9 +110,14 @@ pub fn validate_json_from_str( #[cfg(not(feature = "additional-controls"))] #[wasm_bindgen] /// Validate JSON string from a given CDDL document string -pub fn validate_json_from_str(cddl: &str, json: &str) -> std::result::Result { +pub fn validate_json_from_str( + cddl: &str, + json: &str, + allow_missing_definitions: bool, +) -> std::result::Result { let mut l = Lexer::new(cddl); - let mut p = Parser::new((&mut l).iter(), cddl).map_err(|e| JsValue::from(e.to_string()))?; + let mut p = Parser::new((&mut l).iter(), cddl, allow_missing_definitions) + .map_err(|e| JsValue::from(e.to_string()))?; let c = p.parse_cddl().map_err(|e| JsValue::from(e.to_string()))?; if !p.errors.is_empty() { return Err( @@ -161,10 +156,12 @@ pub fn validate_json_from_str(cddl: &str, json: &str) -> std::result::Result, ) -> cbor::Result { let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(cbor::Error::CDDLParsing)?; + let cddl = cddl_from_str(&mut lexer, cddl, true, allow_missing_definitions) + .map_err(cbor::Error::CDDLParsing)?; let cbor: ciborium::value::Value = ciborium::de::from_reader(cbor_slice).unwrap(); @@ -176,9 +173,14 @@ pub fn validate_cbor_from_slice( #[cfg(feature = "cbor")] #[cfg(not(feature = "additional-controls"))] /// Validate CBOR slice from a given CDDL document string -pub fn validate_cbor_from_slice(cddl: &str, cbor_slice: &[u8]) -> cbor::Result { +pub fn validate_cbor_from_slice( + cddl: &str, + cbor_slice: &[u8], + allow_missing_definitions: bool, +) -> cbor::Result { let mut lexer = lexer_from_str(cddl); - let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(cbor::Error::CDDLParsing)?; + let cddl = + cddl_from_str(&mut lexer, cddl, allow_missing_definitions).map_err(cbor::Error::CDDLParsing)?; let cbor: ciborium::value::Value = ciborium::de::from_reader(cbor_slice).map_err(cbor::Error::CBORParsing)?; @@ -194,10 +196,12 @@ pub fn validate_cbor_from_slice(cddl: &str, cbor_slice: &[u8]) -> cbor::Result>, ) -> std::result::Result { let mut l = Lexer::new(cddl); - let mut p = Parser::new((&mut l).iter(), cddl).map_err(|e| JsValue::from(e.to_string()))?; + let mut p = Parser::new((&mut l).iter(), cddl, allow_missing_definitions) + .map_err(|e| JsValue::from(e.to_string()))?; let c = p.parse_cddl().map_err(|e| JsValue::from(e.to_string()))?; if !p.errors.is_empty() { return Err( @@ -237,9 +241,11 @@ pub fn validate_cbor_from_slice( pub fn validate_cbor_from_slice( cddl: &str, cbor_slice: &[u8], + allow_missing_definitions: bool, ) -> std::result::Result { let mut l = Lexer::new(cddl); - let mut p = Parser::new((&mut l).iter(), cddl).map_err(|e| JsValue::from(e.to_string()))?; + let mut p = Parser::new((&mut l).iter(), cddl, allow_missing_definitions) + .map_err(|e| JsValue::from(e.to_string()))?; let c = p.parse_cddl().map_err(|e| JsValue::from(e.to_string()))?; if !p.errors.is_empty() { return Err( @@ -271,520 +277,6 @@ pub fn validate_cbor_from_slice( .map(|_| JsValue::default()) } -/// Find non-choice alternate rule from a given identifier -pub fn rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a Rule<'a>> { - cddl.rules.iter().find_map(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident && !rule.is_type_choice_alternate => Some(r), - Rule::Group { rule, .. } if rule.name == *ident && !rule.is_group_choice_alternate => Some(r), - _ => None, - }) -} - -/// Find text values from a given identifier -pub fn text_value_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a Type2<'a>> { - cddl.rules.iter().find_map(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => { - rule.value.type_choices.iter().find_map(|tc| { - if tc.type1.operator.is_none() { - match &tc.type1.type2 { - Type2::TextValue { .. } | Type2::UTF8ByteString { .. } => Some(&tc.type1.type2), - Type2::Typename { ident, .. } => text_value_from_ident(cddl, ident), - Type2::ParenthesizedType { pt, .. } => pt.type_choices.iter().find_map(|tc| { - if tc.type1.operator.is_none() { - text_value_from_type2(cddl, &tc.type1.type2) - } else { - None - } - }), - _ => None, - } - } else { - None - } - }) - } - _ => None, - }) -} - -/// Find text values from a given Type2 -pub fn text_value_from_type2<'a>(cddl: &'a CDDL, t2: &'a Type2<'a>) -> Option<&'a Type2<'a>> { - match t2 { - Type2::TextValue { .. } | Type2::UTF8ByteString { .. } => Some(t2), - Type2::Typename { ident, .. } => text_value_from_ident(cddl, ident), - Type2::Array { group, .. } => group.group_choices.iter().find_map(|gc| { - if gc.group_entries.len() == 2 { - if let Some(ge) = gc.group_entries.first() { - if let GroupEntry::ValueMemberKey { ge, .. } = &ge.0 { - if ge.member_key.is_none() { - ge.entry_type.type_choices.iter().find_map(|tc| { - if tc.type1.operator.is_none() { - text_value_from_type2(cddl, &tc.type1.type2) - } else { - None - } - }) - } else { - None - } - } else { - None - } - } else { - None - } - } else { - None - } - }), - Type2::ParenthesizedType { pt, .. } => pt.type_choices.iter().find_map(|tc| { - if tc.type1.operator.is_none() { - text_value_from_type2(cddl, &tc.type1.type2) - } else { - None - } - }), - _ => None, - } -} - -/// Unwrap array, map or tag type rule from ident -pub fn unwrap_rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a Rule<'a>> { - cddl.rules.iter().find_map(|r| match r { - Rule::Type { - rule: - TypeRule { - name, - is_type_choice_alternate, - value: Type { type_choices, .. }, - .. - }, - .. - } if name == ident && !is_type_choice_alternate => { - let match_fn = |tc: &TypeChoice| { - matches!( - tc.type1.type2, - Type2::Map { .. } | Type2::Array { .. } | Type2::TaggedData { .. } - ) - }; - - if type_choices.iter().any(match_fn) { - Some(r) - } else if let Some(ident) = type_choices.iter().find_map(|tc| { - if let Type2::Typename { - ident, - generic_args: None, - .. - } = &tc.type1.type2 - { - Some(ident) - } else { - None - } - }) { - unwrap_rule_from_ident(cddl, ident) - } else { - None - } - } - _ => None, - }) -} - -/// Find non-group choice alternate rule from a given identifier -pub fn group_rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a GroupRule<'a>> { - cddl.rules.iter().find_map(|r| match r { - Rule::Group { rule, .. } if rule.name == *ident && !rule.is_group_choice_alternate => { - Some(rule.as_ref()) - } - _ => None, - }) -} - -/// Find non-group choice alternate rule from a given identifier -pub fn type_rule_from_ident<'a>(cddl: &'a CDDL, ident: &Identifier) -> Option<&'a TypeRule<'a>> { - cddl.rules.iter().find_map(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident && !rule.is_type_choice_alternate => Some(rule), - _ => None, - }) -} - -/// Retrieve the list of generic parameters for a given rule -pub fn generic_params_from_rule<'a>(rule: &Rule<'a>) -> Option> { - match rule { - Rule::Type { rule, .. } => rule - .generic_params - .as_ref() - .map(|gp| gp.params.iter().map(|gp| gp.param.ident).collect()), - Rule::Group { rule, .. } => rule - .generic_params - .as_ref() - .map(|gp| gp.params.iter().map(|gp| gp.param.ident).collect()), - } -} - -/// Find all type choice alternate rules from a given identifier -pub fn type_choice_alternates_from_ident<'a>( - cddl: &'a CDDL, - ident: &Identifier, -) -> Vec<&'a Type<'a>> { - cddl - .rules - .iter() - .filter_map(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => Some(&rule.value), - _ => None, - }) - .collect::>() -} - -/// Find all group choice alternate rules from a given identifier -pub fn group_choice_alternates_from_ident<'a>( - cddl: &'a CDDL, - ident: &Identifier, -) -> Vec<&'a GroupEntry<'a>> { - cddl - .rules - .iter() - .filter_map(|r| match r { - Rule::Group { rule, .. } if &rule.name == ident => Some(&rule.entry), - _ => None, - }) - .collect::>() -} - -/// Convert a given group choice to a list of type choices -pub fn type_choices_from_group_choice<'a>( - cddl: &'a CDDL, - grpchoice: &GroupChoice<'a>, -) -> Vec> { - let mut type_choices = Vec::new(); - for ge in grpchoice.group_entries.iter() { - match &ge.0 { - GroupEntry::ValueMemberKey { ge, .. } => { - type_choices.append(&mut ge.entry_type.type_choices.clone()); - } - GroupEntry::TypeGroupname { ge, .. } => { - // TODO: parse generic args - if let Some(r) = rule_from_ident(cddl, &ge.name) { - match r { - Rule::Type { rule, .. } => type_choices.append(&mut rule.value.type_choices.clone()), - Rule::Group { rule, .. } => type_choices.append(&mut type_choices_from_group_choice( - cddl, - &GroupChoice::new(vec![rule.entry.clone()]), - )), - } - } - } - GroupEntry::InlineGroup { group, .. } => { - for gc in group.group_choices.iter() { - type_choices.append(&mut type_choices_from_group_choice(cddl, gc)); - } - } - } - } - - type_choices -} - -/// Is the given identifier associated with a null data type -pub fn is_ident_null_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::NULL | Token::NIL = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_null_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a boolean data type -pub fn is_ident_bool_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::BOOL = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_bool_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Does the given boolean identifier match the boolean value -pub fn ident_matches_bool_value(cddl: &CDDL, ident: &Identifier, value: bool) -> bool { - if let Token::TRUE = lookup_ident(ident.ident) { - if value { - return true; - } - } - - if let Token::FALSE = lookup_ident(ident.ident) { - if !value { - return true; - } - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - ident_matches_bool_value(cddl, ident, value) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a URI data type -pub fn is_ident_uri_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::URI = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_uri_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a b64url data type -pub fn is_ident_b64url_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::B64URL = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_b64url_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a tdate data type -pub fn is_ident_tdate_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::TDATE = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_tdate_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a time data type -pub fn is_ident_time_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::TIME = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if &rule.name == ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_time_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a numeric data type -pub fn is_ident_numeric_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::UINT - | Token::NINT - | Token::INTEGER - | Token::INT - | Token::NUMBER - | Token::FLOAT - | Token::FLOAT16 - | Token::FLOAT32 - | Token::FLOAT64 - | Token::FLOAT1632 - | Token::FLOAT3264 - | Token::UNSIGNED = lookup_ident(ident.ident) - { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_numeric_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a uint data type -pub fn is_ident_uint_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::UINT = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_uint_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a nint data type -pub fn is_ident_nint_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::NINT = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_nint_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with an integer data type -pub fn is_ident_integer_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::INT | Token::INTEGER | Token::NINT | Token::UINT | Token::NUMBER | Token::UNSIGNED = - lookup_ident(ident.ident) - { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_integer_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a float data type -pub fn is_ident_float_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::FLOAT - | Token::FLOAT16 - | Token::FLOAT1632 - | Token::FLOAT32 - | Token::FLOAT3264 - | Token::FLOAT64 = lookup_ident(ident.ident) - { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_float_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a string data type -pub fn is_ident_string_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::TEXT | Token::TSTR = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_string_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with the any type -pub fn is_ident_any_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::ANY = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_any_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - -/// Is the given identifier associated with a byte string data type -pub fn is_ident_byte_string_data_type(cddl: &CDDL, ident: &Identifier) -> bool { - if let Token::BSTR | Token::BYTES = lookup_ident(ident.ident) { - return true; - } - - cddl.rules.iter().any(|r| match r { - Rule::Type { rule, .. } if rule.name == *ident => rule.value.type_choices.iter().any(|tc| { - if let Type2::Typename { ident, .. } = &tc.type1.type2 { - is_ident_byte_string_data_type(cddl, ident) - } else { - false - } - }), - _ => false, - }) -} - /// Validate array length and \[non\]homogeneity based on a given optional /// occurrence indicator. The first bool in the returned tuple indicates whether /// or not a subsequent validation of the array's elements shouch be homogenous. @@ -900,58 +392,6 @@ pub fn validate_array_occurrence<'de, T: Deserialize<'de>>( Ok((iter_items, allow_empty_array)) } -/// Retrieve number of group entries from a group choice. This is currently only -/// used for determining map equality/inequality and for validating the number -/// of entries in arrays, but may be useful in other contexts. The occurrence is -/// only captured for the second element of the CDDL array to avoid ambiguity in -/// non-homogenous array definitions -pub fn entry_counts_from_group_choice(cddl: &CDDL, group_choice: &GroupChoice) -> EntryCount { - let mut count = 0; - let mut entry_occurrence = None; - - for (idx, ge) in group_choice.group_entries.iter().enumerate() { - match &ge.0 { - GroupEntry::ValueMemberKey { ge, .. } => { - if idx == 1 { - if let Some(occur) = &ge.occur { - entry_occurrence = Some(occur.occur.clone()) - } - } - - count += 1; - } - GroupEntry::InlineGroup { group, occur, .. } => { - if idx == 1 { - if let Some(occur) = occur { - entry_occurrence = Some(occur.occur.clone()) - } - } - for gc in group.group_choices.iter() { - count += entry_counts_from_group_choice(cddl, gc).count; - } - } - GroupEntry::TypeGroupname { ge, .. } => { - if idx == 1 { - if let Some(occur) = &ge.occur { - entry_occurrence = Some(occur.occur.clone()) - } - } - if let Some(gr) = group_rule_from_ident(cddl, &ge.name) { - count += - entry_counts_from_group_choice(cddl, &GroupChoice::new(vec![gr.entry.clone()])).count; - } else { - count += 1; - } - } - } - } - - EntryCount { - count, - entry_occurrence, - } -} - /// Validate the number of entries given an array of possible valid entry counts pub fn validate_entry_count(valid_entry_counts: &[EntryCount], num_entries: usize) -> bool { valid_entry_counts.iter().any(|ec| { @@ -982,43 +422,3 @@ pub fn validate_entry_count(valid_entry_counts: &[EntryCount], num_entries: usiz } }) } - -/// Entry count -#[derive(Clone, Debug)] -pub struct EntryCount { - /// Count - pub count: u64, - /// Optional occurrence - pub entry_occurrence: Option, -} - -/// Regex needs to be formatted in a certain way so it can be parsed. See -/// -pub fn format_regex(input: &str) -> Option { - let mut formatted_regex = String::from(input); - let mut unescape = Vec::new(); - for (idx, c) in formatted_regex.char_indices() { - if c == '\\' { - if let Some(c) = formatted_regex.chars().nth(idx + 1) { - if !regex_syntax::is_meta_character(c) && c != 'd' { - unescape.push(format!("\\{}", c)); - } - } - } - } - - for replace in unescape.iter() { - formatted_regex = - formatted_regex.replace(replace, &replace.chars().nth(1).unwrap().to_string()); - } - - for find in ["?=", "?!", "?<=", "? { fn visit_control_operator( &mut self, target: &Type2<'a>, - _ctrl: &str, + _ctrl: &Token<'a>, controller: &Type2<'a>, ) -> Result { walk_control_operator(self, target, controller) @@ -133,7 +138,7 @@ pub trait Visitor<'a, E: Error> { } /// Visit nonmemberkey - fn visit_nonmemberkey(&mut self, nmk: &NonMemberKey<'a>) -> Result { + fn visit_nonmemberkey(&mut self, nmk: &ParenthesizedTypeOrGroup<'a>) -> Result { walk_nonmemberkey(self, nmk) } } @@ -213,7 +218,13 @@ where RangeCtlOp::RangeOp { is_inclusive, .. } => { visitor.visit_range(&target.type2, &o.type2, *is_inclusive) } - RangeCtlOp::CtlOp { ctrl, .. } => visitor.visit_control_operator(&target.type2, ctrl, &o.type2), + RangeCtlOp::CtlOp { ctrl, .. } => { + if let Some(token) = lookup_control_from_str(ctrl) { + visitor.visit_control_operator(&target.type2, &token, &o.type2) + } else { + Ok(()) + } + } } } @@ -248,47 +259,47 @@ where V: Visitor<'a, E> + ?Sized, { match t2 { - Type2::Array { group, .. } => visitor.visit_group(group), - Type2::Map { group, .. } => visitor.visit_group(group), - Type2::ChoiceFromGroup { + Type2::Array(Array { group, .. }) => visitor.visit_group(group), + Type2::Map(Map { group, .. }) => visitor.visit_group(group), + Type2::ChoiceFromGroup(ChoiceFromGroup { generic_args, ident, .. - } => { + }) => { if let Some(ga) = generic_args { visitor.visit_genericargs(ga)?; } visitor.visit_identifier(ident) } - Type2::ChoiceFromInlineGroup { group, .. } => visitor.visit_group(group), - Type2::TaggedData { t, .. } => visitor.visit_type(t), - Type2::Typename { ident, .. } => visitor.visit_identifier(ident), - Type2::Unwrap { + Type2::ChoiceFromInlineGroup(ChoiceFromInlineGroup { group, .. }) => visitor.visit_group(group), + Type2::TaggedData(TaggedData { t, .. }) => visitor.visit_type(t), + Type2::Typename(Typename { ident, .. }) => visitor.visit_identifier(ident), + Type2::Unwrap(Unwrap { generic_args, ident, .. - } => { + }) => { if let Some(ga) = generic_args { visitor.visit_genericargs(ga)?; } visitor.visit_identifier(ident) } - Type2::ParenthesizedType { pt, .. } => visitor.visit_type(pt), - Type2::B16ByteString { value, .. } => { + Type2::ParenthesizedType(ParenthesizedType { pt, .. }) => visitor.visit_type(pt), + Type2::B16ByteString(B16ByteString { value, .. }) => { visitor.visit_value(&Value::BYTE(ByteValue::B16(value.clone()))) } - Type2::B64ByteString { value, .. } => { + Type2::B64ByteString(B64ByteString { value, .. }) => { visitor.visit_value(&Value::BYTE(ByteValue::B64(value.clone()))) } - Type2::UTF8ByteString { value, .. } => { + Type2::UTF8ByteString(Utf8ByteString { value, .. }) => { visitor.visit_value(&Value::BYTE(ByteValue::UTF8(value.clone()))) } - Type2::FloatValue { value, .. } => visitor.visit_value(&Value::FLOAT(*value)), - Type2::IntValue { value, .. } => visitor.visit_value(&Value::INT(*value)), - Type2::UintValue { value, .. } => visitor.visit_value(&Value::UINT(*value)), - Type2::TextValue { value, .. } => visitor.visit_value(&Value::TEXT(value.clone())), + Type2::FloatValue(FloatValue { value, .. }) => visitor.visit_value(&Value::FLOAT(*value)), + Type2::IntValue(IntValue { value, .. }) => visitor.visit_value(&Value::INT(*value)), + Type2::UintValue(UintValue { value, .. }) => visitor.visit_value(&Value::UINT(*value)), + Type2::TextValue(TextValue { value, .. }) => visitor.visit_value(&Value::TEXT(value.clone())), _ => Ok(()), } } @@ -398,10 +409,12 @@ where V: Visitor<'a, E> + ?Sized, { match mk { - MemberKey::Type1 { t1, .. } => visitor.visit_type1(t1), - MemberKey::Bareword { ident, .. } => visitor.visit_identifier(ident), - MemberKey::Value { value, .. } => visitor.visit_value(value), - MemberKey::NonMemberKey { non_member_key, .. } => visitor.visit_nonmemberkey(non_member_key), + MemberKey::Type1(Type1MemberKey { t1, .. }) => visitor.visit_type1(t1), + MemberKey::Bareword(BarewordMemberKey { ident, .. }) => visitor.visit_identifier(ident), + MemberKey::Value(ValueMemberKey { value, .. }) => visitor.visit_value(value), + MemberKey::Parenthesized(ParenthesizedMemberKey { non_member_key, .. }) => { + visitor.visit_nonmemberkey(non_member_key) + } } } @@ -428,13 +441,13 @@ where } /// Walk nonmemberkey -pub fn walk_nonmemberkey<'a, E, V>(visitor: &mut V, nmk: &NonMemberKey<'a>) -> Result +pub fn walk_nonmemberkey<'a, E, V>(visitor: &mut V, nmk: &ParenthesizedTypeOrGroup<'a>) -> Result where E: Error, V: Visitor<'a, E> + ?Sized, { match nmk { - NonMemberKey::Group(group) => visitor.visit_group(group), - NonMemberKey::Type(t) => visitor.visit_type(t), + ParenthesizedTypeOrGroup::Group(group) => visitor.visit_group(group), + ParenthesizedTypeOrGroup::Type(t) => visitor.visit_type(&t.pt), } } diff --git a/tests/cddl.rs b/tests/cddl.rs index f74045a8..bd613cee 100644 --- a/tests/cddl.rs +++ b/tests/cddl.rs @@ -15,7 +15,12 @@ fn verify_cddl_compiles() -> Result<(), parser::Error> { } let file_content = fs::read_to_string(file.path()).unwrap(); - match parser::cddl_from_str(&mut lexer_from_str(&file_content), &file_content, true) { + match parser::cddl_from_str( + &mut lexer_from_str(&file_content), + &file_content, + true, + true, + ) { Ok(_) => println!("file: {:#?} ... success", file.path()), Err(_) => { return Err(parser::Error::INCREMENTAL); @@ -31,6 +36,7 @@ fn verify_json_validation() -> json::Result { validate_json_from_str( &fs::read_to_string("tests/fixtures/cddl/reputon.cddl").unwrap(), &fs::read_to_string("tests/fixtures/json/reputon.json").unwrap(), + true, None, ) } diff --git a/tests/did.rs b/tests/did.rs index b7e0a323..37f1ea6b 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -42,9 +42,9 @@ fn validate_did_json_examples() -> Result<(), Box> { let file = file?; if file.path().extension().and_then(OsStr::to_str).unwrap() == "json" { #[cfg(feature = "additional-controls")] - let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?, None); + let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?, true, None); #[cfg(not(feature = "additional-controls"))] - let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?); + let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?, true); println!("assert ok {:?}", file.path()); if let Err(e) = &r { println!("error validating {:?}\n", file.path()); @@ -92,9 +92,9 @@ fn validate_did_cbor_examples() -> Result<(), Box> { let mut data = Vec::new(); f.read_to_end(&mut data)?; #[cfg(feature = "additional-controls")] - let r = validate_cbor_from_slice(&cddl, &data, None); + let r = validate_cbor_from_slice(&cddl, &data, true, None); #[cfg(not(feature = "additional-controls"))] - let r = validate_cbor_from_slice(&cddl, &data); + let r = validate_cbor_from_slice(&cddl, &data, true); println!("assert ok {:?}", file.path()); if let Err(e) = &r { diff --git a/tests/parser.rs b/tests/parser.rs index bb447f68..0a5e4423 100644 --- a/tests/parser.rs +++ b/tests/parser.rs @@ -30,7 +30,7 @@ fn verify_cddl() -> Result<()> { "# ); - match Parser::new(Lexer::new(input).iter(), input) { + match Parser::new(Lexer::new(input).iter(), input, true) { Ok(mut p) => match p.parse_cddl() { Ok(cddl) => { let expected_output = CDDL { @@ -40,91 +40,85 @@ fn verify_cddl() -> Result<()> { name: Identifier { ident: "myrule".into(), socket: None, - span: (0, 6, 1), + span: Span(0, 6, 1), }, generic_params: None, is_type_choice_alternate: false, value: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "secondrule".into(), socket: None, - span: (9, 19, 1), + span: Span(9, 19, 1), }, generic_args: None, - span: (9, 19, 1), - }, - operator: None, - comments_after_type: None, - span: (9, 19, 1), + span: Span(9, 19, 1), + }), + span: Span(9, 19, 1), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (9, 19, 1), + span: Span(9, 19, 1), }, - comments_before_assignt: None, - comments_after_assignt: None, + ..Default::default() }, comments_after_rule: None, - span: (0, 19, 1), + span: Span(0, 19, 1), }, Rule::Type { rule: TypeRule { name: Identifier { ident: "myrange".into(), socket: None, - span: (20, 27, 2), + span: Span(20, 27, 2), }, - generic_params: None, - is_type_choice_alternate: false, value: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::UintValue { + type2: Type2::UintValue(UintValue { value: 10, - span: (30, 32, 2), - }, + span: Span(30, 32, 2), + }), operator: Some(Operator { operator: RangeCtlOp::RangeOp { is_inclusive: true, - span: (32, 34, 2), + span: Span(32, 34, 2), }, - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "upper".into(), socket: None, - span: (34, 39, 2), + span: Span(34, 39, 2), }, generic_args: None, - span: (34, 39, 2), - }, + span: Span(34, 39, 2), + }), comments_before_operator: None, comments_after_operator: None, }), comments_after_type: None, - span: (30, 39, 2), + span: Span(30, 39, 2), }, comments_before_type: None, comments_after_type: None, }], - span: (30, 39, 2), + span: Span(30, 39, 2), }, - comments_before_assignt: None, - comments_after_assignt: None, + ..Default::default() }, comments_after_rule: None, - span: (20, 39, 2), + span: Span(20, 39, 2), }, Rule::Type { rule: TypeRule { name: Identifier { ident: "upper".into(), socket: None, - span: (40, 45, 3), + span: Span(40, 45, 3), }, generic_params: None, is_type_choice_alternate: false, @@ -132,45 +126,40 @@ fn verify_cddl() -> Result<()> { type_choices: vec![ TypeChoice { type1: Type1 { - type2: Type2::UintValue { + type2: Type2::UintValue(UintValue { value: 500, - span: (48, 51, 3), - }, - operator: None, - comments_after_type: None, - span: (48, 51, 3), + span: Span(48, 51, 3), + }), + span: Span(48, 51, 3), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, TypeChoice { type1: Type1 { - type2: Type2::UintValue { + type2: Type2::UintValue(UintValue { value: 600, - span: (54, 57, 3), - }, - operator: None, - comments_after_type: None, - span: (54, 57, 3), + span: Span(54, 57, 3), + }), + span: Span(54, 57, 3), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (48, 57, 3), + span: Span(48, 57, 3), }, - comments_before_assignt: None, - comments_after_assignt: None, + ..Default::default() }, comments_after_rule: None, - span: (40, 57, 3), + span: Span(40, 57, 3), }, Rule::Group { rule: Box::from(GroupRule { name: Identifier { ident: "gr".into(), socket: None, - span: (58, 60, 4), + span: Span(58, 60, 4), }, generic_params: None, is_group_choice_alternate: false, @@ -179,7 +168,7 @@ fn verify_cddl() -> Result<()> { occur: Occur::Exact { lower: Some(2), upper: None, - span: (63, 65, 4), + span: Span(63, 65, 4), }, comments: None, _a: PhantomData::default(), @@ -193,13 +182,13 @@ fn verify_cddl() -> Result<()> { name: Identifier { ident: "test".into(), socket: None, - span: (68, 72, 4), + span: Span(68, 72, 4), }, generic_args: None, }, leading_comments: None, trailing_comments: None, - span: (68, 72, 4), + span: Span(68, 72, 4), }, OptionalComma { optional_comma: false, @@ -208,93 +197,85 @@ fn verify_cddl() -> Result<()> { }, )], comments_before_grpchoice: None, - span: (68, 72, 4), + span: Span(68, 72, 4), }], - span: (68, 72, 4), + span: Span(68, 72, 4), }, comments_before_group: None, comments_after_group: None, - span: (63, 74, 4), + span: Span(63, 74, 4), }, comments_before_assigng: None, comments_after_assigng: None, }), comments_after_rule: None, - span: (58, 74, 4), + span: Span(58, 74, 4), }, Rule::Type { rule: TypeRule { name: Identifier { ident: "messages".into(), socket: None, - span: (75, 83, 5), + span: Span(75, 83, 5), }, generic_params: None, is_type_choice_alternate: false, value: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "message".into(), socket: None, - span: (86, 93, 5), + span: Span(86, 93, 5), }, generic_args: Some(GenericArgs { args: vec![ GenericArg { arg: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "reboot".into(), - span: (94, 102, 5), - }, - operator: None, - comments_after_type: None, - span: (94, 102, 5), + span: Span(94, 102, 5), + }), + span: Span(94, 102, 5), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, GenericArg { arg: Box::from(Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "now".into(), - span: (104, 109, 5), - }, - operator: None, - comments_after_type: None, - span: (104, 109, 5), + span: Span(104, 109, 5), + }), + span: Span(104, 109, 5), + ..Default::default() }), - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - - span: (93, 110, 5), + span: Span(93, 110, 5), }), - span: (86, 110, 5), - }, - operator: None, - comments_after_type: None, - span: (86, 110, 5), + span: Span(86, 110, 5), + }), + span: Span(86, 110, 5), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (86, 110, 5), + span: Span(86, 110, 5), }, - comments_before_assignt: None, - comments_after_assignt: None, + ..Default::default() }, + span: Span(75, 110, 5), comments_after_rule: None, - span: (75, 110, 5), }, Rule::Type { rule: TypeRule { name: Identifier { ident: "message".into(), socket: None, - span: (111, 118, 6), + span: Span(111, 118, 6), }, generic_params: Some(GenericParams { params: vec![ @@ -302,28 +283,26 @@ fn verify_cddl() -> Result<()> { param: Identifier { ident: "t".into(), socket: None, - span: (119, 120, 6), + span: Span(119, 120, 6), }, - comments_before_ident: None, - comments_after_ident: None, + ..Default::default() }, GenericParam { param: Identifier { ident: "v".into(), socket: None, - span: (122, 123, 6), + span: Span(122, 123, 6), }, - comments_before_ident: None, - comments_after_ident: None, + ..Default::default() }, ], - span: (118, 124, 6), + span: Span(118, 124, 6), }), is_type_choice_alternate: false, value: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Map { + type2: Type2::Map(Map { group: Group { group_choices: vec![GroupChoice { group_entries: vec![ @@ -331,36 +310,33 @@ fn verify_cddl() -> Result<()> { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "type".into(), socket: None, - span: (128, 132, 6), + span: Span(128, 132, 6), }, - comments: None, - comments_after_colon: None, - span: (128, 133, 6), - }), + span: Span(128, 133, 6), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::UintValue { + type2: Type2::UintValue(UintValue { value: 2, - span: (134, 135, 6), - }, - operator: None, - comments_after_type: None, - span: (134, 135, 6), + span: Span(134, 135, 6), + }), + span: Span(134, 135, 6), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (134, 135, 6), + span: Span(134, 135, 6), }, }), leading_comments: None, trailing_comments: None, - span: (128, 136, 6), + span: Span(128, 136, 6), }, OptionalComma { optional_comma: true, @@ -372,41 +348,38 @@ fn verify_cddl() -> Result<()> { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "value".into(), socket: None, - span: (137, 142, 6), + span: Span(137, 142, 6), }, - comments: None, - comments_after_colon: None, - span: (137, 143, 6), - }), + span: Span(137, 143, 6), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "v".into(), socket: None, - span: (144, 145, 6), + span: Span(144, 145, 6), }, generic_args: None, - span: (144, 145, 6), - }, - operator: None, - comments_after_type: None, - span: (144, 145, 6), + span: Span(144, 145, 6), + }), + span: Span(144, 145, 6), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (144, 145, 6), + span: Span(144, 145, 6), }, }), leading_comments: None, trailing_comments: None, - span: (137, 145, 6), + span: Span(137, 145, 6), }, OptionalComma { optional_comma: false, @@ -416,72 +389,64 @@ fn verify_cddl() -> Result<()> { ), ], comments_before_grpchoice: None, - span: (128, 145, 6), + span: Span(128, 145, 6), }], - span: (128, 145, 6), + span: Span(128, 145, 6), }, - comments_before_group: None, - comments_after_group: None, - span: (127, 146, 6), - }, - operator: None, - comments_after_type: None, - span: (127, 146, 6), + span: Span(127, 146, 6), + ..Default::default() + }), + span: Span(127, 146, 6), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (127, 146, 6), + span: Span(127, 146, 6), }, - comments_before_assignt: None, - comments_after_assignt: None, + ..Default::default() }, comments_after_rule: None, - span: (111, 146, 6), + span: Span(111, 146, 6), }, Rule::Type { rule: TypeRule { name: Identifier { ident: "color".into(), socket: None, - span: (147, 152, 7), + span: Span(147, 152, 7), }, generic_params: None, is_type_choice_alternate: false, value: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::ChoiceFromGroup { + type2: Type2::ChoiceFromGroup(ChoiceFromGroup { ident: Identifier { ident: "colors".into(), socket: None, - span: (156, 162, 7), + span: Span(156, 162, 7), }, - generic_args: None, - comments: None, - span: (155, 162, 7), - }, - operator: None, - comments_after_type: None, - span: (155, 162, 7), + span: Span(155, 162, 7), + ..Default::default() + }), + span: Span(155, 162, 7), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (155, 162, 7), + span: Span(155, 162, 7), }, - comments_before_assignt: None, - comments_after_assignt: None, + ..Default::default() }, comments_after_rule: None, - span: (147, 162, 7), + span: Span(147, 162, 7), }, Rule::Group { rule: Box::from(GroupRule { name: Identifier { ident: "colors".into(), socket: None, - span: (163, 169, 8), + span: Span(163, 169, 8), }, generic_params: None, is_group_choice_alternate: false, @@ -493,36 +458,33 @@ fn verify_cddl() -> Result<()> { GroupEntry::ValueMemberKey { ge: Box::from(ValueMemberKeyEntry { occur: None, - member_key: Some(MemberKey::Bareword { + member_key: Some(MemberKey::Bareword(BarewordMemberKey { ident: Identifier { ident: "red".into(), socket: None, - span: (174, 177, 8), + span: Span(174, 177, 8), }, - comments: None, - comments_after_colon: None, - span: (174, 178, 8), - }), + span: Span(174, 178, 8), + ..Default::default() + })), entry_type: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::TextValue { + type2: Type2::TextValue(TextValue { value: "red".into(), - span: (179, 184, 8), - }, - operator: None, - comments_after_type: None, - span: (179, 184, 8), + span: Span(179, 184, 8), + }), + span: Span(179, 184, 8), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (179, 184, 8), + span: Span(179, 184, 8), }, }), leading_comments: None, trailing_comments: None, - span: (174, 184, 8), + span: Span(174, 184, 8), }, OptionalComma { optional_comma: false, @@ -531,93 +493,86 @@ fn verify_cddl() -> Result<()> { }, )], comments_before_grpchoice: None, - span: (174, 184, 8), + span: Span(174, 184, 8), }], - span: (174, 184, 8), + span: Span(174, 184, 8), }, comments_before_group: None, comments_after_group: None, - span: (172, 186, 8), + span: Span(172, 186, 8), }, comments_before_assigng: None, comments_after_assigng: None, }), comments_after_rule: None, - span: (163, 186, 8), + span: Span(163, 186, 8), }, Rule::Type { rule: TypeRule { name: Identifier { ident: "test".into(), socket: None, - span: (187, 191, 9), + span: Span(187, 191, 9), }, generic_params: None, is_type_choice_alternate: false, value: Type { type_choices: vec![TypeChoice { type1: Type1 { - type2: Type2::ParenthesizedType { + type2: Type2::ParenthesizedType(ParenthesizedType { pt: Type { type_choices: vec![ TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "int".into(), socket: None, - span: (196, 199, 9), + span: Span(196, 199, 9), }, generic_args: None, - span: (196, 199, 9), - }, - operator: None, - comments_after_type: None, - span: (196, 199, 9), + span: Span(196, 199, 9), + }), + span: Span(196, 199, 9), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, TypeChoice { type1: Type1 { - type2: Type2::Typename { + type2: Type2::Typename(Typename { ident: Identifier { ident: "float".into(), socket: None, - span: (202, 207, 9), + span: Span(202, 207, 9), }, generic_args: None, - span: (202, 207, 9), - }, - operator: None, - comments_after_type: None, - span: (202, 207, 9), + span: Span(202, 207, 9), + }), + span: Span(202, 207, 9), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }, ], - span: (196, 207, 9), + span: Span(196, 207, 9), }, - comments_before_type: None, - comments_after_type: None, - span: (194, 209, 9), - }, - operator: None, - comments_after_type: None, - span: (194, 210, 9), + span: Span(194, 209, 9), + ..Default::default() + }), + span: Span(194, 210, 9), + ..Default::default() }, - comments_before_type: None, - comments_after_type: None, + ..Default::default() }], - span: (194, 210, 9), + span: Span(194, 210, 9), }, comments_before_assignt: None, comments_after_assignt: None, }, comments_after_rule: None, - span: (187, 210, 9), + span: Span(187, 210, 9), }, ], comments: None, @@ -670,7 +625,7 @@ fn cri_reference() -> std::result::Result<(), String> { ); let mut l = lexer_from_str(cddl); - let c_ast = cddl_from_str(&mut l, cddl, true)?; + let c_ast = cddl_from_str(&mut l, cddl, true, false)?; println!("{}", c_ast); diff --git a/www/package-lock.json b/www/package-lock.json index 0fbc6593..154dd026 100644 --- a/www/package-lock.json +++ b/www/package-lock.json @@ -1,12 +1,12 @@ { "name": "cddl", - "version": "0.9.0-beta.0", + "version": "0.9.0-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cddl", - "version": "0.9.0-beta.0", + "version": "0.9.0-beta.1", "license": "MIT", "dependencies": { "cddl": "file:../pkg" @@ -25,7 +25,7 @@ }, "../pkg": { "name": "cddl", - "version": "0.9.0-beta.0", + "version": "0.9.0-beta.1", "license": "MIT" }, "node_modules/@discoveryjs/json-ext": { diff --git a/www/package.json b/www/package.json index c7ef1209..320f070e 100644 --- a/www/package.json +++ b/www/package.json @@ -1,6 +1,6 @@ { "name": "cddl", - "version": "0.9.0-beta.0", + "version": "0.9.0-beta.1", "description": "Parser for the Concise data definition language (CDDL)", "main": "index.js", "scripts": {