diff --git a/crates/domain/src/message.rs b/crates/domain/src/message.rs deleted file mode 100644 index 4901382..0000000 --- a/crates/domain/src/message.rs +++ /dev/null @@ -1,9 +0,0 @@ -use types::bounded::CapSlot; - -pub const MAX_MESSAGE_WORDS: usize = 4; -pub const MAX_CAP_TRANSFERS: usize = 4; - -pub struct Message { - words: [usize; MAX_MESSAGE_WORDS], - caps: [Option; MAX_CAP_TRANSFERS], -} diff --git a/crates/domain/src/message/mod.rs b/crates/domain/src/message/mod.rs new file mode 100644 index 0000000..3bf5fa7 --- /dev/null +++ b/crates/domain/src/message/mod.rs @@ -0,0 +1,76 @@ +use types::bounded::CapSlot; + +#[cfg(test)] +#[path = "tests.rs"] +mod tests; + +pub const MAX_MESSAGE_WORDS: usize = 4; +pub const MAX_CAP_TRANSFERS: usize = 4; + +pub struct Message { + words: [usize; MAX_MESSAGE_WORDS], + caps: [Option; MAX_CAP_TRANSFERS], +} + +impl Message { + pub fn empty() -> Self { + Self { + words: [0; MAX_MESSAGE_WORDS], + caps: [None; MAX_CAP_TRANSFERS], + } + } + + pub fn from_words(words: [usize; MAX_MESSAGE_WORDS]) -> Self { + Self { + words, + caps: [None; MAX_CAP_TRANSFERS], + } + } + + pub fn words(&self) -> &[usize; MAX_MESSAGE_WORDS] { + &self.words + } + + pub fn word(&self, index: usize) -> usize { + self.words[index] + } + + pub fn caps(&self) -> &[Option; MAX_CAP_TRANSFERS] { + &self.caps + } + + pub fn cap(&self, index: usize) -> Option { + self.caps[index] + } + + pub fn builder() -> MessageBuilder { + MessageBuilder { + message: Message::empty(), + } + } +} + +pub struct MessageBuilder { + message: Message, +} + +impl MessageBuilder { + pub fn word(mut self, index: usize, value: usize) -> Self { + self.message.words[index] = value; + self + } + + pub fn words(mut self, words: [usize; MAX_MESSAGE_WORDS]) -> Self { + self.message.words = words; + self + } + + pub fn cap(mut self, index: usize, slot: CapSlot) -> Self { + self.message.caps[index] = Some(slot); + self + } + + pub fn build(self) -> Message { + self.message + } +} diff --git a/crates/domain/src/message/tests.rs b/crates/domain/src/message/tests.rs new file mode 100644 index 0000000..1627a18 --- /dev/null +++ b/crates/domain/src/message/tests.rs @@ -0,0 +1,86 @@ +use super::*; +use types::bounded::CapSlot; + +fn slot(n: u8) -> CapSlot { + CapSlot::new(n).unwrap() +} + +// --- Construction --- + +#[test] +fn empty_message_has_zeroed_words_and_no_caps() { + let msg = Message::empty(); + assert_eq!(msg.words(), &[0; MAX_MESSAGE_WORDS]); + for i in 0..MAX_CAP_TRANSFERS { + assert!(msg.cap(i).is_none()); + } +} + +#[test] +fn from_words_sets_words_and_no_caps() { + let msg = Message::from_words([10, 20, 30, 40]); + assert_eq!(msg.word(0), 10); + assert_eq!(msg.word(1), 20); + assert_eq!(msg.word(2), 30); + assert_eq!(msg.word(3), 40); + assert!(msg.cap(0).is_none()); +} + +// --- Accessors --- + +#[test] +fn words_returns_full_array() { + let msg = Message::from_words([1, 2, 3, 4]); + assert_eq!(msg.words(), &[1, 2, 3, 4]); +} + +#[test] +fn caps_returns_full_array() { + let msg = Message::empty(); + assert_eq!(msg.caps(), &[None; MAX_CAP_TRANSFERS]); +} + +// --- Builder --- + +#[test] +fn builder_sets_individual_words() { + let msg = Message::builder() + .word(0, 42) + .word(2, 99) + .build(); + assert_eq!(msg.word(0), 42); + assert_eq!(msg.word(1), 0); + assert_eq!(msg.word(2), 99); + assert_eq!(msg.word(3), 0); +} + +#[test] +fn builder_sets_caps() { + let msg = Message::builder() + .cap(0, slot(5)) + .cap(1, slot(10)) + .build(); + assert_eq!(msg.cap(0), Some(slot(5))); + assert_eq!(msg.cap(1), Some(slot(10))); + assert!(msg.cap(2).is_none()); +} + +#[test] +fn builder_sets_words_and_caps_together() { + let msg = Message::builder() + .word(0, 100) + .word(1, 200) + .cap(0, slot(3)) + .build(); + assert_eq!(msg.word(0), 100); + assert_eq!(msg.word(1), 200); + assert_eq!(msg.cap(0), Some(slot(3))); +} + +#[test] +fn builder_with_all_words() { + let msg = Message::builder() + .words([11, 22, 33, 44]) + .build(); + assert_eq!(msg.words(), &[11, 22, 33, 44]); +}