2022-07-18 05:53:16 +00:00
//! CRDT [Container]. Each container may have different CRDT type [ContainerType].
//! Each [Op] has an associated container. It's the [Container]'s responsibility to
//! calculate the state from the [Op]s.
//!
//! Every [Container] can take a [Snapshot], which contains [crate::LoroValue] that describes the state.
//!
2023-07-31 03:49:55 +00:00
use crate ::{ arena ::SharedArena , InternalString , ID } ;
2022-09-01 16:59:39 +00:00
2023-07-31 03:49:55 +00:00
pub mod idx {
use super ::super ::ContainerType ;
2022-10-31 14:36:54 +00:00
2023-07-31 03:49:55 +00:00
/// Inner representation for ContainerID.
/// It contains the unique index for the container and the type of the container.
/// It uses top 4 bits to represent the type of the container.
///
/// It's only used inside this crate and should not be exposed to the user.
///
/// TODO: make this type private in this crate only
///
// During a transaction, we may create some containers which are deleted later. And these containers also need a unique ContainerIdx.
// So when we encode snapshot, we need to sort the containers by ContainerIdx and change the `container` of ops to the index of containers.
// An empty store decodes the snapshot, it will create these containers in a sequence of natural numbers so that containers and ops can correspond one-to-one
#[ derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash) ]
pub struct ContainerIdx ( u32 ) ;
impl std ::fmt ::Debug for ContainerIdx {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
f . debug_tuple ( " ContainerIdx " )
. field ( & self . get_type ( ) )
. field ( & self . to_index ( ) )
. finish ( )
}
}
2022-12-09 08:24:27 +00:00
2023-07-31 03:49:55 +00:00
impl ContainerIdx {
pub ( crate ) const TYPE_MASK : u32 = 0b1111 < < 28 ;
pub ( crate ) const INDEX_MASK : u32 = ! Self ::TYPE_MASK ;
#[ allow(unused) ]
pub ( crate ) fn get_type ( self ) -> ContainerType {
match ( self . 0 & Self ::TYPE_MASK ) > > 28 {
0 = > ContainerType ::Map ,
1 = > ContainerType ::List ,
2 = > ContainerType ::Text ,
2023-10-30 03:13:52 +00:00
3 = > ContainerType ::Tree ,
2023-07-31 03:49:55 +00:00
_ = > unreachable! ( ) ,
}
}
#[ allow(unused) ]
pub ( crate ) fn to_index ( self ) -> u32 {
self . 0 & Self ::INDEX_MASK
}
pub ( crate ) fn from_index_and_type ( index : u32 , container_type : ContainerType ) -> Self {
let prefix : u32 = match container_type {
ContainerType ::Map = > 0 ,
ContainerType ::List = > 1 ,
ContainerType ::Text = > 2 ,
2023-10-30 03:13:52 +00:00
ContainerType ::Tree = > 3 ,
2023-07-31 03:49:55 +00:00
} < < 28 ;
Self ( prefix | index )
}
}
}
2022-07-18 05:53:16 +00:00
2022-08-26 12:19:38 +00:00
pub mod list ;
2022-07-17 17:00:50 +00:00
pub mod map ;
2023-10-29 06:02:13 +00:00
pub mod richtext ;
2023-11-02 09:13:08 +00:00
pub mod tree ;
2022-11-18 08:31:00 +00:00
2023-07-31 03:49:55 +00:00
use idx ::ContainerIdx ;
2022-12-07 13:51:18 +00:00
2023-07-14 16:47:47 +00:00
pub use loro_common ::ContainerType ;
2022-12-07 13:51:18 +00:00
2023-07-31 03:49:55 +00:00
pub use loro_common ::ContainerID ;
2022-11-18 09:30:27 +00:00
2022-11-11 14:26:06 +00:00
pub enum ContainerIdRaw {
Root { name : InternalString } ,
Normal { id : ID } ,
}
2023-07-28 18:03:51 +00:00
pub trait IntoContainerId {
fn into_container_id ( self , arena : & SharedArena , kind : ContainerType ) -> ContainerID ;
}
impl IntoContainerId for String {
fn into_container_id ( self , _arena : & SharedArena , kind : ContainerType ) -> ContainerID {
ContainerID ::Root {
name : InternalString ::from ( self . as_str ( ) ) ,
container_type : kind ,
}
}
}
impl < ' a > IntoContainerId for & ' a str {
fn into_container_id ( self , _arena : & SharedArena , kind : ContainerType ) -> ContainerID {
ContainerID ::Root {
name : InternalString ::from ( self ) ,
container_type : kind ,
}
}
}
impl IntoContainerId for ContainerID {
fn into_container_id ( self , _arena : & SharedArena , _kind : ContainerType ) -> ContainerID {
self
}
}
2023-12-28 10:00:34 +00:00
impl IntoContainerId for & ContainerID {
fn into_container_id ( self , _arena : & SharedArena , _kind : ContainerType ) -> ContainerID {
self . clone ( )
}
}
2023-07-28 18:03:51 +00:00
impl IntoContainerId for ContainerIdx {
fn into_container_id ( self , arena : & SharedArena , kind : ContainerType ) -> ContainerID {
assert_eq! ( self . get_type ( ) , kind ) ;
arena . get_container_id ( self ) . unwrap ( )
}
}
2023-12-28 10:00:34 +00:00
impl IntoContainerId for & ContainerIdx {
fn into_container_id ( self , arena : & SharedArena , kind : ContainerType ) -> ContainerID {
assert_eq! ( self . get_type ( ) , kind ) ;
arena . get_container_id ( * self ) . unwrap ( )
}
}
2023-07-14 16:47:47 +00:00
impl From < String > for ContainerIdRaw {
fn from ( value : String ) -> Self {
ContainerIdRaw ::Root { name : value . into ( ) }
}
}
impl < ' a > From < & ' a str > for ContainerIdRaw {
fn from ( value : & ' a str ) -> Self {
2023-01-11 13:40:16 +00:00
ContainerIdRaw ::Root { name : value . into ( ) }
2022-11-11 14:26:06 +00:00
}
}
impl From < & ContainerID > for ContainerIdRaw {
fn from ( id : & ContainerID ) -> Self {
match id {
ContainerID ::Root { name , .. } = > ContainerIdRaw ::Root { name : name . clone ( ) } ,
2023-07-04 04:33:03 +00:00
ContainerID ::Normal { peer , counter , .. } = > ContainerIdRaw ::Normal {
id : ID ::new ( * peer , * counter ) ,
} ,
2022-11-11 14:26:06 +00:00
}
}
}
impl From < ContainerID > for ContainerIdRaw {
fn from ( id : ContainerID ) -> Self {
match id {
ContainerID ::Root { name , .. } = > ContainerIdRaw ::Root { name } ,
2023-07-04 04:33:03 +00:00
ContainerID ::Normal { peer , counter , .. } = > ContainerIdRaw ::Normal {
id : ID ::new ( peer , counter ) ,
} ,
2022-11-11 14:26:06 +00:00
}
}
}
impl ContainerIdRaw {
pub fn with_type ( self , container_type : ContainerType ) -> ContainerID {
match self {
ContainerIdRaw ::Root { name } = > ContainerID ::Root {
name ,
container_type ,
} ,
2023-07-04 04:33:03 +00:00
ContainerIdRaw ::Normal { id } = > ContainerID ::Normal {
peer : id . peer ,
counter : id . counter ,
container_type ,
} ,
2022-11-11 14:26:06 +00:00
}
}
}
2023-03-24 06:51:02 +00:00
#[ cfg(test) ]
mod test {
use super ::* ;
#[ test ]
fn container_id_convert ( ) {
let container_id = ContainerID ::new_normal ( ID ::new ( 12 , 12 ) , ContainerType ::List ) ;
let s = container_id . to_string ( ) ;
2023-11-02 09:13:08 +00:00
assert_eq! ( s , " cid:12@C:List " ) ;
2023-03-24 06:51:02 +00:00
let actual = ContainerID ::try_from ( s . as_str ( ) ) . unwrap ( ) ;
assert_eq! ( actual , container_id ) ;
let container_id = ContainerID ::new_root ( " 123 " , ContainerType ::Map ) ;
let s = container_id . to_string ( ) ;
2023-11-02 09:13:08 +00:00
assert_eq! ( s , " cid:root-123:Map " ) ;
2023-03-24 06:51:02 +00:00
let actual = ContainerID ::try_from ( s . as_str ( ) ) . unwrap ( ) ;
assert_eq! ( actual , container_id ) ;
let container_id = ContainerID ::new_root ( " kkk " , ContainerType ::Text ) ;
let s = container_id . to_string ( ) ;
2023-11-02 09:13:08 +00:00
assert_eq! ( s , " cid:root-kkk:Text " ) ;
2023-03-24 06:51:02 +00:00
let actual = ContainerID ::try_from ( s . as_str ( ) ) . unwrap ( ) ;
assert_eq! ( actual , container_id ) ;
}
}