loro/crates/loro-internal/src/container.rs

336 lines
10 KiB
Rust
Raw Normal View History

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.
//!
2022-10-20 08:29:38 +00:00
use crate::{
event::{Observer, ObserverHandler, SubscriptionID},
2022-11-23 17:01:40 +00:00
hierarchy::Hierarchy,
log_store::ImportContext,
op::{InnerContent, RemoteContent, RichOp},
2023-01-03 03:46:21 +00:00
version::PatchedVersionVector,
2023-03-17 03:07:30 +00:00
InternalString, LoroError, LoroValue, ID,
2022-10-20 08:29:38 +00:00
};
2022-09-01 16:59:39 +00:00
2022-11-11 16:00:54 +00:00
use serde::{Deserialize, Serialize};
2022-11-21 11:52:09 +00:00
use smallvec::SmallVec;
2022-10-31 14:36:54 +00:00
2022-12-07 13:51:18 +00:00
use std::{
any::Any,
fmt::{Debug, Display},
};
2023-03-21 02:50:18 +00:00
use self::pool_mapping::StateContent;
pub mod pool_mapping;
pub mod registry;
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;
2022-11-21 08:02:29 +00:00
mod pool;
2022-07-17 17:00:50 +00:00
pub mod text;
2022-11-18 08:31:00 +00:00
2023-03-21 02:50:18 +00:00
pub use registry::ContainerIdx;
2022-12-13 14:59:29 +00:00
// Note: It will be encoded into binary format, so the order of its fields should not be changed.
2022-11-18 08:40:11 +00:00
#[cfg_attr(feature = "test_utils", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
2022-11-18 08:31:00 +00:00
pub enum ContainerType {
/// See [`crate::text::TextContent`]
Text,
Map,
List,
// TODO: Users can define their own container types.
// Custom(u16),
}
2022-12-07 13:51:18 +00:00
impl Display for ContainerType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
ContainerType::Text => "Text",
ContainerType::Map => "Map",
ContainerType::List => "List",
})
}
}
impl TryFrom<&str> for ContainerType {
type Error = LoroError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"Text" => Ok(ContainerType::Text),
"Map" => Ok(ContainerType::Map),
"List" => Ok(ContainerType::List),
_ => Err(LoroError::DecodeError(
("Unknown container type".to_string() + value).into(),
)),
}
}
}
pub trait ContainerTrait: Debug + Any + Unpin + Send + Sync {
2022-07-17 17:00:50 +00:00
fn id(&self) -> &ContainerID;
fn idx(&self) -> ContainerIdx;
2022-07-25 05:00:22 +00:00
fn type_(&self) -> ContainerType;
2022-11-08 06:59:13 +00:00
fn get_value(&self) -> LoroValue;
2022-11-18 09:30:27 +00:00
/// Initialize the pool mapping in current state for this container
fn initialize_pool_mapping(&mut self);
/// Encode and release the pool mapping, and return the encoded bytes.
fn encode_and_release_pool_mapping(&mut self) -> StateContent;
/// Convert an op content to new op content(s) that includes the data of the new state of the pool mapping.
fn to_export_snapshot(
&mut self,
content: &InnerContent,
gc: bool,
) -> SmallVec<[InnerContent; 1]>;
/// Decode the pool mapping from the bytes and apply it to the container.
2022-12-19 09:20:21 +00:00
fn to_import_snapshot(
&mut self,
state_content: StateContent,
hierarchy: &mut Hierarchy,
ctx: &mut ImportContext,
);
2022-11-18 09:30:27 +00:00
/// convert an op content to exported format that includes the raw data
2022-11-21 11:52:09 +00:00
fn to_export(&mut self, content: InnerContent, gc: bool) -> SmallVec<[RemoteContent; 1]>;
2022-11-18 09:30:27 +00:00
/// convert an op content to compact imported format
2022-11-21 11:52:09 +00:00
fn to_import(&mut self, content: RemoteContent) -> InnerContent;
2022-11-18 09:30:27 +00:00
2022-12-09 13:20:59 +00:00
/// Initialize tracker at the target version
fn tracker_init(&mut self, vv: &PatchedVersionVector);
2022-11-18 09:30:27 +00:00
/// Tracker need to checkout to target version in order to apply the op.
fn tracker_checkout(&mut self, vv: &PatchedVersionVector);
2022-11-18 09:30:27 +00:00
/// Apply the op to the tracker.
///
/// Here we have not updated the container state yet. Because we
/// need to calculate the effect of the op for [crate::List] and
/// [crate::Text] by using tracker.
fn track_apply(
&mut self,
hierarchy: &mut Hierarchy,
op: &RichOp,
import_context: &mut ImportContext,
);
2022-11-18 09:30:27 +00:00
/// Apply the effect of the op directly to the state.
fn update_state_directly(
&mut self,
hierarchy: &mut Hierarchy,
op: &RichOp,
import_context: &mut ImportContext,
);
2022-11-18 09:30:27 +00:00
/// Make tracker iterate over the target spans and apply the calculated
/// effects to the container state
fn apply_tracked_effects_from(
&mut self,
hierarchy: &mut Hierarchy,
import_context: &mut ImportContext,
);
fn subscribe(
&self,
hierarchy: &mut Hierarchy,
handler: ObserverHandler,
deep: bool,
once: bool,
) -> SubscriptionID {
let observer = Observer::new_container(handler, self.id().clone())
.with_deep(deep)
.with_once(once);
hierarchy.subscribe(observer)
}
fn unsubscribe(&self, hierarchy: &mut Hierarchy, subscription: SubscriptionID) {
hierarchy.unsubscribe(subscription);
}
2022-07-18 09:58:24 +00:00
}
2022-10-31 04:37:20 +00:00
/// [ContainerID] includes the Op's [ID] and the type. So it's impossible to have
/// the same [ContainerID] with conflict [ContainerType].
///
/// This structure is really cheap to clone.
///
/// String representation:
///
/// - Root Container: `/<name>:<type>`
/// - Normal Container: `<counter>@<client>:<type>`
///
/// Note: It will be encoded into binary format, so the order of its fields should not be changed.
2022-11-11 16:00:54 +00:00
#[derive(Hash, PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
2022-07-17 08:33:38 +00:00
pub enum ContainerID {
/// Root container does not need an op to create. It can be created implicitly.
2022-07-17 08:33:38 +00:00
Root {
2022-07-17 17:00:50 +00:00
name: InternalString,
2022-07-17 08:33:38 +00:00
container_type: ContainerType,
},
2022-07-18 06:58:33 +00:00
Normal {
id: ID,
container_type: ContainerType,
},
}
impl Display for ContainerID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ContainerID::Root {
name,
container_type,
} => f.write_fmt(format_args!("/{}:{}", name, container_type))?,
ContainerID::Normal { id, container_type } => {
f.write_fmt(format_args!("{}:{}", id, container_type))?
}
};
Ok(())
}
}
impl TryFrom<&str> for ContainerID {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut parts = value.split(':');
let id = parts.next().ok_or(())?;
let container_type = parts.next().ok_or(())?;
let container_type = ContainerType::try_from(container_type).map_err(|_| ())?;
if let Some(id) = id.strip_prefix('/') {
Ok(ContainerID::Root {
name: id.into(),
container_type,
})
} else {
let mut parts = id.split('@');
let counter = parts.next().ok_or(())?.parse().map_err(|_| ())?;
let client = parts.next().ok_or(())?.parse().map_err(|_| ())?;
Ok(ContainerID::Normal {
id: ID {
counter,
client_id: client,
},
container_type,
})
}
}
}
2022-11-11 14:26:06 +00:00
pub enum ContainerIdRaw {
Root { name: InternalString },
Normal { id: ID },
}
2023-01-11 13:40:16 +00:00
impl<T: Into<InternalString>> From<T> for ContainerIdRaw {
fn from(value: T) -> Self {
ContainerIdRaw::Root { name: value.into() }
2022-11-11 14:26:06 +00:00
}
}
impl From<ID> for ContainerIdRaw {
fn from(id: ID) -> Self {
ContainerIdRaw::Normal { id }
}
}
impl From<&ContainerID> for ContainerIdRaw {
fn from(id: &ContainerID) -> Self {
match id {
ContainerID::Root { name, .. } => ContainerIdRaw::Root { name: name.clone() },
ContainerID::Normal { id, .. } => ContainerIdRaw::Normal { id: *id },
}
}
}
impl From<ContainerID> for ContainerIdRaw {
fn from(id: ContainerID) -> Self {
match id {
ContainerID::Root { name, .. } => ContainerIdRaw::Root { name },
ContainerID::Normal { id, .. } => ContainerIdRaw::Normal { id },
}
}
}
impl ContainerIdRaw {
pub fn with_type(self, container_type: ContainerType) -> ContainerID {
match self {
ContainerIdRaw::Root { name } => ContainerID::Root {
name,
container_type,
},
ContainerIdRaw::Normal { id } => ContainerID::Normal { id, container_type },
}
}
}
2022-07-18 06:58:33 +00:00
impl ContainerID {
#[inline]
pub fn new_normal(id: ID, container_type: ContainerType) -> Self {
ContainerID::Normal { id, container_type }
}
#[inline]
2022-10-28 09:19:58 +00:00
pub fn new_root(name: &str, container_type: ContainerType) -> Self {
2022-07-18 06:58:33 +00:00
ContainerID::Root {
2022-10-28 09:19:58 +00:00
name: name.into(),
2022-07-18 06:58:33 +00:00
container_type,
}
}
#[inline]
pub fn is_root(&self) -> bool {
matches!(self, ContainerID::Root { .. })
}
#[inline]
pub fn is_normal(&self) -> bool {
matches!(self, ContainerID::Normal { .. })
}
#[inline]
pub fn name(&self) -> &InternalString {
match self {
ContainerID::Root { name, .. } => name,
ContainerID::Normal { .. } => unreachable!(),
}
}
#[inline]
pub fn container_type(&self) -> ContainerType {
match self {
ContainerID::Root { container_type, .. } => *container_type,
ContainerID::Normal { container_type, .. } => *container_type,
}
}
2022-07-17 08:33:38 +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();
assert_eq!(s, "12@12:List");
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();
assert_eq!(s, "/123:Map");
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();
assert_eq!(s, "/kkk:Text");
let actual = ContainerID::try_from(s.as_str()).unwrap();
assert_eq!(actual, container_id);
}
}