#![forbid(unsafe_code)]
fn record_open() {}
const fn allow() -> bool {
true
}
macroonz::recipe! {
pub mod generic {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Stage {
Draft,
Published,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Capability {
Read,
Write,
}
bake! {
vocabularies {
Stage;
Capability;
};
relations {
policy(Stage, Capability) {
(Draft, Read) with(crate::allow);
(Published, Read) with(crate::allow);
};
};
postures {
policy {
repetition(refused);
};
};
projections {
companions;
relation_tables {
policy {
pub fn decision(
stage: &Stage,
capability: &Capability,
) -> Option<fn() -> bool>;
};
};
typestate(Stage);
};
}
}
}
macroonz::recipe! {
pub mod codec {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ledger {
pub count: u16,
}
impl Ledger {
#[must_use]
pub const fn assembled(count: u16) -> Self {
Self { count }
}
}
bake! {
codecs {
ledger(Ledger) {
direction(round_trip);
refusal(LedgerDecodeError);
assembly(assembled, total);
members {
count: u16 => count(required);
};
};
};
projections {
codec;
};
}
}
}
macroonz::recipe! {
pub mod configured {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Closed,
Open,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Event {
OpenDoor,
}
bake! {
vocabularies { State; Event; };
transitions(State, Event) {
(Closed, OpenDoor) => Open with(crate::record_open);
};
absence(refused);
projections {
companions;
dispatch(apply);
};
}
}
}
macroonz::recipe! {
pub mod exact {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Closed,
Open,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Event {
OpenDoor,
}
bake! {
vocabularies { State; Event; };
transitions(State, Event) {
(Closed, OpenDoor) => Open with(crate::record_open);
};
absence(refused);
projections {
dispatch {
pub fn advance(
current: State,
event: Event,
) -> Result<State, TransitionRefusal>;
};
};
}
}
}
fn main() {
assert_eq!(
generic::baked::STAGE_VARIANTS,
&[generic::Stage::Draft, generic::Stage::Published],
);
assert_eq!(
generic::baked::POLICY_ROWS,
&[
(generic::Stage::Draft, generic::Capability::Read),
(generic::Stage::Published, generic::Capability::Read),
],
);
assert_eq!(
generic::baked::policy::decision(&generic::Stage::Draft, &generic::Capability::Read,)
.map(|decision| decision()),
Some(true),
);
assert!(
generic::baked::policy::decision(&generic::Stage::Draft, &generic::Capability::Write,)
.is_none()
);
let _draft = generic::baked::typestate::Stage::<generic::baked::typestate::Draft>::new();
assert!(allow());
let ledger = codec::Ledger { count: 7 };
let mut bytes = Vec::new();
ledger.encode_canonical(&mut bytes);
assert_eq!(codec::Ledger::decode_canonical(&bytes), Ok(ledger));
assert_eq!(
configured::baked::apply(configured::State::Closed, configured::Event::OpenDoor),
Ok(configured::State::Open),
);
assert_eq!(
exact::baked::advance(exact::State::Closed, exact::Event::OpenDoor),
Ok(exact::State::Open),
);
}