//! Creating immutable images of state. use super::*; use std::{error,fmt}; /// An image of the entire post container #[derive(Debug, Default, Serialize, Deserialize)] pub struct Freeze { posts: Vec, } impl From for Imouto { #[inline] fn from(from: Freeze) -> Self { Self::from_freeze(from) } } impl TryFrom for Freeze { type Error = FreezeError; #[inline] fn try_from(from: Imouto) -> Result { from.try_into_freeze() } } /// Error returned when a freeze operation fails #[derive(Debug)] pub struct FreezeError{ held: Option>>, } impl FreezeError { /// The post associated with this error, if there is one. pub fn post(&self) -> Option<&Arc>> { self.held.as_ref() } } impl error::Error for FreezeError{} impl fmt::Display for FreezeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f,"Failed to create freeze image")?; if let Some(aref) = &self.held { let cnt = Arc::strong_count(&aref) - 1; if cnt > 0 { write!(f, "Post reference still held in {} other places.", cnt) } else { write!(f, "Post reference was still held in another place at the time, but no longer is.") } } else { Ok(()) } } } impl Imouto { /// Create a serialisable image of this store by cloning each post into it. pub async fn freeze(&self) -> Freeze { let read = &self.all; let mut sis = Freeze{ posts: Vec::with_capacity(read.len()), }; for (_, post) in read.iter() { sis.posts.push(post.read().await.clone()); } sis } /// Consume into a serialisable image of this store. /// /// # Fails /// If references to any posts are still held elsewhere. pub fn try_into_freeze(self) -> Result { let read = self.all; let mut sis = Freeze{ posts: Vec::with_capacity(read.len()), }; for post in read.into_iter() { sis.posts.push(match Arc::try_unwrap(post) { Ok(val) => val.into_inner(), Err(arc) => return Err(FreezeError{held: Some(arc)}), // Err(_arc) => panic!("Reference to post is still being used"),//arc.read().await.clone(), // something else holds the reference, we can't consume it. }); } Ok(sis) } /// Consume into a serialisable image of this store. /// /// # Panics /// If references to any posts are still held elsewhere. pub fn into_freeze(self) -> Freeze { self.try_into_freeze().expect("Failed to consume into freeze") } /// Create a new store from a serialisable image of one by cloning each post in it pub fn unfreeze(freeze: &Freeze) -> Self { let mut posts = Arena::new(); let mut user_map = HashMap::new(); for post in freeze.posts.iter() { let idx = posts.insert(Arc::new(RwLock::new(post.clone()))); user_map.entry(post.owner().clone()) .or_insert_with(|| MaybeVec::new()) .push(idx); } Self { all: posts, user_map, } } /// Create a new store by consuming serialisable image of one by cloning each post in it pub fn from_freeze(freeze: Freeze) -> Self { let mut posts = Arena::new(); let mut user_map = HashMap::new(); for post in freeze.posts.into_iter() { let mapping = user_map.entry(post.owner().clone()) .or_insert_with(|| MaybeVec::new()); let idx = posts.insert(Arc::new(RwLock::new(post))); mapping.push(idx); } Self { all: posts, user_map, } } }