use std::sync::Arc;
use loro::{ContainerTrait, LoroResult, PeerID};
use crate::{
ContainerID, DiffEvent, LoroDoc, LoroValue, LoroValueLike, Subscriber, Subscription,
ValueOrContainer,
};
use super::{LoroCounter, LoroList, LoroMovableList, LoroText, LoroTree};
#[derive(Debug, Clone)]
pub struct LoroMap {
pub(crate) inner: loro::LoroMap,
}
impl LoroMap {
pub fn new() -> Self {
Self {
inner: loro::LoroMap::new(),
}
}
pub fn is_attached(&self) -> bool {
self.inner.is_attached()
}
pub fn get_attached(&self) -> Option<Arc<LoroMap>> {
self.inner
.get_attached()
.map(|x| Arc::new(LoroMap { inner: x }))
}
pub fn delete(&self, key: &str) -> LoroResult<()> {
self.inner.delete(key)
}
pub fn insert(&self, key: &str, value: Arc<dyn LoroValueLike>) -> LoroResult<()> {
self.inner.insert(key, value.as_loro_value())
}
pub fn len(&self) -> u32 {
self.inner.len() as u32
}
pub fn id(&self) -> ContainerID {
self.inner.id().into()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn get(&self, key: &str) -> Option<Arc<dyn ValueOrContainer>> {
self.inner
.get(key)
.map(|v| Arc::new(v) as Arc<dyn ValueOrContainer>)
}
pub fn get_or_create_text_container(
&self,
key: &str,
text: Arc<LoroText>,
) -> LoroResult<Arc<LoroText>> {
#[allow(deprecated)]
let c = self
.inner
.get_or_create_container(key, text.as_ref().clone().inner)?;
Ok(Arc::new(LoroText { inner: c }))
}
pub fn get_or_create_map_container(
&self,
key: &str,
map: Arc<LoroMap>,
) -> LoroResult<Arc<LoroMap>> {
#[allow(deprecated)]
let c = self
.inner
.get_or_create_container(key, map.as_ref().clone().inner)?;
Ok(Arc::new(LoroMap { inner: c }))
}
pub fn get_or_create_tree_container(
&self,
key: &str,
tree: Arc<LoroTree>,
) -> LoroResult<Arc<LoroTree>> {
#[allow(deprecated)]
let c = self
.inner
.get_or_create_container(key, tree.as_ref().clone().inner)?;
Ok(Arc::new(LoroTree { inner: c }))
}
pub fn get_or_create_list_container(
&self,
key: &str,
list: Arc<LoroList>,
) -> LoroResult<Arc<LoroList>> {
#[allow(deprecated)]
let c = self
.inner
.get_or_create_container(key, list.as_ref().clone().inner)?;
Ok(Arc::new(LoroList { inner: c }))
}
pub fn get_or_create_movable_list_container(
&self,
key: &str,
list: Arc<LoroMovableList>,
) -> LoroResult<Arc<LoroMovableList>> {
#[allow(deprecated)]
let c = self
.inner
.get_or_create_container(key, list.as_ref().clone().inner)?;
Ok(Arc::new(LoroMovableList { inner: c }))
}
pub fn get_or_create_counter_container(
&self,
key: &str,
counter: Arc<LoroCounter>,
) -> LoroResult<Arc<LoroCounter>> {
#[allow(deprecated)]
let c = self
.inner
.get_or_create_container(key, counter.as_ref().clone().inner)?;
Ok(Arc::new(LoroCounter { inner: c }))
}
pub fn ensure_mergeable_list(&self, key: &str) -> LoroResult<Arc<LoroList>> {
let c = self.inner.ensure_mergeable_list(key)?;
Ok(Arc::new(LoroList { inner: c }))
}
pub fn ensure_mergeable_map(&self, key: &str) -> LoroResult<Arc<LoroMap>> {
let c = self.inner.ensure_mergeable_map(key)?;
Ok(Arc::new(LoroMap { inner: c }))
}
pub fn ensure_mergeable_tree(&self, key: &str) -> LoroResult<Arc<LoroTree>> {
let c = self.inner.ensure_mergeable_tree(key)?;
Ok(Arc::new(LoroTree { inner: c }))
}
pub fn ensure_mergeable_movable_list(&self, key: &str) -> LoroResult<Arc<LoroMovableList>> {
let c = self.inner.ensure_mergeable_movable_list(key)?;
Ok(Arc::new(LoroMovableList { inner: c }))
}
pub fn ensure_mergeable_text(&self, key: &str) -> LoroResult<Arc<LoroText>> {
let c = self.inner.ensure_mergeable_text(key)?;
Ok(Arc::new(LoroText { inner: c }))
}
pub fn ensure_mergeable_counter(&self, key: &str) -> LoroResult<Arc<LoroCounter>> {
let c = self.inner.ensure_mergeable_counter(key)?;
Ok(Arc::new(LoroCounter { inner: c }))
}
#[inline]
pub fn insert_list_container(
&self,
key: &str,
child: Arc<LoroList>,
) -> LoroResult<Arc<LoroList>> {
let c = self
.inner
.insert_container(key, child.as_ref().clone().inner)?;
Ok(Arc::new(LoroList { inner: c }))
}
#[inline]
pub fn insert_map_container(&self, key: &str, child: Arc<LoroMap>) -> LoroResult<Arc<LoroMap>> {
let c = self
.inner
.insert_container(key, child.as_ref().clone().inner)?;
Ok(Arc::new(LoroMap { inner: c }))
}
#[inline]
pub fn insert_text_container(
&self,
key: &str,
child: Arc<LoroText>,
) -> LoroResult<Arc<LoroText>> {
let c = self
.inner
.insert_container(key, child.as_ref().clone().inner)?;
Ok(Arc::new(LoroText { inner: c }))
}
#[inline]
pub fn insert_tree_container(
&self,
key: &str,
child: Arc<LoroTree>,
) -> LoroResult<Arc<LoroTree>> {
let c = self
.inner
.insert_container(key, child.as_ref().clone().inner)?;
Ok(Arc::new(LoroTree { inner: c }))
}
#[inline]
pub fn insert_movable_list_container(
&self,
key: &str,
child: Arc<LoroMovableList>,
) -> LoroResult<Arc<LoroMovableList>> {
let c = self
.inner
.insert_container(key, child.as_ref().clone().inner)?;
Ok(Arc::new(LoroMovableList { inner: c }))
}
#[inline]
pub fn insert_counter_container(
&self,
key: &str,
child: Arc<LoroCounter>,
) -> LoroResult<Arc<LoroCounter>> {
let c = self
.inner
.insert_container(key, child.as_ref().clone().inner)?;
Ok(Arc::new(LoroCounter { inner: c }))
}
pub fn get_value(&self) -> LoroValue {
self.inner.get_value().into()
}
pub fn get_deep_value(&self) -> LoroValue {
self.inner.get_deep_value().into()
}
pub fn is_deleted(&self) -> bool {
self.inner.is_deleted()
}
pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
self.inner.get_last_editor(key)
}
pub fn clear(&self) -> LoroResult<()> {
self.inner.clear()
}
pub fn keys(&self) -> Vec<String> {
self.inner.keys().map(|k| k.to_string()).collect()
}
pub fn values(&self) -> Vec<Arc<dyn ValueOrContainer>> {
self.inner
.values()
.map(|v| Arc::new(v) as Arc<dyn ValueOrContainer>)
.collect()
}
pub fn doc(&self) -> Option<Arc<LoroDoc>> {
self.inner.doc().map(|x| Arc::new(LoroDoc { doc: x }))
}
pub fn subscribe(&self, subscriber: Arc<dyn Subscriber>) -> Option<Arc<Subscription>> {
self.inner
.subscribe(Arc::new(move |e| {
subscriber.on_diff(DiffEvent::from(e));
}))
.map(|x| Arc::new(x.into()))
}
}
impl Default for LoroMap {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::{ContainerIdLike, LoroValue};
use super::*;
struct TestValue(LoroValue);
impl LoroValueLike for TestValue {
fn as_loro_value(&self) -> LoroValue {
self.0.clone()
}
}
fn value(value: LoroValue) -> Arc<dyn LoroValueLike> {
Arc::new(TestValue(value))
}
fn root_map() -> Arc<LoroMap> {
let doc = LoroDoc::new();
doc.get_map(Arc::new(String::from("root")) as Arc<dyn ContainerIdLike>)
}
#[test]
fn ensure_mergeable_containers_are_stable_and_usable() {
let root = root_map();
let text = root.ensure_mergeable_text("body").unwrap();
text.insert(0, "hello").unwrap();
let same_text = root.ensure_mergeable_text("body").unwrap();
assert_eq!(format!("{:?}", text.id()), format!("{:?}", same_text.id()));
assert_eq!(same_text.slice(0, 5).unwrap(), "hello");
let map = root.ensure_mergeable_map("meta").unwrap();
map.insert(
"name",
value(LoroValue::String {
value: String::from("Ada"),
}),
)
.unwrap();
assert!(map.get("name").unwrap().is_value());
let list = root.ensure_mergeable_list("items").unwrap();
list.push(value(LoroValue::I64 { value: 42 })).unwrap();
assert_eq!(list.len(), 1);
let counter = root.ensure_mergeable_counter("revision").unwrap();
counter.increment(2.0).unwrap();
assert_eq!(counter.get_value(), 2.0);
}
#[test]
fn ensure_mergeable_rejects_non_mergeable_value() {
let root = root_map();
root.insert("plain", value(LoroValue::I64 { value: 1 }))
.unwrap();
assert!(root.ensure_mergeable_map("plain").is_err());
}
}