Skip to content

Instantly share code, notes, and snippets.

@sedovmik
Last active June 15, 2019 15:02
Show Gist options
  • Save sedovmik/db376b94655969e3815303ffc8cd42de to your computer and use it in GitHub Desktop.
Save sedovmik/db376b94655969e3815303ffc8cd42de to your computer and use it in GitHub Desktop.
#[derive(Debug)]
enum Event {
Up,
Down,
BounceBack,
}
#[derive(Debug)]
enum Effect {
ReportErrorNegative
}
#[derive(Debug)]
struct Model {
counter: i32
}
fn handle_event(model: &Model, event: &Event) -> Next<Model, Effect> {
match event {
Event::Up => Next {
model: Some(Model {
counter: model.counter + 1
}),
effects: None,
},
Event::Down => {
let new_value = model.counter - 1;
if new_value < 0 {
Next {
model: None,
effects: Some(vec![Effect::ReportErrorNegative]),
}
} else {
Next {
model: Some(Model {
counter: new_value
}),
effects: None,
}
}
},
Event::BounceBack => {
println!("bounce back!");
Next {
model: None,
effects: None,
}
}
}
}
fn handle_effect(effect: &Effect, event_consumer: &mut EventConsumer<Event>) {
match effect {
Effect::ReportErrorNegative => {
println!("error!");
event_consumer.dispatch_event(&Event::BounceBack)
}
}
}
/// ----- mobius
type EventHandler<M, E, F> = fn(&M, &E) -> Next<M, F>;
type EffectHandler<F, E> = fn(&F, &mut EventConsumer<E>);
trait EventConsumer<E> {
fn dispatch_event(&mut self, event: &E);
}
struct Init<M, F> {
model: M,
effects: Option<Vec<F>>,
}
struct Next<M, F> {
model: Option<M>,
effects: Option<Vec<F>>,
}
struct MobiusLoop<M, E, F> {
model: M,
handle_event: EventHandler<M, E, F>,
handle_effect: EffectHandler<F, E>,
}
impl<M, E, F> MobiusLoop<M, E, F> {
fn new(init: Init<M, F>,
update: EventHandler<M, E, F>,
handle_effect: EffectHandler<F, E>) -> Self {
let mut mobius_loop = MobiusLoop {
model: init.model,
handle_event: update,
handle_effect,
};
mobius_loop.dispatch_effects(init.effects);
return mobius_loop;
}
fn dispatch_effects(&mut self, effects: Option<Vec<F>>) {
match effects {
Some(effects) => effects.iter().for_each(|effect| {
(self.handle_effect)(effect, self)
}),
None => { /* nothing to do */ }
}
}
}
impl<M, E, F> EventConsumer<E> for MobiusLoop<M, E, F> {
fn dispatch_event(&mut self, event: &E) {
let next = (self.handle_event)(&self.model, event);
match next.model {
Some(model) => self.model = model,
None => { /* nothing to do */ }
}
self.dispatch_effects(next.effects)
}
}
#[test]
fn simple_usage() {
let init = Init {
model: Model {
counter: 1
},
effects: None,
};
let mut mobius_loop = MobiusLoop::new(
init,
handle_event,
handle_effect);
mobius_loop.dispatch_event(&Event::Up);
mobius_loop.dispatch_event(&Event::Up);
mobius_loop.dispatch_event(&Event::Down);
mobius_loop.dispatch_event(&Event::Down);
mobius_loop.dispatch_event(&Event::Down);
mobius_loop.dispatch_event(&Event::Down);
mobius_loop.dispatch_event(&Event::Up);
mobius_loop.dispatch_event(&Event::Up);
println!("{:?}", mobius_loop.model);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment