From 01527f834cab18472ca3199581eb8ec1bc85eb11 Mon Sep 17 00:00:00 2001 From: Alex Parrill Date: Tue, 3 Dec 2024 03:00:29 -0500 Subject: [PATCH] Add support for adding XmlFragments to arrays and maps (#195) * Add support for adding XmlFragments to arrays and maps I overlooked support for this in my initial XML support branch because I was not sure if YJS supported XML fragments nested in either of those types, but it works, so I've added support. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/array.rs | 25 ++++++++++++++++++------- src/map.rs | 25 ++++++++++++++++++------- tests/test_xml.py | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/array.rs b/src/array.rs index 1a48c57..b7b5929 100644 --- a/src/array.rs +++ b/src/array.rs @@ -2,13 +2,7 @@ use pyo3::prelude::*; use pyo3::exceptions::{PyValueError, PyTypeError}; use pyo3::types::{PyList, PyString}; use yrs::{ - Any, - ArrayRef, - Array as _Array, - Doc as _Doc, - DeepObservable, - Observable, - TransactionMut, + Any, Array as _Array, ArrayRef, DeepObservable, Doc as _Doc, Observable, TransactionMut, XmlFragmentPrelim }; use yrs::types::ToJson; use yrs::types::text::TextPrelim; @@ -20,6 +14,7 @@ use crate::type_conversions::{events_into_py, py_to_any, ToPython}; use crate::text::Text; use crate::map::Map; use crate::doc::Doc; +use crate::xml::XmlFragment; #[pyclass] @@ -79,6 +74,22 @@ impl Array { Python::with_gil(|py| { Ok(shared.into_py(py)) }) } + fn insert_xmlfragment_prelim(&self, txn: &mut Transaction, index: u32) -> PyResult { + let mut _t = txn.transaction(); + let mut t = _t.as_mut().unwrap().as_mut(); + let integrated = self.array.insert(&mut t, index, XmlFragmentPrelim::default()); + let shared = XmlFragment::from(integrated); + Python::with_gil(|py| { Ok(shared.into_py(py)) }) + } + + fn insert_xmlelement_prelim(&self, _txn: &mut Transaction, _index: u32) -> PyResult { + Err(PyTypeError::new_err("Cannot insert an XmlElement into an array - insert it into an XmlFragment and insert that into the array")) + } + + fn insert_xmltext_prelim(&self, _txn: &mut Transaction, _index: u32) -> PyResult { + Err(PyTypeError::new_err("Cannot insert an XmlText into an array - insert it into an XmlFragment and insert that into the array")) + } + fn insert_doc(&self, txn: &mut Transaction, index: u32, doc: &Bound<'_, PyAny>) -> PyResult<()> { let mut _t = txn.transaction(); let mut t = _t.as_mut().unwrap().as_mut(); diff --git a/src/map.rs b/src/map.rs index 79c28c5..95566a1 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2,13 +2,7 @@ use pyo3::prelude::*; use pyo3::exceptions::{PyValueError, PyTypeError}; use pyo3::types::{PyString, PyDict, PyList}; use yrs::{ - Any, - Doc as _Doc, - MapRef, - Map as _Map, - DeepObservable, - Observable, - TransactionMut, + Any, DeepObservable, Doc as _Doc, Map as _Map, MapRef, Observable, TransactionMut, XmlFragmentPrelim }; use yrs::types::ToJson; use yrs::types::text::TextPrelim; @@ -20,6 +14,7 @@ use crate::type_conversions::{EntryChangeWrapper, events_into_py, py_to_any, ToP use crate::text::Text; use crate::array::Array; use crate::doc::Doc; +use crate::xml::XmlFragment; #[pyclass] @@ -79,6 +74,22 @@ impl Map { Python::with_gil(|py| { Ok(shared.into_py(py)) }) } + fn insert_xmlfragment_prelim(&self, txn: &mut Transaction, key: &str) -> PyResult { + let mut _t = txn.transaction(); + let mut t = _t.as_mut().unwrap().as_mut(); + let integrated = self.map.insert(&mut t, key, XmlFragmentPrelim::default()); + let shared = XmlFragment::from(integrated); + Python::with_gil(|py| { Ok(shared.into_py(py)) }) + } + + fn insert_xmlelement_prelim(&self, _txn: &mut Transaction, _key: &str) -> PyResult { + Err(PyTypeError::new_err("Cannot insert an XmlElement into a map - insert it into an XmlFragment and insert that into the map")) + } + + fn insert_xmltext_prelim(&self, _txn: &mut Transaction, _key: &str) -> PyResult { + Err(PyTypeError::new_err("Cannot insert an XmlText into a map - insert it into an XmlFragment and insert that into the map")) + } + fn insert_doc(&self, txn: &mut Transaction, key: &str, doc: &Bound<'_, PyAny>) -> PyResult<()> { let mut _t = txn.transaction(); let mut t = _t.as_mut().unwrap().as_mut(); diff --git a/tests/test_xml.py b/tests/test_xml.py index 77392c5..ddaedde 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -1,5 +1,5 @@ import pytest -from pycrdt import Doc, XmlElement, XmlFragment, XmlText +from pycrdt import Array, Doc, Map, XmlElement, XmlFragment, XmlText def test_plain_text(): @@ -275,3 +275,37 @@ def callback(event): assert str(events[0][0].target) == "Hello world!" assert events[0][0].delta[0] == {"retain": 1} assert events[0][0].delta[1] == {"retain": 2, "attributes": {"bold": True}} + + +def test_xml_in_array(): + doc = Doc() + array = doc.get("testmap", type=Array) + frag = XmlFragment() + array.append(frag) + frag.children.append("Test XML!") + + assert len(array) == 1 + assert str(array[0]) == "Test XML!" + + with pytest.raises(TypeError): + array.append(XmlText()) + with pytest.raises(TypeError): + array.append(XmlElement("a")) + assert len(array) == 1 + + +def test_xml_in_map(): + doc = Doc() + map = doc.get("testmap", type=Map) + frag = map["testxml"] = XmlFragment() + frag.children.append("Test XML!") + + assert len(map) == 1 + assert "testxml" in map + assert str(map["testxml"]) == "Test XML!" + + with pytest.raises(TypeError): + map["testtext"] = XmlText() + with pytest.raises(TypeError): + map["testel"] = XmlElement("a") + assert len(map) == 1