brisk-machine 0.6.0

Use of the brisk declarative engine to generate state machines.
Documentation
use brisk_it::generator::{Generator, Result};

#[derive(Default)]
pub(super) struct Machine {}

impl Generator for Machine {
    fn generate(
        &self,
        input: brisk_it::component::ComponentInput,
        manager: &brisk_it::generator::Manager,
    ) -> Result<proc_macro2::TokenStream> {
        let visibility = input.visibility.clone();
        let state_info = crate::state::ParsedState::parse(input, manager)?;
        let event_dispatcher = quote::format_ident!("{}EventDispatcher", state_info.id);
        let event_manager = quote::format_ident!("{}EventManager", state_info.id);
        let machine_holder = quote::format_ident!("{}Holder", state_info.id);
        let initial_states = state_info.initialise_states(true).1;

        // Prepare event callbacks for the EventProcessor
        let events_callbacks_args = match state_info.properties {
            Some(_) => quote::quote! { &m.properties },
            None => proc_macro2::TokenStream::new(),
        };
        let events_callbacks = state_info.events().into_iter().map(|ec| {
            quote::quote! {
                pub fn #ec(&self)
                {
                    self.sender.send(Box::new(|m| m.state.borrow_mut().#ec(m.event_dispatcher(), #events_callbacks_args))).unwrap();
                    if let Some(cb) = &self.after_dispatching_event
                    {
                        cb();
                    }
            }
            }
        });

        // Generate states
        let states = state_info.generate(&crate::state::GenerationInfo {
            visibility: visibility.clone(),
            event_dispatcher: event_dispatcher.clone(),
        })?;

        // Tokens
        let id = state_info.id;
        let machine_id = quote::format_ident!("{}Machine", id);
        let mut fields = Vec::<proc_macro2::TokenStream>::new();

        // Generate create function
        let create_function: proc_macro2::TokenStream;
        if let Some(properties) = state_info.properties {
            fields.push(quote::quote! { properties: #properties, });
            create_function = quote::quote! {
                pub fn create(super_properties: #properties) -> Self
                {
                    let (sender, receiver) = std::sync::mpsc::channel::<Box<dyn Fn(&#machine_id) + Send + 'static>>();
                    let properties = super_properties;
                    Self {
                        #initial_states
                        properties,
                        receiver,
                        event_dispatcher: #event_dispatcher
                        {
                            sender,
                            after_dispatching_event: None
                        },
                        after_processing_events: None,
                    }
                }
            }
        } else {
            create_function = quote::quote! {
                pub fn create() -> Self
                {
                    let (sender, receiver) = std::sync::mpsc::channel::<Box<dyn Fn(&#machine_id) + Send + 'static>>();
                    Self {
                        #initial_states,
                        receiver,
                        event_dispatcher: #event_dispatcher
                        {
                            sender,
                            after_dispatching_event: None
                        },
                        after_processing_events: None
                    }
                }
            }
        }

        // Glue everything together
        Ok(quote::quote! {
            #states
            #visibility struct #machine_id
            {
                state: std::cell::RefCell<#id>,
                #(#fields)*
                event_dispatcher: #event_dispatcher,
                receiver: std::sync::mpsc::Receiver<Box<dyn Fn(&#machine_id) + Send + 'static>>,
                after_processing_events: Option<Box<dyn Fn()>>,
            }
            enum #machine_holder<'a>
            {
                RefCell(&'a std::cell::RefCell<#machine_id>),
                RwLock(&'a std::sync::RwLock<#machine_id>)
            }

            impl<'a> From<&'a std::cell::RefCell<#machine_id>> for #machine_holder<'a> {
                fn from(value: &'a std::cell::RefCell<#machine_id>) -> Self {
                    Self::RefCell(value)
                }
            }

            impl<'a> From<&'a std::sync::RwLock<#machine_id>> for #machine_holder<'a> {
                fn from(value: &'a std::sync::RwLock<#machine_id>) -> Self {
                    Self::RwLock(value)
                }
            }

            impl #machine_id
            {
                #create_function
                /// Call this closure after processing events
                pub fn after_processing_events(&mut self, cb: impl Fn() + 'static)
                {
                    self.after_processing_events = Some(Box::new(cb));
                }
                /// Call this closure after dispatching events
                pub fn after_dispatching_event(&mut self, cb: impl Fn() + Send + Sync + 'static)
                {
                    self.event_dispatcher.after_dispatching_event = Some(std::sync::Arc::new(cb));
                }
                /// Create an event dispatcher for the machine
                pub fn event_dispatcher(&self) -> &#event_dispatcher
                {
                    &self.event_dispatcher
                }
                /// Process the events received by the machine
                pub fn process_events(&mut self)
                {
                    let mut processed_event = false;
                    while let Some(evt) = self.receiver.try_recv().ok()
                    {
                        processed_event = true;
                        evt(self);
                    }
                    if processed_event
                    {
                        if let Some(cb) = &self.after_processing_events
                        {
                            cb();
                        }
                    }
                }
                /// Start an event manager. Once the manager is dropped, it will automatically process events.
                pub fn start_events<'a>(machine: impl Into<#machine_holder<'a>>) -> #event_manager<'a> {
                    let machine = machine.into();
                    let event_dispatcher = match machine
                    {
                        #machine_holder::RefCell(machine) => machine.borrow().event_dispatcher().clone(),
                        #machine_holder::RwLock(machine) => machine.read().unwrap().event_dispatcher().clone(),
                    };
                    #event_manager {
                        event_dispatcher,
                        machine,
                    }
                }
                /// Return a borrow of the state
                pub fn borrow_state<'a>(&'a self) -> std::cell::Ref<'a, Main> {
                    self.state.borrow()
                }
            }
            #[derive(Clone)]
            #visibility struct #event_dispatcher
            {
                sender: std::sync::mpsc::Sender<Box<dyn Fn(&#machine_id) + Send + 'static>>,
                after_dispatching_event: Option<std::sync::Arc<dyn Fn() + Send + Sync + 'static>>,
            }
            impl #event_dispatcher
            {
                #(#events_callbacks)*
            }

            #visibility struct #event_manager<'a> {
                event_dispatcher: #event_dispatcher,
                machine: #machine_holder<'a>,
            }

            impl<'a> std::ops::Deref for #event_manager<'a> {
                type Target = #event_dispatcher;
                fn deref(&self) -> &Self::Target {
                    &self.event_dispatcher
                }
            }

            impl<'a> Drop for #event_manager<'a> {
                fn drop(&mut self) {
                    match self.machine
                    {
                        #machine_holder::RefCell(machine) => machine.borrow_mut().process_events(),
                        #machine_holder::RwLock(machine) => machine.write().unwrap().process_events(),
                    }
                }
            }
            // Extensions to
        })
    }
    fn boxed_clone(&self) -> Box<dyn Generator> {
        Box::new(Self {})
    }
}