use std::{collections::HashMap, fmt, str::FromStr};
use crate::crdt::{
CRDTError,
traits::{CRDT, Data},
};
pub mod list;
#[cfg(test)]
mod node_tests;
pub mod path;
pub mod value;
pub use list::List;
pub use path::{Path, PathBuf, PathError};
pub use value::Value;
pub use crate::path;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Doc {
children: HashMap<String, Value>,
}
impl Doc {
pub fn new() -> Self {
Self {
children: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.children.values().all(|v| matches!(v, Value::Deleted))
}
pub fn len(&self) -> usize {
self.children
.values()
.filter(|v| !matches!(v, Value::Deleted))
.count()
}
pub fn contains_key(&self, key: impl AsRef<Path>) -> bool {
let path_buf = PathBuf::from_str(key.as_ref().as_str()).unwrap(); self.get(&path_buf).is_some()
}
pub fn is_tombstone(&self, key: impl AsRef<Path>) -> bool {
let path_str = key.as_ref().as_str();
if path_str.contains('.') {
false
} else {
matches!(self.children.get(path_str), Some(Value::Deleted))
}
}
pub fn get(&self, key: impl AsRef<Path>) -> Option<&Value> {
let path = key.as_ref();
let segments: Vec<_> = path.components().collect();
if segments.is_empty() {
return None;
}
let first_segment = segments.first()?;
let mut current_value = match self.children.get(*first_segment) {
Some(Value::Deleted) => return None, value => value?,
};
for segment in &segments[1..] {
match current_value {
Value::Doc(doc) => {
current_value = match doc.children.get(*segment) {
Some(Value::Deleted) => return None, value => value?,
};
}
Value::List(list) => {
let index: usize = segment.parse().ok()?;
current_value = list.get(index)?;
}
_ => return None, }
}
Some(current_value)
}
pub fn get_mut(&mut self, key: impl AsRef<Path>) -> Option<&mut Value> {
let path = key.as_ref();
let segments: Vec<_> = path.components().collect();
if segments.is_empty() {
return None;
}
let mut current = self;
for segment in &segments[..segments.len() - 1] {
match current.children.get_mut(*segment) {
Some(Value::Doc(doc)) => {
current = doc;
}
_ => return None, }
}
let final_key = segments.last()?;
match current.children.get_mut(*final_key) {
Some(Value::Deleted) => None, value => value,
}
}
pub fn get_as<'a, T>(&'a self, key: impl AsRef<Path>) -> Option<T>
where
T: TryFrom<&'a Value, Error = CRDTError>,
{
let value = self.get(key)?;
T::try_from(value).ok()
}
pub fn set(&mut self, key: impl AsRef<Path>, value: impl Into<Value>) -> Option<Value> {
let path_str = key.as_ref().as_str();
let path_buf = PathBuf::from_str(path_str).unwrap();
if !path_str.contains('.') {
let old = self.children.insert(path_str.to_string(), value.into());
match old {
Some(Value::Deleted) => None, value => value,
}
} else {
self.set_path(&path_buf, value).unwrap_or_default()
}
}
pub fn try_set(
&mut self,
key: impl AsRef<Path>,
value: impl Into<Value>,
) -> crate::Result<Option<Value>> {
let path_str = key.as_ref().as_str();
let path_buf = PathBuf::from_str(path_str).unwrap();
if path_str.is_empty() {
return Err(crate::crdt::CRDTError::InvalidPath {
path: "empty path (not allowed for setting values)".to_string(),
}
.into());
}
if !path_str.contains('.') {
let old = self.children.insert(path_str.to_string(), value.into());
Ok(match old {
Some(Value::Deleted) => None, value => value,
})
} else {
self.set_path(&path_buf, value).map_err(Into::into)
}
}
pub fn set_path(
&mut self,
path: impl AsRef<Path>,
value: impl Into<Value>,
) -> Result<Option<Value>, CRDTError> {
let path = path.as_ref();
let segments: Vec<_> = path.components().collect();
if segments.is_empty() {
return Err(CRDTError::InvalidPath {
path: "(empty path)".to_string(),
});
}
let mut current = self;
for segment in &segments[..segments.len() - 1] {
let entry = current
.children
.entry(segment.to_string())
.or_insert_with(|| Value::Doc(Doc::new()));
match entry {
Value::Doc(doc) => {
current = doc;
}
Value::Deleted => {
*entry = Value::Doc(Doc::new());
match entry {
Value::Doc(doc) => current = doc,
_ => unreachable!(),
}
}
_ => {
*entry = Value::Doc(Doc::new());
match entry {
Value::Doc(doc) => current = doc,
_ => unreachable!(),
}
}
}
}
let final_key = segments.last().unwrap();
let old = current.children.insert(final_key.to_string(), value.into());
Ok(match old {
Some(Value::Deleted) => None, value => value,
})
}
pub fn remove(&mut self, key: impl AsRef<Path>) -> Option<Value> {
let path_str = key.as_ref().as_str();
if !path_str.contains('.') {
let key = path_str.to_string();
let old_value = self.children.get(&key).cloned();
self.children.insert(key, Value::Deleted);
match old_value {
Some(Value::Deleted) => None, value => value,
}
} else {
let _current = self.get(key)?.clone();
None
}
}
pub fn delete(&mut self, key: impl AsRef<Path>) -> bool {
let path_str = key.as_ref().as_str();
if !path_str.contains('.') {
self.remove(path_str).is_some()
} else {
false
}
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Value)> {
self.children
.iter()
.filter(|(_, v)| !matches!(v, Value::Deleted))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&String, &mut Value)> {
self.children.iter_mut()
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.children
.iter()
.filter(|(_, v)| !matches!(v, Value::Deleted))
.map(|(k, _)| k)
}
pub fn values(&self) -> impl Iterator<Item = &Value> {
self.children
.values()
.filter(|v| !matches!(v, Value::Deleted))
}
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Value> {
self.children.values_mut()
}
pub fn clear(&mut self) {
let keys: Vec<_> = self.children.keys().cloned().collect();
for key in keys {
self.children.insert(key, Value::Deleted);
}
}
pub fn to_json_string(&self) -> String {
let mut result = String::with_capacity(self.children.len() * 16);
result.push('{');
let mut first = true;
for (key, value) in self.iter() {
if !first {
result.push(',');
}
result.push_str(&format!("\"{}\":{}", key, value.to_json_string()));
first = false;
}
result.push('}');
result
}
}
impl CRDT for Doc {
fn merge(&self, other: &Self) -> crate::Result<Self> {
let mut result = self.clone();
for (key, other_value) in &other.children {
match result.children.get_mut(key) {
Some(self_value) => {
self_value.merge(other_value);
}
None => {
result.children.insert(key.clone(), other_value.clone());
}
}
}
Ok(result)
}
}
impl Default for Doc {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for Doc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
let mut first = true;
for (key, value) in self.iter() {
if !first {
write!(f, ", ")?;
}
write!(f, "{key}: {value}")?;
first = false;
}
write!(f, "}}")
}
}
impl FromIterator<(String, Value)> for Doc {
fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
let mut doc = Doc::new();
for (key, value) in iter {
doc.set(key, value);
}
doc
}
}
impl Doc {
pub fn with(mut self, key: impl AsRef<Path>, value: impl Into<Value>) -> Self {
self.set(key, value);
self
}
pub fn with_bool(self, key: impl AsRef<Path>, value: bool) -> Self {
self.with(key, Value::Bool(value))
}
pub fn with_int(self, key: impl AsRef<Path>, value: i64) -> Self {
self.with(key, Value::Int(value))
}
pub fn with_text(self, key: impl AsRef<Path>, value: impl Into<String>) -> Self {
self.with(key, Value::Text(value.into()))
}
pub fn with_list(self, key: impl AsRef<Path>, value: impl Into<List>) -> Self {
self.with(key, Value::List(value.into()))
}
pub fn with_doc(self, key: impl AsRef<Path>, value: impl Into<Doc>) -> Self {
self.with(key, Value::Doc(value.into()))
}
}
impl Doc {
pub fn set_raw(&mut self, key: impl AsRef<Path>, value: Value) -> &mut Self {
self.set(key, value);
self
}
pub fn set_json<T>(&mut self, key: impl AsRef<Path>, value: T) -> crate::Result<&mut Self>
where
T: serde::Serialize,
{
let path_str = key.as_ref().as_str();
if !path_str.contains('.') {
let json =
serde_json::to_string(&value).map_err(|e| CRDTError::SerializationFailed {
reason: e.to_string(),
})?;
self.set(path_str, Value::Text(json));
} else {
let json =
serde_json::to_string(&value).map_err(|e| CRDTError::SerializationFailed {
reason: e.to_string(),
})?;
self.set(key, Value::Text(json));
}
Ok(self)
}
pub fn get_json<T>(&self, key: impl AsRef<Path>) -> crate::Result<T>
where
T: for<'de> serde::Deserialize<'de>,
{
let path_str = key.as_ref().as_str();
if !path_str.contains('.') {
match self.children.get(path_str) {
Some(Value::Text(json)) => serde_json::from_str::<T>(json).map_err(|e| {
CRDTError::DeserializationFailed {
reason: format!("Failed to deserialize JSON for key '{path_str}': {e}"),
}
.into()
}),
Some(Value::Deleted) => Err(CRDTError::ElementNotFound {
key: path_str.to_string(),
}
.into()),
Some(other) => Err(CRDTError::TypeMismatch {
expected: "Text (JSON string)".to_string(),
actual: format!("{other:?}"),
}
.into()),
None => Err(CRDTError::ElementNotFound {
key: path_str.to_string(),
}
.into()),
}
} else {
let key_ref = key.as_ref();
let value = self
.get(key_ref)
.ok_or_else(|| CRDTError::ElementNotFound {
key: path_str.to_string(),
})?;
match value {
Value::Text(json) => serde_json::from_str(json).map_err(|e| {
CRDTError::DeserializationFailed {
reason: format!("Failed to deserialize JSON for path '{path_str}': {e}"),
}
.into()
}),
_ => Err(CRDTError::TypeMismatch {
expected: "JSON string".to_string(),
actual: format!("{value:?}"),
}
.into()),
}
}
}
pub fn set_string(&mut self, key: impl AsRef<Path>, value: impl Into<String>) -> &mut Self {
self.set(key, Value::Text(value.into()));
self
}
pub fn set_doc(&mut self, key: impl AsRef<Path>, value: Doc) -> &mut Self {
self.set(key, Value::Doc(value));
self
}
pub fn get_doc(&self, key: impl AsRef<Path>) -> Option<&Doc> {
match self.get(key)? {
Value::Doc(node) => Some(node),
_ => None,
}
}
pub fn get_doc_mut(&mut self, key: impl AsRef<Path>) -> Option<&mut Doc> {
match self.get_mut(key)? {
Value::Doc(node) => Some(node),
_ => None,
}
}
pub fn as_hashmap(&self) -> &HashMap<String, Value> {
&self.children
}
pub fn as_hashmap_mut(&mut self) -> &mut HashMap<String, Value> {
&mut self.children
}
pub fn list_add<K>(&mut self, key: K, value: Value) -> crate::Result<String>
where
K: Into<String>,
{
let key = key.into();
let list = match self.children.get_mut(&key) {
Some(Value::List(list)) => list,
Some(Value::Deleted) => {
let mut new_list = List::new();
let index = new_list.push(value);
self.children.insert(key, Value::List(new_list));
return Ok(index.to_string());
}
Some(_) => {
return Err(CRDTError::TypeMismatch {
expected: "List".to_string(),
actual: "other type".to_string(),
}
.into());
}
None => {
let mut new_list = List::new();
let index = new_list.push(value);
self.children.insert(key, Value::List(new_list));
return Ok(index.to_string());
}
};
Ok(list.push(value).to_string())
}
pub fn list_remove<K>(&mut self, key: K, id: &str) -> crate::Result<bool>
where
K: Into<String> + AsRef<str>,
{
let index: usize = id.parse().map_err(|_| CRDTError::InvalidPath {
path: format!("Invalid list index: {id}"),
})?;
match self.children.get_mut(key.as_ref()) {
Some(Value::List(list)) => Ok(list.remove(index).is_some()),
Some(_) => Err(CRDTError::TypeMismatch {
expected: "List".to_string(),
actual: "other type".to_string(),
}
.into()),
None => Ok(false), }
}
pub fn list_get<K>(&self, key: K, id: &str) -> Option<&Value>
where
K: AsRef<str>,
{
let index: usize = id.parse().ok()?;
match self.children.get(key.as_ref()) {
Some(Value::List(list)) => list.get(index),
Some(Value::Deleted) => None, _ => None,
}
}
pub fn list_ids<K>(&self, key: K) -> Vec<String>
where
K: AsRef<str>,
{
match self.children.get(key.as_ref()) {
Some(Value::List(list)) => {
(0..list.len()).map(|i| i.to_string()).collect()
}
_ => Vec::new(),
}
}
pub fn list_len<K>(&self, key: K) -> usize
where
K: AsRef<str>,
{
match self.children.get(key.as_ref()) {
Some(Value::List(list)) => list.len(),
_ => 0,
}
}
pub fn list_is_empty<K>(&self, key: K) -> bool
where
K: AsRef<str>,
{
match self.children.get(key.as_ref()) {
Some(Value::List(list)) => list.is_empty(),
_ => true, }
}
pub fn list_clear<K>(&mut self, key: K) -> crate::Result<()>
where
K: AsRef<str>,
{
match self.children.get_mut(key.as_ref()) {
Some(Value::List(list)) => {
list.clear();
Ok(())
}
Some(_) => Err(CRDTError::TypeMismatch {
expected: "List".to_string(),
actual: "other type".to_string(),
}
.into()),
None => Ok(()), }
}
pub fn get_or_insert(
&mut self,
key: impl AsRef<Path> + Clone,
default: impl Into<Value>,
) -> &mut Value {
if !self.contains_key(key.clone()) {
self.set(key.clone(), default);
}
self.get_mut(key).expect("Key should exist after insert")
}
pub fn modify<T, F>(&mut self, key: impl AsRef<Path> + Clone, f: F) -> crate::Result<()>
where
T: for<'a> TryFrom<&'a Value, Error = CRDTError> + Into<Value>,
F: FnOnce(&mut T),
{
let mut value = self.get_as::<T>(key.clone()).ok_or_else(|| {
crate::Error::CRDT(CRDTError::ElementNotFound {
key: key.as_ref().as_str().to_string(),
})
})?;
f(&mut value);
self.set(key, value);
Ok(())
}
}
impl Data for Doc {}