//! 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 < post ::Post > ,
}
impl From < Freeze > for Imouto
{
#[ inline ] fn from ( from : Freeze ) -> Self
{
Self ::from_freeze ( from )
}
}
impl TryFrom < Imouto > for Freeze
{
type Error = FreezeError ;
#[ inline ] fn try_from ( from : Imouto ) -> Result < Self , Self ::Error >
{
from . try_into_freeze ( )
}
}
/// Error returned when a freeze operation fails
#[ derive(Debug) ]
pub struct FreezeError {
held : Option < Arc < RwLock < post ::Post > > > ,
}
impl FreezeError
{
/// The post associated with this error, if there is one.
pub fn post ( & self ) -> Option < & Arc < RwLock < post ::Post > > >
{
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 < Freeze , FreezeError >
{
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 ,
}
}
}