korken89 / smlang-rs

A State Machine Language DSL procedual macro for Rust
Apache License 2.0
193 stars 28 forks source link

Pass reference values in guards and owned values in actions #49

Closed oblique closed 1 year ago

oblique commented 1 year ago

Closes #48

oblique commented 1 year ago

Let me know if you have any comments

oblique commented 1 year ago

Pushed new changes. I avoided panicking, however fn state still needs unwrap, so I used unwrap_unchecked instead, but I don't like that it is unsafe.

If you have any other ideas let me know.

oblique commented 1 year ago

I had an idea today to introduce Error::Poisoned. Normally this error should never happen, but it's the only way to avoid unwrap without unsafe.

These are the reasons that this error will happen:

This is the generated code:

    #[inline(always)]
    pub fn state(&self) -> Result<&States, Error> {
        self.state.as_ref().ok_or_else(|| Error::Poisoned)
    }

    pub fn process_event(&mut self, mut event: Events) -> Result<&States, Error> {
        match self.state.take().ok_or_else(|| Error::Poisoned)? {
            States::State1 => match event {
                Events::Event1(event_data) => {
                    match self.context.guard1(&event_data) {
                        Ok(()) => {
                            let _data = self.context.action1(event_data);
                            self.state = Some(States::State2(_data));
                        }
                        Err(e) => {
                            self.state = Some(States::State1);
                            return Err(Error::GuardFailed(e));
                        }
                    }
                    self.state()
                }
                _ => {
                    self.state = Some(States::State1);
                    Err(Error::InvalidEvent)
                }
            },
            States::State2(state_data) => match event {
                _ => {
                    self.state = Some(States::State2(state_data));
                    Err(Error::InvalidEvent)
                }
            },
            state => {
                self.state = Some(state);
                Err(Error::InvalidEvent)
            }
        }
    }
oblique commented 1 year ago

Rebased. Now the generated code is:

    pub fn state(&self) -> Result<&States, Error> {
        self.state.as_ref().ok_or_else(|| Error::Poisoned)
    }

    pub fn process_event(&mut self, mut event: Events) -> Result<&States, Error> {
        match self.state.take().ok_or_else(|| Error::Poisoned)? {
            States::State1 => match event {
                Events::Event1(event_data) => {
                    if let Err(e) = self.context.guard1(&event_data) {
                        self.state = Some(States::State1);
                        return Err(Error::GuardFailed(e));
                    }
                    let _data = self.context.action1(event_data);
                    self.state = Some(States::State2(_data));
                    self.state()
                }
                _ => {
                    self.state = Some(States::State1);
                    Err(Error::InvalidEvent)
                }
            },
            States::State2(state_data) => match event {
                _ => {
                    self.state = Some(States::State2(state_data));
                    Err(Error::InvalidEvent)
                }
            },
            state => {
                self.state = Some(state);
                Err(Error::InvalidEvent)
            }
        }
    }