y-octo 0.0.3

High-performance and thread-safe CRDT implementation compatible with Yjs
Documentation
use std::fmt::Display;

use super::*;

#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    Any(Any),
    Doc(Doc),
    Array(Array),
    Map(Map),
    Text(Text),
    XMLElement(XMLElement),
    XMLFragment(XMLFragment),
    XMLHook(XMLHook),
    XMLText(XMLText),
}

impl Value {
    pub fn to_any(&self) -> Option<Any> {
        match self {
            Value::Any(any) => Some(any.clone()),
            _ => None,
        }
    }

    pub fn to_array(&self) -> Option<Array> {
        match self {
            Value::Array(array) => Some(array.clone()),
            _ => None,
        }
    }

    pub fn to_map(&self) -> Option<Map> {
        match self {
            Value::Map(map) => Some(map.clone()),
            _ => None,
        }
    }

    pub fn to_text(&self) -> Option<Text> {
        match self {
            Value::Text(text) => Some(text.clone()),
            _ => None,
        }
    }

    pub fn from_vec<T: Into<Any>>(el: Vec<T>) -> Self {
        Value::Any(Any::Array(el.into_iter().map(|item| item.into()).collect::<Vec<_>>()))
    }
}

impl From<&Content> for Value {
    fn from(value: &Content) -> Value {
        match value {
            Content::Any(any) => Value::Any(if any.len() == 1 {
                any[0].clone()
            } else {
                Any::Array(any.clone())
            }),
            Content::String(s) => Value::Any(Any::String(s.clone())),
            Content::Json(json) => Value::Any(Any::Array(
                json.iter()
                    .map(|item| {
                        if let Some(s) = item {
                            Any::String(s.clone())
                        } else {
                            Any::Undefined
                        }
                    })
                    .collect::<Vec<_>>(),
            )),
            Content::Binary(buf) => Value::Any(Any::Binary(buf.clone())),
            Content::Embed(v) => Value::Any(v.clone()),
            Content::Type(ty) => match ty.ty().unwrap().kind {
                YTypeKind::Array => Value::Array(Array::from_unchecked(ty.clone())),
                YTypeKind::Map => Value::Map(Map::from_unchecked(ty.clone())),
                YTypeKind::Text => Value::Text(Text::from_unchecked(ty.clone())),
                YTypeKind::XMLElement => Value::XMLElement(XMLElement::from_unchecked(ty.clone())),
                YTypeKind::XMLFragment => Value::XMLFragment(XMLFragment::from_unchecked(ty.clone())),
                YTypeKind::XMLHook => Value::XMLHook(XMLHook::from_unchecked(ty.clone())),
                YTypeKind::XMLText => Value::XMLText(XMLText::from_unchecked(ty.clone())),
                // actually unreachable
                YTypeKind::Unknown => Value::Any(Any::Undefined),
            },
            Content::Doc { guid: _, opts } => Value::Doc(
                DocOptions::try_from(opts.clone())
                    .expect("Failed to parse doc options")
                    .build(),
            ),
            Content::Format { .. } => unimplemented!(),
            // actually unreachable
            Content::Deleted(_) => Value::Any(Any::Undefined),
        }
    }
}

impl From<Value> for Content {
    fn from(value: Value) -> Self {
        match value {
            Value::Any(any) => Content::from(any),
            Value::Doc(doc) => Content::Doc {
                guid: doc.guid().to_owned(),
                opts: Any::from(doc.options().clone()),
            },
            Value::Array(v) => Content::Type(v.0),
            Value::Map(v) => Content::Type(v.0),
            Value::Text(v) => Content::Type(v.0),
            Value::XMLElement(v) => Content::Type(v.0),
            Value::XMLFragment(v) => Content::Type(v.0),
            Value::XMLHook(v) => Content::Type(v.0),
            Value::XMLText(v) => Content::Type(v.0),
        }
    }
}

impl<T: Into<Any>> From<T> for Value {
    fn from(value: T) -> Self {
        Value::Any(value.into())
    }
}

impl From<Doc> for Value {
    fn from(value: Doc) -> Self {
        Value::Doc(value)
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Value::Any(any) => write!(f, "{any}"),
            Value::Text(text) => write!(f, "{text}"),
            _ => write!(f, ""),
        }
    }
}

impl serde::Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            Self::Any(any) => any.serialize(serializer),
            Self::Array(array) => array.serialize(serializer),
            Self::Map(map) => map.serialize(serializer),
            Self::Text(text) => text.serialize(serializer),
            // Self::XMLElement(xml_element) => xml_element.serialize(serializer),
            // Self::XMLFragment(xml_fragment) => xml_fragment.serialize(serializer),
            // Self::XMLHook(xml_hook) => xml_hook.serialize(serializer),
            // Self::XMLText(xml_text) => xml_text.serialize(serializer),
            // Self::Doc(doc) => doc.serialize(serializer),
            _ => serializer.serialize_none(),
        }
    }
}