Merge remote-tracking branch 'upstream/master' into float
# Conflicts: # src/parsing.rs
# Conflicts: # src/parsing.rs
extern crate nom;
use nom::{
IResult,
bytes::complete::{tag, take_while1, take_while}, branch::alt, multi::many0,
IResult,
bytes::complete::{tag, take_while1, take_while}, branch::alt, multi::many0,
};
#[derive(Default, Debug, PartialEq)]
pub struct RuleMetadata {
pub name: Option<String>,
pub is_newtype: bool,
pub name: Option<String>,
pub is_newtype: bool,
pub no_alias: bool,
}
fn merge_metadata(r1: &RuleMetadata, r2: &RuleMetadata) -> RuleMetadata {
RuleMetadata {
pub fn merge_metadata(r1: &RuleMetadata, r2: &RuleMetadata) -> RuleMetadata {
let merged = RuleMetadata {
name: match (r1.name.as_ref(), r2.name.as_ref()) {
(Some(val1), Some(val2)) => panic!("Key \"name\" specified twice: {:?} {:?}", val1, val2),
([email protected](_), _) => val.cloned(),
(_, val) => val.cloned()
},
is_newtype: r1.is_newtype || r2.is_newtype
}
is_newtype: r1.is_newtype || r2.is_newtype,
no_alias: r1.no_alias || r2.no_alias,
};
merged.verify();
merged
}
enum ParseResult {
NewType,
Name(String)
NewType,
Name(String),
DontGenAlias,
}
impl RuleMetadata {
fn from_parse_results(results: &[ParseResult]) -> RuleMetadata {
let mut base = RuleMetadata::default();
for result in results {
match result {
ParseResult::Name(name) => {
match base.name.as_ref() {
Some(old_name) => panic!("Key \"name\" specified twice: {:?} {:?}", old_name, name),
None => { base.name = Some(name.to_string()); }
}
},
ParseResult::NewType => { base.is_newtype = true; }
}
fn from_parse_results(results: &[ParseResult]) -> RuleMetadata {
let mut base = RuleMetadata::default();
for result in results {
match result {
ParseResult::Name(name) => {
match base.name.as_ref() {
Some(old_name) => panic!("Key \"name\" specified twice: {:?} {:?}", old_name, name),
None => { base.name = Some(name.to_string()); }
}
},
ParseResult::NewType => { base.is_newtype = true; },
ParseResult::DontGenAlias => { base.no_alias = true; },
}
}
base.verify();
base
}
fn verify(&self) {
if self.is_newtype && self.no_alias {
// this would make no sense anyway as with newtype we're already not making an alias
panic!("cannot use both @newtype and @no_alias on the same alias");
}
}
base
}
}
fn tag_name(input: &str) -> IResult<&str, ParseResult> {
let (input, _) = tag("@name")(input)?;
let (input, _) = take_while(char::is_whitespace)(input)?;
let (input, name) = take_while1(|ch| !char::is_whitespace(ch))(input)?;
let (input, _) = tag("@name")(input)?;
let (input, _) = take_while(char::is_whitespace)(input)?;
let (input, name) = take_while1(|ch| !char::is_whitespace(ch))(input)?;
Ok((input, ParseResult::Name(name.to_string())))
Ok((input, ParseResult::Name(name.to_string())))
}
fn tag_newtype(input: &str) -> IResult<&str, ParseResult> {
let (input, _) = tag("@newtype")(input)?;
let (input, _) = tag("@newtype")(input)?;
Ok((input, ParseResult::NewType))
}
Ok((input, ParseResult::NewType))
fn tag_no_alias(input: &str) -> IResult<&str, ParseResult> {
let (input, _) = tag("@no_alias")(input)?;
Ok((input, ParseResult::DontGenAlias))
}
fn whitespace_then_tag(input: &str) -> IResult<&str, ParseResult> {
let (input, _) = take_while(char::is_whitespace)(input)?;
let (input, result) = alt((tag_name, tag_newtype))(input)?;
let (input, _) = take_while(char::is_whitespace)(input)?;
let (input, result) = alt((tag_name, tag_newtype, tag_no_alias))(input)?;
Ok((input, result))
Ok((input, result))
}
fn rule_metadata(input: &str) -> IResult<&str, RuleMetadata> {
let (input, parse_results) = many0(whitespace_then_tag)(input)?;
let (input, parse_results) = many0(whitespace_then_tag)(input)?;
Ok((input, RuleMetadata::from_parse_results(&parse_results)))
Ok((input, RuleMetadata::from_parse_results(&parse_results)))
}
impl <'a> From<Option<&'a cddl::ast::Comments<'a>>> for RuleMetadata {
fn from(comments: Option<&'a cddl::ast::Comments<'a>>) -> RuleMetadata {
match comments {
None => RuleMetadata::default(),
Some(c) => metadata_from_comments(&c.0)
fn from(comments: Option<&'a cddl::ast::Comments<'a>>) -> RuleMetadata {
match comments {
None => RuleMetadata::default(),
Some(c) => metadata_from_comments(&c.0)
}
}
}
}
pub fn metadata_from_comments(comments: &[&str]) -> RuleMetadata {
#[test]
fn parse_comment_name() {
assert_eq!(rule_metadata("@name foo"), Ok(("", RuleMetadata {
name: Some("foo".to_string()),
is_newtype: false,
})));
assert_eq!(rule_metadata("@name foo"), Ok(("", RuleMetadata {
name: Some("foo".to_string()),
is_newtype: false,
no_alias: false,
})));
}
#[test]
fn parse_comment_newtype() {
assert_eq!(rule_metadata("@newtype"), Ok(("", RuleMetadata {
name: None,
is_newtype: true
})));
assert_eq!(rule_metadata("@newtype"), Ok(("", RuleMetadata {
name: None,
is_newtype: true,
no_alias: false,
})));
}
#[test]
fn parse_comment_newtype_and_name() {
assert_eq!(rule_metadata("@newtype @name foo"), Ok(("", RuleMetadata {
name: Some("foo".to_string()),
is_newtype: true
})));
assert_eq!(rule_metadata("@newtype @name foo"), Ok(("", RuleMetadata {
name: Some("foo".to_string()),
is_newtype: true,
no_alias: false,
})));
}
#[test]
fn parse_comment_newtype_and_name_inverse() {
assert_eq!(rule_metadata("@name foo @newtype"), Ok(("", RuleMetadata {
name: Some("foo".to_string()),
is_newtype: true
})));
}
\ No newline at end of file
assert_eq!(rule_metadata("@name foo @newtype"), Ok(("", RuleMetadata {
name: Some("foo".to_string()),
is_newtype: true,
no_alias: false,
}
// Structs
let mut wasm_wrappers_generated = BTreeSet::new();
for (rust_ident, rust_struct) in types.rust_structs() {
assert_eq!(rust_ident, rust_struct.ident());
if CLI_ARGS.wasm {
rust_struct.visit_types(types, &mut |ty| {
match ty {
ConceptualRustType::Array(elem) => {
if !ty.directly_wasm_exposable() {
let array_ident = elem.name_as_wasm_array();
if wasm_wrappers_generated.insert(array_ident.clone()) {
self.generate_array_type(types, *elem.clone(), &RustIdent::new(CDDLIdent::new(array_ident)));
{
// we can ignore types already handled by the alias
// otherwise wasm_wrappers_generated may cause us to pointlessly create aliases to aliases
let mut existing_aliases = types.type_aliases().iter().fold(BTreeSet::<RustIdent>::new(), |mut acc, (alias, _)| {
match alias {
AliasIdent::Reserved(_) => {},
AliasIdent::Rust(ident) => { acc.insert(ident.clone()); }
};
acc
});
let mut wasm_wrappers_generated = BTreeSet::new();
for (rust_ident, rust_struct) in types.rust_structs() {
assert_eq!(rust_ident, rust_struct.ident());
if CLI_ARGS.wasm {
rust_struct.visit_types_excluding(types, &mut |ty| {
match ty {
ConceptualRustType::Array(elem) => {
if !ty.directly_wasm_exposable() {
let array_ident = elem.name_as_wasm_array();
if wasm_wrappers_generated.insert(array_ident.clone()) {
self.generate_array_type(types, *elem.clone(), &RustIdent::new(CDDLIdent::new(array_ident)));
}
}
}
},
ConceptualRustType::Map(k, v) => {
let map_ident = ConceptualRustType::name_for_wasm_map(&k, &v);
if wasm_wrappers_generated.insert(map_ident.to_string()) {
codegen_table_type(self, types, &map_ident, *k.clone(), *v.clone(), None);
}
if !ConceptualRustType::Array(Box::new(*k.clone())).directly_wasm_exposable() {
let keys_ident = k.name_as_wasm_array();
if wasm_wrappers_generated.insert(keys_ident.clone()) {
self.generate_array_type(types, *k.clone(), &RustIdent::new(CDDLIdent::new(keys_ident)));
},
ConceptualRustType::Map(k, v) => {
let map_ident = ConceptualRustType::name_for_wasm_map(&k, &v);
if wasm_wrappers_generated.insert(map_ident.to_string()) {
codegen_table_type(self, types, &map_ident, *k.clone(), *v.clone(), None, false);
}
if !ConceptualRustType::Array(Box::new(*k.clone())).directly_wasm_exposable() {
let keys_ident = k.name_as_wasm_array();
if wasm_wrappers_generated.insert(keys_ident.clone()) {
self.generate_array_type(types, *k.clone(), &RustIdent::new(CDDLIdent::new(keys_ident)));
}
}
},
_ => (),
}
}, &mut existing_aliases);
}
match rust_struct.variant() {
RustStructType::Record(record) => {
codegen_struct(self, types, rust_ident, rust_struct.tag(), record);
},
RustStructType::Table { domain, range } => {
if CLI_ARGS.wasm {
let map_ident = ConceptualRustType::name_for_wasm_map(domain, range);
// want to use `contains` instead of insert
// since although map_ident may not be required for this struct
// we may still have to generate it later if a table of the same shape is embedded inside different struct
if !wasm_wrappers_generated.contains(&map_ident.to_string()) {
codegen_table_type(self, types, rust_ident, domain.clone(), range.clone(), rust_struct.tag(), true);
} else {
self.wasm(types, rust_ident).push_type_alias(TypeAlias::new(rust_ident, map_ident));
}
},
_ => (),
}
});
}
match rust_struct.variant() {
RustStructType::Record(record) => {
codegen_struct(self, types, rust_ident, rust_struct.tag(), record);
},
RustStructType::Table { domain, range } => {
if CLI_ARGS.wasm {
let map_ident = ConceptualRustType::name_for_wasm_map(domain, range);
if wasm_wrappers_generated.insert(map_ident.to_string()) {
codegen_table_type(self, types, rust_ident, domain.clone(), range.clone(), rust_struct.tag());
} else {
self.wasm(types, rust_ident)
.push_type_alias(TypeAlias::new(rust_ident, map_ident));
}
}
//self
// .rust()
// .push_type_alias(TypeAlias::new(rust_struct.ident(), ConceptualRustType::name_for_rust_map(domain, range, false)));
},
RustStructType::Array { element_type } => {
if CLI_ARGS.wasm {
self.generate_array_type(types, element_type.clone(), rust_ident);
}
//self
// .rust()
// .push_type_alias(TypeAlias::new(rust_struct.ident(), element_type.name_as_rust_array(false)));
},
RustStructType::TypeChoice { variants } => {
self.generate_type_choices_from_variants(types, rust_ident, variants, rust_struct.tag());
},
RustStructType::GroupChoice { variants, rep } => {
codegen_group_choices(self, types, rust_ident, variants, *rep, rust_struct.tag())
},
RustStructType::Wrapper{ wrapped, min_max } => {
match rust_struct.tag() {
Some(tag) => generate_wrapper_struct(self, types, rust_ident, &wrapped.clone().tag(tag), min_max.clone()),
None => generate_wrapper_struct(self, types, rust_ident, wrapped, min_max.clone()),
}
},
RustStructType::Prelude => {
match rust_ident.to_string().as_ref() {
"Int" => if types.is_referenced(rust_ident) {
generate_int(self, types)
},
other => panic!("prelude not defined: {}", other),
}
},
//self
// .rust()
// .push_type_alias(TypeAlias::new(rust_struct.ident(), ConceptualRustType::name_for_rust_map(domain, range, false)));
},
RustStructType::Array { element_type } => {
if CLI_ARGS.wasm {
self.generate_array_type(types, element_type.clone(), rust_ident);
}
//self
// .rust()
// .push_type_alias(TypeAlias::new(rust_struct.ident(), element_type.name_as_rust_array(false)));
},
RustStructType::TypeChoice { variants } => {
self.generate_type_choices_from_variants(types, rust_ident, variants, rust_struct.tag());
},
RustStructType::GroupChoice { variants, rep } => {
codegen_group_choices(self, types, rust_ident, variants, *rep, rust_struct.tag())
},
RustStructType::Wrapper{ wrapped, min_max } => {
match rust_struct.tag() {
Some(tag) => generate_wrapper_struct(self, types, rust_ident, &wrapped.clone().tag(tag), min_max.clone()),
None => generate_wrapper_struct(self, types, rust_ident, wrapped, min_max.clone()),
}
},
RustStructType::Extern => {
match rust_ident.to_string().as_ref() {
"Int" => if types.is_referenced(rust_ident) {
generate_int(self, types)
},
_ => ()/* user-specified external types */,
}
},
}
}
}
Primitive::Str => deser_primitive(config.final_exprs, "text", "s", "s"),
Primitive::Bool => {
// no encoding differences for bool
deser_code.content.line(&final_result_expr_complete(&mut deser_code.throws, config.final_exprs, "bool::deserialize(raw)"));
deser_code.content.line(&final_result_expr_complete(&mut deser_code.throws, config.final_exprs, "raw.bool()"));
},
Primitive::F32 => {
deser_code.content.line(&final_result_expr_complete(&mut deser_code.throws, config.final_exprs, "f32::deserialize(raw)"));
n => Some(format!("{}.read_elems({})?;", read_len_overload, n)),
};
elem_config = elem_config.overload_read_len(read_len_overload);
let deser_loop = make_deser_loop("len", &format!("{}_read_len.read", config.var_name));
let deser_loop = make_deser_loop("len", &format!("{}_read_len.read()", config.var_name));
(deser_loop, plain_len_check)
},
_ => (make_deser_loop("len", &format!("({}.len() as u64)", arr_var_name)), None)
}
add_wasm_enum_getters(&mut wrapper.s_impl, name, &variants, None);
wrapper.push(self, types);
//push_wasm_wrapper(self, name, s, s_impl);
}
}
// generate array type ie [Foo] generates Foos if not already created
fn generate_array_type(&mut self, types: &IntermediateTypes, element_type: RustType, array_type_ident: &RustIdent) {
if self.already_generated.insert(array_type_ident.clone()) {
let inner_type = element_type.name_as_rust_array(true);
let mut s = codegen::Struct::new(&array_type_ident.to_string());
impl<'a> IntermediateTypes<'a> {
pub fn new() -> Self {
let mut rust_structs = BTreeMap::new();
rust_structs.insert(RustIdent::new(CDDLIdent::new("int")), RustStruct::new_prelude(RustIdent::new(CDDLIdent::new("int"))));
rust_structs.insert(RustIdent::new(CDDLIdent::new("int")), RustStruct::new_extern(RustIdent::new(CDDLIdent::new("int"))));
Self {
plain_groups: BTreeMap::new(),
type_aliases: Self::aliases(),
pub fn new_type(&mut self, raw: &CDDLIdent) -> RustType {
let alias_ident = AliasIdent::new(raw.clone());
let resolved = match self.apply_type_aliases(&alias_ident) {
Some(ty) => match alias_ident {
AliasIdent::Reserved(_) => ty,
AliasIdent::Rust(_) => ty.as_alias(alias_ident.clone())
},
Some((ty, true)) => ty,
Some((ty, false)) => ty.as_alias(alias_ident.clone()),
None => ConceptualRustType::Rust(RustIdent::new(raw.clone())).into(),
};
let resolved_inner = match &resolved.conceptual_type {
}
// see new_type() for why this is mut
pub fn apply_type_aliases(&mut self, alias_ident: &AliasIdent) -> Option<RustType> {
/// returns: (base type, if the alias should be substituted)
pub fn apply_type_aliases(&mut self, alias_ident: &AliasIdent) -> Option<(RustType, bool)> {
// Assumes we are not trying to pass in any kind of compound type (arrays, etc)
match self.type_aliases.get(alias_ident) {
Some((alias, _, _)) => Some(alias.clone()),
Some((alias, gen_alias, _)) => {
Some((alias.clone(), !gen_alias))
}
None => match alias_ident {
AliasIdent::Rust(_rust_ident) => None,
AliasIdent::Reserved(reserved) => if reserved == "int" {
// we auto-include only the parts of the cddl prelude necessary (and supported)
cddl_prelude(reserved).expect(&format!("Reserved ident {} not a part of cddl_prelude?", reserved));
self.emit_prelude(reserved.clone());
Some(ConceptualRustType::Rust(RustIdent::new(CDDLIdent::new(format!("prelude_{}", reserved)))).into())
Some((ConceptualRustType::Rust(RustIdent::new(CDDLIdent::new(format!("prelude_{}", reserved)))).into(), true))
},
},
}
}
}
impl AsRef<str> for RustIdent {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
// identifier for enum (group/type choice) variants
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum VariantIdent {
// See comment in RustStruct::definite_info(), this is the same, returns a string expression
// which evaluates to the length.
// self_expr is an expresison that evaluates to this RustType (e.g. member, etc) at the point where
// self_expr is an expression that evaluates to this RustType (e.g. member, etc) at the point where
// the return of this function will be used.
pub fn definite_info(&self, self_expr: &str, types: &IntermediateTypes) -> String {
match self.expanded_field_count(types) {
pub fn visit_types_excluding<F: FnMut(&Self)>(&self, types: &IntermediateTypes, f: &mut F, already_visited: &mut BTreeSet<RustIdent>) {
f(self);
match self {
Self::Alias(_ident, ty) => ty.visit_types_excluding(types, f, already_visited),
Self::Alias(ident, ty) => {
match ident {
AliasIdent::Rust(rust_ident) => {
if already_visited.insert(rust_ident.clone()) {
ty.visit_types_excluding(types, f, already_visited)
}
},
_ => ty.visit_types_excluding(types, f, already_visited)
};
},
Self::Array(ty) => ty.conceptual_type.visit_types_excluding(types, f, already_visited),
Self::Fixed(_) => (),
Self::Map(k, v) => {
/// This is a no-op in generation but to prevent lookups of things in the prelude
/// e.g. `int` from not being resolved while still being able to detect it when
/// referring to a struct that doesn't exist even after generation.
Prelude,
Extern,
}
impl RustStruct {
}
}
pub fn new_prelude(ident: RustIdent) -> Self {
pub fn new_extern(ident: RustIdent) -> Self {
Self {
ident,
tag: None,
variant: RustStructType::Prelude,
variant: RustStructType::Extern,
}
}
RustStructType::TypeChoice{ .. } => unreachable!("I don't think type choices should be using length?"),
RustStructType::GroupChoice{ .. } => unreachable!("I don't think group choices should be using length?"),
RustStructType::Wrapper{ .. } => unreachable!("wrapper types don't use length"),
RustStructType::Prelude{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
RustStructType::Extern{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
}
}
RustStructType::TypeChoice{ .. } => unreachable!("I don't think type choices should be using length?"),
RustStructType::GroupChoice{ .. } => unreachable!("I don't think group choices should be using length?"),
RustStructType::Wrapper{ .. } => unreachable!("wrapper types don't use length"),
RustStructType::Prelude{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
RustStructType::Extern{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
}
}
RustStructType::TypeChoice{ .. } => unreachable!("I don't think type choices should be using length?"),
RustStructType::GroupChoice{ .. } => unreachable!("I don't think group choices should be using length?"),
RustStructType::Wrapper{ .. } => unreachable!("wrapper types don't use length"),
RustStructType::Prelude{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
RustStructType::Extern{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
}
}
RustStructType::TypeChoice{ .. } => unreachable!("I don't think type choices should be using length?"),
RustStructType::GroupChoice{ .. } => unreachable!("I don't think group choices should be using length?"),
RustStructType::Wrapper{ .. } => unreachable!("wrapper types don't use length"),
RustStructType::Prelude{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
RustStructType::Extern{ .. } => panic!("do we need to look this up ever? will the prelude have structs with fields?"),
}
}
range.conceptual_type.visit_types_excluding(types, f, already_visited);
},
RustStructType::Wrapper{ wrapped, .. } => wrapped.conceptual_type.visit_types_excluding(types, f, already_visited),
RustStructType::Prelude => (),
RustStructType::Extern => (),
}
}
}
RustStructType::Wrapper{ .. } => {
todo!("should we look this up in types to resolve?");
},
RustStructType::Prelude => panic!("generics should not be used on types in the prelude (e.g. int)"),
RustStructType::Extern => panic!("generics should not be used on types in the prelude (e.g. int)"),
};
instance
}
// to specific structs, and the existing comment parsing ast was not suited for this.
// If, in the future, cddl released a feature flag to allow partial cddl we can just
// remove all this and revert back the commit before this one for scope handling.
let input_files_content = input_files
let mut input_files_content = input_files
.iter()
.enumerate()
.map(|(i, input_file)| {
std::fs::read_to_string(input_file)
.map(|raw| format!("\n{}{} = \"{}\"\n{}\n", parsing::SCOPE_MARKER, i, scope, raw))
}).collect::<Result<String, _>>()?;
// 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));
// Plain group / scope marking
let cddl = cddl::parser::cddl_from_str(&input_files_content, true)?;
//panic!("cddl: {:#?}", cddl);
let pv = cddl::ast::parent::ParentVisitor::new(&cddl).unwrap();
let mut types = IntermediateTypes::new();
// mark scope and filter scope markers
use either::{Either};
use std::collections::{BTreeMap};
use crate::comment_ast::{RuleMetadata, metadata_from_comments};
use crate::comment_ast::{RuleMetadata, metadata_from_comments, merge_metadata};
use crate::intermediate::{
AliasIdent,
CDDLIdent,
}
pub const SCOPE_MARKER: &'static str = "_CDDL_CODEGEN_SCOPE_MARKER_";
pub const EXTERN_MARKER: &'static str = "_CDDL_CODEGEN_EXTERN_TYPE_";
/// Some means it is a scope marker, containing the scope
pub fn rule_is_scope_marker(cddl_rule: &cddl::ast::Rule) -> Option<String> {
pub fn parse_rule(types: &mut IntermediateTypes, parent_visitor: &ParentVisitor, cddl_rule: &cddl::ast::Rule) {
match cddl_rule {
cddl::ast::Rule::Type{ rule, .. } => {
// (1) is_type_choice_alternate ignored since shelley.cddl doesn't need it
// It's used, but used for no reason as it is the initial definition
// (which is also valid cddl), but it would be fine as = instead of /=
// (2) ignores control operators - only used in shelley spec to limit string length for application metadata
let rust_ident = RustIdent::new(CDDLIdent::new(rule.name.to_string()));
let generic_params = rule
.generic_params
.as_ref()
.map(|gp| gp.params.iter().map(|id| RustIdent::new(CDDLIdent::new(id.param.to_string()))).collect::<Vec<_>>());
if rule.value.type_choices.len() == 1 {
let choice = &rule.value.type_choices.first().unwrap();
parse_type(types, parent_visitor, &rust_ident, choice, None, generic_params);
if rule.name.to_string() == EXTERN_MARKER {
// ignore - this was inserted by us so that cddl's parsing succeeds
// see comments in main.rs
} else {
parse_type_choices(types, parent_visitor, &rust_ident, &rule.value.type_choices, None, generic_params);
// (1) is_type_choice_alternate ignored since shelley.cddl doesn't need it
// It's used, but used for no reason as it is the initial definition
// (which is also valid cddl), but it would be fine as = instead of /=
// (2) ignores control operators - only used in shelley spec to limit string length for application metadata
let generic_params = rule
.generic_params
.as_ref()
.map(|gp| gp.params.iter().map(|id| RustIdent::new(CDDLIdent::new(id.param.to_string()))).collect::<Vec<_>>());
if rule.value.type_choices.len() == 1 {
let choice = &rule.value.type_choices.first().unwrap();
parse_type(types, parent_visitor, &rust_ident, choice, None, generic_params);
} else {
parse_type_choices(types, parent_visitor, &rust_ident, &rule.value.type_choices, None, generic_params);
}
}
},
cddl::ast::Rule::Group{ rule, .. } => {
Some(tag) => RustType::new(ConceptualRustType::Optional(Box::new(inner_rust_type))).tag(tag),
None => RustType::new(ConceptualRustType::Optional(Box::new(inner_rust_type))),
};
types.register_type_alias(name.clone(), final_type, true, true);
let rule_metadata = RuleMetadata::from(inner_type2.comments_after_type.as_ref());
types.register_type_alias(name.clone(), final_type, !rule_metadata.no_alias, !rule_metadata.no_alias);
} else {
let variants = create_variants_from_type_choices(types, parent_visitor, type_choices);
let rust_struct = RustStruct::new_type_choice(name.clone(), tag, variants);
fn parse_type(types: &mut IntermediateTypes, parent_visitor: &ParentVisitor, type_name: &RustIdent, type_choice: &TypeChoice, outer_tag: Option<usize>, generic_params: Option<Vec<RustIdent>>) {
let type1 = &type_choice.type1;
let rule_metadata = merge_metadata(
&RuleMetadata::from(type1.comments_after_type.as_ref()),
&RuleMetadata::from(type_choice.comments_after_type.as_ref()),
);
match &type1.type2 {
Type2::Typename{ ident, generic_args, .. } => {
// Note: this handles bool constants too, since we apply the type aliases and they resolve
// and there's no Type2::BooleanValue
let cddl_ident = CDDLIdent::new(ident.to_string());
let control = type1.operator.as_ref().map(|op| parse_control_operator(types, parent_visitor, &type1.type2, op));
match control {
Some(control) => {
assert!(generic_params.is_none(), "Generics combined with range specifiers not supported");
// TODO: what about aliases that resolve to these? is it even possible to know this at this stage?
let field_type = || match cddl_ident.to_string().as_str() {
"tstr" | "text" => ConceptualRustType::Primitive(Primitive::Str),
"bstr" | "bytes" => ConceptualRustType::Primitive(Primitive::Bytes),
other => panic!("range control specifiers not supported for type: {}", other),
};
match control {
ControlOperator::Range(min_max) => {
match cddl_ident.to_string().as_str() {
"int" | "uint" | "float" | "float64" | "float32" | "float16" => match range_to_primitive(min_max.0, min_max.1) {
Some(t) => types.register_type_alias(type_name.clone(), t.into(), true, true),
None => panic!("unsupported range for {:?}: {:?}", cddl_ident.to_string().as_str(), control)
},
_ => types.register_rust_struct(parent_visitor, RustStruct::new_wrapper(type_name.clone(), outer_tag, field_type().clone().into(), Some(min_max)))
}
},
ControlOperator::CBOR(ty) => match field_type() {
ConceptualRustType::Primitive(Primitive::Bytes) => {
types.register_type_alias(type_name.clone(), ty.as_bytes(), true, true);
if ident.ident == EXTERN_MARKER {
types.register_rust_struct(parent_visitor, RustStruct::new_extern(type_name.clone()));
} else {
// Note: this handles bool constants too, since we apply the type aliases and they resolve
// and there's no Type2::BooleanValue
let cddl_ident = CDDLIdent::new(ident.to_string());
let control = type1.operator.as_ref().map(|op| parse_control_operator(types, parent_visitor, &type1.type2, op));
match control {
Some(control) => {
assert!(generic_params.is_none(), "Generics combined with range specifiers not supported");
// TODO: what about aliases that resolve to these? is it even possible to know this at this stage?
let field_type = || match cddl_ident.to_string().as_str() {
"tstr" | "text" => ConceptualRustType::Primitive(Primitive::Str),
"bstr" | "bytes" => ConceptualRustType::Primitive(Primitive::Bytes),
other => panic!("range control specifiers not supported for type: {}", other),
};
match control {
ControlOperator::Range(min_max) => {
match cddl_ident.to_string().as_str() {
"int" | "uint" | "float" | "float64" | "float32" | "float16" => match range_to_primitive(min_max.0, min_max.1) {
Some(t) => types.register_type_alias(type_name.clone(), t.into(), !rule_metadata.no_alias, !rule_metadata.no_alias),
None => panic!("unsupported range for {:?}: {:?}", cddl_ident.to_string().as_str(), control)
},
_ => types.register_rust_struct(parent_visitor, RustStruct::new_wrapper(type_name.clone(), outer_tag, field_type().clone().into(), Some(min_max)))
}
},
_ => panic!(".cbor is only allowed on bytes as per CDDL spec"),
},
ControlOperator::Default(default_value) => {
let default_type = rust_type_from_type2(types, parent_visitor, &type1.type2)
.default(default_value);
types.register_type_alias(type_name.clone(), default_type, true, true);
},
}
},
None => {
let mut concrete_type = types.new_type(&cddl_ident);
if let ConceptualRustType::Alias(_ident, ty) = concrete_type.conceptual_type {
concrete_type.conceptual_type = *ty;
};
match &generic_params {
Some(_params) => {
// this should be the only situation where you need this as otherwise the params would be unbound
todo!("generics on defined types e.g. foo<T, U> = [T, U], bar<V> = foo<V, uint>");
// TODO: maybe you could do this by resolving it here then storing the resolved one as GenericDef
},
None => {
match generic_args {
Some(arg) => {
// This is for named generic instances such as:
// foo = bar<text>
let generic_args = arg.args.iter().map(|a| rust_type_from_type1(types, parent_visitor, &a.arg)).collect();
types.register_generic_instance(GenericInstance::new(type_name.clone(), RustIdent::new(cddl_ident.clone()), generic_args))
ControlOperator::CBOR(ty) => match field_type() {
ConceptualRustType::Primitive(Primitive::Bytes) => {
types.register_type_alias(type_name.clone(), ty.as_bytes(), !rule_metadata.no_alias, !rule_metadata.no_alias);
},
None => {
let rule_metadata = RuleMetadata::from(type1.comments_after_type.as_ref());
if rule_metadata.is_newtype {
types.register_rust_struct(parent_visitor, RustStruct::new_wrapper(type_name.clone(), None, concrete_type, None));
} else {
types.register_type_alias(type_name.clone(), concrete_type, true, true);
_ => panic!(".cbor is only allowed on bytes as per CDDL spec"),
},
ControlOperator::Default(default_value) => {
let default_type = rust_type_from_type2(types, parent_visitor, &type1.type2)
.default(default_value);
types.register_type_alias(type_name.clone(), default_type, !rule_metadata.no_alias, !rule_metadata.no_alias);
},
}
},
None => {
let mut concrete_type = types.new_type(&cddl_ident);
if let ConceptualRustType::Alias(_ident, ty) = concrete_type.conceptual_type {
concrete_type.conceptual_type = *ty;
};
match &generic_params {
Some(_params) => {
// this should be the only situation where you need this as otherwise the params would be unbound
todo!("generics on defined types e.g. foo<T, U> = [T, U], bar<V> = foo<V, uint>");
// TODO: maybe you could do this by resolving it here then storing the resolved one as GenericDef
},
None => {
match generic_args {
Some(arg) => {
// This is for named generic instances such as:
// foo = bar<text>
let generic_args = arg.args.iter().map(|a| rust_type_from_type1(types, parent_visitor, &a.arg)).collect();
types.register_generic_instance(GenericInstance::new(type_name.clone(), RustIdent::new(cddl_ident.clone()), generic_args))
},
None => {
if rule_metadata.is_newtype {
types.register_rust_struct(parent_visitor, RustStruct::new_wrapper(type_name.clone(), None, concrete_type, None));
} else {
types.register_type_alias(type_name.clone(), concrete_type, !rule_metadata.no_alias, !rule_metadata.no_alias);
}
}
use std::io::Write;
fn run_test(dir: &str, options: &[&str], export_suffix: Option<&str>) {
fn run_test(
dir: &str,
options: &[&str],
export_suffix: Option<&str>,
external_core_file_path: Option<std::path::PathBuf>,
external_wasm_file_path: Option<std::path::PathBuf>) {
use std::str::FromStr;
let export_path = match export_suffix {
Some(suffix) => format!("export_{}", suffix),
.append(true)
.open(test_path.join(format!("{}/core/src/lib.rs", export_path)))
.unwrap();
// copy external file in too (if needed) too
if let Some(external_core_file_path) = external_core_file_path {
let extern_rs = std::fs::read_to_string(external_core_file_path).unwrap();
lib_rs.write("\n\n".as_bytes()).unwrap();
lib_rs.write_all(extern_rs.as_bytes()).unwrap();
}
let deser_test_rs = std::fs::read_to_string(std::path::PathBuf::from_str("tests").unwrap().join("deser_test")).unwrap();
lib_rs.write("\n\n".as_bytes()).unwrap();
lib_rs.write_all(deser_test_rs.as_bytes()).unwrap();
let test_rs = std::fs::read_to_string(test_path.join("tests.rs")).unwrap();
lib_rs.write("\n\n".as_bytes()).unwrap();
lib_rs.write_all(test_rs.as_bytes()).unwrap();
std::mem::drop(lib_rs);
// run tests in generated code
println!(" ------ testing ------");
let cargo_test = std::process::Command::new("cargo")
}
println!("test stdout:\n{}", String::from_utf8(cargo_test.stdout).unwrap());
assert!(cargo_test.status.success());
// wasm
let wasm_export_dir = test_path.join(format!("{}/wasm", export_path));
let wasm_test_dir = test_path.join("tests_wasm.rs");
// copy external wasm defs if they exist
if let Some(external_wasm_file_path) = external_wasm_file_path {
println!("trying to open: {:?}", external_wasm_file_path);
let mut wasm_lib_rs = std::fs::OpenOptions::new()
.write(true)
.append(true)
.open(test_path.join(format!("{}/wasm/src/lib.rs", export_path)))
.unwrap();
let extern_rs = std::fs::read_to_string(external_wasm_file_path).unwrap();
wasm_lib_rs.write("\n\n".as_bytes()).unwrap();
wasm_lib_rs.write_all(extern_rs.as_bytes()).unwrap();
}
if wasm_test_dir.exists() {
println!(" ------ testing (wasm) ------");
let cargo_test_wasm = std::process::Command::new("cargo")
#[test]
fn core_with_wasm() {
run_test("core", &[], Some("wasm"));
use std::str::FromStr;
let extern_core_path = std::path::PathBuf::from_str("tests").unwrap().join("external_core_defs");
let extern_wasm_path = std::path::PathBuf::from_str("tests").unwrap().join("external_wasm_defs");
run_test("core", &[], Some("wasm"), Some(extern_core_path), Some(extern_wasm_path));
}
#[test]
fn core_no_wasm() {
run_test("core", &["--wasm=false"], None);
use std::str::FromStr;
let extern_core_path = std::path::PathBuf::from_str("tests").unwrap().join("external_core_defs");
run_test("core", &["--wasm=false"], None, Some(extern_core_path), None);
}
#[test]
fn comment_dsl() {
run_test("comment-dsl", &["--preserve-encodings=true"], Some("wasm"));
run_test("comment-dsl", &["--preserve-encodings=true"], Some("wasm"), None , None);
}
#[test]
fn preserve_encodings() {
run_test("preserve-encodings", &["--preserve-encodings=true"], None);
run_test("preserve-encodings", &["--preserve-encodings=true"], None, None, None);
}
#[test]
fn canonical() {
run_test("canonical", &["--preserve-encodings=true", "--canonical-form=true"], None);
run_test("canonical", &["--preserve-encodings=true", "--canonical-form=true"], None, None, None);
}
#[test]
fn rust_wasm_split() {
run_test("rust-wasm-split", &[], None);
run_test("rust-wasm-split", &[], None, None, None);
}
}
}
}
// same as cbor_event::de::Deserialize but with our DeserializeError
pub trait Deserialize {
fn deserialize<R: BufRead + Seek>(
raw: &mut Deserializer<R>,
) -> Result<Self, DeserializeError> where Self: Sized;
}
impl<T: cbor_event::de::Deserialize> Deserialize for T {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<T, DeserializeError> {
T::deserialize(raw).map_err(DeserializeError::from)
}
}
pub trait FromBytes {
fn from_bytes(data: Vec<u8>) -> Result<Self, DeserializeError> where Self: Sized;
}
impl<T: Deserialize + Sized> FromBytes for T {
fn from_bytes(data: Vec<u8>) -> Result<Self, DeserializeError> {
let mut raw = Deserializer::from(std::io::Cursor::new(data));
Self::deserialize(&mut raw)
}
}
// same as cbor_event::de::Deserialize but with our DeserializeError
pub trait Deserialize {
fn from_cbor_bytes(data: &[u8]) -> Result<Self, DeserializeError> where Self: Sized {
let mut raw = Deserializer::from(std::io::Cursor::new(data));
Self::deserialize(&mut raw)
}
fn deserialize<R: BufRead + Seek>(
raw: &mut Deserializer<R>,
) -> Result<Self, DeserializeError> where Self: Sized;
}
impl<T: cbor_event::de::Deserialize> Deserialize for T {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<T, DeserializeError> {
T::deserialize(raw).map_err(DeserializeError::from)
}
}
) -> cbor_event::Result<&'a mut Serializer<W>>;
}
pub trait ToBytes {
fn to_bytes(&self) -> Vec<u8>;
pub trait ToCBORBytes {
fn to_cbor_bytes(&self) -> Vec<u8>;
}
impl<T: cbor_event::se::Serialize> ToBytes for T {
fn to_bytes(&self) -> Vec<u8> {
let mut buf = Serializer::new_vec();
self.serialize(&mut buf).unwrap();
buf.finalize()
}
impl<T: cbor_event::se::Serialize> ToCBORBytes for T {
fn to_cbor_bytes(&self) -> Vec<u8> {
let mut buf = Serializer::new_vec();
self.serialize(&mut buf).unwrap();
buf.finalize()
}
}
\ No newline at end of file
pub struct CBORReadLen {
deser_len: cbor_event::Len,
read: u64,
deser_len: cbor_event::Len,
read: u64,
}
impl CBORReadLen {
pub fn new(len: cbor_event::Len) -> Self {
Self {
deser_len: len,
read: 0,
}
}
pub fn new(len: cbor_event::Len) -> Self {
Self {
deser_len: len,
read: 0,
}
}
// Marks {n} values as being read, and if we go past the available definite length
// given by the CBOR, we return an error.
pub fn read_elems(&mut self, count: usize) -> Result<(), DeserializeFailure> {
match self.deser_len {
cbor_event::Len::Len(n) => {
self.read += count as u64;
if self.read > n {
Err(DeserializeFailure::DefiniteLenMismatch(n, None))
} else {
Ok(())
}
},
cbor_event::Len::Indefinite => Ok(()),
}
}
pub fn read(&self) -> u64 {
self.read
}
pub fn finish(&self) -> Result<(), DeserializeFailure> {
match self.deser_len {
cbor_event::Len::Len(n) => {
if self.read == n {
Ok(())
} else {
Err(DeserializeFailure::DefiniteLenMismatch(n, Some(self.read)))
}
},
cbor_event::Len::Indefinite => Ok(()),
}
}
// Marks {n} values as being read, and if we go past the available definite length
// given by the CBOR, we return an error.
pub fn read_elems(&mut self, count: usize) -> Result<(), DeserializeFailure> {
match self.deser_len {
cbor_event::Len::Len(n) => {
self.read += count as u64;
if self.read > n {
Err(DeserializeFailure::DefiniteLenMismatch(n, None))
} else {
Ok(())
}
},
cbor_event::Len::Indefinite => Ok(()),
}
}
pub fn finish(&self) -> Result<(), DeserializeFailure> {
match self.deser_len {
cbor_event::Len::Len(n) => {
if self.read == n {
Ok(())
} else {
Err(DeserializeFailure::DefiniteLenMismatch(n, Some(self.read)))
}
},
cbor_event::Len::Indefinite => Ok(()),
}
}
}
pub trait DeserializeEmbeddedGroup {
}
pub trait Serialize {
fn to_canonical_cbor_bytes(&self) -> Vec<u8> {
let mut buf = Serializer::new_vec();
self.serialize(&mut buf, true).unwrap();
buf.finalize()
}
fn to_cbor_bytes(&self) -> Vec<u8> {
let mut buf = Serializer::new_vec();
self.serialize(&mut buf, false).unwrap();
buf.finalize()
}
fn serialize<'a, W: Write + Sized>(
&self,
serializer: &'a mut Serializer<W>,
force_canonical: bool,
) -> cbor_event::Result<&'a mut Serializer<W>>;
}
impl<T: cbor_event::se::Serialize> Serialize for T {
fn serialize<'a, W: Write + Sized>(
&self,
serializer: &'a mut Serializer<W>,
_force_canonical: bool,
) -> cbor_event::Result<&'a mut Serializer<W>> {
<T as cbor_event::se::Serialize>::serialize(self, serializer)
}
}
pub trait SerializeEmbeddedGroup {
fn serialize_as_embedded_group<'a, W: Write + Sized>(
&self,
serializer: &'a mut Serializer<W>,
force_canonical: bool,
) -> cbor_event::Result<&'a mut Serializer<W>>;
}
pub trait ToBytes {
fn to_bytes(&self, force_canonical: bool) -> Vec<u8>;
}
impl<T: Serialize> ToBytes for T {
fn to_bytes(&self, force_canonical: bool) -> Vec<u8> {
let mut buf = Serializer::new_vec();
self.serialize(&mut buf, force_canonical).unwrap();
buf.finalize()
}
}
use super::*;
use cbor_event::StringLenSz;
fn deser_test<T: Deserialize + ToBytes>(orig: &T, force_canonical: bool) {
print_cbor_types("orig", &orig.to_bytes(force_canonical));
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_bytes(force_canonical)))).unwrap();
print_cbor_types("deser", &deser.to_bytes(force_canonical));
assert_eq!(orig.to_bytes(force_canonical), deser.to_bytes(force_canonical));
fn deser_test_orig<T: Deserialize + Serialize>(orig: &T) {
print_cbor_types("orig (original enc)", &orig.to_cbor_bytes());
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_cbor_bytes()))).unwrap();
print_cbor_types("deser", &deser.to_cbor_bytes());
assert_eq!(orig.to_cbor_bytes(), deser.to_cbor_bytes());
}
fn deser_test_canonical<T: Deserialize + Serialize>(orig: &T) {
print_cbor_types("orig (canonical)", &orig.to_canonical_cbor_bytes());
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_canonical_cbor_bytes()))).unwrap();
print_cbor_types("deser", &deser.to_canonical_cbor_bytes());
assert_eq!(orig.to_canonical_cbor_bytes(), deser.to_canonical_cbor_bytes());
}
#[test]
vec![0x42, 0xB4, 0xD3],
vec![BREAK]
].into_iter().flatten().clone().collect::<Vec<u8>>();
let foo: Foo = from_bytes(non_canonical_bytes.clone()).unwrap();
assert_eq!(foo.to_bytes(false), non_canonical_bytes);
let foo = Foo::from_cbor_bytes(&non_canonical_bytes).unwrap();
assert_eq!(foo.to_cbor_bytes(), non_canonical_bytes);
let canonical_bytes = vec![
cbor_tag(11),
arr_def(3),
vec![0x09],
cbor_string("jdskfjdsfkjad"),
vec![0x42, 0xB4, 0xD3],
].into_iter().flatten().clone().collect::<Vec<u8>>();
assert_eq!(foo.to_bytes(true), canonical_bytes);
deser_test(&foo, true);
deser_test(&foo, false);
assert_eq!(foo.to_canonical_cbor_bytes(), canonical_bytes);
deser_test_canonical(&foo);
deser_test_orig(&foo);
}
#[test]
vec![0x05u8],
vec![BREAK]
].into_iter().flatten().clone().collect::<Vec<u8>>();
let bar: Bar = from_bytes(non_canonical_bytes.clone()).unwrap();
assert_eq!(bar.to_bytes(false), non_canonical_bytes);
let bar = Bar::from_cbor_bytes(&non_canonical_bytes).unwrap();
assert_eq!(bar.to_cbor_bytes(), non_canonical_bytes);
let canonical_bytes = vec![
map_def(4),
vec![0x01u8],
cbor_string("five"),
vec![0x05u8],
].into_iter().flatten().clone().collect::<Vec<u8>>();
assert_eq!(bar.to_bytes(true), canonical_bytes);
deser_test(&bar, true);
deser_test(&bar, false);
assert_eq!(bar.to_canonical_cbor_bytes(), canonical_bytes);
deser_test_canonical(&bar);
deser_test_orig(&bar);
// tests for all other possible encodings (new tests after complete encoding preservation)
let canonical_bytes_all = vec![
} else {
3
};
let mut canonical_bar = Bar::from_bytes(canonical_bytes_all.clone()).unwrap();
let mut canonical_bar = Bar::from_cbor_bytes(&canonical_bytes_all).unwrap();
if !has_5 {
canonical_bar.key_5 = None;
}
irregular_encoding.extend_from_slice(&keys[key_order[i]]);
}
print_cbor_types("irregular_encoding", &irregular_encoding);
let irregular_bar = Bar::from_bytes(irregular_encoding.clone()).unwrap();
print_cbor_types("irregular_bar.to_bytes(false)", &irregular_bar.to_bytes(false));
assert_eq!(irregular_bar.to_bytes(false), irregular_encoding);
print_cbor_types("irregular_bar.to_bytes(true)", &irregular_bar.to_bytes(true));
assert_eq!(irregular_bar.to_bytes(true), canonical_bar.to_bytes(false));
let irregular_bar = Bar::from_cbor_bytes(&irregular_encoding).unwrap();
print_cbor_types("irregular_bar.to_cbor_bytes()", &irregular_bar.to_cbor_bytes());
assert_eq!(irregular_bar.to_cbor_bytes(), irregular_encoding);
print_cbor_types("irregular_bar.to_canonical_cbor_bytes()", &irregular_bar.to_canonical_cbor_bytes());
assert_eq!(irregular_bar.to_canonical_cbor_bytes(), canonical_bar.to_cbor_bytes());
}
}
}
vec![BREAK],
].into_iter().flatten().clone().collect::<Vec<u8>>();
print_cbor_types("non_canonical_bytes", &non_canonical_bytes);
let table: TableArrMembers = from_bytes(non_canonical_bytes.clone()).unwrap();
assert_eq!(table.to_bytes(false), non_canonical_bytes);
let table = TableArrMembers::from_cbor_bytes(&non_canonical_bytes).unwrap();
assert_eq!(table.to_cbor_bytes(), non_canonical_bytes);
let canonical_bytes = vec![
map_def(3),
cbor_string("arr"),
cbor_string("Sixteen"),
].into_iter().flatten().clone().collect::<Vec<u8>>();
print_cbor_types("canonical_bytes", &canonical_bytes);
assert_eq!(table.to_bytes(true), canonical_bytes);
deser_test(&table, true);
deser_test(&table, false);
assert_eq!(table.to_canonical_cbor_bytes(), canonical_bytes);
deser_test_canonical(&table);
deser_test_orig(&table);
}
#[test]
vec![BREAK],
vec![BREAK],
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular = DeeplyNested::from_bytes(irregular_bytes.clone()).unwrap();
assert_eq!(irregular_bytes, irregular.to_bytes(false));
assert_eq!(canonical_bytes, irregular.to_bytes(true));
let irregular = DeeplyNested::from_cbor_bytes(&irregular_bytes).unwrap();
assert_eq!(irregular_bytes, irregular.to_cbor_bytes());
assert_eq!(canonical_bytes, irregular.to_canonical_cbor_bytes());
}
}
}
cbor_int(1, Sz::Inline),
cbor_int(3, Sz::Inline),
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular_0 = TypeChoice::from_bytes(irregular_bytes_0.clone()).unwrap();
assert_eq!(irregular_bytes_0, irregular_0.to_bytes(false));
assert_eq!(canonical_bytes_0, irregular_0.to_bytes(true));
let irregular_hello_world = TypeChoice::from_bytes(irregular_bytes_hello_world.clone()).unwrap();
assert_eq!(irregular_bytes_hello_world, irregular_hello_world.to_bytes(false));
assert_eq!(canonical_bytes_hello_world, irregular_hello_world.to_bytes(true));
let irregular_uint = TypeChoice::from_bytes(irregular_bytes_uint.clone()).unwrap();
assert_eq!(irregular_bytes_uint, irregular_uint.to_bytes(false));
assert_eq!(canonical_bytes_uint, irregular_uint.to_bytes(true));
let irregular_text = TypeChoice::from_bytes(irregular_bytes_text.clone()).unwrap();
assert_eq!(irregular_bytes_text, irregular_text.to_bytes(false));
assert_eq!(canonical_bytes_text, irregular_text.to_bytes(true));
let irregular_tagged_arr = TypeChoice::from_bytes(irregular_bytes_tagged_arr.clone()).unwrap();
assert_eq!(irregular_bytes_tagged_arr, irregular_tagged_arr.to_bytes(false));
assert_eq!(canonical_bytes_tagged_arr, irregular_tagged_arr.to_bytes(true));
let irregular_0 = TypeChoice::from_cbor_bytes(&irregular_bytes_0).unwrap();
assert_eq!(irregular_bytes_0, irregular_0.to_cbor_bytes());
assert_eq!(canonical_bytes_0, irregular_0.to_canonical_cbor_bytes());
let irregular_hello_world = TypeChoice::from_cbor_bytes(&irregular_bytes_hello_world).unwrap();
assert_eq!(irregular_bytes_hello_world, irregular_hello_world.to_cbor_bytes());
assert_eq!(canonical_bytes_hello_world, irregular_hello_world.to_canonical_cbor_bytes());
let irregular_uint = TypeChoice::from_cbor_bytes(&irregular_bytes_uint).unwrap();
assert_eq!(irregular_bytes_uint, irregular_uint.to_cbor_bytes());
assert_eq!(canonical_bytes_uint, irregular_uint.to_canonical_cbor_bytes());
let irregular_text = TypeChoice::from_cbor_bytes(&irregular_bytes_text).unwrap();
assert_eq!(irregular_bytes_text, irregular_text.to_cbor_bytes());
assert_eq!(canonical_bytes_text, irregular_text.to_canonical_cbor_bytes());
let irregular_tagged_arr = TypeChoice::from_cbor_bytes(&irregular_bytes_tagged_arr).unwrap();
assert_eq!(irregular_bytes_tagged_arr, irregular_tagged_arr.to_cbor_bytes());
assert_eq!(canonical_bytes_tagged_arr, irregular_tagged_arr.to_canonical_cbor_bytes());
}
}
}
cbor_tag(9),
cbor_string("carrot"),
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular_3 = GroupChoice::from_bytes(irregular_bytes_3.clone()).unwrap();
assert_eq!(irregular_bytes_3, irregular_3.to_bytes(false));
assert_eq!(canonical_bytes_3, irregular_3.to_bytes(true));
let irregular_tagged_2 = GroupChoice::from_bytes(irregular_bytes_tagged_2.clone()).unwrap();
assert_eq!(irregular_bytes_tagged_2, irregular_tagged_2.to_bytes(false));
assert_eq!(canonical_bytes_tagged_2, irregular_tagged_2.to_bytes(true));
let irregular_foo = GroupChoice::from_bytes(irregular_bytes_foo.clone()).unwrap();
assert_eq!(irregular_bytes_foo, irregular_foo.to_bytes(false));
assert_eq!(canonical_bytes_foo, irregular_foo.to_bytes(true));
let irregular_inlined = GroupChoice::from_bytes(irregular_bytes_inlined.clone()).unwrap();
assert_eq!(irregular_bytes_inlined, irregular_inlined.to_bytes(false));
assert_eq!(canonical_bytes_inlined, irregular_inlined.to_bytes(true));
let irregular_plain = GroupChoice::from_bytes(irregular_bytes_plain.clone()).unwrap();
assert_eq!(irregular_bytes_plain, irregular_plain.to_bytes(false));
assert_eq!(canonical_bytes_plain, irregular_plain.to_bytes(true));
let irregular_3 = GroupChoice::from_cbor_bytes(&irregular_bytes_3).unwrap();
assert_eq!(irregular_bytes_3, irregular_3.to_cbor_bytes());
assert_eq!(canonical_bytes_3, irregular_3.to_canonical_cbor_bytes());
let irregular_tagged_2 = GroupChoice::from_cbor_bytes(&irregular_bytes_tagged_2).unwrap();
assert_eq!(irregular_bytes_tagged_2, irregular_tagged_2.to_cbor_bytes());
assert_eq!(canonical_bytes_tagged_2, irregular_tagged_2.to_canonical_cbor_bytes());
let irregular_foo = GroupChoice::from_cbor_bytes(&irregular_bytes_foo).unwrap();
assert_eq!(irregular_bytes_foo, irregular_foo.to_cbor_bytes());
assert_eq!(canonical_bytes_foo, irregular_foo.to_canonical_cbor_bytes());
]
table = { * uint => text }
table_arr_members = {
tab: { * text => text },
arr: [*uint],
paren_size = uint .size (1)
paren_cbor = bytes .cbor (text)
no_alias_u32 = 0..4294967295 ; @no_alias
no_alias_u64 = uint .size 8 ; @no_alias
no_alias = [
no_alias_u32,
no_alias_u64
]
external_foo = _CDDL_CODEGEN_EXTERN_TYPE_
externs = [
external_foo
]
; types below test codegen_table_type
standalone_table = { * uint => text }
standalone_text = { * text => text }
embedded_table = { * uint => text }
embedded_text = { * text => text }
table_array_wrapper = [embedded_table]
table_map_wrapper = { 721: embedded_table }
text_array_wrapper = [embedded_text]
text_map_wrapper = { 721: embedded_text }
inline_wrapper = [{ * text => text }]
mod tests {
use super::*;
fn deser_test<T: Deserialize + ToBytes>(orig: &T) {
let orig_bytes = orig.to_bytes();
fn deser_test<T: Deserialize + ToCBORBytes>(orig: &T) {
let orig_bytes = orig.to_cbor_bytes();
print_cbor_types("orig", &orig_bytes);
let mut deserializer = Deserializer::from(std::io::Cursor::new(orig_bytes.clone()));
let deser = T::deserialize(&mut deserializer).unwrap();
print_cbor_types("deser", &deser.to_bytes());
assert_eq!(orig.to_bytes(), deser.to_bytes());
print_cbor_types("deser", &deser.to_cbor_bytes());
assert_eq!(orig.to_cbor_bytes(), deser.to_cbor_bytes());
assert_eq!(deserializer.as_ref().position(), orig_bytes.len() as u64);
}
md.key_2 = "not two".into();
deser_test(&md);
}
#[test]
fn no_alias() {
use std::str::FromStr;
// we can use this test compiling as a test for the presence of an alias by referencing e.g. I8::MIN
// but we need to read the actual code to test that we're NOT using an alias somewhere and are indeed
// using a raw rust primitive instead
let lib_rs_with_tests = std::fs::read_to_string(std::path::PathBuf::from_str("src").unwrap().join("lib.rs")).unwrap();
// lib.rs includes this very test (and thus those strings we're searching for) so we need to strip that part
let lib_rs = &lib_rs_with_tests[..lib_rs_with_tests.find("#[cfg(test)]").unwrap()];
// these don't have @no_alias
assert!(lib_rs.contains("pub type I8 = i8;"));
assert!(lib_rs.contains("pub type I64 = i64;"));
assert!(lib_rs.contains("pub type U8 = u8;"));
assert!(lib_rs.contains("pub type U16 = u16;"));
assert!(lib_rs.contains("pub type U32 = u32;"));
assert!(lib_rs.contains("pub type U64 = u64;"));
// these do
assert!(lib_rs.contains("no_alias_u32: u32"));
assert!(lib_rs.contains("no_alias_u64: u64"));
assert!(!lib_rs.contains("pub type NoAliasU32"));
assert!(!lib_rs.contains("pub type NoAliasU64"));
}
#[test]
fn externs() {
let externs = Externs::new(ExternalFoo::new(436, String::from("jfkdsjfd"), vec![1, 1, 1]));
deser_test(&externs);
}
}
static ARR_INDEF: u8 = 0x9f;
static MAP_INDEF: u8 = 0xbf;
// to work around JsValue error types (TODO: we should fix this in cddl-codegen)
fn from_bytes<T: Deserialize + Sized>(data: Vec<u8>) -> Result<T, DeserializeError> {
let mut raw = Deserializer::from(std::io::Cursor::new(data));
T::deserialize(&mut raw)
}
fn arr_def(len: u8) -> Vec<u8> {
assert!(len <= 0x17);
vec![0x80u8 + len]
if let cbor_event::LenSz::Len(n, _) = len {
*n -= 1;
}
};
}
};
let reduce_depth = |lens: &mut Vec<cbor_event::LenSz>| {
while let Some(cbor_event::LenSz::Len(0, _)) = lens.last() {
#[derive(Clone, Debug)]
pub struct ExternalFoo {
pub index_0: u64,
pub index_1: String,
pub index_2: Vec<u8>,
}
impl ExternalFoo {
pub fn new(index_0: u64, index_1: String, index_2: Vec<u8>) -> Self {
Self {
index_0,
index_1,
index_2,
}
}
}
impl cbor_event::se::Serialize for ExternalFoo {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_array(cbor_event::Len::Len(3))?;
serializer.write_unsigned_integer(self.index_0)?;
serializer.write_text(&self.index_1)?;
serializer.write_bytes(&self.index_2)?;
Ok(serializer)
}
}
impl Deserialize for ExternalFoo {
fn deserialize<R: BufRead + std::io::Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
let len = raw.array()?;
let mut read_len = CBORReadLen::new(len);
read_len.read_elems(3)?;
(|| -> Result<_, DeserializeError> {
let index_0 = Ok(raw.unsigned_integer()? as u64)
.map_err(|e: DeserializeError| e.annotate("index_0"))?;
let index_1 =
Ok(raw.text()? as String).map_err(|e: DeserializeError| e.annotate("index_1"))?;
let index_2 =
Ok(raw.bytes()? as Vec<u8>).map_err(|e: DeserializeError| e.annotate("index_2"))?;
match len {
cbor_event::Len::Len(_) => (),
cbor_event::Len::Indefinite => match raw.special()? {
CBORSpecial::Break => (),
_ => return Err(DeserializeFailure::EndingBreakMissing.into()),
},
}
Ok(ExternalFoo {
index_0,
index_1,
index_2,
})
})()
.map_err(|e| e.annotate("ExternalFoo"))
}
}
#[derive(Clone, Debug)]
#[wasm_bindgen]
pub struct ExternalFoo(core::ExternalFoo);
#[wasm_bindgen]
impl ExternalFoo {
pub fn to_cbor_bytes(&self) -> Vec<u8> {
core::serialization::ToCBORBytes::to_cbor_bytes(&self.0)
}
pub fn from_cbor_bytes(cbor_bytes: &[u8]) -> Result<ExternalFoo, JsValue> {
core::serialization::Deserialize::from_cbor_bytes(cbor_bytes)
.map(Self)
.map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e)))
}
pub fn index_0(&self) -> u64 {
self.0.index_0
}
pub fn index_1(&self) -> String {
self.0.index_1.clone()
}
pub fn index_2(&self) -> Vec<u8> {
self.0.index_2.clone()
}
pub fn new(index_0: u64, index_1: String, index_2: Vec<u8>) -> Self {
Self(core::ExternalFoo::new(index_0, index_1, index_2))
}
}
impl From<core::ExternalFoo> for ExternalFoo {
fn from(native: core::ExternalFoo) -> Self {
Self(native)
}
}
impl From<ExternalFoo> for core::ExternalFoo {
fn from(wasm: ExternalFoo) -> Self {
wasm.0
}
}
impl AsRef<core::ExternalFoo> for ExternalFoo {
fn as_ref(&self) -> &core::ExternalFoo {
&self.0
}
}
use super::*;
use cbor_event::StringLenSz;
fn deser_test<T: Deserialize + ToBytes>(orig: &T) {
print_cbor_types("orig", &orig.to_bytes());
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_bytes()))).unwrap();
print_cbor_types("deser", &deser.to_bytes());
assert_eq!(orig.to_bytes(), deser.to_bytes());
fn deser_test<T: Deserialize + ToCBORBytes>(orig: &T) {
print_cbor_types("orig", &orig.to_cbor_bytes());
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_cbor_bytes()))).unwrap();
print_cbor_types("deser", &deser.to_cbor_bytes());
assert_eq!(orig.to_cbor_bytes(), deser.to_cbor_bytes());
}
#[test]
fn struct_array() {
let mut foo = Foo::new(436, String::from("jfkdsjfd"), vec![1, 1, 1]);
deser_test(&foo);
let definite_bytes = foo.to_bytes();
let definite_bytes = foo.to_cbor_bytes();
let mut encoding = FooEncoding::default();
encoding.len_encoding = LenEncoding::Indefinite;
foo.encodings = Some(encoding);
deser_test(&foo);
let indefinite_bytes = foo.to_bytes();
let indefinite_bytes = foo.to_cbor_bytes();
assert!(definite_bytes != indefinite_bytes);
assert_eq!(definite_bytes[0], 0xc6u8 + 11 - 6);
assert_eq!(definite_bytes[1], 0x83u8);
// full test with key 5 (but without key "derp")
bar.key_5 = Some("text".into());
bar.encodings = Some(bar_encoding.clone());
let definite_bytes = bar.to_bytes();
let definite_bytes = bar.to_cbor_bytes();
bar_encoding.len_encoding = LenEncoding::Indefinite;
bar.encodings = Some(bar_encoding);
deser_test(&bar);
let indefinite_bytes = bar.to_bytes();
let indefinite_bytes = bar.to_cbor_bytes();
let default_indef_bytes = vec![
vec![MAP_INDEF],
cbor_string("foo"),
cbor_tag(13),
bar.foo.to_bytes(),
bar.foo.to_cbor_bytes(),
vec![0x01u8],
vec![NULL],
vec![0x05u8],
cbor_string("text"),
cbor_string("foo"),
cbor_tag(13),
bar.foo.to_bytes(),
bar.foo.to_cbor_bytes(),
cbor_string("five"),
vec![0x05u8],
].into_iter().flatten().clone().collect::<Vec<u8>>();
let mut bar_canonical: Bar = from_bytes(canonical_bytes.clone()).unwrap();
let mut bar_canonical = Bar::from_cbor_bytes(&canonical_bytes).unwrap();
deser_test(&bar_canonical);
assert_eq!(bar_canonical.encodings.as_ref().unwrap().len_encoding, LenEncoding::Canonical);
assert_eq!(bar_canonical.encodings.as_ref().unwrap().orig_deser_order, vec![2, 3, 0, 4]);
[
cbor_str_sz("foo", str_3.clone()),
cbor_tag_sz(13, def_enc),
bar.foo.to_bytes(),
bar.foo.to_cbor_bytes(),
].into_iter().flatten().copied().collect::<Vec<u8>>(),
if has_5 {
[
irregular_encoding.extend_from_slice(&keys[key_order[i]]);
}
print_cbor_types("irregular_encoding", &irregular_encoding);
let irregular_bar = Bar::from_bytes(irregular_encoding.clone()).unwrap();
print_cbor_types("irregular_bar.to_bytes()", &irregular_bar.to_bytes());
assert_eq!(irregular_bar.to_bytes(), irregular_encoding);
let irregular_bar = Bar::from_cbor_bytes(&irregular_encoding).unwrap();
print_cbor_types("irregular_bar.to_cbor_bytes()", &irregular_bar.to_cbor_bytes());
assert_eq!(irregular_bar.to_cbor_bytes(), irregular_encoding);
}
}
}
vec![0x18, 0x20],
cbor_string("thirty two"),
].into_iter().flatten().clone().collect::<Vec<u8>>();
assert_eq!(orig.to_bytes(), expected);
assert_eq!(orig.to_cbor_bytes(), expected);
let indef_other_order = vec![
vec![MAP_INDEF],
cbor_string("arr2"),
vec![BREAK],
vec![BREAK],
].into_iter().flatten().clone().collect::<Vec<u8>>();
let mut other_order: TableArrMembers = from_bytes(indef_other_order.clone()).unwrap();
assert_eq!(other_order.to_bytes(), indef_other_order);
let mut other_order = TableArrMembers::from_cbor_bytes(&indef_other_order).unwrap();
assert_eq!(other_order.to_cbor_bytes(), indef_other_order);
deser_test(&other_order);
assert!(orig.encodings.is_none());
vec![BREAK],
vec![BREAK],
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular = DeeplyNested::from_bytes(irregular_bytes.clone()).unwrap();
assert_eq!(irregular_bytes, irregular.to_bytes());
let irregular = DeeplyNested::from_cbor_bytes(&irregular_bytes).unwrap();
assert_eq!(irregular_bytes, irregular.to_cbor_bytes());
}
}
}
];
for str_enc in str_24_encodings {
let irregular_bytes = cbor_str_sz("-*=[0123456789ABCDEF]=*-", str_enc);
let irregular = String64::from_bytes(irregular_bytes.clone()).unwrap();
assert_eq!(irregular_bytes, irregular.to_bytes());
let irregular = String64::from_cbor_bytes(&irregular_bytes).unwrap();
assert_eq!(irregular_bytes, irregular.to_cbor_bytes());
}
let _ = String64::from_bytes(cbor_str_sz(&(0..64).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Two))).unwrap();
assert!(String64::from_bytes(cbor_str_sz(&(0..65).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Two))).is_err());
let _ = String64::from_cbor_bytes(&cbor_str_sz(&(0..64).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Two))).unwrap();
assert!(String64::from_cbor_bytes(&cbor_str_sz(&(0..65).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Two))).is_err());
}
#[test]
cbor_tag_sz(7, *def_enc),
cbor_str_sz("-*=[0123456789ABCDEF]=*-", str_enc.clone()),
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular = String1632::from_bytes(irregular_bytes.clone()).unwrap();
assert_eq!(irregular_bytes, irregular.to_bytes());
let irregular = String1632::from_cbor_bytes(&irregular_bytes).unwrap();
assert_eq!(irregular_bytes, irregular.to_cbor_bytes());
}
}
let _ = String1632::from_bytes(vec![
let _ = String1632::from_cbor_bytes(&vec![
cbor_tag_sz(7, Sz::One),
cbor_str_sz(&(0..16).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::One)),
].into_iter().flatten().clone().collect::<Vec<u8>>()).unwrap();
let _ = String1632::from_bytes(vec![
let _ = String1632::from_cbor_bytes(&vec![
cbor_tag_sz(7, Sz::Two),
cbor_str_sz(&(0..32).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Two)),
].into_iter().flatten().clone().collect::<Vec<u8>>()).unwrap();
assert!(String1632::from_bytes(vec![
assert!(String1632::from_cbor_bytes(&vec![
cbor_tag_sz(7, Sz::Inline),
cbor_str_sz(&(0..15).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Inline)),
].into_iter().flatten().clone().collect::<Vec<u8>>()).is_err());
assert!(String1632::from_bytes(vec![
assert!(String1632::from_cbor_bytes(&vec![
cbor_tag_sz(7, Sz::Eight),
cbor_str_sz(&(0..33).map(|_| "?").collect::<String>(), StringLenSz::Len(Sz::Eight)),
].into_iter().flatten().clone().collect::<Vec<u8>>()).is_err());
cbor_int(1, *def_enc),
cbor_int(3, *def_enc),
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular_0 = TypeChoice::from_bytes(irregular_bytes_0.clone()).unwrap();
assert_eq!(irregular_bytes_0, irregular_0.to_bytes());
let irregular_hello_world = TypeChoice::from_bytes(irregular_bytes_hello_world.clone()).unwrap();
assert_eq!(irregular_bytes_hello_world, irregular_hello_world.to_bytes());
let irregular_uint = TypeChoice::from_bytes(irregular_bytes_uint.clone()).unwrap();
assert_eq!(irregular_bytes_uint, irregular_uint.to_bytes());
let irregular_text = TypeChoice::from_bytes(irregular_bytes_text.clone()).unwrap();
assert_eq!(irregular_bytes_text, irregular_text.to_bytes());
let irregular_tagged_arr = TypeChoice::from_bytes(irregular_bytes_tagged_arr.clone()).unwrap();
assert_eq!(irregular_bytes_tagged_arr, irregular_tagged_arr.to_bytes());
let irregular_0 = TypeChoice::from_cbor_bytes(&irregular_bytes_0).unwrap();
assert_eq!(irregular_bytes_0, irregular_0.to_cbor_bytes());
let irregular_hello_world = TypeChoice::from_cbor_bytes(&irregular_bytes_hello_world).unwrap();
assert_eq!(irregular_bytes_hello_world, irregular_hello_world.to_cbor_bytes());
let irregular_uint = TypeChoice::from_cbor_bytes(&irregular_bytes_uint).unwrap();
assert_eq!(irregular_bytes_uint, irregular_uint.to_cbor_bytes());
let irregular_text = TypeChoice::from_cbor_bytes(&irregular_bytes_text).unwrap();
assert_eq!(irregular_bytes_text, irregular_text.to_cbor_bytes());
let irregular_tagged_arr = TypeChoice::from_cbor_bytes(&irregular_bytes_tagged_arr).unwrap();
assert_eq!(irregular_bytes_tagged_arr, irregular_tagged_arr.to_cbor_bytes());
}
}
}
cbor_tag_sz(9, *def_enc),
cbor_str_sz("carrot", str_enc.clone()),
].into_iter().flatten().clone().collect::<Vec<u8>>();
let irregular_3 = GroupChoice::from_bytes(irregular_bytes_3.clone()).unwrap();
assert_eq!(irregular_bytes_3, irregular_3.to_bytes());
let irregular_tagged_2 = GroupChoice::from_bytes(irregular_bytes_tagged_2.clone()).unwrap();
assert_eq!(irregular_bytes_tagged_2, irregular_tagged_2.to_bytes());
let irregular_foo = GroupChoice::from_bytes(irregular_bytes_foo.clone()).unwrap();
mod tests {
use super::*;
fn deser_test<T: Deserialize + ToBytes>(orig: &T) {
print_cbor_types("orig", &orig.to_bytes());
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_bytes()))).unwrap();
print_cbor_types("deser", &deser.to_bytes());
assert_eq!(orig.to_bytes(), deser.to_bytes());
fn deser_test<T: Deserialize + ToCBORBytes>(orig: &T) {
print_cbor_types("orig", &orig.to_cbor_bytes());
let deser = T::deserialize(&mut Deserializer::from(std::io::Cursor::new(orig.to_cbor_bytes()))).unwrap();
print_cbor_types("deser", &deser.to_cbor_bytes());
assert_eq!(orig.to_cbor_bytes(), deser.to_cbor_bytes());
}
#[test]
Adding Milkomeda Open Oracle dev guide