| 1 | use serde_json::{json, Value}; |
| 2 | use yrs::{ReadTxn, Text, Xml, XmlFragment, XmlFragmentRef, XmlOut}; |
| 3 | |
| 4 | pub fn node_id(node: &XmlOut) -> String { |
| 5 | serde_json::to_string(&node.id()).expect("branch identity serializes") |
| 6 | } |
| 7 | pub fn children<T: ReadTxn>(root: &XmlFragmentRef, txn: &T) -> Vec<Value> { |
| 8 | (0..root.len(txn)).filter_map(|i| root.get(txn, i)).map(|node| project(&node, txn)).collect() |
| 9 | } |
| 10 | fn project<T: ReadTxn>(node: &XmlOut, txn: &T) -> Value { |
| 11 | match node { |
| 12 | XmlOut::Text(text) => { |
| 13 | let runs: Vec<_> = text.diff(txn, |_| ()).iter().map(|run| { |
| 14 | json!({"insert": run.insert.clone().to_string(txn), "marks": run.attributes}) |
| 15 | }).collect(); |
| 16 | json!({"id": node_id(node), "type": "text", "runs": runs}) |
| 17 | }, |
| 18 | XmlOut::Element(element) => { |
| 19 | let attrs: serde_json::Map<String, Value> = element.attributes(txn) |
| 20 | .map(|(key,value)| (key.to_string(), Value::String(value))).collect(); |
| 21 | json!({"id": node_id(node), "type": element.tag().as_ref(), "attrs": attrs, "children": children(element.as_ref(), txn)}) |
| 22 | }, |
| 23 | XmlOut::Fragment(fragment) => json!({"id": node_id(node), "type": "fragment", "children": children(fragment, txn)}), |
| 24 | } |
| 25 | } |
| 26 | pub fn find<T: ReadTxn>(root: &XmlFragmentRef, txn: &T, id: &str) -> Option<XmlOut> { |
| 27 | for index in 0..root.len(txn) { |
| 28 | let node = root.get(txn, index)?; |
| 29 | if node_id(&node) == id { return Some(node); } |
| 30 | let nested = match &node { |
| 31 | XmlOut::Element(el) => find(el.as_ref(), txn, id), |
| 32 | XmlOut::Fragment(f) => find(f, txn, id), |
| 33 | _ => None, |
| 34 | }; |
| 35 | if nested.is_some() { return nested; } |
| 36 | } |
| 37 | None |
| 38 | } |
| 39 | |