//! 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 Imouto { posts: Vec, } impl From for Oneesan { #[inline] fn from(from: Imouto) -> Self { Self::from_freeze(from) } } impl TryFrom for Imouto { type Error = FreezeError; #[inline] fn try_from(from: Oneesan) -> 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 Oneesan { /// Create a serialisable image of this store by cloning each post into it. pub async fn freeze(&self) -> Imouto { let read = self.posts.read().await; let mut sis = Imouto{ posts: Vec::with_capacity(read.0.len()), }; for (_, post) in read.0.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.posts.into_inner(); let mut sis = Imouto{ posts: Vec::with_capacity(read.0.len()), }; for post in read.0.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) -> Imouto { 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: &Imouto) -> 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 { posts: RwLock::new((posts, user_map)) } } /// Create a new store by consuming serialisable image of one by cloning each post in it pub fn from_freeze(freeze: Imouto) -> 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 { posts: RwLock::new((posts, user_map)) } } }