Skip to content

Commit 1c43e72

Browse files
committed
Add enum config and the "output" option to the dart_enum macro
1 parent 6a40614 commit 1c43e72

File tree

3 files changed

+127
-23
lines changed

3 files changed

+127
-23
lines changed

membrane/src/lib.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,18 +226,26 @@ impl std::fmt::Debug for DeferredTrace {
226226
}
227227
}
228228

229+
#[doc(hidden)]
230+
#[derive(Debug, Clone)]
231+
pub struct Enum {
232+
pub name: &'static str,
233+
pub output: Option<&'static str>,
234+
pub namespace: &'static str,
235+
}
236+
229237
#[doc(hidden)]
230238
#[derive(Clone)]
231239
pub struct DeferredEnumTrace {
232-
pub name: &'static str,
240+
pub enum_data: Enum,
233241
pub namespace: &'static str,
234242
pub trace: fn(tracer: &mut serde_reflection::Tracer),
235243
}
236244

237245
impl std::fmt::Debug for DeferredEnumTrace {
238246
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239247
f.debug_struct("DeferredEnumTrace")
240-
.field("name", &self.name)
248+
.field("enum_data", &self.enum_data)
241249
.field("namespace", &self.namespace)
242250
.finish()
243251
}
@@ -264,8 +272,10 @@ pub struct Membrane {
264272
namespaces: Vec<&'static str>,
265273
namespaced_registry: HashMap<&'static str, serde_reflection::Result<Registry>>,
266274
namespaced_fn_registry: HashMap<&'static str, Vec<Function>>,
275+
namespaced_enum_registry: HashMap<&'static str, Vec<Enum>>,
267276
generated: bool,
268277
c_style_enums: bool,
278+
sealed_enums: bool,
269279
timeout: Option<i32>,
270280
borrows: Borrows,
271281
_inputs: Vec<libloading::Library>,
@@ -346,7 +356,7 @@ impl<'a> Membrane {
346356
);
347357
}
348358

349-
enums.sort_by_cached_key(|e| &e.name);
359+
enums.sort_by_cached_key(|e| &e.enum_data.name);
350360

