diff --git a/src/storage.rs b/src/storage.rs index a1ab246..03f90d8 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -40,6 +40,10 @@ mod rng { } } +/// A newtype over [String] to represent a single quote. +/// [Quote] does not implement [PartialEq] or [Eq] as, on principle, two +/// quotes may be comprised of the same contents whilst still +/// representing distinct quotes in practice (e.g. two different lines of dialogue which happen to be the same). #[derive(Debug, Clone)] pub struct Quote(String); @@ -62,7 +66,47 @@ impl Quote { } pub mod source { - struct SourceQuotes; + use super::*; + + // TODO: Should the quote source filters be + // generic over the exact manager implementation being used? + /// Message to request that a SourceManager source its quotes once again. + pub struct SourceQuotes; + + /// Subtrait of Actor which specifically + /// denotes actors that can handle all relevant source messages. + pub trait SourceManager: Actor {} + + impl SourceManager for T where T: Message, ()>> {} + + /// Implementation of [`SourceManager`] which sources quotes from a Vec + /// that it holds in memory, without accessing external services. + /// Its main purpose is to be used for testing. + #[derive(Actor)] + pub struct MemorySourceManager { + quotes: Vec, + } + + impl MemorySourceManager { + pub fn new(quotes: impl IntoIterator>) -> Self { + Self { + quotes: quotes.into_iter().map(Into::into).collect(), + } + } + } + + impl Message for MemorySourceManager { + type Reply = Result, ()>; + + async fn handle( + &mut self, + _msg: SourceQuotes, + _ctx: &mut Context, + ) -> Self::Reply { + // We just clone the quotes we've been holding onto since startup + Ok(self.quotes.clone()) + } + } } pub mod queue { @@ -128,14 +172,18 @@ pub mod queue { } } -struct QuoteCycle { +struct QuoteCycle { rng: rng::PrngState, - source_manager: (), + source_manager: ActorRef, queue_manager: ActorRef, } -impl QuoteCycle { - pub fn new(rng: rng::PrngState, source_manager: (), queue_manager: ActorRef) -> Self { +impl QuoteCycle { + pub fn new( + rng: rng::PrngState, + source_manager: ActorRef, + queue_manager: ActorRef, + ) -> Self { Self { rng, source_manager, @@ -143,7 +191,7 @@ impl QuoteCycle { } } - pub fn with_thread_rng(source_manager: (), queue_manager: ActorRef) -> Self { + pub fn with_thread_rng(source_manager: ActorRef, queue_manager: ActorRef) -> Self { Self { rng: rng::PrngState::from_thread_rng(), source_manager, @@ -153,6 +201,8 @@ impl QuoteCycle { } mod test { + use crate::storage::QuoteCycle; + #[tokio::test] async fn memory_queue() { use super::Quote; @@ -182,4 +232,30 @@ mod test { ); } } + + #[tokio::test] + async fn memory_source() { + use super::source::*; + use kameo::prelude::*; + + let sample_quotes = ["Minie", "Miney", "Moe", "and", "some", "more"]; + + let source_manager = MemorySourceManager::spawn(MemorySourceManager::new(&sample_quotes)); + + let quotes = source_manager + .ask(SourceQuotes) + .await + .expect("In-memory quote queue storage should be valid for insertion"); + + assert_eq!( + sample_quotes.as_slice(), + quotes + .into_iter() + // Since [Quote] doesn't implement any Equality trait, + // we map the strings into quotes instead + .map(String::from) + .collect::>() + .as_slice(), + ); + } }