|  | 
|  | 1 | +use eventually::{aggregate, message}; | 
|  | 2 | + | 
|  | 3 | +pub type LightSwitchId = String; | 
|  | 4 | + | 
|  | 5 | +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] | 
|  | 6 | +pub enum LightSwitchError { | 
|  | 7 | +    #[error("Light switch has not yet been installed")] | 
|  | 8 | +    NotYetInstalled, | 
|  | 9 | +    #[error("Light switch has already been installed")] | 
|  | 10 | +    AlreadyInstalled, | 
|  | 11 | +    #[error("Light switch is already on")] | 
|  | 12 | +    AlreadyOn, | 
|  | 13 | +    #[error("Light switch is already off")] | 
|  | 14 | +    AlreadyOff, | 
|  | 15 | +} | 
|  | 16 | + | 
|  | 17 | +// events | 
|  | 18 | +#[derive(Debug, Clone, Eq, PartialEq)] | 
|  | 19 | +pub struct Installed { | 
|  | 20 | +    id: LightSwitchId, | 
|  | 21 | +} | 
|  | 22 | + | 
|  | 23 | +#[derive(Debug, Clone, Eq, PartialEq)] | 
|  | 24 | +pub struct SwitchedOn { | 
|  | 25 | +    id: LightSwitchId, | 
|  | 26 | +} | 
|  | 27 | + | 
|  | 28 | +#[derive(Debug, Clone, Eq, PartialEq)] | 
|  | 29 | +pub struct SwitchedOff { | 
|  | 30 | +    id: LightSwitchId, | 
|  | 31 | +} | 
|  | 32 | + | 
|  | 33 | +#[derive(Debug, Clone, Eq, PartialEq)] | 
|  | 34 | +pub enum LightSwitchEvent { | 
|  | 35 | +    Installed(Installed), | 
|  | 36 | +    SwitchedOn(SwitchedOn), | 
|  | 37 | +    SwitchedOff(SwitchedOff), | 
|  | 38 | +} | 
|  | 39 | + | 
|  | 40 | +impl message::Message for LightSwitchEvent { | 
|  | 41 | +    fn name(&self) -> &'static str { | 
|  | 42 | +        match self { | 
|  | 43 | +            LightSwitchEvent::SwitchedOn(_) => "SwitchedOn", | 
|  | 44 | +            LightSwitchEvent::SwitchedOff(_) => "SwitchedOff", | 
|  | 45 | +            LightSwitchEvent::Installed(_) => "Installed", | 
|  | 46 | +        } | 
|  | 47 | +    } | 
|  | 48 | +} | 
|  | 49 | + | 
|  | 50 | +// aggregate | 
|  | 51 | +#[derive(Debug, Clone, Eq, PartialEq)] | 
|  | 52 | +pub enum LightSwitchState { | 
|  | 53 | +    On, | 
|  | 54 | +    Off, | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +#[derive(Debug, Clone)] | 
|  | 58 | +pub struct LightSwitch { | 
|  | 59 | +    id: LightSwitchId, | 
|  | 60 | +    state: LightSwitchState, | 
|  | 61 | +} | 
|  | 62 | + | 
|  | 63 | +impl aggregate::Aggregate for LightSwitch { | 
|  | 64 | +    type Id = LightSwitchId; | 
|  | 65 | +    type Event = LightSwitchEvent; | 
|  | 66 | +    type Error = LightSwitchError; | 
|  | 67 | + | 
|  | 68 | +    fn type_name() -> &'static str { | 
|  | 69 | +        "LightSwitch" | 
|  | 70 | +    } | 
|  | 71 | + | 
|  | 72 | +    fn aggregate_id(&self) -> &Self::Id { | 
|  | 73 | +        &self.id | 
|  | 74 | +    } | 
|  | 75 | + | 
|  | 76 | +    fn apply(state: Option<Self>, event: Self::Event) -> Result<Self, Self::Error> { | 
|  | 77 | +        match state { | 
|  | 78 | +            None => match event { | 
|  | 79 | +                LightSwitchEvent::Installed(installed) => Ok(LightSwitch { | 
|  | 80 | +                    id: installed.id, | 
|  | 81 | +                    state: LightSwitchState::Off, | 
|  | 82 | +                }), | 
|  | 83 | +                LightSwitchEvent::SwitchedOn(_) | LightSwitchEvent::SwitchedOff(_) => { | 
|  | 84 | +                    Err(LightSwitchError::NotYetInstalled) | 
|  | 85 | +                }, | 
|  | 86 | +            }, | 
|  | 87 | +            Some(mut light_switch) => match event { | 
|  | 88 | +                LightSwitchEvent::Installed(_) => Err(LightSwitchError::AlreadyInstalled), | 
|  | 89 | +                LightSwitchEvent::SwitchedOn(_) => match light_switch.state { | 
|  | 90 | +                    LightSwitchState::On => Err(LightSwitchError::AlreadyOn), | 
|  | 91 | +                    LightSwitchState::Off => { | 
|  | 92 | +                        light_switch.state = LightSwitchState::On; | 
|  | 93 | +                        Ok(light_switch) | 
|  | 94 | +                    }, | 
|  | 95 | +                }, | 
|  | 96 | +                LightSwitchEvent::SwitchedOff(_) => match light_switch.state { | 
|  | 97 | +                    LightSwitchState::On => { | 
|  | 98 | +                        light_switch.state = LightSwitchState::Off; | 
|  | 99 | +                        Ok(light_switch) | 
|  | 100 | +                    }, | 
|  | 101 | +                    LightSwitchState::Off => Err(LightSwitchError::AlreadyOff), | 
|  | 102 | +                }, | 
|  | 103 | +            }, | 
|  | 104 | +        } | 
|  | 105 | +    } | 
|  | 106 | +} | 
|  | 107 | + | 
|  | 108 | +// root | 
|  | 109 | +#[derive(Debug, Clone)] | 
|  | 110 | +pub struct LightSwitchRoot(aggregate::Root<LightSwitch>); | 
|  | 111 | + | 
|  | 112 | +// NOTE: The trait implementations for From, Deref and DerefMut below are | 
|  | 113 | +// implemented manually for demonstration purposes, but most would prefer to have them | 
|  | 114 | +// auto-generated at compile time by using the [`eventually_macros::aggregate_root`] macro | 
|  | 115 | +impl From<eventually::aggregate::Root<LightSwitch>> for LightSwitchRoot { | 
|  | 116 | +    fn from(root: eventually::aggregate::Root<LightSwitch>) -> Self { | 
|  | 117 | +        Self(root) | 
|  | 118 | +    } | 
|  | 119 | +} | 
|  | 120 | +impl From<LightSwitchRoot> for eventually::aggregate::Root<LightSwitch> { | 
|  | 121 | +    fn from(value: LightSwitchRoot) -> Self { | 
|  | 122 | +        value.0 | 
|  | 123 | +    } | 
|  | 124 | +} | 
|  | 125 | +impl std::ops::Deref for LightSwitchRoot { | 
|  | 126 | +    type Target = eventually::aggregate::Root<LightSwitch>; | 
|  | 127 | +    fn deref(&self) -> &Self::Target { | 
|  | 128 | +        &self.0 | 
|  | 129 | +    } | 
|  | 130 | +} | 
|  | 131 | +impl std::ops::DerefMut for LightSwitchRoot { | 
|  | 132 | +    fn deref_mut(&mut self) -> &mut Self::Target { | 
|  | 133 | +        &mut self.0 | 
|  | 134 | +    } | 
|  | 135 | +} | 
|  | 136 | + | 
|  | 137 | +impl LightSwitchRoot { | 
|  | 138 | +    pub fn install(id: LightSwitchId) -> Result<Self, LightSwitchError> { | 
|  | 139 | +        aggregate::Root::<LightSwitch>::record_new( | 
|  | 140 | +            LightSwitchEvent::Installed(Installed { id }).into(), | 
|  | 141 | +        ) | 
|  | 142 | +        .map(Self) | 
|  | 143 | +    } | 
|  | 144 | +    pub fn turn_on(&mut self, id: LightSwitchId) -> Result<(), LightSwitchError> { | 
|  | 145 | +        self.record_that(LightSwitchEvent::SwitchedOn(SwitchedOn { id }).into()) | 
|  | 146 | +    } | 
|  | 147 | +    pub fn turn_off(&mut self, id: LightSwitchId) -> Result<(), LightSwitchError> { | 
|  | 148 | +        self.record_that(LightSwitchEvent::SwitchedOff(SwitchedOff { id }).into()) | 
|  | 149 | +    } | 
|  | 150 | +    pub fn get_switch_state(&self) -> Result<LightSwitchState, LightSwitchError> { | 
|  | 151 | +        Ok(self.state.clone()) | 
|  | 152 | +    } | 
|  | 153 | +} | 
0 commit comments