diff --git a/kaitai-codegen-quote/src/lib.rs b/kaitai-codegen-quote/src/lib.rs index 8901458..3a2bb7f 100644 --- a/kaitai-codegen-quote/src/lib.rs +++ b/kaitai-codegen-quote/src/lib.rs @@ -81,8 +81,7 @@ impl Context<'_> { let doc = spec.doc.as_deref(); let doc_ref = spec.doc_ref.as_ref(); let struct_ty = nc.resolve(key).unwrap(); - let st = - structs::codegen_struct(&nc, doc, doc_ref, &spec.seq, &spec.instances, struct_ty)?; + let st = structs::codegen_struct(&nc, doc, doc_ref, &spec.seq, struct_ty).unwrap(); structs.push(st); } @@ -100,7 +99,6 @@ impl Context<'_> { schema.doc.as_deref(), schema.doc_ref.as_ref(), &schema.seq, - &schema.instances, root_ty, ) .unwrap(); diff --git a/kaitai-codegen-quote/src/parser/mod.rs b/kaitai-codegen-quote/src/parser/mod.rs index 0c19e93..1102256 100644 --- a/kaitai-codegen-quote/src/parser/mod.rs +++ b/kaitai-codegen-quote/src/parser/mod.rs @@ -157,76 +157,67 @@ fn codegen_type_ref_parse( p_endian: &Ident, ) -> TokenStream { let in_parent = false; - if let ResolvedTypeKind::Enum(e, w) = &resolved_ty.kind { - let e = nc.get_enum(e).unwrap(); - let i_enum = &e.ident; - let q_parser = primitive::wk_parser(w, size, p_endian); - return quote!(::nom::combinator::map_res(#q_parser, #i_enum::try_from)); - } match ty { TypeRef::WellKnown(w) => primitive::wk_parser(w, size, p_endian), TypeRef::Named(n) => { let _named_ty = nc.resolve(n).unwrap(); user_type(nc, _named_ty, self_ty.is_root, in_parent, p_endian) } - TypeRef::Dynamic { switch_on, cases } => { - match &resolved_ty.kind { - ResolvedTypeKind::Dynamic(_, _) => { - let id = type_ref_id.unwrap(); - let fg = self_ty.field_generics.get(id).expect(id); - if fg.external { - fg.parser.to_token_stream() - } else { - variant_parser_expr(nc, self_ty.is_root, fg, in_parent, p_endian) - } + TypeRef::Dynamic { switch_on, cases } => match &resolved_ty.kind { + ResolvedTypeKind::Dynamic(_, _) => { + let id = type_ref_id.unwrap(); + let fg = self_ty.field_generics.get(id).expect(id); + if fg.external { + fg.parser.to_token_stream() + } else { + variant_parser_expr(nc, self_ty.is_root, fg, in_parent, p_endian) } - ResolvedTypeKind::Magic => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::User(_) => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::Enum(_, _) => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::SInt { .. } => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::Float { .. } => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::Str { .. } => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::Bytes { .. } => panic!("Not a TypeRef::Dynamic"), - ResolvedTypeKind::UInt { width, .. } => { - let switch_expr = match switch_on { + } + ResolvedTypeKind::Magic => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::User(_) => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::Enum(_, _) => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::SInt { .. } => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::Float { .. } => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::Str { .. } => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::Bytes { .. } => panic!("Not a TypeRef::Dynamic"), + ResolvedTypeKind::UInt { width, .. } => { + let switch_expr = match switch_on { + AnyScalar::Null => todo!(), + AnyScalar::Bool(_) => todo!(), + AnyScalar::String(expr) => expr::codegen_expr_str(expr), + AnyScalar::UInt(_) => todo!(), + }; + let ty = uint_ty(*width); + let mut q_cases = Vec::::new(); + for (k, v) in cases { + let case = match k { + AnyScalar::Bool(b) => quote!(#b), AnyScalar::Null => todo!(), - AnyScalar::Bool(_) => todo!(), - AnyScalar::String(expr) => expr::codegen_expr_str(expr), + AnyScalar::String(_) => todo!(), AnyScalar::UInt(_) => todo!(), }; - let ty = uint_ty(*width); - let mut q_cases = Vec::::new(); - for (k, v) in cases { - let case = match k { - AnyScalar::Bool(b) => quote!(#b), - AnyScalar::Null => todo!(), - AnyScalar::String(_) => todo!(), - AnyScalar::UInt(_) => todo!(), - }; - let parser = match v { - TypeRef::WellKnown(w) => primitive::wk_parser(w, None, p_endian), - TypeRef::Named(_) => todo!(), - TypeRef::Dynamic { .. } => todo!(), - }; - q_cases.push( - quote!(#case => Box::new(::nom::combinator::map(#parser, #ty::from))), - ); - } - let exhaustive = true; - if !exhaustive { - q_cases.push(quote!( _ => Box::new(::nom::combinator::map(|_| todo!(), |x: u8| x as #ty)))); - } - - quote!({ - let __parser: Box ::nom::IResult<&'a[u8], #ty>> = match #switch_expr { - #(#q_cases,)* - }; - __parser - }) - /**/ + let parser = match v { + TypeRef::WellKnown(w) => primitive::wk_parser(w, None, p_endian), + TypeRef::Named(_) => todo!(), + TypeRef::Dynamic { .. } => todo!(), + }; + q_cases.push( + quote!(#case => Box::new(::nom::combinator::map(#parser, #ty::from))), + ); + } + let exhaustive = true; + if !exhaustive { + q_cases.push(quote!( _ => Box::new(::nom::combinator::map(|_| todo!(), |x: u8| x as #ty)))); } + + quote!({ + let __parser: Box ::nom::IResult<&'a[u8], #ty>> = match #switch_expr { + #(#q_cases,)* + }; + __parser + }) } - } + }, } } @@ -240,15 +231,23 @@ fn codegen_attr_parse( ) -> TokenStream { let f_ident = field.ident(); let mut parser = if let Some(ty) = &attr.ty { - codegen_type_ref_parse( - attr.id.as_deref(), - nc, - ty, - attr.size.as_deref(), - self_ty, - field.resolved_ty(), - p_endian, - ) + let size = attr.size.as_deref(); + if let ResolvedTypeKind::Enum(e, w) = &field.resolved_ty().kind { + let e = nc.get_enum(e).unwrap(); + let i_enum = &e.ident; + let q_parser = primitive::wk_parser(w, size, p_endian); + quote!(::nom::combinator::map_res(#q_parser, #i_enum::try_from)) + } else { + codegen_type_ref_parse( + attr.id.as_deref(), + nc, + ty, + size, + self_ty, + field.resolved_ty(), + p_endian, + ) + } } else if let Some(contents) = &attr.contents { let tag = match contents { Contents::String(s) => quote!(#s), diff --git a/kaitai-codegen-quote/src/parser/primitive.rs b/kaitai-codegen-quote/src/parser/primitive.rs index 8d03a7d..10697fa 100644 --- a/kaitai-codegen-quote/src/parser/primitive.rs +++ b/kaitai-codegen-quote/src/parser/primitive.rs @@ -1,4 +1,4 @@ -use kaitai_struct_types::{EndianSpec, IntTypeRef, WellKnownTypeRef}; +use kaitai_struct_types::{EndianSpec, FloatTypeRef, IntTypeRef, WellKnownTypeRef}; use proc_macro2::{Ident, TokenStream}; use quote::quote; @@ -85,15 +85,17 @@ pub fn wk_parser(w: &WellKnownTypeRef, size: Option<&str>, p_endian: &Ident) -> EndianSpec::Big => quote!(::nom::number::complete::be_i64), }, }, - WellKnownTypeRef::F4(e) => match e { - EndianSpec::Implicit => quote!(::nom::number::complete::f32(#p_endian)), - EndianSpec::Little => quote!(::nom::number::complete::le_f32), - EndianSpec::Big => quote!(::nom::number::complete::be_f32), - }, - WellKnownTypeRef::F8(e) => match e { - EndianSpec::Implicit => quote!(::nom::number::complete::f64(#p_endian)), - EndianSpec::Little => quote!(::nom::number::complete::le_f64), - EndianSpec::Big => quote!(::nom::number::complete::be_f64), + WellKnownTypeRef::Float(f) => match f { + FloatTypeRef::Float4(e) => match e { + EndianSpec::Implicit => quote!(::nom::number::complete::f32(#p_endian)), + EndianSpec::Little => quote!(::nom::number::complete::le_f32), + EndianSpec::Big => quote!(::nom::number::complete::be_f32), + }, + FloatTypeRef::Float8(e) => match e { + EndianSpec::Implicit => quote!(::nom::number::complete::f64(#p_endian)), + EndianSpec::Little => quote!(::nom::number::complete::le_f64), + EndianSpec::Big => quote!(::nom::number::complete::be_f64), + }, }, WellKnownTypeRef::Str => { if let Some(size_expr) = size { diff --git a/kaitai-codegen-quote/src/structs.rs b/kaitai-codegen-quote/src/structs.rs index 1ed7ede..298981a 100644 --- a/kaitai-codegen-quote/src/structs.rs +++ b/kaitai-codegen-quote/src/structs.rs @@ -1,10 +1,7 @@ use kaitai_struct_types::{Attribute, StringOrArray}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, ToTokens}; -use std::{ - collections::{BTreeMap, BTreeSet}, - io, -}; +use std::collections::BTreeSet; use crate::{ ctx::NamingContext, @@ -14,6 +11,11 @@ use crate::{ }, }; +#[derive(Debug)] +pub enum Error { + RequiredFieldGenerics, +} + fn codegen_named_ty(nc: &NamingContext, n: &str) -> TokenStream { let ty = nc.resolve(n).unwrap(); let mut q_ty = ty.token_stream(); @@ -66,8 +68,8 @@ fn codegen_cases( // Field name or enum variant discriminant: &Ident, field_generics: Option<&FieldGenerics>, -) -> TokenStream { - let gen = field_generics.unwrap(); +) -> Result { + let gen = field_generics.ok_or(Error::RequiredFieldGenerics)?; let g = &gen.type_; let t = &gen.trait_; if gen.external { @@ -84,7 +86,7 @@ fn codegen_cases( let enclosing_type = gen.var_enum.to_string(); for (i, case_type) in cases.iter().enumerate() { let n = gen.cases[i].ident(); - let inner = codegen_resolved_ty(nc, &case_type.ty, &enclosing_type, tc, n, field_generics); + let inner = codegen_resolved_ty(nc, &case_type.ty, &enclosing_type, tc, n, field_generics)?; enum_cases.push(quote! { #n(#inner), }); @@ -128,11 +130,11 @@ fn codegen_cases( #(#trait_impl_cases)* }); - if gen.external { + Ok(if gen.external { quote!(#g) } else { quote!(#v_use) - } + }) } fn codegen_attr_ty( @@ -140,14 +142,13 @@ fn codegen_attr_ty( self_ty: &Type, field: &Field, tc: &mut TyContext, -) -> io::Result { +) -> Result { let attr_id = field.ident(); let orig_attr_id = field.id(); let fg = self_ty.field_generics.get(orig_attr_id); let resolved = field.resolved_ty(); let enclosing_type = self_ty.rust_struct_name.as_str(); - let ty = codegen_resolved_ty(nc, resolved, enclosing_type, tc, attr_id, fg); - Ok(ty) + codegen_resolved_ty(nc, resolved, enclosing_type, tc, attr_id, fg) } fn codegen_resolved_ty( @@ -157,14 +158,14 @@ fn codegen_resolved_ty( tc: &mut TyContext, attr_id: &Ident, fg: Option<&FieldGenerics>, -) -> TokenStream { - let inner = codegen_resolved_ty_kind(&ty.kind, enclosing_type, nc, tc, attr_id, fg); - match &ty.count { +) -> Result { + let inner = codegen_resolved_ty_kind(&ty.kind, enclosing_type, nc, tc, attr_id, fg)?; + Ok(match &ty.count { ResolvedTypeCount::Required => inner, ResolvedTypeCount::Optional => quote!(Option<#inner>), ResolvedTypeCount::Variable => quote!(Vec<#inner>), ResolvedTypeCount::Fixed(i) => quote!([#inner; #i]), - } + }) } fn codegen_resolved_ty_kind( @@ -174,18 +175,18 @@ fn codegen_resolved_ty_kind( tc: &mut TyContext, attr_id: &Ident, fg: Option<&FieldGenerics>, -) -> TokenStream { +) -> Result { match ty_kind { ResolvedTypeKind::Dynamic(_switch_on, cases) => { codegen_cases(cases, nc, tc, enclosing_type, attr_id, fg) } - ResolvedTypeKind::Magic => quote!(()), - ResolvedTypeKind::User(n) => codegen_named_ty(nc, n), - ResolvedTypeKind::Enum(e, _) => nc.get_enum(e).unwrap().ident.to_token_stream(), - ResolvedTypeKind::UInt { width, .. } => r#type::uint_ty(*width), - ResolvedTypeKind::SInt { width, .. } => r#type::sint_ty(*width), - ResolvedTypeKind::Float { width, .. } => r#type::float_ty(*width), - ResolvedTypeKind::Str { .. } | ResolvedTypeKind::Bytes { .. } => quote!(&'a [u8]), + ResolvedTypeKind::Magic => Ok(quote!(())), + ResolvedTypeKind::User(n) => Ok(codegen_named_ty(nc, n)), + ResolvedTypeKind::Enum(e, _) => Ok(nc.get_enum(e).unwrap().ident.to_token_stream()), + ResolvedTypeKind::UInt { width, .. } => Ok(r#type::uint_ty(*width)), + ResolvedTypeKind::SInt { width, .. } => Ok(r#type::sint_ty(*width)), + ResolvedTypeKind::Float { width, .. } => Ok(r#type::float_ty(*width)), + ResolvedTypeKind::Str { .. } | ResolvedTypeKind::Bytes { .. } => Ok(quote!(&'a [u8])), } } @@ -195,7 +196,7 @@ fn codegen_attr( self_ty: &Type, field: &Field, tc: &mut TyContext, -) -> io::Result { +) -> Result { let attr_doc = doc::doc_attr(attr); let serialize_with = parser::serialize_with(attr); @@ -213,7 +214,7 @@ fn codegen_struct_body( self_ty: &Type, nc: &NamingContext, tc: &mut TyContext, -) -> Result { +) -> Result { if self_ty.is_var_len_str() { return Ok(quote!((pub &'a [u8]);)); } else if self_ty.is_newtype() { @@ -240,14 +241,16 @@ fn codegen_instance_fn( _nc: &NamingContext, _self_ty: &Type, instance: &Field, - _attr: &Attribute, _tc: &mut TyContext, -) -> io::Result { +) -> Result { let id = format_ident!("parse_{}", instance.ident()); - //let ty = codegen_attr_ty(nc, attr, self_ty, instance, tc)?; + let (ty, parser) = match codegen_attr_ty(_nc, _self_ty, instance, _tc) { + Ok(ty) => (ty, quote!(todo!())), + Err(_) => (quote!(()), quote!(todo!())), + }; Ok(quote!( - pub fn #id<'a>(&self, input: &'a [u8]) -> ::nom::IResult<&'a [u8], ()> { - Ok((input, ())) + pub fn #id<'a>(&self, input: &'a [u8]) -> ::nom::IResult<&'a [u8], #ty> { + #parser } )) } @@ -257,9 +260,8 @@ pub(super) fn codegen_struct( doc: Option<&str>, doc_ref: Option<&StringOrArray>, seq: &[Attribute], - instances: &BTreeMap, self_ty: &Type, -) -> io::Result { +) -> Result { let q_doc = doc::doc_struct(self_ty, nc, doc, doc_ref); let needs_lifetime = self_ty.needs_lifetime; @@ -275,8 +277,7 @@ pub(super) fn codegen_struct( let q_parser = parser::codegen_parser_fn(self_ty, seq, &tc, nc); for instance in &self_ty.instances { - let attr = instances.get(instance.id()).unwrap(); - let _fn = codegen_instance_fn(nc, self_ty, instance, attr, &mut tc)?; + let _fn = codegen_instance_fn(nc, self_ty, instance, &mut tc)?; q_impls.push(_fn) } diff --git a/kaitai-codegen-quote/src/type/field.rs b/kaitai-codegen-quote/src/type/field.rs new file mode 100644 index 0000000..10dac78 --- /dev/null +++ b/kaitai-codegen-quote/src/type/field.rs @@ -0,0 +1,38 @@ +use kaitai_struct_types::Attribute; +use proc_macro2::Ident; + +use super::{ident_of, ResolvedType}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Field { + ident: Ident, + id: String, + + resolved_type: ResolvedType, +} + +impl Field { + pub(crate) fn new(orig_attr_id: &str, a: &Attribute) -> Self { + let resolved_type = ResolvedType::of_attribute(a); + Self { + ident: ident_of(orig_attr_id), + id: orig_attr_id.to_owned(), + resolved_type, + } + } + + /// ID for this field + pub fn resolved_ty(&self) -> &ResolvedType { + &self.resolved_type + } + + /// ID for this field + pub fn id(&self) -> &str { + &self.id + } + + /// Rust identifier for this field + pub fn ident(&self) -> &Ident { + &self.ident + } +} diff --git a/kaitai-codegen-quote/src/type/mod.rs b/kaitai-codegen-quote/src/type/mod.rs index 7ca3ef0..9cd7bbe 100644 --- a/kaitai-codegen-quote/src/type/mod.rs +++ b/kaitai-codegen-quote/src/type/mod.rs @@ -6,11 +6,13 @@ use kaitai_struct_types::{Attribute, Endian, KsySchema, TypeRef, TypeSpec, WellK use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +mod field; mod field_generics; mod obligations; mod resolved; pub use self::resolved::{ResolvedType, ResolvedTypeCount, ResolvedTypeKind}; +pub use field::Field; pub use field_generics::FieldGenerics; pub use obligations::ObligationTree; @@ -177,10 +179,10 @@ impl Type { fn push_seq_elem(&mut self, a: &Attribute) { let orig_attr_id = a.id.as_deref().unwrap(); - let f = Field::new(orig_attr_id, ResolvedType::of_attribute(a)); + let f = Field::new(orig_attr_id, a); - self.needs_lifetime = self.needs_lifetime || f.resolved_type.kind.needs_lifetime_a_priori(); - if let ResolvedTypeKind::User(n) = &f.resolved_type.kind { + self.needs_lifetime = self.needs_lifetime || f.resolved_ty().kind.needs_lifetime_a_priori(); + if let ResolvedTypeKind::User(n) = &f.resolved_ty().kind { self.depends_on .entry(n.to_owned()) .or_default() @@ -188,7 +190,7 @@ impl Type { .insert(orig_attr_id.to_owned()); } - if let ResolvedTypeKind::Dynamic(s, cases) = &f.resolved_type.kind { + if let ResolvedTypeKind::Dynamic(s, cases) = &f.resolved_ty().kind { let fg = self.new_field_generics(orig_attr_id, cases, s); self.field_generics.insert(orig_attr_id.to_string(), fg); } @@ -202,14 +204,14 @@ impl Type { } fn push_instances_elem(&mut self, key: &str, value: &Attribute) { - let f = Field::new(key, ResolvedType::of_attribute(value)); - if let ResolvedTypeKind::User(n) = &f.resolved_type.kind { + let f = Field::new(key, value); + if let ResolvedTypeKind::User(n) = &f.resolved_ty().kind { self.may_depend_on .entry(n.to_owned()) .or_default() .fields .insert(key.to_owned()); - } else if let ResolvedTypeKind::Dynamic(_s, cases) = &f.resolved_type.kind { + } else if let ResolvedTypeKind::Dynamic(_s, cases) = &f.resolved_ty().kind { for case in cases { if let ResolvedTypeKind::User(n) = &case.ty.kind { self.may_depend_on @@ -293,39 +295,6 @@ impl Type { } } -#[derive(Debug, Clone, PartialEq)] -pub struct Field { - ident: Ident, - id: String, - - resolved_type: ResolvedType, -} - -impl Field { - pub(crate) fn new(orig_attr_id: &str, resolved_type: ResolvedType) -> Self { - Self { - ident: ident_of(orig_attr_id), - id: orig_attr_id.to_owned(), - resolved_type, - } - } - - /// ID for this field - pub fn resolved_ty(&self) -> &ResolvedType { - &self.resolved_type - } - - /// ID for this field - pub fn id(&self) -> &str { - &self.id - } - - /// Rust identifier for this field - pub fn ident(&self) -> &Ident { - &self.ident - } -} - pub(crate) fn ident_of(id: &str) -> Ident { match id { "type" => format_ident!("r#type"), diff --git a/kaitai-codegen-quote/src/type/resolved.rs b/kaitai-codegen-quote/src/type/resolved.rs index 3532b7f..12a27e2 100644 --- a/kaitai-codegen-quote/src/type/resolved.rs +++ b/kaitai-codegen-quote/src/type/resolved.rs @@ -78,8 +78,10 @@ impl ResolvedTypeKind { width: s.bytes(), endian: s.endian(), }, - WellKnownTypeRef::F4(endian) => ResolvedTypeKind::Float { width: 4, endian }, - WellKnownTypeRef::F8(endian) => ResolvedTypeKind::Float { width: 8, endian }, + WellKnownTypeRef::Float(f) => ResolvedTypeKind::Float { + width: f.bytes(), + endian: f.endian(), + }, WellKnownTypeRef::Str => ResolvedTypeKind::Str { encoding: (), zero_terminator: false, diff --git a/kaitai-struct-types/src/attribute.rs b/kaitai-struct-types/src/attribute.rs new file mode 100644 index 0000000..3a5468f --- /dev/null +++ b/kaitai-struct-types/src/attribute.rs @@ -0,0 +1,78 @@ +use serde::Deserialize; + +use crate::{StringOrArray, TypeRef}; + +/// An attribute in a type schema +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Attribute { + /// Unique Identifier within this struct + /// + /// Attributes with the same name are merged + #[serde(default)] + pub id: Option, + /// Used to give a more detailed description of a user-defined type. + #[serde(default)] + pub doc: Option, + /// Reference to external documentation + #[serde(default)] + pub doc_ref: Option, + + /// The type of this attribute + #[serde(rename = "type")] + pub ty: Option, + + /// The size of this attribute (if present, creates a substream) + #[serde(default)] + pub size: Option, + + /// The size of this attribute (if present, creates a substream) + #[serde(default, rename = "if")] + pub if_expr: Option, + + /// Whether this attribute is repeated + #[serde(default)] + pub repeat: Option, + + /// If [Repeat::Expr], the expression to repeat + #[serde(default)] + pub repeat_expr: Option, + + /// If [Repeat::Expr], the expression to check for repetition end + #[serde(default)] + pub repeat_until: Option, + + /// Fixed expected contents + #[serde(default)] + pub contents: Option, + + /// String encoding + #[serde(default)] + pub encoding: Option, + + /// Name of an enumeration + #[serde(default)] + pub r#enum: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(untagged)] +/// A string or an array +pub enum Contents { + /// A String + String(String), + /// An array of strings + Bytes(Vec), +} + +/// Kind of repetition +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub enum Repeat { + /// Based on an expression + Expr, + /// Repeat until the end of the stream + Eos, + /// Repeat until a token is reached + Until, +} diff --git a/kaitai-struct-types/src/enum_spec.rs b/kaitai-struct-types/src/enum_spec.rs new file mode 100644 index 0000000..9dc0afc --- /dev/null +++ b/kaitai-struct-types/src/enum_spec.rs @@ -0,0 +1,82 @@ +use std::{collections::BTreeMap, fmt}; + +use serde::Deserialize; + +use crate::Identifier; + +/// An enum specification +#[derive(Deserialize, Debug)] +pub struct EnumSpec(pub BTreeMap); + +/// An enum value specification +#[derive(Debug)] +pub struct EnumValueSpec { + /// Identifier + pub id: Identifier, +} + +impl<'de> serde::Deserialize<'de> for EnumValueSpec { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct __Visitor; + + impl<'de> serde::de::Visitor<'de> for __Visitor { + type Value = EnumValueSpec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "string or object") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(EnumValueSpec { + id: Identifier(v.to_string()), + }) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(EnumValueSpec { id: Identifier(v) }) + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Ok(EnumValueSpec { + id: Identifier(match v { + true => "true".to_string(), + false => "false".to_string(), + }), + }) + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut id = None; + while let Some(key) = map.next_key::<&str>()? { + match key { + "id" => { + id = Some(map.next_value()?); + } + // FIXME: doc, doc-ref + _ => { + map.next_value::()?; + } + } + } + Ok(EnumValueSpec { id: id.unwrap() }) + } + } + + deserializer.deserialize_any(__Visitor) + } +} diff --git a/kaitai-struct-types/src/ksy_schema.rs b/kaitai-struct-types/src/ksy_schema.rs new file mode 100644 index 0000000..2ff82a3 --- /dev/null +++ b/kaitai-struct-types/src/ksy_schema.rs @@ -0,0 +1,34 @@ +use std::collections::BTreeMap; + +use serde::Deserialize; + +use crate::{Attribute, EnumSpec, MetaSpec, ParamSpec, StringOrArray, TypeSpec}; + +/// Definition of a type or file format +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct KsySchema { + /// Metadata + pub meta: MetaSpec, + /// Used to give a more detailed description of a user-defined type. + #[serde(default)] + pub doc: Option, + /// Reference to external documentation + #[serde(default)] + pub doc_ref: Option, + /// Generic Parameters + #[serde(default)] + pub params: Vec, + /// The sequence of attributes + #[serde(default)] + pub seq: Vec, + /// Named, dynamic attributes + #[serde(default)] + pub instances: BTreeMap, + /// A sequence of internal types + #[serde(default)] + pub types: BTreeMap, + /// Enums defined by this schema + #[serde(default)] + pub enums: BTreeMap, +} diff --git a/kaitai-struct-types/src/lib.rs b/kaitai-struct-types/src/lib.rs index 2cf1b1a..838c08b 100644 --- a/kaitai-struct-types/src/lib.rs +++ b/kaitai-struct-types/src/lib.rs @@ -1,540 +1,21 @@ #![warn(missing_docs)] //! # Base Types for Kaitai -use std::{collections::BTreeMap, fmt, str::FromStr}; - -use serde::Deserialize; - -/// Definition of a type or file format -#[derive(Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct KsySchema { - /// Metadata - pub meta: MetaSpec, - /// Used to give a more detailed description of a user-defined type. - #[serde(default)] - pub doc: Option, - /// Reference to external documentation - #[serde(default)] - pub doc_ref: Option, - /// Generic Parameters - #[serde(default)] - pub params: Vec, - /// The sequence of attributes - #[serde(default)] - pub seq: Vec, - /// Named, dynamic attributes - #[serde(default)] - pub instances: BTreeMap, - /// A sequence of internal types - #[serde(default)] - pub types: BTreeMap, - /// Enums defined by this schema - #[serde(default)] - pub enums: BTreeMap, -} - -/// Specification for a type -#[derive(Deserialize, Debug)] -pub struct TypeSpec { - /// Metadata - #[serde(default)] - pub meta: Option, - /// Used to give a more detailed description of a user-defined type. - #[serde(default)] - pub doc: Option, - /// Reference to external documentation - #[serde(default)] - pub doc_ref: Option, - /// Generic Parameters - #[serde(default)] - pub params: Vec, - /// The sequence of attributes - #[serde(default)] - pub seq: Vec, - /// Named, dynamic attributes - #[serde(default)] - pub instances: BTreeMap, - /// A sequence of internal types - #[serde(default)] - pub types: BTreeMap, - /// Enums defined by this schema - #[serde(default)] - pub enums: BTreeMap, -} - -/// An enum specification -#[derive(Deserialize, Debug)] -pub struct EnumSpec(pub BTreeMap); - -/// An enum value specification -#[derive(Debug)] -pub struct EnumValueSpec { - /// Identifier - pub id: Identifier, -} - -impl<'de> serde::Deserialize<'de> for EnumValueSpec { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct __Visitor; - - impl<'de> serde::de::Visitor<'de> for __Visitor { - type Value = EnumValueSpec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "string or object") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(EnumValueSpec { - id: Identifier(v.to_string()), - }) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(EnumValueSpec { id: Identifier(v) }) - } - - fn visit_bool(self, v: bool) -> Result - where - E: serde::de::Error, - { - Ok(EnumValueSpec { - id: Identifier(match v { - true => "true".to_string(), - false => "false".to_string(), - }), - }) - } - - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut id = None; - while let Some(key) = map.next_key::<&str>()? { - match key { - "id" => { - id = Some(map.next_value()?); - } - // FIXME: doc, doc-ref - _ => { - map.next_value::()?; - } - } - } - Ok(EnumValueSpec { id: id.unwrap() }) - } - } - - deserializer.deserialize_any(__Visitor) - } -} - -#[derive(Deserialize, Debug)] -/// Generic Paramter -pub struct ParamSpec { - // TODO -} - -/// An attribute in a type schema -#[derive(Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct Attribute { - /// Unique Identifier within this struct - /// - /// Attributes with the same name are merged - #[serde(default)] - pub id: Option, - /// Used to give a more detailed description of a user-defined type. - #[serde(default)] - pub doc: Option, - /// Reference to external documentation - #[serde(default)] - pub doc_ref: Option, - - /// The type of this attribute - #[serde(rename = "type")] - pub ty: Option, - - /// The size of this attribute (if present, creates a substream) - #[serde(default)] - pub size: Option, - - /// The size of this attribute (if present, creates a substream) - #[serde(default, rename = "if")] - pub if_expr: Option, - - /// Whether this attribute is repeated - #[serde(default)] - pub repeat: Option, - - /// If [Repeat::Expr], the expression to repeat - #[serde(default)] - pub repeat_expr: Option, - - /// If [Repeat::Expr], the expression to check for repetition end - #[serde(default)] - pub repeat_until: Option, - - /// Fixed expected contents - #[serde(default)] - pub contents: Option, - - /// String encoding - #[serde(default)] - pub encoding: Option, - - /// Name of an enumeration - #[serde(default)] - pub r#enum: Option, -} - -#[derive(Deserialize, Debug)] -#[serde(untagged)] -/// A string or an array -pub enum Contents { - /// A String - String(String), - /// An array of strings - Bytes(Vec), -} - -/// Kind of repetition -#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)] -#[serde(rename_all = "kebab-case")] -pub enum Repeat { - /// Based on an expression - Expr, - /// Repeat until the end of the stream - Eos, - /// Repeat until a token is reached - Until, -} - -#[derive(Deserialize, Debug)] -/// An identifier -pub struct Identifier(pub String); - -impl fmt::Display for Identifier { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[derive(Deserialize, Debug)] -#[serde(untagged)] -/// A string or an array -pub enum StringOrArray { - /// A String - String(String), - /// An array of strings - Array(Vec), -} - -impl StringOrArray { - /// Turn this into a slice of string - pub fn as_slice(&self) -> &[String] { - match self { - StringOrArray::String(s) => std::slice::from_ref(s), - StringOrArray::Array(s) => s, - } - } -} - -#[derive(Default, Deserialize, Debug)] -/// Cross-References -pub struct XRef {} - -/// Metadata for a type -#[derive(Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct MetaSpec { - /// The ID for this format - pub id: Identifier, - /// The name of the schema - #[serde(default)] - pub title: Option, - /// The application the schema comes from - #[serde(default)] - pub application: Option, - /// The file extensions used for this schema - #[serde(default)] - pub file_extension: Option, - /// Cross References - #[serde(default)] - pub xref: Option, - /// License SPDX Identifier - #[serde(default)] - pub license: Option, - /// Version of Kaitai Struct - #[serde(default)] - pub ks_version: Option, - /// Whether to enable debug in KSC - #[serde(default)] - pub ks_debug: bool, - /// Whether to enable opaque types - #[serde(default)] - pub ks_opaque_types: bool, - /// KSY files that this schema depends on - #[serde(default)] - pub imports: Vec, - /// The default encoding used for strings - #[serde(default)] - pub encoding: Option, - /// The default endianness - #[serde(default)] - pub endian: Option, -} - -#[derive(Deserialize, Debug, Copy, Clone)] -/// Endianness qualifier -pub enum Endian { - /// Least Significant Byte first - #[serde(rename = "le")] - LittleEndian, - /// Most significant Byte first, aka "Network Byte Order" - #[serde(rename = "be")] - BigEndian, -} - -/// Any scalar YAML value -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum AnyScalar { - /// null - Null, - /// Boolean - Bool(bool), - /// string - String(String), - /// integer - UInt(u64), -} - -struct AnyScalarVisitor; - -impl<'de> serde::de::Visitor<'de> for AnyScalarVisitor { - type Value = AnyScalar; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "any scalar") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(AnyScalar::String(v.to_string())) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(AnyScalar::String(v)) - } - - fn visit_bool(self, v: bool) -> Result - where - E: serde::de::Error, - { - Ok(AnyScalar::Bool(v)) - } - - fn visit_u64(self, v: u64) -> Result - where - E: serde::de::Error, - { - Ok(AnyScalar::UInt(v)) - } -} - -impl<'de> Deserialize<'de> for AnyScalar { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_any(AnyScalarVisitor) - } -} - -/// Reference to a Type -#[derive(Debug, Clone, PartialEq)] -pub enum TypeRef { - /// A well-known type - WellKnown(WellKnownTypeRef), - /// A named type - Named(String), - /// A type that depends on an expression - Dynamic { - /// Expression to switch on - switch_on: AnyScalar, - /// Types depending on the value of the expression - cases: BTreeMap, - }, -} - -struct TypeRefVisitor; - -impl<'de> serde::de::Visitor<'de> for TypeRefVisitor { - type Value = TypeRef; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "string or {{ switch-on, cases }}") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - match WellKnownTypeRef::from_str(v) { - Ok(w) => Ok(TypeRef::WellKnown(w)), - Err(()) => Ok(TypeRef::Named(v.to_string())), - } - } - - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut switch_on = None; - let mut cases = None; - - while let Some(key) = map.next_key::<&str>()? { - match key { - "switch-on" => { - switch_on = Some(map.next_value()?); - } - "cases" => { - cases = Some(map.next_value()?); - } - _ => return Err(serde::de::Error::custom("Invalid field")), - } - } - - Ok(TypeRef::Dynamic { - switch_on: switch_on.unwrap(), - cases: cases.unwrap(), - }) - } -} - -impl<'de> Deserialize<'de> for TypeRef { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_any(TypeRefVisitor) - } -} - -/// Endian Specification -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum EndianSpec { - /// Unspecified - Implicit, - /// Always Little Endian - Little, - /// Always Big Endian - Big, -} - -/// Integer byte width specification -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum IntTypeRef { - /// 8 bit integer - Int1, - /// 16 bit integer - Int2(EndianSpec), - /// 32 bit integer - Int4(EndianSpec), - /// 64 bit integer - Int8(EndianSpec), -} - -impl IntTypeRef { - /// Get the number of bytes for values of this type - pub fn bytes(&self) -> usize { - match self { - IntTypeRef::Int1 => 1, - IntTypeRef::Int2(_) => 2, - IntTypeRef::Int4(_) => 4, - IntTypeRef::Int8(_) => 8, - } - } - - /// Get the endian spec for this int type ref - pub fn endian(&self) -> EndianSpec { - match self { - IntTypeRef::Int1 => EndianSpec::Implicit, - IntTypeRef::Int2(e) | IntTypeRef::Int4(e) | IntTypeRef::Int8(e) => *e, - } - } -} - -/// Well known [TypeRef]s -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum WellKnownTypeRef { - /// unsigned integer - Unsigned(IntTypeRef), - /// signed integer - Signed(IntTypeRef), - /// 32 bit IEEE floating point number (single precision) - F4(EndianSpec), - /// 64 bit IEEE floating point number (double precision) - F8(EndianSpec), - /// String - Str, - /// Nul-Terminated String - StrZ, -} - -impl FromStr for WellKnownTypeRef { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "u1" => Self::Unsigned(IntTypeRef::Int1), - "u2" => Self::Unsigned(IntTypeRef::Int2(EndianSpec::Implicit)), - "u2le" => Self::Unsigned(IntTypeRef::Int2(EndianSpec::Little)), - "u2be" => Self::Unsigned(IntTypeRef::Int2(EndianSpec::Big)), - "u4" => Self::Unsigned(IntTypeRef::Int4(EndianSpec::Implicit)), - "u4le" => Self::Unsigned(IntTypeRef::Int4(EndianSpec::Little)), - "u4be" => Self::Unsigned(IntTypeRef::Int4(EndianSpec::Big)), - "u8" => Self::Unsigned(IntTypeRef::Int8(EndianSpec::Implicit)), - "u8le" => Self::Unsigned(IntTypeRef::Int8(EndianSpec::Little)), - "u8be" => Self::Unsigned(IntTypeRef::Int8(EndianSpec::Big)), - - "s1" => Self::Signed(IntTypeRef::Int1), - "s2" => Self::Signed(IntTypeRef::Int2(EndianSpec::Implicit)), - "s2le" => Self::Signed(IntTypeRef::Int2(EndianSpec::Little)), - "s2be" => Self::Signed(IntTypeRef::Int2(EndianSpec::Big)), - "s4" => Self::Signed(IntTypeRef::Int4(EndianSpec::Implicit)), - "s4le" => Self::Signed(IntTypeRef::Int4(EndianSpec::Little)), - "s4be" => Self::Signed(IntTypeRef::Int4(EndianSpec::Big)), - "s8" => Self::Signed(IntTypeRef::Int8(EndianSpec::Implicit)), - "s8le" => Self::Signed(IntTypeRef::Int8(EndianSpec::Little)), - "s8be" => Self::Signed(IntTypeRef::Int8(EndianSpec::Big)), - - "f4" => Self::F4(EndianSpec::Implicit), - "f4le" => Self::F4(EndianSpec::Little), - "f4be" => Self::F4(EndianSpec::Big), - "f8" => Self::F8(EndianSpec::Implicit), - "f8le" => Self::F8(EndianSpec::Little), - "f8be" => Self::F8(EndianSpec::Big), - - "str" => Self::Str, - "strz" => Self::StrZ, - _ => return Err(()), - }) - } -} +mod attribute; +mod enum_spec; +mod ksy_schema; +mod meta_spec; +mod param_spec; +mod scalar; +mod type_ref; +mod type_spec; +pub use { + attribute::{Attribute, Contents, Repeat}, + enum_spec::{EnumSpec, EnumValueSpec}, + ksy_schema::KsySchema, + meta_spec::{Endian, MetaSpec, XRef}, + param_spec::ParamSpec, + scalar::{AnyScalar, Identifier, StringOrArray}, + type_ref::{EndianSpec, FloatTypeRef, IntTypeRef, TypeRef, WellKnownTypeRef}, + type_spec::TypeSpec, +}; diff --git a/kaitai-struct-types/src/meta_spec.rs b/kaitai-struct-types/src/meta_spec.rs new file mode 100644 index 0000000..f14cebb --- /dev/null +++ b/kaitai-struct-types/src/meta_spec.rs @@ -0,0 +1,59 @@ +use serde::Deserialize; + +use crate::{Identifier, StringOrArray}; + +#[derive(Default, Deserialize, Debug)] +/// Cross-References +pub struct XRef {} + +/// Metadata for a type +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct MetaSpec { + /// The ID for this format + pub id: Identifier, + /// The name of the schema + #[serde(default)] + pub title: Option, + /// The application the schema comes from + #[serde(default)] + pub application: Option, + /// The file extensions used for this schema + #[serde(default)] + pub file_extension: Option, + /// Cross References + #[serde(default)] + pub xref: Option, + /// License SPDX Identifier + #[serde(default)] + pub license: Option, + /// Version of Kaitai Struct + #[serde(default)] + pub ks_version: Option, + /// Whether to enable debug in KSC + #[serde(default)] + pub ks_debug: bool, + /// Whether to enable opaque types + #[serde(default)] + pub ks_opaque_types: bool, + /// KSY files that this schema depends on + #[serde(default)] + pub imports: Vec, + /// The default encoding used for strings + #[serde(default)] + pub encoding: Option, + /// The default endianness + #[serde(default)] + pub endian: Option, +} + +#[derive(Deserialize, Debug, Copy, Clone)] +/// Endianness qualifier +pub enum Endian { + /// Least Significant Byte first + #[serde(rename = "le")] + LittleEndian, + /// Most significant Byte first, aka "Network Byte Order" + #[serde(rename = "be")] + BigEndian, +} diff --git a/kaitai-struct-types/src/param_spec.rs b/kaitai-struct-types/src/param_spec.rs new file mode 100644 index 0000000..cad8534 --- /dev/null +++ b/kaitai-struct-types/src/param_spec.rs @@ -0,0 +1,7 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug)] +/// Generic Paramter +pub struct ParamSpec { + // TODO +} diff --git a/kaitai-struct-types/src/scalar.rs b/kaitai-struct-types/src/scalar.rs new file mode 100644 index 0000000..be68832 --- /dev/null +++ b/kaitai-struct-types/src/scalar.rs @@ -0,0 +1,92 @@ +use serde::Deserialize; +use std::fmt; + +#[derive(Deserialize, Debug)] +/// An identifier +pub struct Identifier(pub String); + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[derive(Deserialize, Debug)] +#[serde(untagged)] +/// A string or an array +pub enum StringOrArray { + /// A String + String(String), + /// An array of strings + Array(Vec), +} + +impl StringOrArray { + /// Turn this into a slice of string + pub fn as_slice(&self) -> &[String] { + match self { + StringOrArray::String(s) => std::slice::from_ref(s), + StringOrArray::Array(s) => s, + } + } +} + +/// Any scalar YAML value +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum AnyScalar { + /// null + Null, + /// Boolean + Bool(bool), + /// string + String(String), + /// integer + UInt(u64), +} + +struct AnyScalarVisitor; + +impl<'de> serde::de::Visitor<'de> for AnyScalarVisitor { + type Value = AnyScalar; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "any scalar") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(AnyScalar::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(AnyScalar::String(v)) + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Ok(AnyScalar::Bool(v)) + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + Ok(AnyScalar::UInt(v)) + } +} + +impl<'de> Deserialize<'de> for AnyScalar { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(AnyScalarVisitor) + } +} diff --git a/kaitai-struct-types/src/type_ref.rs b/kaitai-struct-types/src/type_ref.rs new file mode 100644 index 0000000..3452971 --- /dev/null +++ b/kaitai-struct-types/src/type_ref.rs @@ -0,0 +1,201 @@ +use std::{collections::BTreeMap, fmt, str::FromStr}; + +use serde::Deserialize; + +use crate::AnyScalar; + +/// Reference to a Type +#[derive(Debug, Clone, PartialEq)] +pub enum TypeRef { + /// A well-known type + WellKnown(WellKnownTypeRef), + /// A named type + Named(String), + /// A type that depends on an expression + Dynamic { + /// Expression to switch on + switch_on: AnyScalar, + /// Types depending on the value of the expression + cases: BTreeMap, + }, +} + +struct TypeRefVisitor; + +impl<'de> serde::de::Visitor<'de> for TypeRefVisitor { + type Value = TypeRef; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "string or {{ switch-on, cases }}") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match WellKnownTypeRef::from_str(v) { + Ok(w) => Ok(TypeRef::WellKnown(w)), + Err(()) => Ok(TypeRef::Named(v.to_string())), + } + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut switch_on = None; + let mut cases = None; + + while let Some(key) = map.next_key::<&str>()? { + match key { + "switch-on" => { + switch_on = Some(map.next_value()?); + } + "cases" => { + cases = Some(map.next_value()?); + } + _ => return Err(serde::de::Error::custom("Invalid field")), + } + } + + Ok(TypeRef::Dynamic { + switch_on: switch_on.unwrap(), + cases: cases.unwrap(), + }) + } +} + +impl<'de> Deserialize<'de> for TypeRef { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(TypeRefVisitor) + } +} + +/// Endian Specification +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum EndianSpec { + /// Unspecified + Implicit, + /// Always Little Endian + Little, + /// Always Big Endian + Big, +} + +/// Integer byte width specification +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum IntTypeRef { + /// 8 bit integer + Int1, + /// 16 bit integer + Int2(EndianSpec), + /// 32 bit integer + Int4(EndianSpec), + /// 64 bit integer + Int8(EndianSpec), +} + +impl IntTypeRef { + /// Get the number of bytes for values of this type + pub fn bytes(&self) -> usize { + match self { + IntTypeRef::Int1 => 1, + IntTypeRef::Int2(_) => 2, + IntTypeRef::Int4(_) => 4, + IntTypeRef::Int8(_) => 8, + } + } + + /// Get the endian spec for this int type ref + pub fn endian(&self) -> EndianSpec { + match self { + IntTypeRef::Int1 => EndianSpec::Implicit, + IntTypeRef::Int2(e) | IntTypeRef::Int4(e) | IntTypeRef::Int8(e) => *e, + } + } +} + +/// Float byte width specification +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FloatTypeRef { + /// 32 bit IEEE floating point number (single precision) + Float4(EndianSpec), + /// 64 bit IEEE floating point number (double precision) + Float8(EndianSpec), +} + +impl FloatTypeRef { + /// Get the number of bytes + pub fn bytes(&self) -> usize { + match self { + FloatTypeRef::Float4(_) => 4, + FloatTypeRef::Float8(_) => 8, + } + } + + /// Get the endian spec for this float type ref + pub fn endian(&self) -> EndianSpec { + match self { + FloatTypeRef::Float4(e) | FloatTypeRef::Float8(e) => *e, + } + } +} + +/// Well known [TypeRef]s +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum WellKnownTypeRef { + /// unsigned integer + Unsigned(IntTypeRef), + /// signed integer + Signed(IntTypeRef), + /// floating point number + Float(FloatTypeRef), + /// String + Str, + /// Nul-Terminated String + StrZ, +} + +impl FromStr for WellKnownTypeRef { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "u1" => Self::Unsigned(IntTypeRef::Int1), + "u2" => Self::Unsigned(IntTypeRef::Int2(EndianSpec::Implicit)), + "u2le" => Self::Unsigned(IntTypeRef::Int2(EndianSpec::Little)), + "u2be" => Self::Unsigned(IntTypeRef::Int2(EndianSpec::Big)), + "u4" => Self::Unsigned(IntTypeRef::Int4(EndianSpec::Implicit)), + "u4le" => Self::Unsigned(IntTypeRef::Int4(EndianSpec::Little)), + "u4be" => Self::Unsigned(IntTypeRef::Int4(EndianSpec::Big)), + "u8" => Self::Unsigned(IntTypeRef::Int8(EndianSpec::Implicit)), + "u8le" => Self::Unsigned(IntTypeRef::Int8(EndianSpec::Little)), + "u8be" => Self::Unsigned(IntTypeRef::Int8(EndianSpec::Big)), + + "s1" => Self::Signed(IntTypeRef::Int1), + "s2" => Self::Signed(IntTypeRef::Int2(EndianSpec::Implicit)), + "s2le" => Self::Signed(IntTypeRef::Int2(EndianSpec::Little)), + "s2be" => Self::Signed(IntTypeRef::Int2(EndianSpec::Big)), + "s4" => Self::Signed(IntTypeRef::Int4(EndianSpec::Implicit)), + "s4le" => Self::Signed(IntTypeRef::Int4(EndianSpec::Little)), + "s4be" => Self::Signed(IntTypeRef::Int4(EndianSpec::Big)), + "s8" => Self::Signed(IntTypeRef::Int8(EndianSpec::Implicit)), + "s8le" => Self::Signed(IntTypeRef::Int8(EndianSpec::Little)), + "s8be" => Self::Signed(IntTypeRef::Int8(EndianSpec::Big)), + + "f4" => Self::Float(FloatTypeRef::Float4(EndianSpec::Implicit)), + "f4le" => Self::Float(FloatTypeRef::Float4(EndianSpec::Little)), + "f4be" => Self::Float(FloatTypeRef::Float4(EndianSpec::Big)), + "f8" => Self::Float(FloatTypeRef::Float8(EndianSpec::Implicit)), + "f8le" => Self::Float(FloatTypeRef::Float8(EndianSpec::Little)), + "f8be" => Self::Float(FloatTypeRef::Float8(EndianSpec::Big)), + + "str" => Self::Str, + "strz" => Self::StrZ, + _ => return Err(()), + }) + } +} diff --git a/kaitai-struct-types/src/type_spec.rs b/kaitai-struct-types/src/type_spec.rs new file mode 100644 index 0000000..56181ff --- /dev/null +++ b/kaitai-struct-types/src/type_spec.rs @@ -0,0 +1,34 @@ +use std::collections::BTreeMap; + +use serde::Deserialize; + +use crate::{Attribute, EnumSpec, MetaSpec, ParamSpec, StringOrArray}; + +/// Specification for a type +#[derive(Deserialize, Debug)] +pub struct TypeSpec { + /// Metadata + #[serde(default)] + pub meta: Option, + /// Used to give a more detailed description of a user-defined type. + #[serde(default)] + pub doc: Option, + /// Reference to external documentation + #[serde(default)] + pub doc_ref: Option, + /// Generic Parameters + #[serde(default)] + pub params: Vec, + /// The sequence of attributes + #[serde(default)] + pub seq: Vec, + /// Named, dynamic attributes + #[serde(default)] + pub instances: BTreeMap, + /// A sequence of internal types + #[serde(default)] + pub types: BTreeMap, + /// Enums defined by this schema + #[serde(default)] + pub enums: BTreeMap, +}