351361
functions.sort_by_cached_key(|f| {
352362
format!(
@@ -367,6 +377,7 @@ impl<'a> Membrane {
367377
let mut namespaced_registry = HashMap::new();
368378
let mut namespaced_samples = HashMap::new();
369379
let mut namespaced_fn_registry = HashMap::new();
380+
let mut namespaced_enum_registry = HashMap::new();
370381
let mut borrows: Borrows = HashMap::new();
371382

372383
// collect all the metadata about functions (without tracing them yet)
@@ -382,6 +393,14 @@ impl<'a> Membrane {
382393
Self::create_borrows(&namespaced_fn_registry, namespace, &mut borrows);
383394
});
384395

396+
// collect all the metadata about functions (without tracing them yet)
397+
enums.iter().for_each(|item| {
398+
namespaced_enum_registry
399+
.entry(item.namespace)
400+
.or_insert_with(Vec::new)
401+
.push(item.enum_data.clone());
402+
});
403+
385404
// trace all the enums at least once
386405
enums.iter().for_each(|item| {
387406
// trace the enum into the borrowing namespace's registry
@@ -390,7 +409,7 @@ impl<'a> Membrane {
390409
.into_iter()
391410
.for_each(|(for_namespace, from_namespaces)| {
392411
if let Some((types, _location)) = from_namespaces.get(item.namespace) {
393-
if types.contains(item.name) {
412+
if types.contains(item.enum_data.name) {
394413
let tracer = namespaced_registry
395414
.entry(for_namespace)
396415
.or_insert_with(|| Tracer::new(TracerConfig::default()));
@@ -449,9 +468,11 @@ impl<'a> Membrane {
449468
.map(|(key, val)| (key, val.registry()))
450469
.collect(),
451470
namespaced_fn_registry,
471+
namespaced_enum_registry,
452472
namespaces,
453473
generated: false,
454474
c_style_enums: true,
475+
sealed_enums: true,
455476
timeout: None,
456477
borrows,
457478
_inputs: input_libs,
@@ -544,7 +565,24 @@ impl<'a> Membrane {
544565
debug!("Generating lib/src/ code for namespace {}", namespace);
545566
let config = serde_generate::CodeGeneratorConfig::new(namespace.to_string())
546567
.with_encodings(vec![serde_generate::Encoding::Bincode])
547-
.with_c_style_enums(self.c_style_enums);
568+
.with_c_style_enums(self.c_style_enums)
569+
.with_sealed_enums(self.sealed_enums)
570+
.with_enum_type_overrides(
571+
self
572+
.namespaced_enum_registry
573+
.get(namespace)
574+
.into_iter()
575+
.flat_map(|enums| {
576+
enums.iter().filter_map(|x| {
577+
if x.output.is_some() {
578+
Some((x.name, x.output.unwrap()))
579+
} else {
580+
None
581+
}
582+
})
583+
})
584+
.collect::<HashMap<&'static str, &'static str>>(),
585+
);
548586

549587
let registry = match self.namespaced_registry.get(namespace).unwrap() {
550588
Ok(reg) => reg,
@@ -604,6 +642,16 @@ impl<'a> Membrane {
604642
self
605643
}
606644

645+
///
646+
/// When set to `true` (the default) we generate sealed classes for complex enums
647+
/// instead of abstract classes. When set to `false`
648+
/// abstract classes are generated.
649+
pub fn with_sealed_enums(&mut self, val: bool) -> &mut Self {
650+
return_if_error!(self);
651+
self.sealed_enums = val;
652+
self
653+
}
654+
607655
///
608656
/// Configures the global timeout for non-stream receive ports.
609657
/// Streams do not use the global timeout as it is unusual to want a stream to timeout

membrane_macro/src/lib.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ use membrane_types::heck::ToLowerCamelCase;
55
use membrane_types::rust::{flatten_types, RustArgs, RustExternParams, RustTransforms};
66
use membrane_types::syn::Attribute;
77
use membrane_types::{proc_macro2, quote, syn, Input, OutputStyle};
8-
use options::{extract_function_options, FunctionOptions};
8+
use options::{extract_enum_options, extract_function_options, FunctionOptions};
99
use proc_macro::TokenStream;
1010
use proc_macro2::{Span, TokenStream as TokenStream2};
1111
use quote::quote;
1212
use std::convert::TryFrom;
1313
use syn::parse::{Parse, ParseStream, Result};
1414
use syn::{parse_macro_input, punctuated::Punctuated, Block, Ident, MetaNameValue, Token, Type};
1515

16+
use crate::options::EnumOptions;
17+
1618
mod options;
1719
mod parsers;
1820
mod utils;
@@ -436,16 +438,16 @@ impl Parse for ReprDartEnum {
436438
///
437439
/// Valid options:
438440
/// * `namespace`, used to select the Dart implementation code directory.
441+
/// * `output`, used to override global enum config of `enum`, `sealed`, or `abstract`.
439442
#[proc_macro_attribute]
440443
pub fn dart_enum(attrs: TokenStream, input: TokenStream) -> TokenStream {
441-
let FunctionOptions {
442-
namespace, borrow, ..
443-
} = match extract_function_options(
444+
let EnumOptions {
445+
namespace, output, ..
446+
} = match extract_enum_options(
444447
parse_macro_input!(attrs with Punctuated::<MetaNameValue, Token![,]>::parse_terminated)
445448
.into_iter()
446449
.collect(),
447-
FunctionOptions::default(),
448-
false,
450+
EnumOptions::default(),
449451
) {
450452
Ok(options) => options,
451453
Err(err) => {
@@ -458,24 +460,22 @@ pub fn dart_enum(attrs: TokenStream, input: TokenStream) -> TokenStream {
458460
let mut variants = TokenStream::new();
459461
variants.extend(input.clone());
460462

461-
if !borrow.is_empty() {
462-
variants.extend::<TokenStream>(
463-
syn::Error::new(
464-
Span::call_site(),
465-
"`borrow` is not a valid option for #[dart_enum]",
466-
)
467-
.to_compile_error()
468-
.into(),
469-
);
470-
}
471-
472463
let ReprDartEnum { name } = parse_macro_input!(input as ReprDartEnum);
473464
let enum_name = name.to_string();
465+
let output = if let Some(val) = output {
466+
quote! { Some(#val) }
467+
} else {
468+
quote! { None }
469+
};
474470

475471
let _deferred_trace = quote! {
476472
::membrane::inventory::submit! {
477473
::membrane::DeferredEnumTrace {
478-
name: #enum_name,
474+
enum_data: ::membrane::Enum {
475+
name: #enum_name,
476+
output: #output,
477+
namespace: #namespace
478+
},
479479
namespace: #namespace,
480480
trace: |
481481
tracer: &mut ::membrane::serde_reflection::Tracer

membrane_macro/src/options.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ pub(crate) struct FunctionOptions {
1010
pub borrow: Vec<String>,
1111
}
1212

13+
#[derive(Debug, Default)]
14+
pub(crate) struct EnumOptions {
15+
pub namespace: String,
16+
pub output: Option<String>,
17+
}
18+
1319
pub(crate) fn extract_function_options(
1420
mut input: Vec<MetaNameValue>,
1521
mut options: FunctionOptions,
@@ -127,6 +133,56 @@ pub(crate) fn extract_function_options(
127133
extract_function_options(input, options, sync)
128134
}
129135

136+
pub(crate) fn extract_enum_options(
137+
mut input: Vec<MetaNameValue>,
138+
mut options: EnumOptions,
139+
) -> Result<EnumOptions, String> {
140+
let option = match input.pop() {
141+
Some(syn::MetaNameValue { path, value, .. }) => {
142+
let ident = path.get_ident().unwrap().clone();
143+
Some((ident, value))
144+
}
145+
_ => None,
146+
};
147+
148+
let options = match option {
149+
Some((
150+
ident,
151+
Lit(ExprLit {
152+
lit: syn::Lit::Str(val),
153+
..
154+
}),
155+
)) if ident == "namespace" => {
156+
options.namespace = val.value();
157+
options
158+
}
159+
Some((
160+
ident,
161+
Lit(ExprLit {
162+
lit: syn::Lit::Str(val),
163+
..
164+
}),
165+
)) if ident == "output" => {
166+
options.output = Some(val.value());
167+
options
168+
}
169+
Some(_) => {
170+
return Err(
171+
r#"only `namespace=""`, and `output="enum|sealed|abstract"` are valid options"#.to_string(),
172+
);
173+
}
174+
None => {
175+
// we've iterated over all options and didn't find a namespace (required)
176+
if options.namespace.is_empty() {
177+
return Err(format!("#[dart_enum] expects a `namespace` attribute"));
178+
}
179+
180+
return Ok(options);
181+
}
182+
};
183+
184+
extract_enum_options(input, options)
185+
}
130186
fn invalid_option(macr: &str, opt: &str) -> Result<FunctionOptions, String> {
131187
Err(format!(
132188
"`{opt}` is not a valid option for `{m}`",

0 commit comments

Comments
 (0)