-
Notifications
You must be signed in to change notification settings - Fork 232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP give ComponentInterface
members a pointer to their parent.
#1003
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,65 +78,84 @@ | |
|
||
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, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the |
||
|
||
#[derive(Debug, Clone, Hash)] | ||
pub struct Enum { | ||
pub struct EnumDescr { | ||
pub(super) name: String, | ||
pub(super) variants: Vec<Variant>, | ||
pub(super) variants: Vec<VariantDescr>, | ||
// "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<Variant<'_, Self>> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, when consuming code lists the variants of the enum, so see that we create a bunch of |
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey look, no more clone! |
||
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason to keep |
||
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<Enum> for weedle::EnumDefinition<'_> { | ||
fn convert(&self, _ci: &mut ComponentInterface) -> Result<Enum> { | ||
Ok(Enum { | ||
impl APIConverter<EnumDescr> for weedle::EnumDefinition<'_> { | ||
fn convert(&self, _ci: &mut ComponentInterface) -> Result<EnumDescr> { | ||
Ok(EnumDescr { | ||
name: self.identifier.0.to_string(), | ||
variants: self | ||
.values | ||
.body | ||
.list | ||
.iter() | ||
.map::<Result<_>, _>(|v| { | ||
Ok(Variant { | ||
Ok(VariantDescr { | ||
name: v.0.to_string(), | ||
..Default::default() | ||
}) | ||
|
@@ -147,20 +166,20 @@ impl APIConverter<Enum> for weedle::EnumDefinition<'_> { | |
} | ||
} | ||
|
||
impl APIConverter<Enum> for weedle::InterfaceDefinition<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<Enum> { | ||
impl APIConverter<EnumDescr> for weedle::InterfaceDefinition<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<EnumDescr> { | ||
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::<Result<Variant>, _>(|member| match member { | ||
.map::<Result<VariantDescr>, _>(|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<Enum> 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this starts to get a bit complicated...the An alternative could be to have it point directly to the containing |
||
pub(super) descr: &'a VariantDescr, | ||
} | ||
|
||
#[derive(Debug, Clone, Default, Hash)] | ||
pub struct Variant { | ||
pub struct VariantDescr { | ||
pub(super) name: String, | ||
pub(super) fields: Vec<Field>, | ||
pub(super) fields: Vec<FieldDescr>, | ||
} | ||
|
||
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<Field<'_, Self>> { | ||
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> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could imagine generating these impls automatically from some sort of macro. |
||
fn ci(&self) -> &ComponentInterface { | ||
self.parent.ci() | ||
} | ||
} | ||
|
||
impl APIConverter<Variant> for weedle::interface::OperationInterfaceMember<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<Variant> { | ||
impl APIConverter<VariantDescr> for weedle::interface::OperationInterfaceMember<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<VariantDescr> { | ||
if self.special.is_some() { | ||
bail!("special operations not supported"); | ||
} | ||
|
@@ -219,7 +253,7 @@ impl APIConverter<Variant> for weedle::interface::OperationInterfaceMember<'_> { | |
_ => bail!("enum interface members must have plain identifers as names"), | ||
} | ||
}; | ||
Ok(Variant { | ||
Ok(VariantDescr { | ||
name, | ||
fields: self | ||
.args | ||
|
@@ -232,17 +266,17 @@ impl APIConverter<Variant> for weedle::interface::OperationInterfaceMember<'_> { | |
} | ||
} | ||
|
||
impl APIConverter<Field> for weedle::argument::Argument<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> { | ||
impl APIConverter<FieldDescr> for weedle::argument::Argument<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<FieldDescr> { | ||
match self { | ||
weedle::argument::Argument::Single(t) => t.convert(ci), | ||
weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"), | ||
} | ||
} | ||
} | ||
|
||
impl APIConverter<Field> for weedle::argument::SingleArgument<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> { | ||
impl APIConverter<FieldDescr> for weedle::argument::SingleArgument<'_> { | ||
fn convert(&self, ci: &mut ComponentInterface) -> Result<FieldDescr> { | ||
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<Field> 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, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a nice improvement in usability for the template logic.