Skip to content

Commit

Permalink
Document _CDDL_CODEGEN_RAW_BYTES_TYPE_ + trait export (#192)
Browse files Browse the repository at this point in the history
* 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 update for typo + clarification

* fix tests

* comment in docs folder instead
  • Loading branch information
rooooooooob authored Jul 19, 2023
1 parent b05d631 commit e25dbe7
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 27 deletions.
17 changes: 16 additions & 1 deletion docs/docs/comment_dsl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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.
14 changes: 13 additions & 1 deletion src/generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,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 {
Expand Down Expand Up @@ -1076,6 +1081,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);
Expand Down Expand Up @@ -1118,6 +1127,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()
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
})
})
.collect::<Result<String, _>>()?;
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
Expand Down Expand Up @@ -149,7 +150,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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();
Expand Down
2 changes: 2 additions & 0 deletions static/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 20 additions & 0 deletions static/raw_bytes_encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pub trait RawBytesEncoding {
fn to_raw_bytes(&self) -> &[u8];

fn from_raw_bytes(bytes: &[u8]) -> Result<Self, DeserializeError>
where
Self: Sized;

fn to_raw_hex(&self) -> String {
hex::encode(self.to_raw_bytes())
}

fn from_raw_hex(hex_str: &str) -> Result<Self, DeserializeError>
where
Self: Sized,
{
let bytes = hex::decode(hex_str)
.map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)))?;
Self::from_raw_bytes(bytes.as_ref())
}
}
2 changes: 1 addition & 1 deletion static/serialization_non_force_canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ impl<T: cbor_event::se::Serialize> ToCBORBytes for T {
self.serialize(&mut buf).unwrap();
buf.finalize()
}
}
}
2 changes: 1 addition & 1 deletion static/serialization_preserve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,4 @@ impl From<cbor_event::StringLenSz> for StringEncoding {
cbor_event::StringLenSz::Indefinite(lens) => Self::Indefinite(lens),
}
}
}
}
28 changes: 6 additions & 22 deletions tests/external_rust_raw_bytes_def
Original file line number Diff line number Diff line change
@@ -1,23 +1,4 @@
pub trait RawBytesEncoding {
fn to_raw_bytes(&self) -> &[u8];

fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError>
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]);
Expand All @@ -27,8 +8,11 @@ impl RawBytesEncoding for PubKey {
&self.0
}

fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
fn from_raw_bytes(bytes: &[u8]) -> Result<Self, DeserializeError> {
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())
}
}

0 comments on commit e25dbe7

Please sign in to comment.