Skip to content

Commit

Permalink
Add option for default value to attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
mdeloof committed Jan 8, 2025
1 parent c20ad41 commit 514ca86
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 13 deletions.
4 changes: 3 additions & 1 deletion examples/macro/barrier/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ enum Event {
impl Foo {
#[state(superstate = "waiting_for_initialization")]
fn initializing(
#[default] a: &mut bool,
#[default = "true"] a: &mut bool,
#[default] b: &mut bool,
#[default] c: &mut bool,
event: &Event,
Expand Down Expand Up @@ -51,6 +51,8 @@ impl Foo {
fn main() {
let mut state_machine = Foo::default().uninitialized_state_machine().init();

dbg!(state_machine.state());

state_machine.handle(&Event::A);
state_machine.handle(&Event::B);
state_machine.handle(&Event::C);
Expand Down
57 changes: 49 additions & 8 deletions macro/src/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use std::collections::HashMap;
use proc_macro_error::abort;
use syn::parse::Parser;
use syn::{
parse_quote, Attribute, AttributeArgs, ExprCall, Field, FnArg, Generics, Ident, ImplItem,
ImplItemMethod, ItemImpl, Lit, Meta, MetaList, NestedMeta, Pat, PatType, Path, Receiver, Type,
Visibility,
parse_quote, Attribute, AttributeArgs, Expr, Field, FnArg, Generics, Ident, ImplItem,
ImplItemMethod, ItemImpl, Lit, Meta, MetaList, NestedMeta, Pat, PatIdent, PatType, Path,
Receiver, Type, Visibility,
};

/// Model of the state machine.
Expand All @@ -27,7 +27,7 @@ pub struct Model {
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub struct StateMachine {
/// The inital state of the state machine.
pub initial_state: ExprCall,
pub initial_state: Expr,
/// The type on which the state machine is implemented.
pub shared_storage_type: Type,
/// The path of the shared storage.
Expand Down Expand Up @@ -72,7 +72,7 @@ pub struct State {
/// Local storage,
pub local_storage: Vec<Field>,
/// Local storage default.
pub local_storage_default: Vec<Ident>,
pub local_storage_default: Vec<LocalStorageDefault>,
/// Inputs required by the state handler.
pub inputs: Vec<FnArg>,
/// Optional receiver input for the state handler (e.g. `&mut self`).
Expand Down Expand Up @@ -125,6 +125,22 @@ pub struct Action {
pub is_async: bool,
}

/// Information regarding a local storage default.
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub enum LocalStorageDefault {
Empty { ident: Ident },
Value { ident: Ident, value: Expr },
}

impl LocalStorageDefault {
pub(crate) fn ident(&self) -> &Ident {
match self {
LocalStorageDefault::Empty { ident } => ident,
LocalStorageDefault::Value { ident, .. } => ident,
}
}
}

/// Analyze the impl block and create a model.
pub fn analyze(attribute_args: AttributeArgs, mut item_impl: ItemImpl) -> Model {
let state_machine = analyze_state_machine(&attribute_args, &item_impl);
Expand Down Expand Up @@ -178,7 +194,7 @@ pub fn analyze_state_machine(attribute_args: &AttributeArgs, item_impl: &ItemImp
let shared_storage_generics = item_impl.generics.clone();
let shared_storage_path = get_shared_storage_path(&shared_storage_type);

let mut initial_state: Option<ExprCall> = None;
let mut initial_state: Option<Expr> = None;

let mut state_ident = parse_quote!(State);
let mut state_derives = Vec::new();
Expand Down Expand Up @@ -418,8 +434,9 @@ pub fn analyze_state(method: &mut ImplItemMethod, state_machine: &StateMachine)
.iter()
.position(|attr| attr.path.is_ident("default"))
{
pat_type.attrs.swap_remove(index);
local_storage_default.push(input.ident.clone());
let attr = pat_type.attrs.swap_remove(index);
let default = analyze_local_storage_default(input, &attr);
local_storage_default.push(default);
}

state_inputs.push(pat_type.clone());
Expand Down Expand Up @@ -611,6 +628,30 @@ pub fn analyze_action(method: &ImplItemMethod) -> Action {
}
}

fn analyze_local_storage_default(input: &PatIdent, attribute: &Attribute) -> LocalStorageDefault {
let Ok(meta) = attribute.parse_meta() else {
abort!(attribute, "attribute must use meta syntax")
};
match meta {
Meta::Path(_) => LocalStorageDefault::Empty {
ident: input.ident.clone(),
},
Meta::NameValue(name_value) => {
let Lit::Str(literal) = name_value.lit else {
abort!(name_value.lit, "must be a string literal")
};
let Ok(expr) = literal.parse() else {
abort!(literal, "must be an expression")
};
LocalStorageDefault::Value {
ident: input.ident.clone(),
value: expr,
}
}
_ => abort!(attribute, "wrong attribute format"),
}
}

/// Parse the attributes as a meta item.
pub fn get_meta(attrs: &[Attribute], name: &str) -> Vec<Meta> {
attrs
Expand Down
13 changes: 10 additions & 3 deletions macro/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,12 +475,19 @@ pub fn lower_state(state: &analyze::State, state_machine: &analyze::StateMachine
// Check if variant field should use default value.
for field in &variant_fields {
let field_name = &field.ident;
if state
if let Some(default) = state
.local_storage_default
.iter()
.any(|default| field.ident.as_ref().unwrap() == default)
.find(|default| field.ident.as_ref().unwrap() == default.ident())
{
field_values.push(parse_quote!(#field_name: core::default::Default::default()))
match default {
analyze::LocalStorageDefault::Empty { ident } => {
field_values.push(parse_quote!(#ident: core::default::Default::default()))
}
analyze::LocalStorageDefault::Value { ident, value } => {
field_values.push(parse_quote!(#ident: #value))
}
}
} else {
constructor_args.push(field.clone());
field_values.push(parse_quote!(#field_name));
Expand Down
5 changes: 4 additions & 1 deletion statig/tests/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ mod tests {
#[state_machine(initial = "State::bar()")]
impl Foo {
#[state]
fn bar(#[default] _local: &mut usize) -> Response<State> {
fn bar(
#[default] _local: &mut usize,
#[default = "100"] _local_2: &mut usize,
) -> Response<State> {
Handled
}
}
Expand Down

0 comments on commit 514ca86

Please sign in to comment.