From abaaa03e821c535b3fc983332b45c345ac352771 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 15 Jun 2023 10:25:28 -0700 Subject: [PATCH 1/4] Document _CDDL_CODEGEN_RAW_BYTES_TYPE_ + trait export The `RawBytesEncoding` trait will now be exported in `serialization.rs` when it's used in the CDDL input. Documented `_CDDL_CODEGEN_RAW_BYTES_TYPE_` usage in the README too. --- README.md | 16 ++++++++++++++-- src/generation.rs | 11 ++++++++++- src/main.rs | 3 ++- static/raw_bytes_encoding.rs | 19 +++++++++++++++++++ static/serialization_non_force_canonical.rs | 2 +- static/serialization_preserve.rs | 2 +- 6 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 static/raw_bytes_encoding.rs diff --git a/README.md b/README.md index 62fd0ed..4f51a3c 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ pub struct Bar { #### _CDDL_CODEGEN_EXTERN_TYPE_ -While not as a comment, this allows you to compose in hand-written structs into a cddl spec. +This allows you to compose in hand-written structs into a cddl spec. ```cddl foo = _CDDL_CODEGEN_EXTERN_TYPE_ bar = [ @@ -177,4 +177,16 @@ bar = [ ] ``` This will treat `Foo` as a type that will exist and that has implemented the `Serialize` and `Deserialize` traits, so the (de)serialization logic in `Bar` here will call `Foo::serialize()` and `Foo::deserialize()`. -This can also be useful when you have a spec that is either very awkward to use (so you hand-write or hand-modify after generation) in some type so you don't generate those types and instead manually merge those hand-written/hand-modified structs back in to the code afterwards. This saves you from having to manually remove all code that is generated regarding `Foo` first before merging in your own. \ No newline at end of file +This can also be useful when you have a spec that is either very awkward to use (so you hand-write or hand-modify after generation) in some type so you don't generate those types and instead manually merge those hand-written/hand-modified structs back in to the code afterwards. This saves you from having to manually remove all code that is generated regarding `Foo` first before merging in your own. + +#### _CDDL_CODEGEN_RAW_BYTES_TYPE_ + +Allows encoding as `bytes` but imposing hand-written constraints defined elsewhere. +```cddl +foo = _CDDL_CODEGEN_RAW_BYTES_TYPE_ +bar = [ + foo, +] +``` +This will treat `foo` as some external type called `Foo`. This type must implement the exported (in `serialize.rs`) trait `RawBytesEncoding`. +This can be useful for example when working with cryptographic primtivies e.g. a hash or pubkey, as it allows users to have those crypto structs be from a crypto library then they only need to implement the trait for them and be directlry used without needing any useless wrapper struct for the in between. \ No newline at end of file diff --git a/src/generation.rs b/src/generation.rs index 8aaa24c..11c7a21 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -938,7 +938,12 @@ impl GenerationScope { /// Exports all already-generated state to the provided directory. /// Call generate() first to populate the generation state. - pub fn export(&self, types: &IntermediateTypes, cli: &Cli) -> std::io::Result<()> { + pub fn export( + &self, + types: &IntermediateTypes, + export_raw_bytes_encoding_trait: bool, + cli: &Cli, + ) -> std::io::Result<()> { // package.json / scripts let rust_dir = if cli.package_json { if cli.json_schema_export { @@ -1020,6 +1025,10 @@ impl GenerationScope { serialize_paths.push(cli.static_dir.join("serialization_non_preserve.rs")); serialize_paths.push(cli.static_dir.join("serialization_non_force_canonical.rs")); } + // raw_bytes_encoding in serialization too + if export_raw_bytes_encoding_trait { + serialize_paths.push(cli.static_dir.join("raw_bytes_encoding.rs")); + } let mut merged_rust_serialize_scope = codegen::Scope::new(); merged_rust_serialize_scope.raw(concat_files(&serialize_paths)?); merged_rust_serialize_scope.append(&self.rust_serialize_lib_scope); diff --git a/src/main.rs b/src/main.rs index 4dd49e9..83c370e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,6 +70,7 @@ fn main() -> Result<(), Box> { }) }) .collect::>()?; + let export_raw_bytes_encoding_trait = input_files_content.contains(parsing::RAW_BYTES_MARKER); // we also need to mark the extern marker to a placeholder struct that won't get codegened input_files_content.push_str(&format!("{} = [0]", parsing::EXTERN_MARKER)); // and a raw bytes one too @@ -126,7 +127,7 @@ fn main() -> Result<(), Box> { println!("\n-----------------------------------------\n- Generating code...\n------------------------------------"); let mut gen_scope = GenerationScope::new(); gen_scope.generate(&types, &CLI_ARGS); - gen_scope.export(&types, &CLI_ARGS)?; + gen_scope.export(&types, export_raw_bytes_encoding_trait, &CLI_ARGS)?; types.print_info(); gen_scope.print_structs_without_deserialize(); diff --git a/static/raw_bytes_encoding.rs b/static/raw_bytes_encoding.rs new file mode 100644 index 0000000..7900e64 --- /dev/null +++ b/static/raw_bytes_encoding.rs @@ -0,0 +1,19 @@ +pub trait RawBytesEncoding { + fn to_raw_bytes(&self) -> &[u8]; + + fn from_raw_bytes(bytes: &[u8]) -> Result + where + Self: Sized; + + fn to_raw_hex(&self) -> String { + hex::encode(self.to_raw_bytes()) + } + + fn from_raw_hex(hex_str: &str) -> Result + where + Self: Sized, + { + let bytes = hex::decode(hex_str)?; + Self::from_raw_bytes(bytes.as_ref()) + } +} diff --git a/static/serialization_non_force_canonical.rs b/static/serialization_non_force_canonical.rs index 391643a..ac8e899 100644 --- a/static/serialization_non_force_canonical.rs +++ b/static/serialization_non_force_canonical.rs @@ -15,4 +15,4 @@ impl ToCBORBytes for T { self.serialize(&mut buf).unwrap(); buf.finalize() } -} \ No newline at end of file +} diff --git a/static/serialization_preserve.rs b/static/serialization_preserve.rs index a0fa36e..c90a77a 100644 --- a/static/serialization_preserve.rs +++ b/static/serialization_preserve.rs @@ -106,4 +106,4 @@ impl From for StringEncoding { cbor_event::StringLenSz::Indefinite(lens) => Self::Indefinite(lens), } } -} \ No newline at end of file +} From 4ce539bafd937358a8a43ab6b2698b930437f4e1 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 15 Jun 2023 10:30:59 -0700 Subject: [PATCH 2/4] README.md update for typo + clarification --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f51a3c..673afd3 100644 --- a/README.md +++ b/README.md @@ -188,5 +188,5 @@ bar = [ foo, ] ``` -This will treat `foo` as some external type called `Foo`. This type must implement the exported (in `serialize.rs`) trait `RawBytesEncoding`. -This can be useful for example when working with cryptographic primtivies e.g. a hash or pubkey, as it allows users to have those crypto structs be from a crypto library then they only need to implement the trait for them and be directlry used without needing any useless wrapper struct for the in between. \ No newline at end of file +This will treat `foo` as some external type called `Foo`. This type must implement the exported (in `serialization.rs`) trait `RawBytesEncoding`. +This can be useful for example when working with cryptographic primtivies e.g. a hash or pubkey, as it allows users to have those crypto structs be from a crypto library then they only need to implement the trait for them and they will be able to be directly used without needing any useless generated wrapper struct for the in between. \ No newline at end of file From f8b3084ebb7aab4870f6680c47c1b860e76f59d3 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 15 Jun 2023 11:07:53 -0700 Subject: [PATCH 3/4] fix tests --- src/generation.rs | 3 +++ static/error.rs | 2 ++ static/raw_bytes_encoding.rs | 7 ++++--- tests/external_rust_raw_bytes_def | 28 ++++++---------------------- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 11c7a21..8894b75 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1068,6 +1068,9 @@ impl GenerationScope { if cli.json_schema_export { rust_cargo_toml.push_str("schemars = \"0.8.8\"\n"); } + if export_raw_bytes_encoding_trait { + rust_cargo_toml.push_str("hex = \"0.4.3\"\n"); + } if cli.wasm && types .rust_structs() diff --git a/static/error.rs b/static/error.rs index 12aa6a0..74d64c4 100644 --- a/static/error.rs +++ b/static/error.rs @@ -70,6 +70,8 @@ impl DeserializeError { } } +impl std::error::Error for DeserializeError {} + impl std::fmt::Display for DeserializeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.location { diff --git a/static/raw_bytes_encoding.rs b/static/raw_bytes_encoding.rs index 7900e64..749238a 100644 --- a/static/raw_bytes_encoding.rs +++ b/static/raw_bytes_encoding.rs @@ -1,7 +1,7 @@ pub trait RawBytesEncoding { fn to_raw_bytes(&self) -> &[u8]; - fn from_raw_bytes(bytes: &[u8]) -> Result + fn from_raw_bytes(bytes: &[u8]) -> Result where Self: Sized; @@ -9,11 +9,12 @@ pub trait RawBytesEncoding { hex::encode(self.to_raw_bytes()) } - fn from_raw_hex(hex_str: &str) -> Result + fn from_raw_hex(hex_str: &str) -> Result where Self: Sized, { - let bytes = hex::decode(hex_str)?; + let bytes = hex::decode(hex_str) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)))?; Self::from_raw_bytes(bytes.as_ref()) } } diff --git a/tests/external_rust_raw_bytes_def b/tests/external_rust_raw_bytes_def index 965ec13..ef5dad6 100644 --- a/tests/external_rust_raw_bytes_def +++ b/tests/external_rust_raw_bytes_def @@ -1,23 +1,4 @@ -pub trait RawBytesEncoding { - fn to_raw_bytes(&self) -> &[u8]; - - fn from_raw_bytes(bytes: &[u8]) -> Result - where - Self: Sized; -} - -#[derive(Debug)] -pub enum CryptoError { - WrongSize, -} - -impl std::fmt::Display for CryptoError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "CryptoError::WrongSize") - } -} - -impl std::error::Error for CryptoError {} +use crate::RawBytesEncoding; #[derive(Clone, Debug)] pub struct PubKey([u8; 32]); @@ -27,8 +8,11 @@ impl RawBytesEncoding for PubKey { &self.0 } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { use std::convert::TryInto; - bytes.try_into().map(PubKey).or(Err(CryptoError::WrongSize)) + bytes + .try_into() + .map(PubKey) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) } } From e8db0405e9272834f089aa0d8bb6977d510342ed Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 19 Jul 2023 07:13:37 -0700 Subject: [PATCH 4/4] comment in docs folder instead --- docs/docs/comment_dsl.mdx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/docs/comment_dsl.mdx b/docs/docs/comment_dsl.mdx index 7d426a1..e5c3932 100644 --- a/docs/docs/comment_dsl.mdx +++ b/docs/docs/comment_dsl.mdx @@ -93,4 +93,19 @@ bar = [ ] ``` This will treat `Foo` as a type that will exist and that has implemented the `Serialize` and `Deserialize` traits, so the (de)serialization logic in `Bar` here will call `Foo::serialize()` and `Foo::deserialize()`. -This can also be useful when you have a spec that is either very awkward to use (so you hand-write or hand-modify after generation) in some type so you don't generate those types and instead manually merge those hand-written/hand-modified structs back in to the code afterwards. This saves you from having to manually remove all code that is generated regarding `Foo` first before merging in your own. \ No newline at end of file +This can also be useful when you have a spec that is either very awkward to use (so you hand-write or hand-modify after generation) in some type so you don't generate those types and instead manually merge those hand-written/hand-modified structs back in to the code afterwards. This saves you from having to manually remove all code that is generated regarding `Foo` first before merging in your own. + +This can also be useful when you have a spec that is either very awkward to use (so you hand-write or hand-modify after generation) in some type so you don't generate those types and instead manually merge those hand-written/hand-modified structs back in to the code afterwards. This saves you from having to manually remove all code that is generated regarding `Foo` first before merging in your own. + + +#### _CDDL_CODEGEN_RAW_BYTES_TYPE_ + +Allows encoding as `bytes` but imposing hand-written constraints defined elsewhere. +```cddl +foo = _CDDL_CODEGEN_RAW_BYTES_TYPE_ +bar = [ + foo, +] +``` +This will treat `foo` as some external type called `Foo`. This type must implement the exported (in `serialization.rs`) trait `RawBytesEncoding`. +This can be useful for example when working with cryptographic primtivies e.g. a hash or pubkey, as it allows users to have those crypto structs be from a crypto library then they only need to implement the trait for them and they will be able to be directly used without needing any useless generated wrapper struct for the in between. \ No newline at end of file