├── .duvet
├── snapshot.txt
├── .gitignore
└── config.toml
├── duvet
├── README.md
├── src
│ ├── text
│ │ ├── snapshots
│ │ │ ├── duvet__text__find__tests__empty.snap
│ │ │ ├── duvet__text__find__tests__end.snap
│ │ │ ├── duvet__text__find__tests__start.snap
│ │ │ ├── duvet__text__find__tests__end_2.snap
│ │ │ ├── duvet__text__find__tests__middle.snap
│ │ │ ├── duvet__text__find__tests__middle_2.snap
│ │ │ ├── duvet__text__find__tests__start_2.snap
│ │ │ ├── duvet__text__find__tests__hyphenated_haystack.snap
│ │ │ ├── duvet__text__find__tests__hyphenated_needle.snap
│ │ │ ├── duvet__text__find__tests__punctuation_test.snap
│ │ │ └── duvet__text__find__tests__ws_difference.snap
│ │ ├── whitespace.rs
│ │ └── view.rs
│ ├── comment
│ │ ├── snapshots
│ │ │ ├── duvet__comment__tokenizer__tests__empty.snap
│ │ │ ├── duvet__comment__tests__content_without_meta.snap
│ │ │ ├── duvet__comment__tests__meta_without_content.snap
│ │ │ ├── duvet__comment__tokenizer__tests__only_unnamed.snap
│ │ │ ├── duvet__comment__tokenizer__tests__configured.snap
│ │ │ ├── duvet__comment__tokenizer__tests__duplicate_meta.snap
│ │ │ ├── duvet__comment__tokenizer__tests__basic.snap
│ │ │ ├── duvet__comment__tests__missing_new_line.snap
│ │ │ ├── duvet__comment__tests__type_citation.snap
│ │ │ ├── duvet__comment__tests__type_test.snap
│ │ │ ├── duvet__comment__tests__type_todo.snap
│ │ │ └── duvet__comment__tests__type_exception.snap
│ │ └── tests.rs
│ ├── specification
│ │ ├── ietf
│ │ │ ├── snapshots.tar.gz
│ │ │ ├── break_filter.rs
│ │ │ └── parser.rs
│ │ ├── markdown
│ │ │ ├── snapshots
│ │ │ │ ├── duvet__specification__markdown__tests__simple__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multi_line_header__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multi_line_header_strong_heading_attrs__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multi_line_header_link_heading_attrs__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__simple__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multi_line_header__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multi_line_header_strong_heading_attrs__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multi_line_header_link_heading_attrs__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__duplicate_sections__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__duplicate_sections__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__heading_attributes__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__heading_attributes__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multiple__tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__multiple__tree.snap
│ │ │ │ ├── duvet__specification__markdown__tests__simple_tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__list_example_tokens.snap
│ │ │ │ ├── duvet__specification__markdown__tests__list_example__tree.snap
│ │ │ │ └── duvet__specification__markdown__tests__list_example__tokens.snap
│ │ │ ├── break_filter.rs
│ │ │ ├── tests.rs
│ │ │ └── parser.rs
│ │ ├── ietf.rs
│ │ └── markdown.rs
│ ├── text.rs
│ ├── snapshots
│ │ ├── duvet__tests__invalid_section.snap
│ │ ├── duvet__tests__invalid_quote.snap
│ │ ├── duvet__tests__inner_whitespace.snap
│ │ └── duvet__tests__markdown_report.snap
│ ├── source.rs
│ ├── lib.rs
│ ├── extract
│ │ ├── tests.rs
│ │ └── snapshots
│ │ │ └── duvet__extract__tests__esdk_streaming.snap
│ ├── main.rs
│ ├── comment.rs
│ ├── report
│ │ ├── html.rs
│ │ ├── ci.rs
│ │ ├── stats.rs
│ │ └── status.rs
│ └── config.rs
├── www
│ ├── Makefile
│ ├── src
│ │ ├── link.js
│ │ ├── index.js
│ │ └── App.js
│ ├── public
│ │ └── index.html
│ ├── .gitignore
│ └── package.json
└── Cargo.toml
├── .clippy.toml
├── duvet-core
├── .gitignore
├── tests
│ ├── line_count
│ │ ├── c.txt
│ │ ├── a.txt
│ │ ├── manifest.txt
│ │ └── b.txt
│ ├── macros.rs
│ └── line_count.rs
├── src
│ ├── macro_support.rs
│ ├── artifact.rs
│ ├── testing.rs
│ ├── lib.rs
│ ├── env.rs
│ ├── dir.rs
│ ├── hash.rs
│ ├── progress.rs
│ ├── contents.rs
│ ├── http.rs
│ ├── diff.rs
│ ├── glob.rs
│ ├── vfs.rs
│ └── path.rs
├── README.md
└── Cargo.toml
├── CODEOWNERS
├── guide
├── .gitignore
├── src
│ ├── config.md
│ ├── SUMMARY.md
│ ├── specifications.md
│ ├── introduction.md
│ ├── example-config.toml
│ ├── reports.md
│ └── annotations.md
└── book.toml
├── .cargo
└── config.toml
├── NOTICE
├── _typos.toml
├── rust-toolchain
├── .gitignore
├── .rustfmt.toml
├── .gitattributes
├── integration
├── s2n-tls.toml
├── aws-encryption-sdk-dafny.toml
├── s2n-quic.toml
├── snapshots
│ ├── h3_json.snap
│ ├── init-c_json.snap
│ ├── init-java_json.snap
│ ├── init-rust_json.snap
│ ├── s2n-tls_json.snap
│ ├── init-python_json.snap
│ ├── s2n-quic_json.snap
│ ├── init-quick-start_json.snap
│ ├── report-markdown_json.snap
│ ├── report-ignore-whitespace_json.snap
│ ├── aws-encryption-sdk-dafny_json.snap
│ ├── extract-compound-requirement_json.snap
│ ├── extract-duplicate-requirement_json.snap
│ ├── report-relative-spec-path_json.snap
│ ├── extract-compound-mixed-requirement_json.snap
│ ├── aws-database-encryption-sdk-dynamodb_json.snap
│ ├── aws-cryptographic-material-providers-library_json.snap
│ ├── init-c.snap
│ ├── init-java.snap
│ ├── init-rust.snap
│ ├── init-python.snap
│ ├── report-ignore-whitespace.snap
│ ├── extract-duplicate-requirement.snap
│ ├── extract-compound-mixed-requirement.snap
│ ├── extract-compound-requirement.snap
│ ├── report-markdown.snap
│ ├── report-relative-spec-path.snap
│ ├── report-missing-section_stderr.snap
│ ├── report-invalid-quote_stderr.snap
│ ├── report-snapshot-missing_stderr.snap
│ ├── report-snapshot-out-of-date_stderr.snap
│ └── init-quick-start.snap
├── aws-database-encryption-sdk-dynamodb.toml
├── aws-cryptographic-material-providers-library.toml
├── h3.toml
├── init-quick-start.toml
├── extract-compound-requirement.toml
├── extract-compound-mixed-requirement.toml
├── init-rust.toml
├── init-c.toml
├── init-python.toml
├── init-java.toml
├── report-invalid-quote.toml
├── report-missing-section.toml
├── report-snapshot-missing.toml
├── report-ignore-whitespace.toml
├── extract-duplicate-requirement.toml
├── report-markdown.toml
├── report-snapshot-out-of-date.toml
└── report-relative-spec-path.toml
├── CODE_OF_CONDUCT.md
├── duvet-macros
├── README.md
└── Cargo.toml
├── Cargo.toml
├── xtask
├── src
│ ├── changelog.rs
│ ├── main.rs
│ ├── publish.rs
│ ├── build.rs
│ ├── args.rs
│ ├── checks.rs
│ └── guide.rs
└── Cargo.toml
├── action.yml
├── .github
├── dependabot.yml
├── workflows
│ ├── dependencies.yml
│ ├── guide.yml
│ └── ci.yml
└── config
│ └── cargo-deny.toml
├── README.md
├── CHANGELOG.md
└── CONTRIBUTING.md
/.duvet/snapshot.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/duvet/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/.clippy.toml:
--------------------------------------------------------------------------------
1 | msrv = "1.85.0"
2 |
--------------------------------------------------------------------------------
/.duvet/.gitignore:
--------------------------------------------------------------------------------
1 | reports/
2 |
--------------------------------------------------------------------------------
/duvet-core/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @awslabs/duvet-owners
2 |
--------------------------------------------------------------------------------
/guide/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | command/
3 |
--------------------------------------------------------------------------------
/duvet-core/tests/line_count/c.txt:
--------------------------------------------------------------------------------
1 | short
2 | file
3 |
--------------------------------------------------------------------------------
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [alias]
2 | xtask = "run --package xtask --"
3 |
--------------------------------------------------------------------------------
/duvet-core/tests/line_count/a.txt:
--------------------------------------------------------------------------------
1 | this
2 | is
3 | a
4 | file
5 |
--------------------------------------------------------------------------------
/duvet-core/tests/line_count/manifest.txt:
--------------------------------------------------------------------------------
1 | a.txt
2 | b.txt
3 | c.txt
4 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
--------------------------------------------------------------------------------
/duvet-core/tests/line_count/b.txt:
--------------------------------------------------------------------------------
1 | this
2 | is
3 | another
4 | longer
5 | file
6 |
--------------------------------------------------------------------------------
/_typos.toml:
--------------------------------------------------------------------------------
1 | [files]
2 | extend-exclude = ["*.snap", "integration/snapshots/**", "specs/**"]
3 |
--------------------------------------------------------------------------------
/rust-toolchain:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "1.85.0"
3 | components = [ "rustc", "clippy", "rustfmt" ]
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | Cargo.lock
4 | target
5 | .history
6 | *.snap.new
7 | /duvet/src/specification/ietf/snapshots
8 |
--------------------------------------------------------------------------------
/.rustfmt.toml:
--------------------------------------------------------------------------------
1 | edition = "2021"
2 | format_macro_matchers = true
3 | imports_granularity = "Crate"
4 | use_field_init_shorthand = true
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | integration/snapshots/*_json.snap filter=lfs diff=lfs merge=lfs -text
2 | **/snapshots.tar.gz filter=lfs diff=lfs merge=lfs
3 |
--------------------------------------------------------------------------------
/integration/s2n-tls.toml:
--------------------------------------------------------------------------------
1 | source = { repo = "https://github.com/aws/s2n-tls", version = "v1.5.5" }
2 | cmd = ["bash compliance/generate_report.sh"]
3 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__empty.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"\", \"\")"
4 | ---
5 | None
6 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tokenizer__tests__empty.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tokenizer.rs
3 | expression: tokens
4 | ---
5 | []
6 |
--------------------------------------------------------------------------------
/integration/aws-encryption-sdk-dafny.toml:
--------------------------------------------------------------------------------
1 | source = { repo = "https://github.com/aws/aws-encryption-sdk-dafny", version = "v4.1.0" }
2 | cmd = ["make duvet"]
3 |
--------------------------------------------------------------------------------
/integration/s2n-quic.toml:
--------------------------------------------------------------------------------
1 | source = { repo = "https://github.com/aws/s2n-quic", version = "005f9461612df4f882b91844fa978a5ee603e9f8" }
2 | cmd = ["bash scripts/compliance"]
3 |
--------------------------------------------------------------------------------
/integration/snapshots/h3_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:04e431bdbf269446424d927cfe7fd0758df69c9eb05190971193d2d3d3c7174f
3 | size 679794
4 |
--------------------------------------------------------------------------------
/integration/snapshots/init-c_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:017ff9e279ee86a2a64fefcb72d5f47f614d261fd059463449e066cf3bbd5ee1
3 | size 25629
4 |
--------------------------------------------------------------------------------
/integration/snapshots/init-java_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:60727deb2e3ced49182c072a9ccf5191a2400b7d1ee949f5434720c4d34bb8f2
3 | size 25655
4 |
--------------------------------------------------------------------------------
/integration/snapshots/init-rust_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:1ebdd04a504e1c6120641b68e8fa0426cbb316edd4d28d4b4820b1b8d78fff1e
3 | size 25641
4 |
--------------------------------------------------------------------------------
/integration/snapshots/s2n-tls_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9cd60cf24f3177f84d866b8206a1703897a22646b0536a9c237d26b03818ccf4
3 | size 3272000
4 |
--------------------------------------------------------------------------------
/integration/aws-database-encryption-sdk-dynamodb.toml:
--------------------------------------------------------------------------------
1 | source = { repo = "https://github.com/aws/aws-database-encryption-sdk-dynamodb", version = "v3.7.0" }
2 | cmd = ["make duvet"]
3 |
--------------------------------------------------------------------------------
/integration/snapshots/init-python_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:f405615de471becd49fdb82363fc00a7d3c8f15edf0a84b81cfdb194d47b42c3
3 | size 25655
4 |
--------------------------------------------------------------------------------
/integration/snapshots/s2n-quic_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:51e43062ba43379dd13a47e195a8094faaaba394d4f3e04bf00da08f7761e286
3 | size 6286445
4 |
--------------------------------------------------------------------------------
/duvet/src/specification/ietf/snapshots.tar.gz:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:2cdfabea8c45af7b7741717788e5dba44fcf10aacd2b6e25468009cb902d966f
3 | size 135902532
4 |
--------------------------------------------------------------------------------
/integration/snapshots/init-quick-start_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:7c814969aff20660fbd261127ba5cb2f5ea471176b4a6fe276d4617d2147ddc4
3 | size 60608
4 |
--------------------------------------------------------------------------------
/integration/snapshots/report-markdown_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:dc6a07cab206b14689926a7695a54c239e95a99184889ccfdad74a62550e0103
3 | size 26555
4 |
--------------------------------------------------------------------------------
/integration/snapshots/report-ignore-whitespace_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:80f549c042ebd0221160d7e921471ebce54300be410fc2d25f7df7a98462e02b
3 | size 26148
4 |
--------------------------------------------------------------------------------
/integration/aws-cryptographic-material-providers-library.toml:
--------------------------------------------------------------------------------
1 | source = { repo = "https://github.com/aws/aws-cryptographic-material-providers-library", version = "v1.7.0" }
2 | cmd = ["make duvet"]
3 |
--------------------------------------------------------------------------------
/integration/snapshots/aws-encryption-sdk-dafny_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:2a20c886afe92e87a135fe58500f329bd6aa8f223ab7fce0b1cc52be7011fe67
3 | size 1250014
4 |
--------------------------------------------------------------------------------
/integration/snapshots/extract-compound-requirement_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:cd0858f9ea41c87d81c253f75b32002d2e253c0864cec88988465713b0a00551
3 | size 26040
4 |
--------------------------------------------------------------------------------
/integration/snapshots/extract-duplicate-requirement_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:7ee7c2d2ffcf8d5b08285174eba23c792ca1a3454034058cbdb4acfb846bbad1
3 | size 26212
4 |
--------------------------------------------------------------------------------
/integration/snapshots/report-relative-spec-path_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:870bd29b572c7a83f851241c9695bfc08a7ed435bad984877f1e068f2b598df0
3 | size 26392
4 |
--------------------------------------------------------------------------------
/integration/h3.toml:
--------------------------------------------------------------------------------
1 | source = { repo = "https://github.com/hyperium/h3", version = "c6b92cbca902a62850b72269384fcbc32d30cb96" }
2 | cmd = ["bash ci/compliance/extract.sh", "bash ci/compliance/report.sh"]
3 |
--------------------------------------------------------------------------------
/integration/snapshots/extract-compound-mixed-requirement_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:6623d51392681c7d319b98d20c72bccc85074351d3ee66fd99daa2f973384da3
3 | size 26014
4 |
--------------------------------------------------------------------------------
/duvet-core/src/macro_support.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | pub use ::tokio;
5 | pub use once_cell::sync::OnceCell;
6 |
--------------------------------------------------------------------------------
/integration/snapshots/aws-database-encryption-sdk-dynamodb_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:8ae0c4411ac861558e1dc3401052a92e51f8d3b1de303da7abeb5b48e7758d3e
3 | size 1165056
4 |
--------------------------------------------------------------------------------
/integration/snapshots/aws-cryptographic-material-providers-library_json.snap:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:689da86e0a60f61a921b4ee3167d72d8d1edba6924336431e359d3f8e19cc455
3 | size 1851925
4 |
--------------------------------------------------------------------------------
/integration/snapshots/init-c.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [C](my-spec.md)
6 | SECTION: [C](#c)
7 | TEXT[implementation]: C SHOULD be auto-detected.
8 |
--------------------------------------------------------------------------------
/guide/src/config.md:
--------------------------------------------------------------------------------
1 | # Configuration
2 |
3 | Configuration files are written in the [TOML format](https://toml.io/). The following is a quick overview of all settings:
4 |
5 | ```toml
6 | {{#include example-config.toml}}
7 | ```
8 |
--------------------------------------------------------------------------------
/integration/snapshots/init-java.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [Java](my-spec.md)
6 | SECTION: [Java](#java)
7 | TEXT[implementation]: Java SHOULD be auto-detected.
8 |
--------------------------------------------------------------------------------
/integration/snapshots/init-rust.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [Rust](my-spec.md)
6 | SECTION: [Rust](#rust)
7 | TEXT[implementation]: Rust SHOULD be auto-detected.
8 |
--------------------------------------------------------------------------------
/integration/snapshots/init-python.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [Python](my-spec.md)
6 | SECTION: [Python](#python)
7 | TEXT[implementation]: Python SHOULD be auto-detected.
8 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__end.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"d\", \"a b c d\")"
4 | ---
5 | Some(
6 | (
7 | 6..7,
8 | Exact,
9 | "d",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__start.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"a\", \"a b c d\")"
4 | ---
5 | Some(
6 | (
7 | 0..1,
8 | Exact,
9 | "a",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__content_without_meta.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//=,//#\", r#\"\n //# This is some content without meta\n \"#)"
4 | ---
5 | (
6 | {},
7 | [],
8 | )
9 |
--------------------------------------------------------------------------------
/duvet/src/text.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | pub mod find;
5 | pub mod view;
6 | pub mod whitespace;
7 |
8 | pub use find::find;
9 | pub use view::view;
10 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__end_2.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"c d\", \"a b c d\")"
4 | ---
5 | Some(
6 | (
7 | 4..7,
8 | Exact,
9 | "c d",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__middle.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"b\", \"a b c d\")"
4 | ---
5 | Some(
6 | (
7 | 2..3,
8 | Exact,
9 | "b",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__middle_2.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"b c\", \"a b c d\")"
4 | ---
5 | Some(
6 | (
7 | 2..5,
8 | Exact,
9 | "b c",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__start_2.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"a b\", \"a b c d\")"
4 | ---
5 | Some(
6 | (
7 | 0..3,
8 | Exact,
9 | "a b",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/integration/snapshots/report-ignore-whitespace.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [Testing](my-spec.md)
6 | SECTION: [Testing](#testing)
7 | TEXT[!SHOULD,implementation]: This SHOULD ignore whitespace.
8 |
--------------------------------------------------------------------------------
/integration/snapshots/extract-duplicate-requirement.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | snapshot_kind: text
5 | ---
6 | SPECIFICATION: [Testing](my-spec.md)
7 | SECTION: [Testing](#testing)
8 | TEXT[!MUST,implementation]: This MUST deduplicate the requirement.
9 |
--------------------------------------------------------------------------------
/duvet/www/Makefile:
--------------------------------------------------------------------------------
1 | public/script.js: node_modules $(wildcard src/*.js)
2 | @rm -rf build
3 | @npm run build
4 | @awk 1 build/static/js/*.js > public/script.js
5 |
6 | node_modules:
7 | @npm install
8 |
9 | dev: node_modules src/result.test.json
10 | @npm start
11 |
12 | .PHONY: dev
13 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__meta_without_content.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//=,//#\", r#\"\n //= type=todo\n \"#)"
4 | ---
5 | (
6 | {},
7 | [
8 | "comment is missing source specification",
9 | ],
10 | )
11 |
--------------------------------------------------------------------------------
/integration/snapshots/extract-compound-mixed-requirement.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [Testing](my-spec.md)
6 | SECTION: [Testing](#testing)
7 | TEXT[!MUST]: This SHOULD support compound requirements and it MUST deduplicate them for now.
8 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__hyphenated_haystack.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"this is a new-line\", \"this is a new-\\nline\")"
4 | ---
5 | Some(
6 | (
7 | 0..19,
8 | Exact,
9 | "this is a new-\nline",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__hyphenated_needle.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\"this is a new-\\nline\", \"this is a new-line\")"
4 | ---
5 | Some(
6 | (
7 | 0..18,
8 | Exact,
9 | "this is a new-line",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/integration/snapshots/extract-compound-requirement.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | snapshot_kind: text
5 | ---
6 | SPECIFICATION: [Testing](my-spec.md)
7 | SECTION: [Testing](#testing)
8 | TEXT[!SHOULD]: This SHOULD support compound requirements and it SHOULD deduplicate them for now.
9 |
--------------------------------------------------------------------------------
/integration/snapshots/report-markdown.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | expression: snapshot
4 | ---
5 | SPECIFICATION: [My spec](my-spec.md)
6 | SECTION: [Testing](#testing)
7 | TEXT[!MUST,implementation]: This quote MUST work
8 | TEXT[implementation]: * with
9 | TEXT[implementation]: * bullets
10 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/integration/snapshots/report-relative-spec-path.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | assertion_line: 302
4 | expression: snapshot
5 | ---
6 | SPECIFICATION: [Test Specification](../specifications/spec.md)
7 | SECTION: [Section 1](#section-1)
8 | TEXT[!MUST,implementation]: This is a test implementation requirement that MUST be annotated.
9 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__punctuation_test.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\" Second Sentence. \",\n\" First sentence. Second Sentence. Third Sentence. \")"
4 | ---
5 | Some(
6 | (
7 | 22..38,
8 | Exact,
9 | "Second Sentence.",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/duvet/src/snapshots/duvet__tests__invalid_section.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/tests.rs
3 | expression: error
4 | ---
5 | × missing section "foo" in my-spec.md
6 | ╭─[src/my-code.rs:2:5]
7 | 1 │
8 | 2 │ //= my-spec.md#foo
9 | · ───────┬──────
10 | · ╰── referenced here
11 | 3 │ //# This quote MUST NOT work
12 | ╰────
13 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tokenizer__tests__only_unnamed.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tokenizer.rs
3 | expression: tokens
4 | ---
5 | [
6 | UnnamedMeta {
7 | value: "this is meta",
8 | line: 1,
9 | },
10 | UnnamedMeta {
11 | value: "this is other meta",
12 | line: 2,
13 | },
14 | ]
15 |
--------------------------------------------------------------------------------
/guide/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | - [Introduction](./introduction.md)
4 | - [Configuration](./config.md)
5 | - [Specifications](./specifications.md)
6 | - [Annotations](./annotations.md)
7 | - [Reports](./reports.md)
8 | - [Commands]()
9 | - [init](./command/init.md)
10 | - [extract](./command/extract.md)
11 | - [report](./command/report.md)
12 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tokenizer__tests__configured.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tokenizer.rs
3 | expression: tokens
4 | ---
5 | [
6 | Meta {
7 | key: "meta",
8 | value: "goes here",
9 | line: 2,
10 | },
11 | Content {
12 | value: "content goes here",
13 | line: 3,
14 | },
15 | ]
16 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tokenizer__tests__duplicate_meta.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tokenizer.rs
3 | expression: tokens
4 | ---
5 | [
6 | Meta {
7 | key: "meta",
8 | value: "1",
9 | line: 1,
10 | },
11 | Meta {
12 | key: "meta",
13 | value: "2",
14 | line: 2,
15 | },
16 | ]
17 |
--------------------------------------------------------------------------------
/duvet/www/src/link.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | import { Link as A } from "react-router-dom";
5 | import B from "@material-ui/core/Link";
6 |
7 | export function Link(props) {
8 | if (props.href) return ;
9 |
10 | return ;
11 | }
12 |
--------------------------------------------------------------------------------
/duvet/src/snapshots/duvet__tests__invalid_quote.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/tests.rs
3 | expression: error
4 | ---
5 | × could not find text in section "section" of my-spec.md
6 | ╭─[src/my-code.rs:3:5]
7 | 2 │ //= my-spec.md#section
8 | 3 │ //# Here is missing text
9 | · ──────────┬─────────
10 | · ╰── text here
11 | 4 │
12 | ╰────
13 |
--------------------------------------------------------------------------------
/duvet/www/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.duvet/config.toml:
--------------------------------------------------------------------------------
1 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
2 |
3 | [[source]]
4 | pattern = "duvet/**/*.rs"
5 |
6 | [report.html]
7 | enabled = true
8 | issue-link = "https://github.com/awslabs/duvet/issues"
9 | blob-link = "https://github.com/awslabs/duvet/blob/${{ GITHUB_SHA || 'main' }}"
10 |
11 | [report.json]
12 | enabled = true
13 |
14 | [report.snapshot]
15 | enabled = true
16 |
--------------------------------------------------------------------------------
/duvet-core/README.md:
--------------------------------------------------------------------------------
1 | # duvet-core
2 |
3 | This is an internal crate used by [duvet](https://github.com/awslabs/duvet). The API is not currently stable and should not be used directly.
4 |
5 | ## License
6 |
7 | This project is licensed under the [Apache-2.0 License][license-url].
8 |
9 | [license-badge]: https://img.shields.io/badge/license-apache-blue.svg
10 | [license-url]: https://aws.amazon.com/apache-2-0/
11 |
--------------------------------------------------------------------------------
/integration/init-quick-start.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = [
3 | "duvet init --lang-rust --specification https://www.rfc-editor.org/rfc/rfc2324",
4 | "duvet report",
5 | ]
6 |
7 | [[file]]
8 | path = "src/lib.rs"
9 | contents = """
10 | //= https://www.rfc-editor.org/rfc/rfc2324#section-2.1.1
11 | //# A coffee pot server MUST accept both the BREW and POST method
12 | //# equivalently.
13 | """
14 |
--------------------------------------------------------------------------------
/duvet-macros/README.md:
--------------------------------------------------------------------------------
1 | # duvet-macros
2 |
3 | This is an internal crate used by [duvet](https://github.com/awslabs/duvet). The API is not currently stable and should not be used directly.
4 |
5 | ## License
6 |
7 | This project is licensed under the [Apache-2.0 License][license-url].
8 |
9 | [license-badge]: https://img.shields.io/badge/license-apache-blue.svg
10 | [license-url]: https://aws.amazon.com/apache-2-0/
11 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["duvet", "duvet-core", "duvet-macros", "xtask"]
3 | resolver = "2"
4 |
5 | [profile.bench]
6 | lto = true
7 | codegen-units = 1
8 | incremental = false
9 | # improve flamegraph information
10 | debug = true
11 |
12 | [profile.fuzz]
13 | inherits = "dev"
14 | opt-level = 3
15 | incremental = false
16 | codegen-units = 1
17 |
18 | [profile.release-debug]
19 | inherits = "dev"
20 | opt-level = 3
21 |
--------------------------------------------------------------------------------
/duvet/src/text/snapshots/duvet__text__find__tests__ws_difference.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/text/find.rs
3 | expression: "find(\" this should ignore whitespace differences\",\n\" this should ignore whitespace differences\")"
4 | ---
5 | Some(
6 | (
7 | 9..85,
8 | Exact,
9 | "this should ignore whitespace differences",
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/integration/extract-compound-requirement.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[specification]]
10 | source = "my-spec.md"
11 | """
12 |
13 | [[file]]
14 | path = "my-spec.md"
15 | contents = """
16 | # Testing
17 |
18 | This SHOULD support compound requirements and it SHOULD deduplicate them for now.
19 | """
20 |
--------------------------------------------------------------------------------
/integration/extract-compound-mixed-requirement.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[specification]]
10 | source = "my-spec.md"
11 | """
12 |
13 | [[file]]
14 | path = "my-spec.md"
15 | contents = """
16 | # Testing
17 |
18 | This SHOULD support compound requirements and it MUST deduplicate them for now.
19 | """
20 |
--------------------------------------------------------------------------------
/integration/init-rust.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet init", "duvet report"]
3 |
4 | [[file]]
5 | path = "Cargo.toml"
6 | contents = """
7 | [package]
8 | name = "testing"
9 | version = "0.1.0"
10 | """
11 |
12 | [[file]]
13 | path = "src/lib.rs"
14 | contents = """
15 | //= my-spec.md#rust
16 | //# Rust SHOULD be auto-detected.
17 | """
18 |
19 | [[file]]
20 | path = "my-spec.md"
21 | contents = """
22 | # Rust
23 |
24 | Rust SHOULD be auto-detected.
25 | """
26 |
--------------------------------------------------------------------------------
/duvet-macros/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "duvet-macros"
3 | version = "0.4.1"
4 | description = "Internal crate used by duvet"
5 | authors = ["Cameron Bytheway "]
6 | edition = "2021"
7 | license = "Apache-2.0"
8 | repository = "https://github.com/awslabs/duvet"
9 | rust-version = "1.85"
10 |
11 | [lib]
12 | proc-macro = true
13 |
14 | [dependencies]
15 | darling = "0.14"
16 | proc-macro2 = "1"
17 | quote = "1"
18 | syn = { version = "1", features = ["full"] }
19 |
--------------------------------------------------------------------------------
/integration/init-c.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet init", "duvet report"]
3 |
4 | [[file]]
5 | path = "CMakeLists.txt"
6 | contents = """
7 | cmake_minimum_required (VERSION 3.9)
8 | project (testing C)
9 | """
10 |
11 | [[file]]
12 | path = "src/testing.c"
13 | contents = """
14 | /**
15 | *= my-spec.md#c
16 | *# C SHOULD be auto-detected.
17 | */
18 | """
19 |
20 | [[file]]
21 | path = "my-spec.md"
22 | contents = """
23 | # C
24 |
25 | C SHOULD be auto-detected.
26 | """
27 |
--------------------------------------------------------------------------------
/integration/init-python.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet init", "duvet report"]
3 |
4 | [[file]]
5 | path = "pyproject.toml"
6 | contents = """
7 | [project]
8 | name = "testing"
9 | version = "0.1.0"
10 | """
11 |
12 | [[file]]
13 | path = "src/testing.py"
14 | contents = """
15 | ##= my-spec.md#python
16 | ##% Python SHOULD be auto-detected.
17 | """
18 |
19 | [[file]]
20 | path = "my-spec.md"
21 | contents = """
22 | # Python
23 |
24 | Python SHOULD be auto-detected.
25 | """
26 |
--------------------------------------------------------------------------------
/duvet/www/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 | /src/result.test.json
11 |
12 | # production
13 | /build
14 | package-lock.json
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | public/script.js
28 |
--------------------------------------------------------------------------------
/guide/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["Cameron Bytheway"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "Duvet"
7 |
8 | [build]
9 | build-dir = "build"
10 |
11 | [output.html]
12 | copy-fonts = true
13 | git-repository-url = "https://github.com/awslabs/duvet"
14 | edit-url-template = "https://github.com/awslabs/duvet/edit/main/book/{path}"
15 |
16 | [output.html.playground]
17 | editable = false
18 | copyable = true
19 | copy-js = true
20 | line-numbers = false
21 | runnable = false
22 |
--------------------------------------------------------------------------------
/guide/src/specifications.md:
--------------------------------------------------------------------------------
1 | # Specifications
2 |
3 | Duvet currently supports two specification formats: IETF and Markdown. Specifications using either of these formats will be scanned for requirements using the [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) key words (e.g. `MUST`, `SHOULD`, `MAY`, etc.) and track completion of these requirements. If a specification does not use these key words, or has additional requirements, then [requirement files](./annotations.md#spec) can be provided in the [configuration](./config.md).
4 |
--------------------------------------------------------------------------------
/integration/init-java.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet init", "duvet report"]
3 |
4 | [[file]]
5 | path = "build.gradle"
6 | contents = """
7 | plugins {
8 | id 'java'
9 | }
10 |
11 | group 'com.example'
12 | version '1.0-SNAPSHOT'
13 | """
14 |
15 | [[file]]
16 | path = "src/main/HelloDuvet.java"
17 | contents = """
18 | //= my-spec.md#java
19 | //# Java SHOULD be auto-detected.
20 | """
21 |
22 | [[file]]
23 | path = "my-spec.md"
24 | contents = """
25 | # Java
26 |
27 | Java SHOULD be auto-detected.
28 | """
29 |
--------------------------------------------------------------------------------
/xtask/src/changelog.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::Result;
5 | use clap::Parser;
6 | use xshell::{cmd, Shell};
7 |
8 | #[derive(Debug, Default, Parser)]
9 | pub struct Changelog {}
10 |
11 | impl Changelog {
12 | pub fn run(&self, sh: &Shell) -> Result {
13 | cmd!(
14 | sh,
15 | "npx conventional-changelog-cli -p conventionalcommits -i CHANGELOG.md -s"
16 | )
17 | .run()?;
18 |
19 | Ok(())
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/integration/report-invalid-quote.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 | """
15 |
16 | [[file]]
17 | path = "src/my-code.rs"
18 | contents = """
19 | //= my-spec.md#section
20 | //# Here is missing text
21 | """
22 |
23 | [[file]]
24 | path = "my-spec.md"
25 | contents = """
26 | # Section
27 |
28 | here is a spec
29 | """
30 |
--------------------------------------------------------------------------------
/integration/report-missing-section.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 | """
15 |
16 | [[file]]
17 | path = "src/my-code.rs"
18 | contents = """
19 | //= my-spec.md#foo
20 | //# This quote MUST NOT work
21 | """
22 |
23 | [[file]]
24 | path = "my-spec.md"
25 | contents = """
26 | # Section
27 |
28 | here is a spec
29 | """
30 |
--------------------------------------------------------------------------------
/duvet/src/snapshots/duvet__tests__inner_whitespace.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/tests.rs
3 | expression: "out[\"specifications\"][\"my-spec.md\"]"
4 | ---
5 | {
6 | "format": "markdown",
7 | "requirements": [],
8 | "sections": [
9 | {
10 | "id": "testing",
11 | "lines": [
12 | [
13 | [
14 | [
15 | 0
16 | ],
17 | 16,
18 | "This SHOULD ignore whitespace."
19 | ]
20 | ]
21 | ],
22 | "title": "Testing"
23 | }
24 | ],
25 | "title": "Testing"
26 | }
27 |
--------------------------------------------------------------------------------
/xtask/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "xtask"
3 | version = "0.0.0"
4 | edition = "2021"
5 | publish = false
6 |
7 | [dependencies]
8 | anyhow = "1"
9 | clap = { version = "4", features = ["derive"] }
10 | insta = { version = "1", features = ["json"] }
11 | serde = { version = "1", features = ["derive"] }
12 | serde_json = "1"
13 | toml = "0.9"
14 | xshell = "0.2"
15 |
16 | [lints.rust.unexpected_cfgs]
17 | level = "warn"
18 | check-cfg = [
19 | # xshell uses this `cfg` to make rust analyzer highlight the `cmd!` macro arguments
20 | 'cfg(trick_rust_analyzer_into_highlighting_interpolated_bits)',
21 | ]
22 |
--------------------------------------------------------------------------------
/xtask/src/main.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use clap::Parser;
5 | use xshell::Shell;
6 |
7 | type Error = anyhow::Error;
8 | type Result = core::result::Result;
9 |
10 | mod args;
11 | mod build;
12 | mod changelog;
13 | mod checks;
14 | mod guide;
15 | mod publish;
16 | mod tests;
17 |
18 | fn main() {
19 | let sh = Shell::new().unwrap();
20 | if let Err(err) = args::Args::parse().run(&sh) {
21 | eprintln!("{err:?}");
22 | std::process::exit(1);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: Duvet
2 | description: 'Installs Duvet in the GitHub Actions environment'
3 | inputs:
4 | version:
5 | description: 'Version of Duvet to install'
6 | default: 0.3.0
7 | required: false
8 | runs:
9 | using: "composite"
10 | steps:
11 | - name: Install rust toolchain
12 | id: toolchain
13 | shell: bash
14 | run: |
15 | rustup toolchain install stable
16 | rustup override set stable
17 |
18 | - name: Install Duvet
19 | uses: camshaft/install@v1
20 | with:
21 | crate: duvet
22 | version: ${{ inputs.version }}
23 |
24 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tokenizer__tests__basic.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tokenizer.rs
3 | expression: tokens
4 | ---
5 | [
6 | UnnamedMeta {
7 | value: "thing goes here",
8 | line: 1,
9 | },
10 | Meta {
11 | key: "meta",
12 | value: "foo",
13 | line: 2,
14 | },
15 | Meta {
16 | key: "meta2",
17 | value: "bar",
18 | line: 3,
19 | },
20 | Content {
21 | value: "content goes",
22 | line: 4,
23 | },
24 | Content {
25 | value: "here",
26 | line: 5,
27 | },
28 | ]
29 |
--------------------------------------------------------------------------------
/integration/report-snapshot-missing.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report --ci"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 |
15 | [report.snapshot]
16 | enabled = true
17 | """
18 |
19 | [[file]]
20 | path = "src/my-code.rs"
21 | contents = """
22 | //= my-spec.md#section
23 | //# here is a spec
24 | """
25 |
26 | [[file]]
27 | path = "my-spec.md"
28 | contents = """
29 | # Section
30 |
31 | here is a spec
32 | """
33 |
--------------------------------------------------------------------------------
/integration/report-ignore-whitespace.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 | """
15 |
16 | [[file]]
17 | path = "src/my-code.rs"
18 | contents = """
19 | //= my-spec.md#testing
20 | //# This SHOULD ignore whitespace.
21 | """
22 |
23 | [[file]]
24 | path = "my-spec.md"
25 | contents = """
26 | # Testing
27 |
28 | This SHOULD ignore whitespace.
29 | """
30 |
--------------------------------------------------------------------------------
/integration/extract-duplicate-requirement.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 | """
15 |
16 | [[file]]
17 | path = "src/my-code.rs"
18 | contents = """
19 | //= my-spec.md#testing
20 | //# This MUST deduplicate the requirement.
21 | """
22 |
23 | [[file]]
24 | path = "my-spec.md"
25 | contents = """
26 | # Testing
27 |
28 | This MUST deduplicate the requirement.
29 |
30 | This MUST deduplicate the requirement.
31 | """
32 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | # Maintain dependencies for GitHub Actions
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | interval: "daily"
13 |
14 | # Maintain dependencies for cargo
15 | - package-ecosystem: "cargo"
16 | directory: "/"
17 | schedule:
18 | interval: "daily"
19 |
--------------------------------------------------------------------------------
/integration/report-markdown.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 | """
15 |
16 | [[file]]
17 | path = "src/my-code.rs"
18 | contents = """
19 | //= my-spec.md#testing
20 | //# This quote MUST work
21 | //# * with
22 | //# * bullets
23 | """
24 |
25 | [[file]]
26 | path = "my-spec.md"
27 | contents = """
28 | # My spec
29 |
30 | here is a spec
31 |
32 | ## Testing
33 |
34 | This quote MUST work
35 | * with
36 | * bullets
37 | """
38 |
--------------------------------------------------------------------------------
/xtask/src/publish.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::Result;
5 | use clap::Parser;
6 | use xshell::{cmd, Shell};
7 |
8 | #[derive(Debug, Default, Parser)]
9 | pub struct Publish {}
10 |
11 | impl Publish {
12 | pub fn run(&self, sh: &Shell) -> Result {
13 | crate::build::Build::default().run(sh)?;
14 |
15 | cmd!(sh, "git diff --exit-code").run()?;
16 |
17 | for pkg in ["duvet-macros", "duvet-core", "duvet"] {
18 | let _dir = sh.push_dir(pkg);
19 | cmd!(sh, "cargo publish --allow-dirty").run()?;
20 | }
21 |
22 | Ok(())
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__simple__tokens.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: tokens(& contents).collect :: < Vec < _ >> ()"
4 | ---
5 | [
6 | Content {
7 | value: "",
8 | line: 1,
9 | },
10 | Section {
11 | id: None,
12 | title: "This is a test",
13 | level: 1,
14 | line: 2,
15 | },
16 | Content {
17 | value: "",
18 | line: 3,
19 | },
20 | Content {
21 | value: "Content goes here. Another",
22 | line: 4,
23 | },
24 | Content {
25 | value: "sentence here.",
26 | line: 5,
27 | },
28 | ]
29 |
--------------------------------------------------------------------------------
/duvet/www/src/index.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | import { default as React, useEffect } from "react";
5 | import ReactDOM from "react-dom";
6 | import { HashRouter, useLocation } from "react-router-dom";
7 | import App from "./App";
8 |
9 | function ScrollToTop() {
10 | const { pathname } = useLocation();
11 |
12 | useEffect(() => {
13 | window.scrollTo(0, 0);
14 | }, [pathname]);
15 |
16 | return null;
17 | }
18 |
19 | ReactDOM.render(
20 |
21 |
22 |
23 |
24 |
25 | ,
26 | document.getElementById("root")
27 | );
28 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__multi_line_header__tokens.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: tokens(& contents).collect :: < Vec < _ >> ()"
4 | ---
5 | [
6 | Content {
7 | value: "",
8 | line: 1,
9 | },
10 | Section {
11 | id: None,
12 | title: "Foo *bar\nbaz*",
13 | level: 1,
14 | line: 2,
15 | },
16 | Content {
17 | value: "",
18 | line: 5,
19 | },
20 | Content {
21 | value: "Content goes here. Another",
22 | line: 6,
23 | },
24 | Content {
25 | value: "sentence here.",
26 | line: 7,
27 | },
28 | ]
29 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/break_filter.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use super::tokenizer::Token;
5 |
6 | /// Filters out duplicate breaks
7 | pub fn break_filter>(tokens: T) -> impl Iterator- {
8 | let mut state = false;
9 | tokens.filter(move |token| {
10 | let prev_break = core::mem::take(&mut state);
11 |
12 | match token {
13 | Token::Section { .. } | Token::Content { .. } => true,
14 | Token::Break { .. } => {
15 | state = true;
16 |
17 | // dedup breaks
18 | !prev_break
19 | }
20 | }
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/integration/report-snapshot-out-of-date.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report --ci"]
3 |
4 | [[file]]
5 | path = ".duvet/config.toml"
6 | contents = """
7 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
8 |
9 | [[source]]
10 | pattern = "src/my-code.rs"
11 |
12 | [[specification]]
13 | source = "my-spec.md"
14 |
15 | [report.snapshot]
16 | enabled = true
17 | """
18 |
19 | [[file]]
20 | path = ".duvet/snapshot.txt"
21 | contents = """
22 | SPECIFICATION: [Section](my-spec.md)
23 | """
24 |
25 | [[file]]
26 | path = "src/my-code.rs"
27 | contents = """
28 | //= my-spec.md#section
29 | //# here is a spec
30 | """
31 |
32 | [[file]]
33 | path = "my-spec.md"
34 | contents = """
35 | # Section
36 |
37 | here is a spec
38 | """
39 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__multi_line_header_strong_heading_attrs__tokens.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: tokens(& contents).collect :: < Vec < _ >> ()"
4 | ---
5 | [
6 | Content {
7 | value: "",
8 | line: 1,
9 | },
10 | Section {
11 | id: Some(
12 | "blah",
13 | ),
14 | title: "Foo **bar\nbaz**",
15 | level: 1,
16 | line: 2,
17 | },
18 | Content {
19 | value: "",
20 | line: 5,
21 | },
22 | Content {
23 | value: "Content goes here. Another",
24 | line: 6,
25 | },
26 | Content {
27 | value: "sentence here.",
28 | line: 7,
29 | },
30 | ]
31 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__multi_line_header_link_heading_attrs__tokens.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: tokens(& contents).collect :: < Vec < _ >> ()"
4 | ---
5 | [
6 | Content {
7 | value: "",
8 | line: 1,
9 | },
10 | Section {
11 | id: Some(
12 | "blah",
13 | ),
14 | title: "Foo **bar\nbaz** [I'm link](http://something)",
15 | level: 1,
16 | line: 2,
17 | },
18 | Content {
19 | value: "",
20 | line: 5,
21 | },
22 | Content {
23 | value: "Content goes here. Another",
24 | line: 6,
25 | },
26 | Content {
27 | value: "sentence here.",
28 | line: 7,
29 | },
30 | ]
31 |
--------------------------------------------------------------------------------
/duvet-core/src/artifact.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use std::path::Path;
5 |
6 | /// Synchronizes a value to the file system
7 | ///
8 | /// When the `CI` environment variable is set, this method asserts the value matches
9 | /// what is on disk.
10 | pub fn sync(path: impl AsRef, value: impl AsRef) {
11 | let path = path.as_ref();
12 | let value = value.as_ref();
13 | if std::env::var("CI").is_err() {
14 | if let Some(parent) = path.parent() {
15 | std::fs::create_dir_all(parent).unwrap();
16 | }
17 | std::fs::write(path, value).unwrap();
18 | return;
19 | }
20 |
21 | let actual = std::fs::read_to_string(path).unwrap();
22 | assert_eq!(actual, value);
23 | }
24 |
--------------------------------------------------------------------------------
/integration/snapshots/report-missing-section_stderr.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | assertion_line: 284
4 | expression: stderr
5 | ---
6 | $ duvet report
7 | EXIT: Some(1)
8 | Extracting requirements
9 | Extracted requirements from 1 specifications
10 | Scanning sources
11 | Scanned 1 sources
12 | Parsing annotations
13 | Parsed 1 annotations
14 | Loading specifications
15 | Loaded 1 specifications
16 | Mapping sections
17 | Mapped 1 sections
18 | Matching references
19 | × × missing section "foo" in my-spec.md
20 | │ ╭─[src/my-code.rs:1:5]
21 | │ 1 │ //= my-spec.md#foo
22 | │ · ───────┬──────
23 | │ · ╰── referenced here
24 | │ 2 │ //# This quote MUST NOT work
25 | │ ╰────
26 | │
27 | │
28 | ╰─▶ encountered 1 errors
29 |
--------------------------------------------------------------------------------
/integration/snapshots/report-invalid-quote_stderr.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | assertion_line: 284
4 | expression: stderr
5 | ---
6 | $ duvet report
7 | EXIT: Some(1)
8 | Extracting requirements
9 | Extracted requirements from 1 specifications
10 | Scanning sources
11 | Scanned 1 sources
12 | Parsing annotations
13 | Parsed 1 annotations
14 | Loading specifications
15 | Loaded 1 specifications
16 | Mapping sections
17 | Mapped 1 sections
18 | Matching references
19 | × × could not find text in section "section" of my-spec.md
20 | │ ╭─[src/my-code.rs:2:5]
21 | │ 1 │ //= my-spec.md#section
22 | │ 2 │ //# Here is missing text
23 | │ · ──────────┬─────────
24 | │ · ╰── text here
25 | │ ╰────
26 | │
27 | │
28 | ╰─▶ encountered 1 errors
29 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__simple__tree.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: parse(& contents)"
4 | ---
5 | Ok(
6 | Specification {
7 | title: Some(
8 | "This is a test",
9 | ),
10 | sections: [
11 | Section {
12 | id: "this-is-a-test",
13 | title: "This is a test",
14 | full_title: "This is a test",
15 | lines: [
16 | Str(
17 | "Content goes here. Another",
18 | ),
19 | Str(
20 | "sentence here.",
21 | ),
22 | ],
23 | },
24 | ],
25 | format: Markdown,
26 | },
27 | )
28 |
--------------------------------------------------------------------------------
/.github/workflows/dependencies.yml:
--------------------------------------------------------------------------------
1 | name: dependencies
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '**/Cargo.toml'
9 | - '**/Cargo.lock'
10 | - '.github/workflows/dependencies.yml'
11 |
12 | pull_request:
13 | branches:
14 | - main
15 | paths:
16 | - '**/Cargo.toml'
17 | - '**/Cargo.lock'
18 | - '.github/workflows/dependencies.yml'
19 |
20 | schedule:
21 | # run every morning at 10am Pacific Time
22 | - cron: '0 17 * * *'
23 |
24 | jobs:
25 | deny:
26 | runs-on: ubuntu-latest
27 | steps:
28 | - uses: actions/checkout@v6
29 |
30 | - name: "Remove rust-toolchain"
31 | run: rm rust-toolchain
32 |
33 | - uses: EmbarkStudios/cargo-deny-action@v2
34 | with:
35 | command: check --config .github/config/cargo-deny.toml
36 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__multi_line_header__tree.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: parse(& contents)"
4 | ---
5 | Ok(
6 | Specification {
7 | title: Some(
8 | "Foo *bar baz*",
9 | ),
10 | sections: [
11 | Section {
12 | id: "foo-bar-baz",
13 | title: "Foo *bar baz*",
14 | full_title: "Foo *bar\nbaz*",
15 | lines: [
16 | Str(
17 | "Content goes here. Another",
18 | ),
19 | Str(
20 | "sentence here.",
21 | ),
22 | ],
23 | },
24 | ],
25 | format: Markdown,
26 | },
27 | )
28 |
--------------------------------------------------------------------------------
/integration/snapshots/report-snapshot-missing_stderr.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | assertion_line: 284
4 | expression: stderr
5 | ---
6 | $ duvet report --ci
7 | EXIT: Some(1)
8 | Extracting requirements
9 | Extracted requirements from 1 specifications
10 | Scanning sources
11 | Scanned 1 sources
12 | Parsing annotations
13 | Parsed 1 annotations
14 | Loading specifications
15 | Loaded 1 specifications
16 | Mapping sections
17 | Mapped 1 sections
18 | Matching references
19 | Matched 1 references
20 | Sorting references
21 | Sorted 1 references
22 | Writing .duvet/reports/report.html
23 | Wrote .duvet/reports/report.html
24 | Checking .duvet/snapshot.txt
25 | × .duvet/snapshot.txt
26 | ╰─▶ Could not read report snapshot. This is required to enforce CI checks.
27 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__multi_line_header_strong_heading_attrs__tree.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: parse(& contents)"
4 | ---
5 | Ok(
6 | Specification {
7 | title: Some(
8 | "Foo **bar baz**",
9 | ),
10 | sections: [
11 | Section {
12 | id: "blah",
13 | title: "Foo **bar baz**",
14 | full_title: "Foo **bar\nbaz**",
15 | lines: [
16 | Str(
17 | "Content goes here. Another",
18 | ),
19 | Str(
20 | "sentence here.",
21 | ),
22 | ],
23 | },
24 | ],
25 | format: Markdown,
26 | },
27 | )
28 |
--------------------------------------------------------------------------------
/integration/report-relative-spec-path.toml:
--------------------------------------------------------------------------------
1 | source = { local = true }
2 | cmd = ["duvet report"]
3 | cwd = "project/subdir"
4 |
5 | [[file]]
6 | path = "project/subdir/.duvet/config.toml"
7 | contents = """
8 | '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"
9 |
10 | [[source]]
11 | pattern = "src/**/*.rs"
12 |
13 | [[specification]]
14 | source = "../specifications/spec.md"
15 |
16 | [report.html]
17 | enabled = true
18 |
19 | [report.snapshot]
20 | enabled = true
21 | """
22 |
23 | [[file]]
24 | path = "project/subdir/src/lib.rs"
25 | contents = """
26 | //= ../specifications/spec.md#section-1
27 | //# This is a test implementation requirement that MUST be annotated.
28 | pub fn test() {}
29 | """
30 |
31 | [[file]]
32 | path = "project/specifications/spec.md"
33 | contents = """
34 | # Test Specification
35 |
36 | ## Section 1
37 |
38 | This is a test implementation requirement that MUST be annotated.
39 | """
40 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__missing_new_line.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//@=,//@#\",\nr#\"\n //@= https://example.com/spec.txt\n //@# Here is my citation\"#)"
4 | ---
5 | (
6 | {
7 | Annotation {
8 | source: "file.rs",
9 | anno_line: 2,
10 | original_target: "https://example.com/spec.txt",
11 | original_text: "https://example.com/spec.txt\n //@# Here is my citation",
12 | original_quote: "Here is my citation",
13 | anno: Citation,
14 | target: "https://example.com/spec.txt",
15 | quote: "Here is my citation",
16 | comment: "",
17 | manifest_dir: "[CWD]",
18 | level: Auto,
19 | format: Auto,
20 | tracking_issue: "",
21 | feature: "",
22 | tags: {},
23 | },
24 | },
25 | [],
26 | )
27 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__type_citation.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//@=,//@#\",\nr#\"\n //@= https://example.com/spec.txt\n //@# Here is my citation\n \"#)"
4 | ---
5 | (
6 | {
7 | Annotation {
8 | source: "file.rs",
9 | anno_line: 2,
10 | original_target: "https://example.com/spec.txt",
11 | original_text: "https://example.com/spec.txt\n //@# Here is my citation",
12 | original_quote: "Here is my citation",
13 | anno: Citation,
14 | target: "https://example.com/spec.txt",
15 | quote: "Here is my citation",
16 | comment: "",
17 | manifest_dir: "[CWD]",
18 | level: Auto,
19 | format: Auto,
20 | tracking_issue: "",
21 | feature: "",
22 | tags: {},
23 | },
24 | },
25 | [],
26 | )
27 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__multi_line_header_link_heading_attrs__tree.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: parse(& contents)"
4 | ---
5 | Ok(
6 | Specification {
7 | title: Some(
8 | "Foo **bar baz** [I'm link](http://something)",
9 | ),
10 | sections: [
11 | Section {
12 | id: "blah",
13 | title: "Foo **bar baz** [I'm link](http://something)",
14 | full_title: "Foo **bar\nbaz** [I'm link](http://something)",
15 | lines: [
16 | Str(
17 | "Content goes here. Another",
18 | ),
19 | Str(
20 | "sentence here.",
21 | ),
22 | ],
23 | },
24 | ],
25 | format: Markdown,
26 | },
27 | )
28 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__type_test.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//@=,//@#\",\nr#\"\n //@= https://example.com/spec.txt\n //@= type=test\n //@# Here is my citation\n \"#)"
4 | ---
5 | (
6 | {
7 | Annotation {
8 | source: "file.rs",
9 | anno_line: 2,
10 | original_target: "https://example.com/spec.txt",
11 | original_text: "https://example.com/spec.txt\n //@= type=test\n //@# Here is my citation",
12 | original_quote: "Here is my citation",
13 | anno: Test,
14 | target: "https://example.com/spec.txt",
15 | quote: "Here is my citation",
16 | comment: "",
17 | manifest_dir: "[CWD]",
18 | level: Auto,
19 | format: Auto,
20 | tracking_issue: "",
21 | feature: "",
22 | tags: {},
23 | },
24 | },
25 | [],
26 | )
27 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__duplicate_sections__tokens.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: tokens(& contents).collect :: < Vec < _ >> ()"
4 | ---
5 | [
6 | Content {
7 | value: "",
8 | line: 1,
9 | },
10 | Section {
11 | id: None,
12 | title: "Duplicate header",
13 | level: 1,
14 | line: 2,
15 | },
16 | Content {
17 | value: "",
18 | line: 3,
19 | },
20 | Content {
21 | value: "testing 123",
22 | line: 4,
23 | },
24 | Content {
25 | value: "",
26 | line: 5,
27 | },
28 | Section {
29 | id: None,
30 | title: "Duplicate header",
31 | level: 2,
32 | line: 6,
33 | },
34 | Content {
35 | value: "",
36 | line: 7,
37 | },
38 | Content {
39 | value: "other test",
40 | line: 8,
41 | },
42 | ]
43 |
--------------------------------------------------------------------------------
/duvet-core/src/testing.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | pub fn init_tracing() {
5 | use std::sync::Once;
6 |
7 | static TRACING: Once = Once::new();
8 |
9 | // make sure this only gets initialized once
10 | TRACING.call_once(|| {
11 | let format = tracing_subscriber::fmt::format()
12 | //.with_level(false) // don't include levels in formatted output
13 | //.with_ansi(false)
14 | .compact(); // Use a less verbose output format.
15 |
16 | let env_filter = tracing_subscriber::EnvFilter::builder()
17 | .with_default_directive(tracing::Level::DEBUG.into())
18 | .with_env_var("DUVET_LOG")
19 | .from_env()
20 | .unwrap();
21 |
22 | tracing_subscriber::fmt()
23 | .with_env_filter(env_filter)
24 | .event_format(format)
25 | .with_test_writer()
26 | .init();
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/duvet/src/snapshots/duvet__tests__markdown_report.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/tests.rs
3 | expression: "out[\"specifications\"][\"my-spec.md\"]"
4 | ---
5 | {
6 | "format": "markdown",
7 | "requirements": [],
8 | "sections": [
9 | {
10 | "id": "my-spec",
11 | "lines": [
12 | "here is a spec"
13 | ],
14 | "title": "My spec"
15 | },
16 | {
17 | "id": "testing",
18 | "lines": [
19 | [
20 | [
21 | [
22 | 0
23 | ],
24 | 16,
25 | "This quote MUST work"
26 | ]
27 | ],
28 | [
29 | [
30 | [
31 | 0
32 | ],
33 | 16,
34 | "* with"
35 | ]
36 | ],
37 | [
38 | [
39 | [
40 | 0
41 | ],
42 | 16,
43 | "* bullets"
44 | ]
45 | ]
46 | ],
47 | "title": "Testing"
48 | }
49 | ],
50 | "title": "My spec"
51 | }
52 |
--------------------------------------------------------------------------------
/integration/snapshots/report-snapshot-out-of-date_stderr.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: xtask/src/tests.rs
3 | assertion_line: 284
4 | expression: stderr
5 | ---
6 | $ duvet report --ci
7 | EXIT: Some(1)
8 | Extracting requirements
9 | Extracted requirements from 1 specifications
10 | Scanning sources
11 | Scanned 1 sources
12 | Parsing annotations
13 | Parsed 1 annotations
14 | Loading specifications
15 | Loaded 1 specifications
16 | Mapping sections
17 | Mapped 1 sections
18 | Matching references
19 | Matched 1 references
20 | Sorting references
21 | Sorted 1 references
22 | Writing .duvet/reports/report.html
23 | Wrote .duvet/reports/report.html
24 | Checking .duvet/snapshot.txt
25 |
26 | Differences detected in .duvet/snapshot.txt:
27 |
28 | @@ -1 +1,3 @@
29 | SPECIFICATION: [Section](my-spec.md)
30 | + SECTION: [Section](#section)
31 | + TEXT[implementation]: here is a spec
32 | × .duvet/snapshot.txt
33 | ╰─▶ Report snapshot does not match with CI mode enabled.
34 |
--------------------------------------------------------------------------------
/xtask/src/build.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::Result;
5 | use clap::Parser;
6 | use std::path::PathBuf;
7 | use xshell::{cmd, Shell};
8 |
9 | #[derive(Debug, Default, Parser)]
10 | pub struct Build {
11 | #[clap(long, default_value = "dev")]
12 | pub profile: String,
13 | }
14 |
15 | impl Build {
16 | pub fn run(&self, sh: &Shell) -> Result {
17 | {
18 | let _dir = sh.push_dir("duvet/www");
19 | cmd!(sh, "make").run()?;
20 | }
21 |
22 | let args = vec!["--profile".to_string(), self.profile.clone()];
23 |
24 | cmd!(sh, "cargo build -p duvet {args...}").run()?;
25 |
26 | let path = if self.profile == "dev" {
27 | sh.current_dir().join("target/debug/duvet")
28 | } else {
29 | sh.current_dir()
30 | .join("target")
31 | .join(&self.profile)
32 | .join("duvet")
33 | };
34 |
35 | Ok(path)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__duplicate_sections__tree.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: parse(& contents)"
4 | ---
5 | Ok(
6 | Specification {
7 | title: Some(
8 | "Duplicate header",
9 | ),
10 | sections: [
11 | Section {
12 | id: "duplicate-header",
13 | title: "Duplicate header",
14 | full_title: "Duplicate header",
15 | lines: [
16 | Str(
17 | "testing 123",
18 | ),
19 | ],
20 | },
21 | Section {
22 | id: "duplicate-header-1",
23 | title: "Duplicate header",
24 | full_title: "Duplicate header",
25 | lines: [
26 | Str(
27 | "other test",
28 | ),
29 | ],
30 | },
31 | ],
32 | format: Markdown,
33 | },
34 | )
35 |
--------------------------------------------------------------------------------
/duvet-core/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | #[macro_export]
5 | macro_rules! ensure {
6 | ($cond:expr) => {
7 | ensure!($cond, ());
8 | };
9 | ($cond:expr, $otherwise:expr) => {
10 | if !($cond) {
11 | return $otherwise;
12 | }
13 | };
14 | }
15 |
16 | #[cfg(any(test, feature = "testing"))]
17 | pub mod testing;
18 |
19 | #[cfg(any(test, feature = "testing"))]
20 | pub mod artifact;
21 | mod cache;
22 | pub mod contents;
23 | pub mod diagnostic;
24 | #[cfg(feature = "diff")]
25 | pub mod diff;
26 | pub mod dir;
27 | pub mod env;
28 | pub mod file;
29 | pub mod glob;
30 | pub mod hash;
31 | #[cfg(feature = "http")]
32 | pub mod http;
33 | pub mod path;
34 | pub mod progress;
35 | mod query;
36 | pub mod vfs;
37 |
38 | #[doc(hidden)]
39 | pub mod macro_support;
40 |
41 | pub use ::console;
42 | pub use cache::Cache;
43 | pub use duvet_macros::*;
44 | pub use query::Query;
45 |
46 | pub type Result = core::result::Result;
47 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__type_todo.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//@=,//@#\",\nr#\"\n //@= https://example.com/spec.txt\n //@= type=todo\n //@= feature=cool-things\n //@= tracking-issue=123\n //@# Here is my citation\n \"#)"
4 | ---
5 | (
6 | {
7 | Annotation {
8 | source: "file.rs",
9 | anno_line: 2,
10 | original_target: "https://example.com/spec.txt",
11 | original_text: "https://example.com/spec.txt\n //@= type=todo\n //@= feature=cool-things\n //@= tracking-issue=123\n //@# Here is my citation",
12 | original_quote: "Here is my citation",
13 | anno: Todo,
14 | target: "https://example.com/spec.txt",
15 | quote: "Here is my citation",
16 | comment: "",
17 | manifest_dir: "[CWD]",
18 | level: Auto,
19 | format: Auto,
20 | tracking_issue: "123",
21 | feature: "cool-things",
22 | tags: {},
23 | },
24 | },
25 | [],
26 | )
27 |
--------------------------------------------------------------------------------
/duvet/src/source.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::{
5 | annotation::{AnnotationSet, AnnotationType},
6 | comment, Error,
7 | };
8 | use duvet_core::path::Path;
9 |
10 | pub mod toml;
11 |
12 | #[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
13 | pub enum SourceFile {
14 | Text {
15 | pattern: comment::Pattern,
16 | default_type: AnnotationType,
17 | path: Path,
18 | },
19 | Toml(Path),
20 | }
21 |
22 | impl SourceFile {
23 | pub async fn annotations(&self) -> (AnnotationSet, Vec) {
24 | match self {
25 | Self::Text {
26 | pattern,
27 | default_type,
28 | path,
29 | } => match duvet_core::vfs::read_string(path).await {
30 | Ok(text) => comment::extract(&text, pattern, *default_type),
31 | Err(err) => (Default::default(), vec![err]),
32 | },
33 | Self::Toml(file) => toml::load(file).await,
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/duvet/src/comment/snapshots/duvet__comment__tests__type_exception.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/comment/tests.rs
3 | expression: "parse(\"//@=,//@#\",\nr#\"\n //@= https://example.com/spec.txt\n //@= type=exception\n //@= reason=This isn't possible currently\n //@# Here is my citation\n \"#)"
4 | ---
5 | (
6 | {
7 | Annotation {
8 | source: "file.rs",
9 | anno_line: 2,
10 | original_target: "https://example.com/spec.txt",
11 | original_text: "https://example.com/spec.txt\n //@= type=exception\n //@= reason=This isn't possible currently\n //@# Here is my citation",
12 | original_quote: "Here is my citation",
13 | anno: Exception,
14 | target: "https://example.com/spec.txt",
15 | quote: "Here is my citation",
16 | comment: "This isn't possible currently",
17 | manifest_dir: "[CWD]",
18 | level: Auto,
19 | format: Auto,
20 | tracking_issue: "",
21 | feature: "",
22 | tags: {},
23 | },
24 | },
25 | [],
26 | )
27 |
--------------------------------------------------------------------------------
/duvet-core/src/env.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::{diagnostic::IntoDiagnostic, path::Path, Result};
5 | use core::cell::RefCell;
6 | use once_cell::sync::Lazy;
7 | use std::sync::Arc;
8 |
9 | static GLOBAL_ARGS: Lazy> = Lazy::new(|| std::env::args().collect());
10 | static GLOBAL_DIR: Lazy> =
11 | Lazy::new(|| std::env::current_dir().map(|v| v.into()).into_diagnostic());
12 |
13 | thread_local! {
14 | static ARGS: RefCell> = RefCell::new(GLOBAL_ARGS.clone());
15 | static DIR: RefCell> = RefCell::new(GLOBAL_DIR.clone());
16 | }
17 |
18 | pub fn args() -> Arc<[String]> {
19 | ARGS.with(|current| current.borrow().clone())
20 | }
21 |
22 | pub fn set_args(args: Arc<[String]>) {
23 | ARGS.with(|current| *current.borrow_mut() = args);
24 | }
25 |
26 | pub fn current_dir() -> Result {
27 | DIR.with(|current| current.borrow().clone())
28 | }
29 |
30 | pub fn set_current_dir(dir: Path) {
31 | DIR.with(|current| *current.borrow_mut() = Ok(dir));
32 | }
33 |
--------------------------------------------------------------------------------
/.github/config/cargo-deny.toml:
--------------------------------------------------------------------------------
1 | [advisories]
2 | yanked = "deny"
3 |
4 | [bans]
5 | skip-tree = [
6 | # all of these are going to be just test dependencies
7 | { name = "insta" },
8 | ]
9 |
10 | [sources]
11 | unknown-registry = "deny"
12 | unknown-git = "deny"
13 |
14 | [licenses]
15 | confidence-threshold = 0.9
16 | # ignore licenses for private crates
17 | private = { ignore = true }
18 | allow = [
19 | "Apache-2.0",
20 | "BSD-2-Clause",
21 | "BSD-3-Clause",
22 | "CC0-1.0",
23 | "ISC",
24 | "MIT",
25 | "OpenSSL",
26 | "Unicode-DFS-2016",
27 | "Zlib",
28 | "Unicode-3.0",
29 | ]
30 |
31 | [[licenses.clarify]]
32 | name = "ring"
33 | expression = "MIT AND ISC AND OpenSSL"
34 | license-files = [
35 | { path = "LICENSE", hash = 0xbd0eed23 },
36 | ]
37 |
38 | [[licenses.clarify]]
39 | name = "webpki"
40 | expression = "ISC"
41 | license-files = [
42 | { path = "LICENSE", hash = 0x001c7e6c },
43 | ]
44 |
45 | [[licenses.clarify]]
46 | name = "encoding_rs"
47 | version = "*"
48 | expression = "(Apache-2.0 OR MIT) AND BSD-3-Clause"
49 | license-files = [
50 | { path = "COPYRIGHT", hash = 0x39f8ad31 }
51 | ]
52 |
--------------------------------------------------------------------------------
/duvet/www/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "duvet-report",
3 | "version": "0.2.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "4",
7 | "@mui/x-data-grid": "4",
8 | "@material-ui/icons": "4",
9 | "clsx": "1",
10 | "copy-to-clipboard": "3",
11 | "react": "17",
12 | "react-dom": "17",
13 | "react-router-dom": "5"
14 | },
15 | "devDependencies": {
16 | "@testing-library/jest-dom": "5",
17 | "@testing-library/react": "11",
18 | "@testing-library/user-event": "12",
19 | "react-scripts": "5"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/duvet/src/specification/ietf.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use super::{Format, Line, Section, Specification};
5 | use crate::Result;
6 | use duvet_core::file::SourceFile;
7 |
8 | pub mod break_filter;
9 | pub mod parser;
10 | pub mod tokenizer;
11 |
12 | #[cfg(test)]
13 | mod tests;
14 |
15 | pub fn parse(contents: &SourceFile) -> Result {
16 | let tokens = tokenizer::tokens(contents);
17 | let tokens = break_filter::break_filter(tokens);
18 | let parser = parser::parse(tokens);
19 |
20 | let sections = parser
21 | .map(|section| {
22 | let id = section.id.to_string();
23 |
24 | let section = Section {
25 | title: section.title.to_string(),
26 | id: id.clone(),
27 | full_title: section.title,
28 | lines: section.lines.into_iter().map(Line::Str).collect(),
29 | };
30 |
31 | (id, section)
32 | })
33 | .collect();
34 |
35 | Ok(Specification {
36 | title: None,
37 | sections,
38 | format: Format::Ietf,
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/duvet-core/tests/macros.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use duvet_core::{query, Query};
5 |
6 | #[query]
7 | async fn add(a: Query, b: Query) -> u64 {
8 | let a = *a.get().await;
9 | let b = *b.get().await;
10 | a + b
11 | }
12 |
13 | #[query(cache)]
14 | async fn args() -> Vec {
15 | std::env::args().collect()
16 | }
17 |
18 | #[query(delegate)]
19 | async fn delegate() -> Vec {
20 | args()
21 | }
22 |
23 | #[query(cache)]
24 | async fn add_cache(a: Query, b: Query) -> u64 {
25 | let a = *a.get().await;
26 | let b = *b.get().await;
27 | a + b
28 | }
29 |
30 | #[query(cache)]
31 | async fn mixed_cache(a: Query, b: u64) -> u64 {
32 | let a = *a.get().await;
33 | a + b
34 | }
35 |
36 | #[query(cache)]
37 | async fn ignored_cache(a: Query, b: Query, #[skip] log: bool) -> u64 {
38 | let a = *a.get().await;
39 | let b = *b.get().await;
40 | let value = a + b;
41 |
42 | if log {
43 | dbg!(value);
44 | }
45 |
46 | value
47 | }
48 |
49 | #[query(cache, delegate)]
50 | async fn cache_delegate(a: Query) -> Vec {
51 | if a.await {
52 | args()
53 | } else {
54 | delegate()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/duvet/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use clap::Parser;
5 | use std::sync::Arc;
6 |
7 | mod annotation;
8 | mod comment;
9 | mod config;
10 | mod extract;
11 | mod init;
12 | mod project;
13 | mod reference;
14 | mod report;
15 | mod source;
16 | mod specification;
17 | mod target;
18 | mod text;
19 |
20 | pub use duvet_core::{diagnostic::Error, Result};
21 |
22 | #[allow(clippy::large_enum_variant)]
23 | #[derive(Debug, Parser)]
24 | pub enum Arguments {
25 | /// Initializes a duvet project
26 | Init(init::Init),
27 | /// Extracts requirements out of a specification
28 | Extract(extract::Extract),
29 | /// Generates reports for the project
30 | Report(report::Report),
31 | }
32 |
33 | #[duvet_core::query(cache)]
34 | pub async fn arguments() -> Arc {
35 | Arc::new(Arguments::parse())
36 | }
37 |
38 | impl Arguments {
39 | pub async fn exec(&self) -> Result {
40 | match self {
41 | Self::Init(args) => args.exec().await,
42 | Self::Extract(args) => args.exec().await,
43 | Self::Report(args) => args.exec().await,
44 | }
45 | }
46 | }
47 |
48 | pub async fn run() -> Result {
49 | arguments().await.exec().await?;
50 | Ok(())
51 | }
52 |
--------------------------------------------------------------------------------
/duvet/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "duvet"
3 | version = "0.4.1"
4 | description = "A requirements traceability tool"
5 | authors = [
6 | "Cameron Bytheway ",
7 | "Ryan Emery ",
8 | ]
9 | edition = "2021"
10 | license = "Apache-2.0"
11 | repository = "https://github.com/awslabs/duvet"
12 | include = ["/src/**/*.rs", "/www/public"]
13 | default-run = "duvet"
14 | rust-version = "1.85"
15 |
16 | [dependencies]
17 | clap = { version = "4", features = ["derive"] }
18 | duvet-core = { version = "0.4", path = "../duvet-core" }
19 | futures = { version = "0.3" }
20 | glob = "0.3"
21 | lazy_static = "1"
22 | mimalloc = { version = "0.1", default-features = false }
23 | once_cell = "1"
24 | pulldown-cmark = { version = "0.13", default-features = false }
25 | regex = "1"
26 | serde = { version = "1", features = ["derive"] }
27 | serde_spanned = "1.0"
28 | slug = { version = "0.1" }
29 | tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
30 | tracing = "0.1"
31 | tracing-subscriber = { version = "0.3", features = ["env-filter"] }
32 | triple_accel = "0.4"
33 | url = "2"
34 | v_jsonescape = "0.7"
35 |
36 | [dev-dependencies]
37 | bolero = "0.13"
38 | duvet-core = { version = "0.4", path = "../duvet-core", features = ["testing"] }
39 | insta = { version = "1", features = ["filters", "json"] }
40 | schemars = "1.1"
41 | serde_json = "1"
42 |
--------------------------------------------------------------------------------
/duvet-core/src/dir.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::{glob::Glob, path::Path};
5 | use futures::Stream;
6 | use std::sync::Arc;
7 |
8 | pub mod walk;
9 |
10 | #[derive(Clone)]
11 | pub struct Directory {
12 | pub(crate) path: Path,
13 | pub(crate) contents: Arc<[Path]>,
14 | }
15 |
16 | impl Directory {
17 | pub fn iter(&self) -> impl Iterator
- {
18 | self.contents.iter()
19 | }
20 |
21 | pub fn walk(&self) -> impl Stream
- {
22 | walk::dir(self.path.clone())
23 | }
24 |
25 | pub fn glob(&self, include: Glob, ignore: Glob) -> impl Stream
- {
26 | walk::glob(self.path.clone(), include, ignore)
27 | }
28 | }
29 |
30 | impl IntoIterator for Directory {
31 | type Item = Path;
32 | type IntoIter = DirIter;
33 |
34 | fn into_iter(self) -> Self::IntoIter {
35 | DirIter {
36 | contents: self.contents,
37 | index: 0,
38 | }
39 | }
40 | }
41 |
42 | pub struct DirIter {
43 | contents: Arc<[Path]>,
44 | index: usize,
45 | }
46 |
47 | impl Iterator for DirIter {
48 | type Item = Path;
49 |
50 | fn next(&mut self) -> Option {
51 | let index = self.index;
52 | self.index += 1;
53 | self.contents.get(index).cloned()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/guide/src/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Duvet is a tool that establishes a bidirectional link between implementation and specification. This practice is called [requirements traceability](https://en.wikipedia.org/wiki/Requirements_traceability), which is defined as:
4 |
5 | > the ability to describe and follow the life of a requirement in both a forwards and backwards direction (i.e., from its origins, through its development and specification, to its subsequent deployment and use, and through periods of ongoing refinement and iteration in any of these phases)
6 |
7 | ## Quick Start
8 |
9 | Before getting started, Duvet requires a [rust toolchain](https://www.rust-lang.org/tools/install).
10 |
11 | 1. Install command
12 |
13 | ```console
14 | $ cargo install duvet --locked
15 | ```
16 |
17 | 2. Initialize repository
18 |
19 | In this example, we are using Rust. However, Duvet can be used with any language.
20 |
21 | ```console
22 | $ duvet init --lang-rust --specification https://www.rfc-editor.org/rfc/rfc2324
23 | ```
24 |
25 | 3. Add a implementation comment in the project
26 |
27 | ```rust
28 | // src/lib.rs
29 |
30 | //= https://www.rfc-editor.org/rfc/rfc2324#section-2.1.1
31 | //# A coffee pot server MUST accept both the BREW and POST method
32 | //# equivalently.
33 | ```
34 |
35 | 4. Generate a report
36 |
37 | ```console
38 | $ duvet report
39 | ```
40 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__heading_attributes__tree.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: parse(& contents)"
4 | ---
5 | Ok(
6 | Specification {
7 | title: Some(
8 | "Heading with ID",
9 | ),
10 | sections: [
11 | Section {
12 | id: "custom-id",
13 | title: "Heading with ID",
14 | full_title: "Heading with ID",
15 | lines: [
16 | Str(
17 | "Content under heading with custom ID.",
18 | ),
19 | ],
20 | },
21 | Section {
22 | id: "another-id",
23 | title: "Another heading",
24 | full_title: "Another heading",
25 | lines: [
26 | Str(
27 | "More content here.",
28 | ),
29 | ],
30 | },
31 | Section {
32 | id: "regular-heading",
33 | title: "Regular heading",
34 | full_title: "Regular heading",
35 | lines: [
36 | Str(
37 | "This heading doesn't have a custom ID.",
38 | ),
39 | ],
40 | },
41 | ],
42 | format: Markdown,
43 | },
44 | )
45 |
--------------------------------------------------------------------------------
/duvet-core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "duvet-core"
3 | version = "0.4.1"
4 | description = "Internal crate used by duvet"
5 | authors = ["Cameron Bytheway "]
6 | edition = "2021"
7 | license = "Apache-2.0"
8 | repository = "https://github.com/awslabs/duvet"
9 | rust-version = "1.85"
10 |
11 | [features]
12 | default = ["diff", "http"]
13 | diff = ["dep:similar"]
14 | http = ["dep:http", "reqwest"]
15 | testing = ["tracing-subscriber"]
16 |
17 | [dependencies]
18 | blake3 = "1"
19 | bytes = "1"
20 | console = "0.16"
21 | duvet-macros = { version = "0.4", path = "../duvet-macros" }
22 | futures = { version = "0.3", default-features = false }
23 | rustc-hash = "2.1"
24 | globset = "0.4"
25 | http = { version = "1", optional = true }
26 | miette = { version = "7", features = ["fancy"] }
27 | once_cell = "1"
28 | reqwest = { version = "0.12", optional = true, features = ["native-tls"] }
29 | serde = { version = "1", features = ["derive", "rc"] }
30 | serde_json = "1"
31 | similar = { version = "2.6", features = ["inline"], optional = true }
32 | tokio = { version = "1", features = ["fs", "sync"] }
33 | tokio-util = "0.7"
34 | toml_edit = { version = "0.23", features = ["parse", "serde"] }
35 | tracing = "0.1"
36 | tracing-subscriber = { version = "0.3", features = [
37 | "env-filter",
38 | ], optional = true }
39 |
40 | [dev-dependencies]
41 | tokio = { version = "1", features = ["full"] }
42 | tracing-subscriber = { version = "0.3", features = ["env-filter"] }
43 |
--------------------------------------------------------------------------------
/duvet/src/specification/markdown/snapshots/duvet__specification__markdown__tests__heading_attributes__tokens.snap:
--------------------------------------------------------------------------------
1 | ---
2 | source: duvet/src/specification/markdown/tests.rs
3 | expression: "super :: tokens(& contents).collect :: < Vec < _ >> ()"
4 | ---
5 | [
6 | Content {
7 | value: "",
8 | line: 1,
9 | },
10 | Section {
11 | id: Some(
12 | "custom-id",
13 | ),
14 | title: "Heading with ID",
15 | level: 1,
16 | line: 2,
17 | },
18 | Content {
19 | value: "",
20 | line: 3,
21 | },
22 | Content {
23 | value: "Content under heading with custom ID.",
24 | line: 4,
25 | },
26 | Content {
27 | value: "",
28 | line: 5,
29 | },
30 | Section {
31 | id: Some(
32 | "another-id",
33 | ),
34 | title: "Another heading",
35 | level: 2,
36 | line: 6,
37 | },
38 | Content {
39 | value: "",
40 | line: 7,
41 | },
42 | Content {
43 | value: "More content here.",
44 | line: 8,
45 | },
46 | Content {
47 | value: "",
48 | line: 9,
49 | },
50 | Section {
51 | id: None,
52 | title: "Regular heading",
53 | level: 1,
54 | line: 10,
55 | },
56 | Content {
57 | value: "",
58 | line: 11,
59 | },
60 | Content {
61 | value: "This heading doesn't have a custom ID.",
62 | line: 12,
63 | },
64 | ]
65 |
--------------------------------------------------------------------------------
/duvet/src/extract/tests.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use super::*;
5 |
6 | macro_rules! snapshot_test {
7 | ($name:ident) => {
8 | snapshot_test!($name, ".txt");
9 | };
10 | ($name:ident, $ext:expr) => {
11 | #[test]
12 | fn $name() {
13 | let contents = include_str!(concat!(
14 | env!("CARGO_MANIFEST_DIR"),
15 | "/../specs/",
16 | stringify!($name),
17 | $ext,
18 | ));
19 | let path = concat!(stringify!($name), $ext);
20 | let contents = duvet_core::file::SourceFile::new(path, contents).unwrap();
21 |
22 | let spec = Format::Auto.parse(&contents).unwrap();
23 | let sections = extract_sections(&spec);
24 |
25 | let results: Vec<_> = sections
26 | .iter()
27 | .flat_map(|(section, features)| {
28 | let id = &*section.id;
29 | features.iter().map(move |feature| (id, feature))
30 | })
31 | .collect();
32 |
33 | insta::assert_debug_snapshot!(stringify!($name), results);
34 | }
35 | };
36 | }
37 |
38 | snapshot_test!(rfc9000);
39 | snapshot_test!(rfc9001);
40 | snapshot_test!(rfc9114);
41 | snapshot_test!(esdk_client, ".md");
42 | snapshot_test!(esdk_decrypt, ".md");
43 | snapshot_test!(esdk_encrypt, ".md");
44 | snapshot_test!(esdk_streaming, ".md");
45 |
--------------------------------------------------------------------------------
/duvet/src/specification/ietf/break_filter.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use super::tokenizer::{Break, Token};
5 |
6 | /// Filters out duplicate breaks, headers, and headers misclassified as contents
7 | pub fn break_filter>(tokens: T) -> impl Iterator
- {
8 | let mut break_ty = None;
9 | tokens.filter(move |token| {
10 | let prev_break = core::mem::take(&mut break_ty);
11 |
12 | match token {
13 | Token::Section { .. } | Token::Appendix { .. } | Token::NamedSection { .. } => {}
14 | Token::Break { ty, .. } => {
15 | break_ty = Some(*ty);
16 |
17 | // dedupe breaks
18 | if prev_break.is_some() {
19 | return false;
20 | }
21 | }
22 | Token::Content { .. } => {
23 | // if we previously had a page break then ignore the next line - it's a header that
24 | // didn't tokenize correctly
25 | if matches!(prev_break, Some(Break::Page)) {
26 | break_ty = Some(Break::Line);
27 | return false;
28 | }
29 | }
30 | Token::Header { value: _, line: _ } => {
31 | // set up a break since we skipped a line
32 | break_ty = Some(Break::Line);
33 | return false;
34 | }
35 | }
36 |
37 | true
38 | })
39 | }
40 |
--------------------------------------------------------------------------------
/duvet/src/main.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | #[global_allocator]
5 | static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
6 |
7 | fn main() {
8 | let format = tracing_subscriber::fmt::format().compact(); // Use a less verbose output format.
9 |
10 | let env_filter = tracing_subscriber::EnvFilter::builder()
11 | .with_default_directive(tracing::Level::ERROR.into())
12 | .with_env_var("DUVET_LOG")
13 | .from_env()
14 | .unwrap();
15 |
16 | tracing_subscriber::fmt()
17 | .with_env_filter(env_filter)
18 | .event_format(format)
19 | .with_test_writer()
20 | .init();
21 |
22 | let cache = duvet_core::Cache::default();
23 | let fs = duvet_core::vfs::fs::Fs::default();
24 |
25 | let runtime = tokio::runtime::Builder::new_current_thread()
26 | .on_thread_start({
27 | let cache = cache.clone();
28 | let fs = fs.clone();
29 | move || {
30 | cache.setup_thread();
31 | fs.setup_thread();
32 | }
33 | })
34 | .enable_all()
35 | // it usually takes longer to spawn threads than complete the program so keep the max low
36 | .max_blocking_threads(8)
37 | .build()
38 | .unwrap();
39 |
40 | runtime.block_on(async {
41 | if let Err(err) = duvet::run().await {
42 | eprintln!("{err:?}");
43 | std::process::exit(1);
44 | }
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/xtask/src/args.rs:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | use crate::Result;
5 | use clap::Parser;
6 | use xshell::Shell;
7 |
8 | #[derive(Debug, Parser)]
9 | pub enum Args {
10 | Guide(crate::guide::Guide),
11 | Build(crate::build::Build),
12 | Changelog(crate::changelog::Changelog),
13 | Checks(crate::checks::Checks),
14 | Publish(crate::publish::Publish),
15 | Test(crate::tests::Tests),
16 | }
17 |
18 | impl Args {
19 | pub fn run(&self, sh: &Shell) -> Result {
20 | match self {
21 | Args::Guide(args) => args.run(sh),
22 | Args::Build(args) => args.run(sh).map(|_| ()),
23 | Args::Changelog(args) => args.run(sh),
24 | Args::Checks(args) => args.run(sh),
25 | Args::Publish(args) => args.run(sh),
26 | Args::Test(args) => args.run(sh),
27 | }
28 | }
29 | }
30 |
31 | pub trait FlagExt {
32 | fn is_enabled(&self, default: bool) -> bool;
33 | }
34 |
35 | impl FlagExt for Option {
36 | fn is_enabled(&self, default: bool) -> bool {
37 | match self {
38 | Some(v) => *v,
39 | None => default,
40 | }
41 | }
42 | }
43 |
44 | /// Allows for argument flexibility
45 | /// * `duvet` -> default
46 | /// * `duvet --foo` -> true
47 | /// * `duvet --foo=true` -> true
48 | /// * `duvet --foo=false` -> false
49 | impl FlagExt for Option