colla 0.1.0

Operational Transformation library for nested documents with text and rich-text
Documentation
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use std::sync::Arc;

use crate::error::ValueError;
use crate::value::FiniteF64;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AttrValue {
    Bool(bool),
    Int(i64),
    Float(FiniteF64),
    String(Arc<str>),
}

impl AttrValue {
    pub fn float(value: f64) -> Result<Self, ValueError> {
        Ok(Self::Float(FiniteF64::new(value)?))
    }
    pub fn string(value: impl Into<String>) -> Self {
        Self::String(Arc::from(value.into()))
    }
}

impl From<bool> for AttrValue {
    fn from(value: bool) -> Self {
        Self::Bool(value)
    }
}
impl From<i64> for AttrValue {
    fn from(value: i64) -> Self {
        Self::Int(value)
    }
}
impl From<String> for AttrValue {
    fn from(value: String) -> Self {
        Self::String(Arc::from(value))
    }
}
impl From<&str> for AttrValue {
    fn from(value: &str) -> Self {
        Self::String(Arc::from(value))
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Attrs(Arc<BTreeMap<String, AttrValue>>);

impl Hash for Attrs {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for (key, value) in self.0.iter() {
            key.hash(state);
            value.hash(state);
        }
    }
}

impl Attrs {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn from_entries<I, K>(entries: I) -> Result<Self, ValueError>
    where
        I: IntoIterator<Item = (K, AttrValue)>,
        K: Into<String>,
    {
        let mut map = BTreeMap::new();
        for (key, value) in entries {
            let key = key.into();
            if map.insert(key.clone(), value).is_some() {
                return Err(ValueError::DuplicateKey(key));
            }
        }
        Ok(Self(Arc::new(map)))
    }
    pub(crate) fn from_btree(map: BTreeMap<String, AttrValue>) -> Self {
        Self(Arc::new(map))
    }
    pub fn get(&self, key: &str) -> Option<&AttrValue> {
        self.0.get(key)
    }
    pub fn iter(&self) -> impl Iterator<Item = (&String, &AttrValue)> {
        self.0.iter()
    }
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub(crate) fn to_btree(&self) -> BTreeMap<String, AttrValue> {
        self.0.as_ref().clone()
    }
    pub fn apply_patch(&self, patch: &AttrPatch) -> Self {
        let mut out = self.to_btree();
        for (key, change) in patch.iter() {
            match change {
                AttrChange::Set(value) => {
                    out.insert(key.clone(), value.clone());
                }
                AttrChange::Remove => {
                    out.remove(key);
                }
            }
        }
        Self::from_btree(out)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AttrChange {
    Set(AttrValue),
    Remove,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AttrPatch(Arc<BTreeMap<String, AttrChange>>);

impl Hash for AttrPatch {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for (key, value) in self.0.iter() {
            key.hash(state);
            value.hash(state);
        }
    }
}

impl AttrPatch {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn from_entries<I, K>(entries: I) -> Result<Self, ValueError>
    where
        I: IntoIterator<Item = (K, AttrChange)>,
        K: Into<String>,
    {
        let mut map = BTreeMap::new();
        for (key, value) in entries {
            let key = key.into();
            if map.insert(key.clone(), value).is_some() {
                return Err(ValueError::DuplicateKey(key));
            }
        }
        Ok(Self(Arc::new(map)))
    }
    pub(crate) fn from_btree(map: BTreeMap<String, AttrChange>) -> Self {
        Self(Arc::new(map))
    }
    pub fn iter(&self) -> impl Iterator<Item = (&String, &AttrChange)> {
        self.0.iter()
    }
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub(crate) fn to_btree(&self) -> BTreeMap<String, AttrChange> {
        self.0.as_ref().clone()
    }
}