//! State use super::*; use tokio::{ sync::{ watch, }, }; use config::Config; use msg::Initialiser; #[derive(Debug)] pub struct ShutdownError; impl error::Error for ShutdownError{} impl fmt::Display for ShutdownError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "shutdown signal caught") } } #[derive(Debug, Clone)] pub struct State { config: Arc, //to avoid cloning config exclude: Arc<(sanitise::filter::Filter, sanitise::filter::Filter)>, chain: Arc>>, save: Arc, save_begin: Initialiser, shutdown: Arc>, shutdown_recv: watch::Receiver, } impl State { /// Consume this `state` into its initialiser pub fn into_save_initialiser(self) -> Initialiser { self.save_begin } /// Allow the saver task to start work pub fn init_save(self) -> Result<(), msg::InitError> { self.save_begin.set() } /// Has `init_save` been called? pub fn is_init_save(&self) -> bool { self.save_begin.is_set() } /// A future that completes either when `init_save` is called, or `shutdown`. pub async fn on_init_save(&mut self) -> Result<(), ShutdownError> { tokio::select!{ Ok(()) = self.save_begin.wait() => { Ok(()) } Some(true) = self.shutdown_recv.recv() => { debug!("on_init_save(): shutdown received"); Err(ShutdownError) } else => Err(ShutdownError) } } pub fn inbound_filter(&self) -> &sanitise::filter::Filter { &self.exclude.0 } pub fn outbound_filter(&self) -> &sanitise::filter::Filter { &self.exclude.1 } pub fn new(config: Config, chain: Arc>>, save: Arc) -> Self { let (shutdown, shutdown_recv) = watch::channel(false); Self { exclude: Arc::new((config.filter.get_inbound_filter(), config.filter.get_outbound_filter())), config: Arc::new(config), chain, save, save_begin: Initialiser::new(), shutdown: Arc::new(shutdown), shutdown_recv, } } pub fn config(&self) -> &Config { self.config.as_ref() } pub fn notify_save(&self) { self.save.notify(); } pub fn chain(&self) -> &RwLock> { &self.chain.as_ref() } pub fn when(&self) -> &Arc { &self.save } pub fn shutdown(self) { self.shutdown.broadcast(true).expect("Failed to communicate shutdown"); self.save.notify(); } pub fn has_shutdown(&self) -> bool { *self.shutdown_recv.borrow() } pub async fn on_shutdown(&mut self) { if !self.has_shutdown() { while let Some(false) = self.shutdown_recv.recv().await { } } } }