diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index e002ca7ab2..41d7353d2e 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -36,7 +36,7 @@ enum class {{ e.name()|class_name_kt }} { {% else %} {% call kt::unsigned_types_annotation(e) %} -sealed class {{ e.name()|class_name_kt }}{% if e.contains_object_references(ci) %}: Disposable {% endif %} { +sealed class {{ e.name()|class_name_kt }}{% if e.contains_object_references() %}: Disposable {% endif %} { {% for variant in e.variants() -%} {% if !variant.has_fields() -%} object {{ variant.name()|class_name_kt }} : {{ e.name()|class_name_kt }}() @@ -85,14 +85,14 @@ sealed class {{ e.name()|class_name_kt }}{% if e.contains_object_references(ci) }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } - {% if e.contains_object_references(ci) %} + {% if e.contains_object_references() %} @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here override fun destroy() { when(this) { {%- for variant in e.variants() %} is {{ e.name()|class_name_kt }}.{{ variant.name()|class_name_kt }} -> { {% for field in variant.fields() -%} - {%- if ci.type_contains_object_references(field.type_()) -%} + {%- if field.contains_object_references() -%} this.{{ field.name() }}?.destroy() {% endif -%} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt index f057bc7368..ce03e867f6 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt @@ -27,7 +27,7 @@ interface CallStatusErrorHandler { // Error {{ e.name() }} {%- let toplevel_name=e.name()|exception_name_kt %} -sealed class {{ toplevel_name }}: Exception(){% if e.contains_object_references(ci) %}, Disposable {% endif %} { +sealed class {{ toplevel_name }}: Exception(){% if e.contains_object_references() %}, Disposable {% endif %} { // Each variant is a nested class {% for variant in e.variants() -%} {% if !variant.has_fields() -%} @@ -60,14 +60,14 @@ sealed class {{ toplevel_name }}: Exception(){% if e.contains_object_references( } } - {% if e.contains_object_references(ci) %} + {% if e.contains_object_references() %} @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here override fun destroy() { when(this) { {%- for variant in e.variants() %} is {{ e.name()|class_name_kt }}.{{ variant.name()|class_name_kt }} -> { {% for field in variant.fields() -%} - {%- if ci.type_contains_object_references(field.type_()) -%} + {%- if field.contains_object_references() -%} this.{{ field.name() }}?.destroy() {% endif -%} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index 65c1eb1be1..3672e78f1a 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -8,7 +8,7 @@ data class {{ rec.name()|class_name_kt }} ( {%- endmatch -%} {% if !loop.last %}, {% endif %} {%- endfor %} -) {% if rec.contains_object_references(ci) %}: Disposable {% endif %}{ +) {% if rec.contains_object_references() %}: Disposable {% endif %}{ companion object { internal fun lift(rbuf: RustBuffer.ByValue): {{ rec.name()|class_name_kt }} { return liftFromRustBuffer(rbuf) { buf -> {{ rec.name()|class_name_kt }}.read(buf) } @@ -33,11 +33,11 @@ data class {{ rec.name()|class_name_kt }} ( {% endfor %} } - {% if rec.contains_object_references(ci) %} + {% if rec.contains_object_references() %} @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here override fun destroy() { {% for field in rec.fields() %} - {%- if ci.type_contains_object_references(field.type_()) -%} + {%- if field.contains_object_references() -%} this.{{ field.name() }}?.destroy() {% endif -%} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift index ab4c09efb9..a8f2002060 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift @@ -40,6 +40,6 @@ extension {{ e.name()|class_name_swift }}: ViaFfiUsingByteBuffer, ViaFfi { } } -{% if ! e.contains_object_references(ci) %} +{% if ! e.contains_object_references() %} extension {{ e.name()|class_name_swift }}: Equatable, Hashable {} {% endif %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift index bcc093b8ab..889fbff70f 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift @@ -82,7 +82,7 @@ extension {{ e.name()|class_name_swift }}: ViaFfiUsingByteBuffer, ViaFfi { } } -{% if !e.contains_object_references(ci) %} +{% if !e.contains_object_references() %} extension {{ e.name()|class_name_swift }}: Equatable, Hashable {} {% endif %} extension {{ e.name()|class_name_swift }}: Error { } diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index 751a4c965b..75ded6ab44 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -12,7 +12,7 @@ public struct {{ rec.name()|class_name_swift }} { } } -{% if ! rec.contains_object_references(ci) %} +{% if ! rec.contains_object_references() %} extension {{ rec.name()|class_name_swift }}: Equatable, Hashable { public static func ==(lhs: {{ rec.name()|class_name_swift }}, rhs: {{ rec.name()|class_name_swift }}) -> Bool { {%- for field in rec.fields() %} diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 4b230c91fe..5fef70d6eb 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -78,57 +78,76 @@ use anyhow::{bail, Result}; -use super::record::Field; +use super::record::{Field, FieldDescr}; use super::types::Type; -use super::{APIConverter, ComponentInterface}; +use super::{APIConverter, ComponentInterface, CINode}; /// Represents an enum with named variants, each of which may have named /// and typed fields. /// /// Enums are passed across the FFI by serializing to a bytebuffer, with a /// i32 indicating the variant followed by the serialization of each field. +#[derive(Debug)] +pub struct Enum<'a> { + pub(super) parent: &'a ComponentInterface, + pub(super) descr: &'a EnumDescr, +} + #[derive(Debug, Clone, Hash)] -pub struct Enum { +pub struct EnumDescr { pub(super) name: String, - pub(super) variants: Vec, + pub(super) variants: Vec, // "Flat" enums do not have, and will never have, variants with associated data. pub(super) flat: bool, } -impl Enum { +impl<'a> Enum<'a> { pub fn name(&self) -> &str { - &self.name + &self.descr.name } - pub fn variants(&self) -> Vec<&Variant> { - self.variants.iter().collect() + pub fn variants(&self) -> Vec> { + self.descr + .variants + .iter() + .map(|v| Variant { + parent: self, + descr: v, + }) + .collect() } pub fn is_flat(&self) -> bool { - self.flat + self.descr.flat } - pub fn contains_object_references(&self, ci: &ComponentInterface) -> bool { - // *sigh* at the clone here, the relationship between a ComponentInterace - // and its contained types could use a bit of a cleanup. - ci.type_contains_object_references(&Type::Enum(self.name.clone())) + pub fn contains_object_references(&self) -> bool { + self.variants().iter().any(|v| { + v.contains_object_references() + }) } - pub fn contains_unsigned_types(&self, ci: &ComponentInterface) -> bool { - self.variants().iter().any(|v| { - v.fields() + pub fn contains_unsigned_types(&self, _ci: &ComponentInterface) -> bool { + self.descr.variants.iter().any(|v| { + v.fields .iter() - .any(|f| ci.type_contains_unsigned_types(&f.type_)) + .any(|f| self.parent.type_contains_unsigned_types(&f.type_)) }) } } +impl<'a> CINode for Enum<'a> { + fn ci(&self) -> &ComponentInterface { + self.parent + } +} + // Note that we have two `APIConverter` impls here - one for the `enum` case // and one for the `[Enum] interface` case. -impl APIConverter for weedle::EnumDefinition<'_> { - fn convert(&self, _ci: &mut ComponentInterface) -> Result { - Ok(Enum { +impl APIConverter for weedle::EnumDefinition<'_> { + fn convert(&self, _ci: &mut ComponentInterface) -> Result { + Ok(EnumDescr { name: self.identifier.0.to_string(), variants: self .values @@ -136,7 +155,7 @@ impl APIConverter for weedle::EnumDefinition<'_> { .list .iter() .map::, _>(|v| { - Ok(Variant { + Ok(VariantDescr { name: v.0.to_string(), ..Default::default() }) @@ -147,20 +166,20 @@ impl APIConverter for weedle::EnumDefinition<'_> { } } -impl APIConverter for weedle::InterfaceDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { +impl APIConverter for weedle::InterfaceDefinition<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { if self.inheritance.is_some() { bail!("interface inheritence is not supported for enum interfaces"); } // We don't need to check `self.attributes` here; if calling code has dispatched // to this impl then we already know there was an `[Enum]` attribute. - Ok(Enum { + Ok(EnumDescr { name: self.identifier.0.to_string(), variants: self .members .body .iter() - .map::, _>(|member| match member { + .map::, _>(|member| match member { weedle::interface::InterfaceMember::Operation(t) => Ok(t.convert(ci)?), _ => bail!( "interface member type {:?} not supported in enum interface", @@ -176,27 +195,42 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { /// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. + +#[derive(Debug)] +pub struct Variant<'a, Parent: CINode> { + pub(super) parent: &'a Parent, + pub(super) descr: &'a VariantDescr, +} + #[derive(Debug, Clone, Default, Hash)] -pub struct Variant { +pub struct VariantDescr { pub(super) name: String, - pub(super) fields: Vec, + pub(super) fields: Vec, } -impl Variant { +impl<'a, Parent: CINode> Variant<'a, Parent> { pub fn name(&self) -> &str { - &self.name + &self.descr.name } - pub fn fields(&self) -> Vec<&Field> { - self.fields.iter().collect() + pub fn fields(&self) -> Vec> { + self.descr.fields.iter().map(|f| Field { parent: self, descr: f }).collect() } - pub fn has_fields(&self) -> bool { - !self.fields.is_empty() + !self.descr.fields.is_empty() + } + pub fn contains_object_references(&self) -> bool { + self.fields().iter().any(|f| f.contains_object_references()) + } +} + +impl<'a, Parent: CINode + 'a> CINode for Variant<'a, Parent> { + fn ci(&self) -> &ComponentInterface { + self.parent.ci() } } -impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { +impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { if self.special.is_some() { bail!("special operations not supported"); } @@ -219,7 +253,7 @@ impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { _ => bail!("enum interface members must have plain identifers as names"), } }; - Ok(Variant { + Ok(VariantDescr { name, fields: self .args @@ -232,8 +266,8 @@ impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { } } -impl APIConverter for weedle::argument::Argument<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { +impl APIConverter for weedle::argument::Argument<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { match self { weedle::argument::Argument::Single(t) => t.convert(ci), weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"), @@ -241,8 +275,8 @@ impl APIConverter for weedle::argument::Argument<'_> { } } -impl APIConverter for weedle::argument::SingleArgument<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { +impl APIConverter for weedle::argument::SingleArgument<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { let type_ = ci.resolve_type_expression(&self.type_)?; if let Type::Object(_) = type_ { bail!("Objects cannot currently be used in enum variant data"); @@ -255,7 +289,7 @@ impl APIConverter for weedle::argument::SingleArgument<'_> { } // TODO: maybe we should use our own `Field` type here with just name and type, // rather than appropriating record::Field..? - Ok(Field { + Ok(FieldDescr { name: self.identifier.0.to_string(), type_, required: false, diff --git a/uniffi_bindgen/src/interface/error.rs b/uniffi_bindgen/src/interface/error.rs index 1275520742..2ec9c47da7 100644 --- a/uniffi_bindgen/src/interface/error.rs +++ b/uniffi_bindgen/src/interface/error.rs @@ -82,10 +82,8 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` -use anyhow::Result; - -use super::enum_::{Enum, Variant}; -use super::{APIConverter, ComponentInterface}; +use super::enum_::{Enum, EnumDescr, Variant}; +use super::{ComponentInterface, CINode}; /// Represents an Error that might be thrown by functions/methods in the component interface. /// @@ -93,51 +91,50 @@ use super::{APIConverter, ComponentInterface}; /// they're handled in the FFI very differently. We create them in `uniffi::call_with_result()` if /// the wrapped function returns an `Err` value /// struct and assign an integer error code to each variant. -#[derive(Debug, Clone, Hash)] -pub struct Error { - pub name: String, - enum_: Enum, +#[derive(Debug)] +pub struct Error<'a> { + pub(super) parent: &'a ComponentInterface, + pub(super) descr: &'a ErrorDescr, } -impl Error { - pub fn from_enum(enum_: Enum) -> Self { - Self { - name: enum_.name.clone(), - enum_, - } - } +pub type ErrorDescr = EnumDescr; +impl<'a> Error<'a> { pub fn name(&self) -> &str { - &self.name + &self.descr.name } - pub fn wrapped_enum(&self) -> &Enum { - &self.enum_ + pub fn wrapped_enum(&self) -> Enum<'a> { + Enum { + parent: self.parent, + descr: self.descr, + } } - pub fn variants(&self) -> Vec<&Variant> { - self.enum_.variants() + pub fn variants(&self) -> Vec> { + self.descr + .variants + .iter() + .map(|v| Variant { + parent: self, + descr: v, + }) + .collect() } pub fn is_flat(&self) -> bool { - self.enum_.is_flat() + self.wrapped_enum().is_flat() } // For compatibility with the Enum interface - pub fn contains_object_references(&self, ci: &ComponentInterface) -> bool { - self.enum_.contains_object_references(ci) + pub fn contains_object_references(&self) -> bool { + self.wrapped_enum().contains_object_references() } } -impl APIConverter for weedle::EnumDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - Ok(Error::from_enum(APIConverter::::convert(self, ci)?)) - } -} - -impl APIConverter for weedle::InterfaceDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - Ok(Error::from_enum(APIConverter::::convert(self, ci)?)) +impl<'a> CINode for Error<'a> { + fn ci(&self) -> &ComponentInterface { + self.parent } } @@ -196,7 +193,7 @@ mod test { "#; let ci = ComponentInterface::from_webidl(UDL).unwrap(); assert_eq!(ci.iter_error_definitions().len(), 1); - let error: &Error = ci.get_error_definition("Testing").unwrap(); + let error = ci.get_error_definition("Testing").unwrap(); assert_eq!( error .variants() diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 2464530150..e13ce14f16 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -62,8 +62,10 @@ mod callbacks; pub use callbacks::CallbackInterface; mod enum_; pub use enum_::Enum; +use enum_::EnumDescr; mod error; pub use error::Error; +use error::ErrorDescr; mod function; pub use function::{Argument, Function}; mod literal; @@ -73,6 +75,7 @@ pub use namespace::Namespace; mod object; pub use object::{Constructor, Method, Object}; mod record; +use record::RecordDescr; pub use record::{Field, Record}; pub mod ffi; @@ -92,12 +95,12 @@ pub struct ComponentInterface { /// The unique prefix that we'll use for namespacing when exposing this component's API. namespace: String, /// The high-level API provided by the component. - enums: Vec, - records: Vec, + enums: Vec, + records: Vec, functions: Vec, objects: Vec, callback_interfaces: Vec, - errors: Vec, + errors: Vec, } impl<'ci> ComponentInterface { @@ -141,25 +144,34 @@ impl<'ci> ComponentInterface { } /// List the definitions for every Enum type in the interface. - pub fn iter_enum_definitions(&self) -> Vec { - self.enums.to_vec() + pub fn iter_enum_definitions(&self) -> Vec> { + self.enums + .iter() + .map(|e| Enum { + parent: self, + descr: e, + }) + .collect() } /// Get an Enum definition by name, or None if no such Enum is defined. - pub fn get_enum_definition(&self, name: &str) -> Option<&Enum> { + pub fn get_enum_definition(&self, name: &str) -> Option> { // TODO: probably we could store these internally in a HashMap to make this easier? - self.enums.iter().find(|e| e.name == name) + self.enums.iter().find(|e| e.name == name).map(|e| Enum { + parent: self, + descr: e, + }) } /// List the definitions for every Record type in the interface. - pub fn iter_record_definitions(&self) -> Vec { - self.records.to_vec() + pub fn iter_record_definitions(&self) -> Vec> { + self.records.iter().map(|r| Record { parent: self, descr: r }).collect() } /// Get a Record definition by name, or None if no such Record is defined. - pub fn get_record_definition(&self, name: &str) -> Option<&Record> { + pub fn get_record_definition(&self, name: &str) -> Option> { // TODO: probably we could store these internally in a HashMap to make this easier? - self.records.iter().find(|r| r.name == name) + self.records.iter().find(|r| r.name == name).map(|r| Record { parent: self, descr: r }) } /// List the definitions for every Function in the interface. @@ -196,14 +208,23 @@ impl<'ci> ComponentInterface { } /// List the definitions for every Error type in the interface. - pub fn iter_error_definitions(&self) -> Vec { - self.errors.to_vec() + pub fn iter_error_definitions(&self) -> Vec> { + self.errors + .iter() + .map(|e| Error { + parent: self, + descr: e, + }) + .collect() } /// Get an Error definition by name, or None if no such Error is defined. - pub fn get_error_definition(&self, name: &str) -> Option<&Error> { + pub fn get_error_definition(&self, name: &str) -> Option> { // TODO: probably we could store these internally in a HashMap to make this easier? - self.errors.iter().find(|e| e.name == name) + self.errors.iter().find(|e| e.name == name).map(|e| Error { + parent: self, + descr: e, + }) } /// Iterate over all known types in the interface. @@ -228,21 +249,11 @@ impl<'ci> ComponentInterface { } Type::Record(name) => self .get_record_definition(name) - .map(|rec| { - rec.fields() - .iter() - .any(|f| self.type_contains_object_references(&f.type_)) - }) + .map(|rec| rec.contains_object_references()) .unwrap_or(false), Type::Enum(name) => self .get_enum_definition(name) - .map(|e| { - e.variants().iter().any(|v| { - v.fields() - .iter() - .any(|f| self.type_contains_object_references(&f.type_)) - }) - }) + .map(|e| e.contains_object_references()) .unwrap_or(false), _ => false, } @@ -479,15 +490,15 @@ impl<'ci> ComponentInterface { } /// Called by `APIBuilder` impls to add a newly-parsed enum definition to the `ComponentInterface`. - fn add_enum_definition(&mut self, defn: Enum) { + fn add_enum_definition(&mut self, descr: EnumDescr) { // Note that there will be no duplicates thanks to the previous type-finding pass. - self.enums.push(defn); + self.enums.push(descr); } /// Called by `APIBuilder` impls to add a newly-parsed record definition to the `ComponentInterface`. - fn add_record_definition(&mut self, defn: Record) { + fn add_record_definition(&mut self, descr: RecordDescr) { // Note that there will be no duplicates thanks to the previous type-finding pass. - self.records.push(defn); + self.records.push(descr); } /// Called by `APIBuilder` impls to add a newly-parsed function definition to the `ComponentInterface`. @@ -517,9 +528,9 @@ impl<'ci> ComponentInterface { } /// Called by `APIBuilder` impls to add a newly-parsed error definition to the `ComponentInterface`. - fn add_error_definition(&mut self, defn: Error) { + fn add_error_definition(&mut self, descr: ErrorDescr) { // Note that there will be no duplicates thanks to the previous type-finding pass. - self.errors.push(defn); + self.errors.push(descr); } /// Perform global consistency checks on the declared interface. @@ -534,10 +545,14 @@ impl<'ci> ComponentInterface { // To keep codegen tractable, enum variant names must not shadow type names. for e in self.enums.iter() { for variant in e.variants.iter() { - if self.types.get_type_definition(variant.name()).is_some() { + if self + .types + .get_type_definition(variant.name.as_str()) + .is_some() + { bail!( "Enum variant names must not shadow type names: \"{}\"", - variant.name() + variant.name ) } } @@ -589,6 +604,21 @@ impl Hash for ComponentInterface { } } + +/// Trait for managing parent references in `ComponentInterface` members. +/// +/// It's useful for the various members of a `ComponentInterface` to be able +/// to have a pointer back to their containing instance, and for such references +/// to be able to nest. This trait helps with that - any struct that will be a +/// member of a `ComponentInterface` can impl `CINode` in order to act as a +/// parent for other structs. +/// +/// (I'm not sure those docs really capture what's going on here, we'll see...) +pub trait CINode { + fn ci(&self) -> &ComponentInterface; +} + + /// Trait to help build a `ComponentInterface` from WedIDL syntax nodes. /// /// This trait does structural matching on the various weedle AST nodes and diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index c9652c99a1..4d5b32771d 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -48,50 +48,60 @@ use anyhow::{bail, Result}; use super::literal::{convert_default_value, Literal}; use super::types::Type; -use super::{APIConverter, ComponentInterface}; +use super::{APIConverter, ComponentInterface, CINode}; /// Represents a "data class" style object, for passing around complex values. /// /// In the FFI these are represented as a byte buffer, which one side explicitly /// serializes the data into and the other serializes it out of. So I guess they're /// kind of like "pass by clone" values. +#[derive(Debug)] +pub struct Record<'a> { + pub(super) parent: &'a ComponentInterface, + pub(super) descr: &'a RecordDescr, +} + #[derive(Debug, Clone, Hash)] -pub struct Record { +pub struct RecordDescr { pub(super) name: String, - pub(super) fields: Vec, + pub(super) fields: Vec, } -impl Record { +impl<'a> Record<'a> { pub fn name(&self) -> &str { - &self.name + &self.descr.name } - pub fn fields(&self) -> Vec<&Field> { - self.fields.iter().collect() + pub fn fields(&self) -> Vec> { + self.descr.fields.iter().map(|f| Field { parent: self, descr: f }).collect() } - pub fn contains_object_references(&self, ci: &ComponentInterface) -> bool { - // *sigh* at the clone here, the relationship between a ComponentInterace - // and its contained types could use a bit of a cleanup. - ci.type_contains_object_references(&Type::Record(self.name.clone())) + pub fn contains_object_references(&self) -> bool { + self.fields().iter() + .any(|f| f.contains_object_references()) } - pub fn contains_unsigned_types(&self, ci: &ComponentInterface) -> bool { - self.fields() - .iter() - .any(|f| ci.type_contains_unsigned_types(&f.type_)) + pub fn contains_unsigned_types(&self, _ci: &ComponentInterface) -> bool { + self.descr.fields.iter() + .any(|f| self.ci().type_contains_unsigned_types(&f.type_)) } } -impl APIConverter for weedle::DictionaryDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { +impl<'a> CINode for Record<'a> { + fn ci(&self) -> &ComponentInterface { + self.parent + } +} + +impl APIConverter for weedle::DictionaryDefinition<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { if self.attributes.is_some() { bail!("dictionary attributes are not supported yet"); } if self.inheritance.is_some() { bail!("dictionary inheritence is not supported"); } - Ok(Record { + Ok(RecordDescr { name: self.identifier.0.to_string(), fields: self.members.body.convert(ci)?, }) @@ -99,28 +109,46 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { } // Represents an individual field on a Record. +#[derive(Debug)] +pub struct Field<'a, Parent: CINode> { + pub(super) parent: &'a Parent, + pub(super) descr: &'a FieldDescr, +} + #[derive(Debug, Clone, Hash)] -pub struct Field { +pub struct FieldDescr { pub(super) name: String, pub(super) type_: Type, pub(super) required: bool, pub(super) default: Option, } -impl Field { +impl<'a, Parent: CINode + 'a> Field<'a, Parent> { pub fn name(&self) -> &str { - &self.name + &self.descr.name } pub fn type_(&self) -> Type { - self.type_.clone() + self.descr.type_.clone() } pub fn default_value(&self) -> Option { - self.default.clone() + self.descr.default.clone() + } + pub fn required(&self) -> bool { + self.descr.required + } + pub fn contains_object_references(&self) -> bool { + self.ci().type_contains_object_references(&self.descr.type_) + } +} + +impl<'a, Parent: CINode + 'a> CINode for Field<'a, Parent> { + fn ci(&self) -> &ComponentInterface { + self.parent.ci() } } -impl APIConverter for weedle::dictionary::DictionaryMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { +impl APIConverter for weedle::dictionary::DictionaryMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { if self.attributes.is_some() { bail!("dictionary member attributes are not supported yet"); } @@ -132,7 +160,7 @@ impl APIConverter for weedle::dictionary::DictionaryMember<'_> { None => None, Some(v) => Some(convert_default_value(&v.value, &type_)?), }; - Ok(Field { + Ok(FieldDescr { name: self.identifier.0.to_string(), type_, required: self.required.is_some(), @@ -172,7 +200,7 @@ mod test { assert_eq!(record.fields().len(), 1); assert_eq!(record.fields()[0].name(), "field"); assert_eq!(record.fields()[0].type_().canonical_name(), "u32"); - assert!(!record.fields()[0].required); + assert!(!record.fields()[0].required()); assert!(record.fields()[0].default_value().is_none()); let record = ci.get_record_definition("Complex").unwrap(); @@ -183,18 +211,18 @@ mod test { record.fields()[0].type_().canonical_name(), "Optionalstring" ); - assert!(!record.fields()[0].required); + assert!(!record.fields()[0].required()); assert!(record.fields()[0].default_value().is_none()); assert_eq!(record.fields()[1].name(), "value"); assert_eq!(record.fields()[1].type_().canonical_name(), "u32"); - assert!(!record.fields()[1].required); + assert!(!record.fields()[1].required()); assert!(matches!( record.fields()[1].default_value(), Some(Literal::UInt(0, Radix::Decimal, Type::UInt32)) )); assert_eq!(record.fields()[2].name(), "spin"); assert_eq!(record.fields()[2].type_().canonical_name(), "bool"); - assert!(record.fields()[2].required); + assert!(record.fields()[2].required()); assert!(record.fields()[2].default_value().is_none()); }