diff --git a/src/shared_types.rs b/src/shared_types.rs index 92ab091..7e552f2 100644 --- a/src/shared_types.rs +++ b/src/shared_types.rs @@ -5,7 +5,7 @@ use crate::{ y_xml::{YXmlElement, YXmlText}, }; use pyo3::prelude::*; -use std::convert::TryFrom; +use std::{convert::TryFrom, fmt::Display}; use yrs::types::TYPE_REFS_XML_ELEMENT; use yrs::types::TYPE_REFS_XML_TEXT; use yrs::types::{TypeRefs, TYPE_REFS_ARRAY, TYPE_REFS_MAP, TYPE_REFS_TEXT}; @@ -37,6 +37,19 @@ pub enum Shared { XmlText(Py), } +impl Display for Shared { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str_repr = Python::with_gil(|py| match &self { + Shared::Text(text) => text.borrow(py).__str__(), + Shared::Array(arr) => arr.borrow(py).__str__(), + Shared::Map(map) => map.borrow(py).__str__(), + Shared::XmlElement(xml_el) => xml_el.borrow(py).__str__(), + Shared::XmlText(xml_text) => xml_text.borrow(py).__str__(), + }); + write!(f, "{}", str_repr) + } +} + impl Shared { pub fn is_prelim(&self) -> bool { Python::with_gil(|py| match self { diff --git a/src/type_conversions.rs b/src/type_conversions.rs index 8181247..266c4c0 100644 --- a/src/type_conversions.rs +++ b/src/type_conversions.rs @@ -1,6 +1,10 @@ use lib0::any::Any; +use pyo3::create_exception; +use pyo3::exceptions::PyException; +use pyo3::exceptions::PyTypeError; use pyo3::prelude::*; use pyo3::types as pytypes; + use std::collections::HashMap; use std::convert::TryFrom; use std::ops::Deref; @@ -14,6 +18,8 @@ use crate::y_map::YMap; use crate::y_text::YText; use crate::y_xml::{YXmlElement, YXmlText}; +create_exception!(y_py, MultipleIntegrationError, PyException); + pub trait ToPython { fn into_py(self, py: Python) -> PyObject; } @@ -137,28 +143,38 @@ struct PyObjectWrapper(PyObject); impl Prelim for PyObjectWrapper { fn into_content(self, _txn: &mut Transaction) -> (ItemContent, Option) { - let guard = Python::acquire_gil(); - let py = guard.python(); - let content = if let Some(any) = py_into_any(self.0.clone()) { - ItemContent::Any(vec![any]) - } else if let Ok(shared) = Shared::extract(self.0.as_ref(py)) { - if shared.is_prelim() { - let branch = Branch::new(shared.type_ref(), None); - ItemContent::Type(branch) + Python::with_gil(|py| { + let content = if let Some(any) = py_into_any(self.0.clone()) { + ItemContent::Any(vec![any]) + } else if let Ok(shared) = Shared::extract(self.0.as_ref(py)) { + if shared.is_prelim() { + let branch = Branch::new(shared.type_ref(), None); + ItemContent::Type(branch) + } else { + let error_message = format!( + "Cannot integrate data that is already a part of another YDoc: {shared}" + ); + let type_error = MultipleIntegrationError::new_err(error_message); + type_error.restore(py); + panic!(); + } } else { - panic!("Cannot integrate this type") - } - } else { - panic!("Cannot integrate this type") - }; + let unknown_type = self.0; + let error_message = + format!("Cannot integrate this Python type into a YDoc: {unknown_type}"); + let type_error = PyTypeError::new_err(error_message); + type_error.restore(py); + panic!(); + }; - let this = if let ItemContent::Type(_) = &content { - Some(self) - } else { - None - }; + let this = if let ItemContent::Type(_) = &content { + Some(self) + } else { + None + }; - (content, this) + (content, this) + }) } fn integrate(self, txn: &mut Transaction, inner_ref: BranchPtr) { @@ -167,7 +183,8 @@ impl Prelim for PyObjectWrapper { let obj_ref = self.0.as_ref(py); if let Ok(shared) = Shared::extract(obj_ref) { if shared.is_prelim() { - Python::with_gil(|py| match shared { + Python::with_gil(|py| { + match shared { Shared::Text(v) => { let text = Text::from(inner_ref); let mut y_text = v.borrow_mut(py); @@ -196,7 +213,8 @@ impl Prelim for PyObjectWrapper { } y_map.0 = SharedType::Integrated(map.clone()); } - _ => panic!("Cannot integrate this type"), + Shared::XmlElement(_) | Shared::XmlText(_) => unreachable!("As defined in Shared::is_prelim(), neither XML type can ever exist outside a YDoc"), + } }) } } @@ -335,10 +353,20 @@ impl Prelim for PyValueWrapper { let branch = Branch::new(shared.type_ref(), None); ItemContent::Type(branch) } else { - panic!("Cannot integrate this type") + let error_message = format!( + "Cannot integrate data that is already a part of another YDoc: {shared}" + ); + let type_error = MultipleIntegrationError::new_err(error_message); + Python::with_gil(|py| type_error.restore(py)); + ItemContent::Any(Vec::new()) } } else { - panic!("Cannot integrate this type") + let unknown_type = self.0; + let error_message = + format!("Cannot integrate this Python type into a YDoc: {unknown_type}"); + let type_error = PyTypeError::new_err(error_message); + Python::with_gil(|py| type_error.restore(py)); + panic!(); }; let this = if let ItemContent::Type(_) = &content { @@ -353,7 +381,8 @@ impl Prelim for PyValueWrapper { fn integrate(self, txn: &mut Transaction, inner_ref: BranchPtr) { if let Ok(shared) = Shared::try_from(self.0) { if shared.is_prelim() { - Python::with_gil(|py| match shared { + Python::with_gil(|py| { + match shared { Shared::Text(v) => { let text = Text::from(inner_ref); let mut y_text = v.borrow_mut(py); @@ -383,7 +412,8 @@ impl Prelim for PyValueWrapper { } y_map.0 = SharedType::Integrated(map.clone()); } - _ => panic!("Cannot integrate this type"), + Shared::XmlElement(_) | Shared::XmlText(_) => unreachable!("As defined in Shared::is_prelim(), neither XML type can ever exist outside a YDoc"), + } }) } }