diff --git a/crates/loro-core/examples/encoding.rs b/crates/loro-core/examples/encoding.rs index d2abe59d..feec667d 100644 --- a/crates/loro-core/examples/encoding.rs +++ b/crates/loro-core/examples/encoding.rs @@ -39,11 +39,14 @@ fn main() { 0, start.elapsed().as_millis() ); + let json1 = loro.to_json(); let start = Instant::now(); let loro = LoroCore::decode_snapshot(&buf, None, Configure::default()); println!("decode used {}ms", start.elapsed().as_millis()); let buf2 = loro.encode_snapshot(); assert_eq!(buf, buf2); + let json2 = loro.to_json(); + assert_eq!(json1, json2); let mut last = 100; let mut count = 0; let mut max_count = 0; diff --git a/crates/loro-core/src/container/list/list_container.rs b/crates/loro-core/src/container/list/list_container.rs index 2db7ecad..80d342ec 100644 --- a/crates/loro-core/src/container/list/list_container.rs +++ b/crates/loro-core/src/container/list/list_container.rs @@ -270,6 +270,16 @@ impl ListContainer { } } } + + #[cfg(feature = "json")] + pub fn to_json(&self) -> serde_json::Value { + let mut arr = Vec::new(); + for i in 0..self.values_len() { + let v = self.get(i).unwrap(); + arr.push(v.to_json_value()); + } + serde_json::Value::Array(arr) + } } impl Container for ListContainer { diff --git a/crates/loro-core/src/container/map/map_container.rs b/crates/loro-core/src/container/map/map_container.rs index e50f20f5..fc6cc5cc 100644 --- a/crates/loro-core/src/container/map/map_container.rs +++ b/crates/loro-core/src/container/map/map_container.rs @@ -216,6 +216,16 @@ impl MapContainer { .get(key) .map(|v| self.pool.slice(&(v.value..v.value + 1)).first().unwrap()) } + + #[cfg(feature = "json")] + pub fn to_json(&self) -> serde_json::Value { + let mut map = serde_json::Map::new(); + for (k, v) in self.state.iter() { + let value = self.pool.slice(&(v.value..v.value + 1)).first().unwrap(); + map.insert(k.to_string(), value.to_json_value()); + } + serde_json::Value::Object(map) + } } fn calculate_map_diff( diff --git a/crates/loro-core/src/container/registry.rs b/crates/loro-core/src/container/registry.rs index cc679ef4..ab2bb17f 100644 --- a/crates/loro-core/src/container/registry.rs +++ b/crates/loro-core/src/container/registry.rs @@ -269,6 +269,22 @@ impl ContainerRegistry { } } + #[cfg(feature = "json")] + pub fn to_json(&self) -> serde_json::Value { + let mut map = serde_json::Map::new(); + for ContainerAndId { container, id } in self.containers.iter() { + let container = container.lock().unwrap(); + let json = match container.deref() { + ContainerInstance::Map(x) => x.to_json(), + ContainerInstance::Text(x) => x.to_json(), + ContainerInstance::Dyn(_) => unreachable!("registry to json"), + ContainerInstance::List(x) => x.to_json(), + }; + map.insert(serde_json::to_string(id).unwrap(), json); + } + serde_json::Value::Object(map) + } + pub(crate) fn export(&self) -> (&FxHashMap, Vec) { ( &self.container_to_idx, diff --git a/crates/loro-core/src/container/text/text_container.rs b/crates/loro-core/src/container/text/text_container.rs index 44458c87..a4a11703 100644 --- a/crates/loro-core/src/container/text/text_container.rs +++ b/crates/loro-core/src/container/text/text_container.rs @@ -163,6 +163,15 @@ impl TextContainer { ); self.state.debug_inspect(); } + + #[cfg(feature = "json")] + pub fn to_json(&self) -> serde_json::Value { + let mut s = String::new(); + for slice in self.state.iter() { + s.push_str(&self.raw_str.get_str(&slice.as_ref().0)); + } + serde_json::Value::String(s) + } } impl Container for TextContainer { diff --git a/crates/loro-core/src/fuzz.rs b/crates/loro-core/src/fuzz.rs index ad3bebb4..ab2618df 100644 --- a/crates/loro-core/src/fuzz.rs +++ b/crates/loro-core/src/fuzz.rs @@ -295,10 +295,13 @@ pub fn test_single_client_encode(mut actions: Vec) { } } let encode_bytes = store.encode_snapshot(); + let json1 = store.to_json(); let store2 = LoroCore::decode_snapshot(&encode_bytes, None, crate::configure::Configure::default()); let encode_bytes2 = store2.encode_snapshot(); + let json2 = store2.to_json(); assert_eq!(encode_bytes, encode_bytes2); + assert_eq!(json1, json2); } pub fn minify_error(site_num: u8, actions: Vec, f: F, normalize: N) diff --git a/crates/loro-core/src/log_store.rs b/crates/loro-core/src/log_store.rs index 664b8db3..f0935402 100644 --- a/crates/loro-core/src/log_store.rs +++ b/crates/loro-core/src/log_store.rs @@ -363,6 +363,11 @@ impl LogStore { self.hierarchy = hierarchy; result } + + #[cfg(feature = "json")] + pub fn to_json(&self) -> String { + self.reg.to_json().to_string() + } } impl Dag for LogStore { diff --git a/crates/loro-core/src/loro.rs b/crates/loro-core/src/loro.rs index b0f8b9fe..2bacf664 100644 --- a/crates/loro-core/src/loro.rs +++ b/crates/loro-core/src/loro.rs @@ -100,4 +100,9 @@ impl LoroCore { pub fn debug_inspect(&self) { self.log_store.write().unwrap().debug_inspect(); } + + #[cfg(feature = "json")] + pub fn to_json(&self) -> String { + self.log_store.read().unwrap().to_json() + } } diff --git a/crates/loro-core/src/value.rs b/crates/loro-core/src/value.rs index 8c359b0c..5da4430a 100644 --- a/crates/loro-core/src/value.rs +++ b/crates/loro-core/src/value.rs @@ -61,6 +61,30 @@ impl LoroValue { serde_json::to_string(self).unwrap() } + #[cfg(feature = "json")] + pub fn to_json_value(&self) -> serde_json::Value { + match self { + LoroValue::Null => serde_json::Value::Null, + LoroValue::Bool(b) => serde_json::Value::Bool(*b), + LoroValue::Double(d) => { + serde_json::Value::Number(serde_json::Number::from_f64(*d).unwrap()) + } + LoroValue::I32(i) => serde_json::Value::Number(serde_json::Number::from(*i)), + LoroValue::String(s) => serde_json::Value::String(s.to_string()), + LoroValue::List(l) => { + serde_json::Value::Array(l.iter().map(|v| v.to_json_value()).collect()) + } + LoroValue::Map(m) => serde_json::Value::Object( + m.iter() + .map(|(k, v)| (k.to_string(), v.to_json_value())) + .collect(), + ), + LoroValue::Unresolved(id) => { + serde_json::Value::String(serde_json::to_string(id).unwrap()) + } + } + } + #[cfg(feature = "json")] pub fn from_json(s: &str) -> Self { serde_json::from_str(s).unwrap()