diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62e5058..06b6619 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,11 @@ on: permissions: contents: read +env: + # configure sccache to cache the build artifacts (on github caches) + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + jobs: tests: runs-on: ${{ matrix.os }} @@ -22,22 +27,10 @@ jobs: with: fetch-depth: 0 submodules: "true" - # - uses: jdx/mise-action@v2 - - name: install mise until mise-action is allowed - uses: taiki-e/install-action@v2 - with: - tool: mise, cargo-nextest, cargo-hack - - name: Rust Cache - uses: actions/cache@v4 - continue-on-error: false - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git/db/ - key: ${{ runner.os }}-sdk-rust-${{ hashFiles('**/Cargo.toml') }} # no Cargo.lock in this repo - restore-keys: | - ${{ runner.os }}-sdk-rust- + - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: jdx/mise-action@v2 + - run: mise install + shell: bash - run: make generate - run: make check_no_uncommitted_changes_on_sdk - run: make check diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index a05e923..178e77d 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -19,6 +19,11 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} cancel-in-progress: true +env: + # configure sccache to cache the build artifacts (on github caches) + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + jobs: lint_commits: name: Lint Commit Messages @@ -48,27 +53,13 @@ jobs: with: fetch-depth: 0 submodules: "true" - - name: Install rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - components: clippy, rustfmt - - name: Install development tools - uses: taiki-e/install-action@v2 - with: - tool: cargo-deny, dprint - - name: Rust Cache - uses: actions/cache@v4 - continue-on-error: false - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git/db/ - key: ${{ runner.os }}-sdk-rust-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-sdk-rust- + - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: jdx/mise-action@v2 + - run: mise install + shell: bash - name: Check cargo version run: cargo --version - name: Run ${{ matrix.lint_projects }} run: make -f Makefile lint_${{ matrix.lint_projects }} + - run: ${SCCACHE_PATH} --show-stats + shell: bash diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index ffbc664..4d2a4c2 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -8,6 +8,12 @@ on: push: branches: - main + workflow_dispatch: + +env: + # configure sccache to cache the build artifacts (on github caches) + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" jobs: # Release unpublished packages. @@ -21,15 +27,16 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - name: Run release-plz - uses: release-plz/action@v0.5 + - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: dtolnay/rust-toolchain@stable + - uses: release-plz/action@v0.5 with: command: release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + - run: ${SCCACHE_PATH} --show-stats + shell: bash # Create a PR with the new versions and changelog, preparing the next release. release-plz-pr: @@ -46,12 +53,13 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - name: Run release-plz - uses: release-plz/action@v0.5 + - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: dtolnay/rust-toolchain@stable + - uses: release-plz/action@v0.5 with: command: release-pr env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + - run: ${SCCACHE_PATH} --show-stats + shell: bash diff --git a/.mise.toml b/.mise.toml index ca92244..8af1479 100644 --- a/.mise.toml +++ b/.mise.toml @@ -6,11 +6,11 @@ MISE_CARGO_BINSTALL = "true" [tools] "aqua:cargo-bins/cargo-binstall" = "1" -rust = "1.85" +rust = { version = "1.85", profile = "default" } make = "latest" # # experimental "cargo:cargo-nextest" = "0.9" -# "cargo:cargo-hack" = "latest" +"cargo:cargo-hack" = "0.6" "cargo:cargo-deny" = "0.16" # "cargo:git-cliff" = "latest" "cargo:dprint" = "0.45" diff --git a/Makefile b/Makefile index ce7077d..ed494b3 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,9 @@ test: # git submodule add -f -b spec-v0.4 https://github.com/cdevents/spec.git cdevents-specs/spec-v0.4 # git submodule update -f --rebase -- cdevents-specs/main +update_cdevents-specs: + git submodule update --recursive --remote + .PHONY: generate \ check check_no_uncommitted_changes_on_sdk \ diff --git a/README.md b/README.md index 2652397..9f75100 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The SDK can be used to create CDEvents and send them as CloudEvents, as well as Import the modules in your code ```toml -cdevents-sdk = "0.1.0" +cdevents-sdk = "0.1" ``` To send a CDEvent as CloudEvent: @@ -60,6 +60,24 @@ fn main() -> Result<(), Box> { See the [CloudEvents](https://github.com/cloudevents/sdk-rust) docs as well. +## Features + +- [x] support cdevents spec 0.3.0 +- [x] support cdevents spec 0.4.1 +- [ ] support of custom event + - [ ] compile-time generation of type for custom event + - [ ] runtime validation (download of jsonschemas & validation) + - [x] serialize/deserialize of custom event (type `dev.cdeventsx.{subject}.{predicate}.{version}`) + - [x] partial validation of custom event (`subject.content` is not validated, stored as json) +- [x] Cloudevents support (provide wrapper/extractor for CDEvent) +- [x] CDEvent stored into static types (=> no use of jsonshema at runtime) +- [x] rutime validation for scalar types (purl, datetime, uri-reference) +- [ ] report clear and readable error messages on deserialization +- [x] provide type with builder pattern (fluent) +- [x] provide random/sample generator for CDEvent (property based testing) +- [x] test `serialization(deserialization(of spec's examples & conformances)) == spec's examples & conformances` +- [x] test `serialization(random CDEvent) matches the jsonschemas` + ## References - [CDEvents](https://cdevents.dev) diff --git a/cdevents-sdk/Cargo.toml b/cdevents-sdk/Cargo.toml index 2588e8d..310c4c3 100644 --- a/cdevents-sdk/Cargo.toml +++ b/cdevents-sdk/Cargo.toml @@ -5,10 +5,10 @@ authors.workspace = true edition.workspace = true license.workspace = true publish = true +readme = "README.md" repository.workspace = true rust-version.workspace = true description = "A Rust SDK for CDEvents" -readme = "README.md" [dependencies] cloudevents-sdk = { version = "0.8", optional = true, default-features = false } diff --git a/cdevents-sdk/src/generated/change_created_0_2_0.rs b/cdevents-sdk/src/generated/change_created_0_2_0.rs deleted file mode 100644 index f4500e7..0000000 --- a/cdevents-sdk/src/generated/change_created_0_2_0.rs +++ /dev/null @@ -1,41 +0,0 @@ -// @generated -// by cdevents/sdk-rust/generator (subject.hbs) - -#[cfg(feature = "testkit")] use proptest_derive::Arbitrary; -use serde::{Serialize, Deserialize}; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr(feature = "testkit", derive(Arbitrary))] -#[serde(deny_unknown_fields)] -pub struct Content { - #[serde(rename = "description", default, skip_serializing_if = "Option::is_none",)] - pub description: Option, - #[serde(rename = "repository", default, skip_serializing_if = "Option::is_none",)] - pub repository: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr(feature = "testkit", derive(Arbitrary))] -#[serde(deny_unknown_fields)] -pub struct ContentRepository { - #[serde(rename = "id",)] - pub id: crate::Id, - #[serde(rename = "source", default, skip_serializing_if = "Option::is_none",)] - pub source: Option, -} - -#[cfg(test)] -mod tests { - use proptest::prelude::*; - use super::*; - - proptest! { - #[test] - #[cfg(feature = "testkit")] - fn arbitraries_are_json_valid(s in any::()) { - let json_str = serde_json::to_string(&s).unwrap(); - let actual = serde_json::from_str::(&json_str).unwrap(); - assert_eq!(s, actual); - } - } -} diff --git a/cdevents-sdk/src/generated/mod.rs b/cdevents-sdk/src/generated/mod.rs index a9ad04e..3ac35a6 100644 --- a/cdevents-sdk/src/generated/mod.rs +++ b/cdevents-sdk/src/generated/mod.rs @@ -23,7 +23,6 @@ pub mod build_started_0_1_1; pub mod build_started_0_2_0; pub mod change_abandoned_0_1_2; pub mod change_abandoned_0_2_0; -pub mod change_created_0_2_0; pub mod change_created_0_1_2; pub mod change_created_0_3_0; pub mod change_merged_0_1_2; @@ -177,45 +176,6 @@ pub mod spec_0_3_0 { pub use super::testsuiterun_queued_0_1_0 as testsuiterun_queued; pub use super::testsuiterun_started_0_1_0 as testsuiterun_started; } -pub mod spec_0_4_0_draft { - pub use super::artifact_signed_0_1_0 as artifact_signed; - pub use super::branch_created_0_1_2 as branch_created; - pub use super::branch_deleted_0_1_2 as branch_deleted; - pub use super::build_finished_0_1_1 as build_finished; - pub use super::build_queued_0_1_1 as build_queued; - pub use super::build_started_0_1_1 as build_started; - pub use super::change_abandoned_0_1_2 as change_abandoned; - pub use super::change_created_0_2_0 as change_created; - pub use super::change_merged_0_1_2 as change_merged; - pub use super::change_reviewed_0_1_2 as change_reviewed; - pub use super::change_updated_0_1_2 as change_updated; - pub use super::environment_created_0_1_1 as environment_created; - pub use super::environment_deleted_0_1_1 as environment_deleted; - pub use super::environment_modified_0_1_1 as environment_modified; - pub use super::incident_detected_0_1_0 as incident_detected; - pub use super::incident_reported_0_1_0 as incident_reported; - pub use super::incident_resolved_0_1_0 as incident_resolved; - pub use super::pipelinerun_finished_0_1_1 as pipelinerun_finished; - pub use super::pipelinerun_queued_0_1_1 as pipelinerun_queued; - pub use super::pipelinerun_started_0_1_1 as pipelinerun_started; - pub use super::repository_created_0_1_1 as repository_created; - pub use super::repository_deleted_0_1_1 as repository_deleted; - pub use super::repository_modified_0_1_1 as repository_modified; - pub use super::service_deployed_0_1_1 as service_deployed; - pub use super::service_published_0_1_1 as service_published; - pub use super::service_removed_0_1_1 as service_removed; - pub use super::service_rolledback_0_1_1 as service_rolledback; - pub use super::service_upgraded_0_1_1 as service_upgraded; - pub use super::taskrun_finished_0_1_1 as taskrun_finished; - pub use super::taskrun_started_0_1_1 as taskrun_started; - pub use super::testcaserun_finished_0_1_0 as testcaserun_finished; - pub use super::testcaserun_queued_0_1_0 as testcaserun_queued; - pub use super::testcaserun_started_0_1_0 as testcaserun_started; - pub use super::testoutput_published_0_1_0 as testoutput_published; - pub use super::testsuiterun_finished_0_1_0 as testsuiterun_finished; - pub use super::testsuiterun_queued_0_1_0 as testsuiterun_queued; - pub use super::testsuiterun_started_0_1_0 as testsuiterun_started; -} pub mod spec_0_4_1 { pub use super::artifact_deleted_0_1_0 as artifact_deleted; pub use super::artifact_downloaded_0_1_0 as artifact_downloaded; @@ -284,7 +244,6 @@ pub const BUILD_STARTED_0_1_1: &str = "dev.cdevents.build.started.0.1.1"; pub const BUILD_STARTED_0_2_0: &str = "dev.cdevents.build.started.0.2.0"; pub const CHANGE_ABANDONED_0_1_2: &str = "dev.cdevents.change.abandoned.0.1.2"; pub const CHANGE_ABANDONED_0_2_0: &str = "dev.cdevents.change.abandoned.0.2.0"; -pub const CHANGE_CREATED_0_2_0: &str = "dev.cdevents.change.created.0.2.0"; pub const CHANGE_CREATED_0_1_2: &str = "dev.cdevents.change.created.0.1.2"; pub const CHANGE_CREATED_0_3_0: &str = "dev.cdevents.change.created.0.3.0"; pub const CHANGE_MERGED_0_1_2: &str = "dev.cdevents.change.merged.0.1.2"; @@ -373,7 +332,6 @@ pub enum Content { BuildStarted020(build_started_0_2_0::Content), ChangeAbandoned012(change_abandoned_0_1_2::Content), ChangeAbandoned020(change_abandoned_0_2_0::Content), - ChangeCreated020(change_created_0_2_0::Content), ChangeCreated012(change_created_0_1_2::Content), ChangeCreated030(change_created_0_3_0::Content), ChangeMerged012(change_merged_0_1_2::Content), @@ -438,6 +396,12 @@ pub enum Content { TicketClosed010(ticket_closed_0_1_0::Content), TicketCreated010(ticket_created_0_1_0::Content), TicketUpdated010(ticket_updated_0_1_0::Content), + Custom{ + #[serde(skip)] + ty: String, + #[serde(flatten)] + json: serde_json::Value, + }, } impl Content { @@ -523,10 +487,6 @@ impl Content { let variant: change_abandoned_0_2_0::Content = serde_json::from_value(json)?; Ok(variant.into()) }, - CHANGE_CREATED_0_2_0 => { - let variant: change_created_0_2_0::Content = serde_json::from_value(json)?; - Ok(variant.into()) - }, CHANGE_CREATED_0_1_2 => { let variant: change_created_0_1_2::Content = serde_json::from_value(json)?; Ok(variant.into()) @@ -783,14 +743,18 @@ impl Content { let variant: ticket_updated_0_1_0::Content = serde_json::from_value(json)?; Ok(variant.into()) }, - variant => Err(serde_json::Error::custom(format_args!( - "unknown variant `{}`, expected 'dev.cdevents.{{subject}}.{{predicate}}.{{version}}'", - variant, - ))), + variant => if variant.starts_with("dev.cdeventsx.") { + Ok(Self::Custom{ ty: ty.to_string(), json }) + } else { + Err(serde_json::Error::custom(format_args!( + "unknown variant `{}`, expected 'dev.cdevents.{{subject}}.{{predicate}}.{{version}}'", + variant, + ))) + }, } } - pub fn ty(&self) -> &'static str { + pub fn ty(&self) -> &str { match self { Self::ArtifactDeleted010(_) => ARTIFACT_DELETED_0_1_0, Self::ArtifactDownloaded010(_) => ARTIFACT_DOWNLOADED_0_1_0, @@ -812,7 +776,6 @@ impl Content { Self::BuildStarted020(_) => BUILD_STARTED_0_2_0, Self::ChangeAbandoned012(_) => CHANGE_ABANDONED_0_1_2, Self::ChangeAbandoned020(_) => CHANGE_ABANDONED_0_2_0, - Self::ChangeCreated020(_) => CHANGE_CREATED_0_2_0, Self::ChangeCreated012(_) => CHANGE_CREATED_0_1_2, Self::ChangeCreated030(_) => CHANGE_CREATED_0_3_0, Self::ChangeMerged012(_) => CHANGE_MERGED_0_1_2, @@ -877,10 +840,11 @@ impl Content { Self::TicketClosed010(_) => TICKET_CLOSED_0_1_0, Self::TicketCreated010(_) => TICKET_CREATED_0_1_0, Self::TicketUpdated010(_) => TICKET_UPDATED_0_1_0, + Self::Custom{ty, ..} => ty, } } - pub fn subject(&self) -> &'static str { + pub fn subject(&self) -> &str { match self { Self::ArtifactDeleted010(_) => "artifact", Self::ArtifactDownloaded010(_) => "artifact", @@ -902,7 +866,6 @@ impl Content { Self::BuildStarted020(_) => "build", Self::ChangeAbandoned012(_) => "change", Self::ChangeAbandoned020(_) => "change", - Self::ChangeCreated020(_) => "change", Self::ChangeCreated012(_) => "change", Self::ChangeCreated030(_) => "change", Self::ChangeMerged012(_) => "change", @@ -967,10 +930,11 @@ impl Content { Self::TicketClosed010(_) => "ticket", Self::TicketCreated010(_) => "ticket", Self::TicketUpdated010(_) => "ticket", + Self::Custom{ty, ..} => ty.split('.').nth(2).unwrap_or_default(), } } - pub fn predicate(&self) -> &'static str { + pub fn predicate(&self) -> &str { match self { Self::ArtifactDeleted010(_) => "deleted", Self::ArtifactDownloaded010(_) => "downloaded", @@ -992,7 +956,6 @@ impl Content { Self::BuildStarted020(_) => "started", Self::ChangeAbandoned012(_) => "abandoned", Self::ChangeAbandoned020(_) => "abandoned", - Self::ChangeCreated020(_) => "created", Self::ChangeCreated012(_) => "created", Self::ChangeCreated030(_) => "created", Self::ChangeMerged012(_) => "merged", @@ -1057,11 +1020,13 @@ impl Content { Self::TicketClosed010(_) => "closed", Self::TicketCreated010(_) => "created", Self::TicketUpdated010(_) => "updated", + Self::Custom{ty, ..} => ty.split('.').nth(3).unwrap_or_default(), } } } -// due to inconstency in case/format the subject could be not be extracted from the context.type (ty), jsonshema $id, spec filename (shema, examples) +/// Due to inconstency in case/format the subject could be not be extracted from the context.type (ty), jsonshema $id, spec filename (shema, examples) +/// Custom type are not supported pub fn extract_subject_predicate(ty: &str) -> Option<(&str, &str)>{ // let mut split = ty.split('.'); match ty { @@ -1085,7 +1050,6 @@ pub fn extract_subject_predicate(ty: &str) -> Option<(&str, &str)>{ BUILD_STARTED_0_2_0 => Some(("build", "started")), CHANGE_ABANDONED_0_1_2 => Some(("change", "abandoned")), CHANGE_ABANDONED_0_2_0 => Some(("change", "abandoned")), - CHANGE_CREATED_0_2_0 => Some(("change", "created")), CHANGE_CREATED_0_1_2 => Some(("change", "created")), CHANGE_CREATED_0_3_0 => Some(("change", "created")), CHANGE_MERGED_0_1_2 => Some(("change", "merged")), @@ -1254,11 +1218,6 @@ impl From for Content { Self::ChangeAbandoned020(value) } } -impl From for Content { - fn from(value: change_created_0_2_0::Content) -> Self { - Self::ChangeCreated020(value) - } -} impl From for Content { fn from(value: change_created_0_1_2::Content) -> Self { Self::ChangeCreated012(value) @@ -1608,7 +1567,6 @@ impl<> proptest::arbitrary::Arbitrary for Content { any::().prop_map(Content::from), any::().prop_map(Content::from), any::().prop_map(Content::from), - any::().prop_map(Content::from), any::().prop_map(Content::from), any::().prop_map(Content::from), any::().prop_map(Content::from), @@ -1680,7 +1638,7 @@ impl<> proptest::arbitrary::Arbitrary for Content { // #[cfg(test)] // mod tests { // use super::*; -// +// // #[test] // fn test_true() { // @@ -1724,8 +1682,6 @@ impl<> proptest::arbitrary::Arbitrary for Content { // // assert_eq!(extract_subject_predicate(CHANGE_ABANDONED_0_2_0), Some(("change","abandoned"))); // -// assert_eq!(extract_subject_predicate(CHANGE_CREATED_0_2_0), Some(("change","created"))); -// // assert_eq!(extract_subject_predicate(CHANGE_CREATED_0_1_2), Some(("change","created"))); // // assert_eq!(extract_subject_predicate(CHANGE_CREATED_0_3_0), Some(("change","created"))); @@ -1855,4 +1811,4 @@ impl<> proptest::arbitrary::Arbitrary for Content { // assert_eq!(extract_subject_predicate(TICKET_UPDATED_0_1_0), Some(("ticket","updated"))); // // } -// } \ No newline at end of file +// } diff --git a/cdevents-sdk/tests/specs.rs b/cdevents-sdk/tests/specs.rs index bc2c636..c5670b7 100644 --- a/cdevents-sdk/tests/specs.rs +++ b/cdevents-sdk/tests/specs.rs @@ -56,14 +56,19 @@ struct HackUrlLoader; impl UrlLoader for HackUrlLoader { fn load(&self, url: &str) -> Result> { let re = regex::Regex::new(r"https://cdevents.dev/(?\d+\.\d+)\.\d+/schema/(?.*)")?; + let re_draft = regex::Regex::new(r"https://cdevents.dev/(?\d+\.\d+)\.\d+-draft/schema/(?.*)")?; if let Some(caps) = re.captures(url) { let path = format!("../cdevents-specs/spec-v{}/schemas/{}.json", &caps["version"], &caps["path"]); let jsonschema: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(path)?)?; Ok(jsonschema) + } else if let Some(caps) = re_draft.captures(url) { + let path = format!("../cdevents-specs/main/schemas/{}.json", &caps["path"]); + let jsonschema: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(path)?)?; + Ok(jsonschema) } else if url.starts_with("https://cdevents.dev/schema/links/") { // HACK to fix a bug in specs 0.4.0 // [Link's ref path needs an update for all the event schemas · Issue #211 · cdevents/spec](https://github.com/cdevents/spec/issues/211) - let path = url.replace("https://cdevents.dev/schema", "../cdevents-specs/spec-v0.4/schemas/"); + let path = url.replace("https://cdevents.dev/schema", "../cdevents-specs/spec-v0.4/schemas"); let jsonschema: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(path)?)?; Ok(jsonschema) } else if url.starts_with("file://") { @@ -77,12 +82,12 @@ impl UrlLoader for HackUrlLoader { } static EVENTS_SCHEMA_CELL: OnceLock = OnceLock::new(); -fn events_schemas() -> &'static EventsSchemas { +fn events_schemas() -> &'static EventsSchemas { EVENTS_SCHEMA_CELL.get_or_init(EventsSchemas::load) } #[rstest] -fn can_serde_example(#[files("../cdevents-specs/spec-*/examples/*.json")] #[files("../cdevents-specs/spec-*/conformance/*.json")] path: PathBuf) { +fn can_serde_example(#[files("../cdevents-specs/spec-*/examples/*.json")] #[files("../cdevents-specs/spec-*/conformance/*.json")] #[files("../cdevents-specs/spec-*/custom/conformance.json")] path: PathBuf) { let example_txt = fs::read_to_string(path).expect("to read file as string"); // HACK uri are stored ad http::Uri, they are "normalized" when serialized, so prenormalization to avoid failure like // json atoms at path ".subject.content.repository.source" are not equal: @@ -96,12 +101,12 @@ fn can_serde_example(#[files("../cdevents-specs/spec-*/examples/*.json")] #[file let example_json: serde_json::Value = serde_json::from_str(&example_txt).expect("to parse as json"); - dbg!(&example_json); + // dbg!(&example_json); let cdevent: CDEvent = serde_json::from_value(example_json.clone()).expect("to parse as cdevent"); - dbg!(&cdevent); + // dbg!(&cdevent); let cdevent_json = serde_json::to_value(cdevent).expect("to convert into json"); - dbg!(&cdevent_json); + // dbg!(&cdevent_json); assert_json_eq!(example_json, cdevent_json); } diff --git a/cdevents-specs/main b/cdevents-specs/main index d6b6f88..b02be28 160000 --- a/cdevents-specs/main +++ b/cdevents-specs/main @@ -1 +1 @@ -Subproject commit d6b6f88c1981d90adf1f8ffa0a3d3eb1bc9aad13 +Subproject commit b02be288bfa24c487f74a711f7acae45c9c6dc2d diff --git a/cdevents-specs/spec-v0.4 b/cdevents-specs/spec-v0.4 index f95df21..984346d 160000 --- a/cdevents-specs/spec-v0.4 +++ b/cdevents-specs/spec-v0.4 @@ -1 +1 @@ -Subproject commit f95df21d7e3045a37d2c85e07f90805130fd65be +Subproject commit 984346d84b084a21ed2d660a6439e5361077b83d diff --git a/generator/Cargo.toml b/generator/Cargo.toml index a78d53c..2b2662b 100644 --- a/generator/Cargo.toml +++ b/generator/Cargo.toml @@ -15,10 +15,7 @@ clap = { version = "4", features = ["derive"] } cruet = "0.15" glob = "0.3" handlebars = { version = "6", features = ["dir_source"] } -handlebars_misc_helpers = { version = "0.17", default-features = false, features = [ - "string", - "json", -] } +handlebars_misc_helpers = { version = "0.17", default-features = false, features = ["string", "json"] } indexmap = "2.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/generator/templates/mod.hbs b/generator/templates/mod.hbs index 17f7010..d9cb755 100644 --- a/generator/templates/mod.hbs +++ b/generator/templates/mod.hbs @@ -25,6 +25,12 @@ pub enum Content { {{#each variants }} {{to_class_case this.rust_module}}({{this.rust_module}}::Content), {{/each}} + Custom{ + #[serde(skip)] + ty: String, + #[serde(flatten)] + json: serde_json::Value, + }, } impl Content { @@ -36,39 +42,47 @@ impl Content { Ok(variant.into()) }, {{/each}} - variant => Err(serde_json::Error::custom(format_args!( - "unknown variant `{}`, expected 'dev.cdevents.\{{subject}}.\{{predicate}}.\{{version}}'", - variant, - ))), + variant => if variant.starts_with("dev.cdeventsx.") { + Ok(Self::Custom{ ty: ty.to_string(), json }) + } else { + Err(serde_json::Error::custom(format_args!( + "unknown variant `{}`, expected 'dev.cdevents.\{{subject}}.\{{predicate}}.\{{version}}'", + variant, + ))) + }, } } - pub fn ty(&self) -> &'static str { + pub fn ty(&self) -> &str { match self { {{#each variants }} Self::{{to_class_case this.rust_module}}(_) => {{to_screaming_snake_case this.rust_module}}, {{/each}} + Self::Custom{ty, ..} => ty, } } - pub fn subject(&self) -> &'static str { + pub fn subject(&self) -> &str { match self { {{#each variants }} Self::{{to_class_case this.rust_module}}(_) => "{{ this.subject_type }}", {{/each}} + Self::Custom{ty, ..} => ty.split('.').nth(2).unwrap_or_default(), } } - pub fn predicate(&self) -> &'static str { + pub fn predicate(&self) -> &str { match self { {{#each variants }} Self::{{to_class_case this.rust_module}}(_) => "{{ this.predicate }}", {{/each}} + Self::Custom{ty, ..} => ty.split('.').nth(3).unwrap_or_default(), } } } -// due to inconstency in case/format the subject could be not be extracted from the context.type (ty), jsonshema $id, spec filename (shema, examples) +/// Due to inconstency in case/format the subject could be not be extracted from the context.type (ty), jsonshema $id, spec filename (shema, examples) +/// Custom type are not supported pub fn extract_subject_predicate(ty: &str) -> Option<(&str, &str)>{ // let mut split = ty.split('.'); match ty { @@ -105,11 +119,11 @@ impl<> proptest::arbitrary::Arbitrary for Content { // #[cfg(test)] // mod tests { // use super::*; -// +// // #[test] // fn test_true() { // {{#each variants }} // assert_eq!(extract_subject_predicate({{to_screaming_snake_case this.rust_module}}), Some(("{{ this.subject }}","{{ this.predicate }}"))); // {{/each}} // } -// } \ No newline at end of file +// } diff --git a/release-plz.toml b/release-plz.toml new file mode 100644 index 0000000..cdb33ff --- /dev/null +++ b/release-plz.toml @@ -0,0 +1,5 @@ +[workspace] +features_always_increment_minor = true + +[changelog] +protect_breaking_commits = true diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c1bc0a6..b07a9c0 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,4 @@ [toolchain] channel = "1.85.0" +components = ["rustc", "cargo", "rust-std", "clippy", "rustfmt"] +profile = "minimal"