use serde_json::{json, Value}; use yrs::{ReadTxn, Text, Xml, XmlFragment, XmlFragmentRef, XmlOut}; pub fn node_id(node: &XmlOut) -> String { serde_json::to_string(&node.id()).expect("branch identity serializes") } pub fn children(root: &XmlFragmentRef, txn: &T) -> Vec { (0..root.len(txn)).filter_map(|i| root.get(txn, i)).map(|node| project(&node, txn)).collect() } fn project(node: &XmlOut, txn: &T) -> Value { match node { XmlOut::Text(text) => { let runs: Vec<_> = text.diff(txn, |_| ()).iter().map(|run| { json!({"insert": run.insert.clone().to_string(txn), "marks": run.attributes}) }).collect(); json!({"id": node_id(node), "type": "text", "runs": runs}) }, XmlOut::Element(element) => { let attrs: serde_json::Map = element.attributes(txn) .map(|(key,value)| (key.to_string(), Value::String(value))).collect(); json!({"id": node_id(node), "type": element.tag().as_ref(), "attrs": attrs, "children": children(element.as_ref(), txn)}) }, XmlOut::Fragment(fragment) => json!({"id": node_id(node), "type": "fragment", "children": children(fragment, txn)}), } } pub fn find(root: &XmlFragmentRef, txn: &T, id: &str) -> Option { for index in 0..root.len(txn) { let node = root.get(txn, index)?; if node_id(&node) == id { return Some(node); } let nested = match &node { XmlOut::Element(el) => find(el.as_ref(), txn, id), XmlOut::Fragment(f) => find(f, txn, id), _ => None, }; if nested.is_some() { return nested; } } None }