From ab271cfdc219fe274fb1dd8c8b53883c97f43974 Mon Sep 17 00:00:00 2001 From: sevenrats Date: Sat, 12 Oct 2024 05:50:15 -0400 Subject: [PATCH 1/2] update to latest rust crate and expose SyncState encode decode Signed-off-by: sevenrats --- rust/Cargo.lock | 6 +++--- rust/Cargo.toml | 2 +- rust/src/lib.rs | 10 ++++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 36aece4..4544b64 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -16,9 +16,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "automerge" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c059747d9ef133802da22926ea54039de90f85b349a920f16a6d1573da0f2580" +checksum = "b0b670b68c38e4042ea4826415f0f8101428810bce821d215e271966b24abac4" dependencies = [ "flate2", "fxhash", @@ -40,7 +40,7 @@ dependencies = [ name = "automerge" version = "1.0.0-rc1" dependencies = [ - "automerge 0.5.7", + "automerge 0.5.11", "hex", "pyo3", "thiserror", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 4d4ac10..8761bbc 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -10,6 +10,6 @@ crate-type = ["cdylib"] [dependencies] pyo3 = "0.19.0" -automerge = "0.5.7" +automerge = "0.5.11" hex = "^0.4.3" thiserror = "^1.0.16" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4d71a5a..158b91c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -742,6 +742,16 @@ impl PySyncState { pub fn new() -> PySyncState { PySyncState(am::sync::State::new()) } + pub fn encode<'py>(&self, py: Python<'py>) -> &'py PyBytes { + PyBytes::new(py, &self.0.clone().encode()) + } + + #[staticmethod] + pub fn decode(bytes: &[u8]) -> PyResult { + Ok(PySyncState( + am::sync::State::decode(bytes).map_err(|e| PyException::new_err(e.to_string()))?, + )) + } } #[pyclass(name = "Message")] From 46a1be0929eb67d1f9806274a4fa338652e19b55 Mon Sep 17 00:00:00 2001 From: sevenrats Date: Tue, 15 Oct 2024 12:26:46 -0400 Subject: [PATCH 2/2] expose patch action --- rust/Cargo.lock | 197 ++++++-------------------- rust/Cargo.toml | 4 +- rust/src/lib.rs | 360 ++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 365 insertions(+), 196 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 36aece4..c020c5f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -16,9 +16,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "automerge" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c059747d9ef133802da22926ea54039de90f85b349a920f16a6d1573da0f2580" +checksum = "b0b670b68c38e4042ea4826415f0f8101428810bce821d215e271966b24abac4" dependencies = [ "flate2", "fxhash", @@ -40,18 +40,12 @@ dependencies = [ name = "automerge" version = "1.0.0-rc1" dependencies = [ - "automerge 0.5.7", + "automerge 0.5.11", "hex", "pyo3", "thiserror", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitmaps" version = "2.1.0" @@ -166,6 +160,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.9" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "itertools" @@ -213,16 +213,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -247,55 +237,39 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" -version = "0.19.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +checksum = "00e89ce2565d6044ca31a3eb79a334c3a79a841120a98f64eea9f579564cb691" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -304,9 +278,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.19.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +checksum = "d8afbaf3abd7325e08f35ffb8deb5892046fcb2608b703db6a583a5ba4cea01e" dependencies = [ "once_cell", "target-lexicon", @@ -314,9 +288,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.19.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +checksum = "ec15a5ba277339d04763f4c23d85987a5b08cbb494860be141e6a10a8eb88022" dependencies = [ "libc", "pyo3-build-config", @@ -324,25 +298,27 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.19.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +checksum = "15e0f01b5364bcfbb686a52fc4181d412b708a68ed20c330db9fc8d2c2bf5a43" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "pyo3-macros-backend" -version = "0.19.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +checksum = "a09b550200e1e5ed9176976d0060cbc2ea82dc8515da07885e7b8153a85caacb" dependencies = [ + "heck", "proc-macro2", + "pyo3-build-config", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -369,21 +345,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "serde" version = "1.0.197" @@ -401,7 +362,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -425,12 +386,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "smallvec" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - [[package]] name = "smol_str" version = "0.2.1" @@ -442,20 +397,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -485,7 +429,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -522,7 +466,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -554,9 +498,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unindent" -version = "0.1.11" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "uuid" @@ -579,60 +523,3 @@ name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 4d4ac10..6b6dc74 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,7 +9,7 @@ name = "automerge" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.19.0" -automerge = "0.5.7" +pyo3 = "0.22.0" +automerge = "0.5.11" hex = "^0.4.3" thiserror = "^1.0.16" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4d71a5a..5fa1988 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,4 +1,5 @@ use std::{ + collections::BTreeMap, mem::transmute, sync::{Arc, RwLock}, }; @@ -181,7 +182,7 @@ impl Document { } } - fn get_actor<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> { + fn get_actor<'py>(&self, py: Python<'py>) -> PyResult> { let inner = self .inner .read() @@ -192,7 +193,7 @@ impl Document { )); } - Ok(PyBytes::new(py, inner.doc.get_actor().to_bytes())) + Ok(PyBytes::new_bound(py, inner.doc.get_actor().to_bytes())) } fn set_actor(&mut self, actor_id: &[u8]) -> PyResult<()> { @@ -229,7 +230,7 @@ impl Document { }) } - fn save<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> { + fn save<'py>(&self, py: Python<'py>) -> PyResult> { let inner = self .inner .read() @@ -240,7 +241,7 @@ impl Document { )); } - Ok(PyBytes::new(py, &inner.doc.save())) + Ok(PyBytes::new_bound(py, &inner.doc.save())) } #[staticmethod] @@ -476,12 +477,12 @@ impl Transaction { Ok(self.clone()) } - #[pyo3(name = "__exit__")] + #[pyo3(name = "__exit__", signature = (exc_type=None, exc_value=None, traceback=None))] fn exit( &self, - exc_type: Option<&PyAny>, - exc_value: Option<&PyAny>, - traceback: Option<&PyAny>, + exc_type: Option>, + exc_value: Option>, + traceback: Option>, ) -> PyResult<()> { let mut inner = self .inner @@ -575,7 +576,7 @@ impl Transaction { obj_id: PyObjId, prop: PyProp, value_type: &PyScalarType, - value: &PyAny, + value: Bound<'_, PyAny>, ) -> PyResult<()> { let mut inner = self .inner @@ -611,7 +612,7 @@ impl Transaction { obj_id: PyObjId, index: usize, value_type: &PyScalarType, - value: &PyAny, + value: Bound<'_, PyAny>, ) -> PyResult<()> { let mut inner = self .inner @@ -673,7 +674,7 @@ impl Transaction { end: usize, name: &str, value_type: &PyScalarType, - value: &PyAny, + value: Bound<'_, PyAny>, expand: &PyExpandMark, ) -> PyResult<()> { let mut inner = self @@ -712,11 +713,14 @@ impl Transaction { } } -fn datetime_to_timestamp(datetime: &PyDateTime) -> PyResult { +fn datetime_to_timestamp(datetime: Bound<'_, PyDateTime>) -> PyResult { Ok((datetime.call_method0("timestamp")?.extract::()? * 1000.0).round() as i64) } -fn import_scalar(value: &PyAny, scalar_type: &PyScalarType) -> Result { +fn import_scalar( + value: Bound<'_, PyAny>, + scalar_type: &PyScalarType, +) -> Result { Ok(match scalar_type { PyScalarType::Bytes => ScalarValue::Bytes(value.extract::<&[u8]>()?.to_owned()), PyScalarType::Str => ScalarValue::Str(value.extract::()?.into()), @@ -724,9 +728,9 @@ fn import_scalar(value: &PyAny, scalar_type: &PyScalarType) -> Result ScalarValue::Uint(value.extract::()?), PyScalarType::F64 => ScalarValue::F64(value.extract::()?), PyScalarType::Counter => todo!(), - PyScalarType::Timestamp => { - ScalarValue::Timestamp(datetime_to_timestamp(value.downcast::()?)?) - } + PyScalarType::Timestamp => ScalarValue::Timestamp(datetime_to_timestamp( + value.downcast::()?.clone(), + )?), PyScalarType::Boolean => ScalarValue::Boolean(value.extract::()?), PyScalarType::Unknown => todo!(), PyScalarType::Null => ScalarValue::Null, @@ -749,8 +753,8 @@ struct PyMessage(am::sync::Message); #[pymethods] impl PyMessage { - pub fn encode<'py>(&self, py: Python<'py>) -> &'py PyBytes { - PyBytes::new(py, &self.0.clone().encode()) + pub fn encode<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { + PyBytes::new_bound(py, &self.0.clone().encode()) } #[staticmethod] @@ -762,13 +766,13 @@ impl PyMessage { } #[pyfunction] -fn random_actor_id<'py>(py: Python<'py>) -> &'py PyBytes { - PyBytes::new(py, ActorId::random().to_bytes()) +fn random_actor_id<'py>(py: Python<'py>) -> Bound<'py, PyBytes> { + PyBytes::new_bound(py, ActorId::random().to_bytes()) } /// A Python module implemented in Rust. #[pymodule] -fn _automerge(_py: Python, m: &PyModule) -> PyResult<()> { +fn _automerge(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { // Classes m.add_class::()?; m.add_class::()?; @@ -779,20 +783,33 @@ fn _automerge(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + // m.add_class::()?; // Constants m.add("ROOT", PyObjId(am::ROOT))?; // Functions m.add_function(wrap_pyfunction!(random_actor_id, m)?)?; + + // Submodule + let submodule = PyModule::new_bound(py, "patch_action")?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + m.add_submodule(&submodule)?; Ok(()) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PyProp(Prop); impl<'a> FromPyObject<'a> for PyProp { - fn extract(prop: &'a PyAny) -> PyResult { + fn extract_bound(prop: &Bound<'a, PyAny>) -> PyResult { Ok(PyProp(match prop.extract::() { Ok(s) => Prop::Map(s), Err(_) => match prop.extract::() { @@ -803,11 +820,20 @@ impl<'a> FromPyObject<'a> for PyProp { } } -#[derive(Debug)] +impl IntoPy for PyProp { + fn into_py(self, py: Python<'_>) -> PyObject { + match self.0 { + am::Prop::Map(s) => s.into_py(py), + am::Prop::Seq(i) => i.into_py(py), + } + } +} + +#[derive(Debug, Clone)] pub struct PyObjId(am::ObjId); impl<'a> FromPyObject<'a> for PyObjId { - fn extract(prop: &'a PyAny) -> PyResult { + fn extract_bound(prop: &Bound<'a, PyAny>) -> PyResult { prop.extract::<&[u8]>() .and_then(|b| am::ObjId::try_from(b).map_err(|e| PyException::new_err(e.to_string()))) .map(PyObjId) @@ -825,7 +851,7 @@ impl IntoPy for PyObjId { pub struct PyChangeHash(am::ChangeHash); impl<'a> FromPyObject<'a> for PyChangeHash { - fn extract(v: &'a PyAny) -> PyResult { + fn extract_bound(v: &Bound<'a, PyAny>) -> PyResult { v.extract::<&[u8]>() .and_then(|b| { am::ChangeHash::try_from(b).map_err(|e| PyException::new_err(e.to_string())) @@ -840,8 +866,8 @@ impl IntoPy for PyChangeHash { } } -#[derive(Debug)] -#[pyclass(name = "ObjType")] +#[derive(Debug, Clone, PartialEq)] +#[pyclass(name = "ObjType", eq, eq_int)] pub enum PyObjType { Map, List, @@ -869,8 +895,8 @@ impl Into for &PyObjType { } } -#[derive(Debug, Clone)] -#[pyclass(name = "ScalarType")] +#[derive(Debug, Clone, PartialEq)] +#[pyclass(name = "ScalarType", eq, eq_int)] pub enum PyScalarType { Bytes, Str, @@ -897,7 +923,7 @@ impl IntoPy for PyScalarValue { ScalarValue::Counter(v) => todo!(), ScalarValue::Timestamp(v) => ( PyScalarType::Timestamp, - PyDateTime::from_timestamp(py, (v as f64) / 1000.0, None) + PyDateTime::from_timestamp_bound(py, (v as f64) / 1000.0, None) .unwrap() .into_py(py), ), @@ -910,13 +936,13 @@ impl IntoPy for PyScalarValue { } impl<'a> FromPyObject<'a> for PyScalarValue { - fn extract(v: &'a PyAny) -> PyResult { - v.extract::<(PyScalarType, &PyAny)>() + fn extract_bound(v: &Bound<'a, PyAny>) -> PyResult { + v.extract::<(PyScalarType, Bound<'a, PyAny>)>() .and_then(|(t, v)| import_scalar(v, &t).map(|v| PyScalarValue(v))) } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PyValue<'a>(am::Value<'a>); impl<'a> IntoPy for PyValue<'a> { @@ -929,7 +955,7 @@ impl<'a> IntoPy for PyValue<'a> { } #[pyclass(name = "Mark", get_all, set_all)] -#[derive(Debug)] +#[derive(Debug, Clone)] struct PyMark { start: usize, end: usize, @@ -1023,13 +1049,13 @@ impl PyChange { } #[getter] - fn timestamp<'py>(&self, py: Python<'py>) -> PyResult<&'py PyDateTime> { - PyDateTime::from_timestamp(py, (self.0.timestamp() as f64) / 1000.0, None) + fn timestamp<'py>(&self, py: Python<'py>) -> PyResult> { + PyDateTime::from_timestamp_bound(py, (self.0.timestamp() as f64) / 1000.0, None) } #[getter] - fn bytes<'py>(&mut self, py: Python<'py>) -> &'py PyBytes { - PyBytes::new(py, self.0.bytes().as_ref()) + fn bytes<'py>(&mut self, py: Python<'py>) -> Bound<'py, PyBytes> { + PyBytes::new_bound(py, self.0.bytes().as_ref()) } #[getter] @@ -1052,4 +1078,260 @@ impl PyPatch { fn __repr__(&self) -> String { format!("{:?}", self.0) } + + #[getter] + fn obj(&self) -> PyObjId { + PyObjId(self.0.obj.clone()) + } + + #[getter] + fn path(&self) -> Vec<(PyObjId, PyProp)> { + self.0 + .path + .iter() + .map(|(obj, prop)| (PyObjId(obj.clone()), PyProp(prop.clone()))) + .collect() + } + #[getter] + fn action(&self, py: Python<'_>) -> PyObject { + let action: PyActionType = self.0.action.clone().into(); + match action { + PyActionType::Conflict(v) => v.into_py(py), + PyActionType::DeleteMap(v) => v.into_py(py), + PyActionType::DeleteSeq(v) => v.into_py(py), + PyActionType::Increment(v) => v.into_py(py), + PyActionType::Insert(v) => v.into_py(py), + PyActionType::Mark(v) => v.into_py(py), + PyActionType::PutMap(v) => v.into_py(py), + PyActionType::PutSeq(v) => v.into_py(py), + PyActionType::SpliceText(v) => v.into_py(py), + } + } +} + +impl From for PyActionType { + fn from(value: am::PatchAction) -> PyActionType { + match value { + am::PatchAction::PutMap { + key, + value, + conflict, + } => PyActionType::PutMap(PutMap { + key: key.clone(), + value: (PyValue(value.0.clone()), PyObjId(value.1.clone())), + conflict: conflict, + }), + + am::PatchAction::PutSeq { + index, + value, + conflict, + } => PyActionType::PutSeq(PutSeq { + index: index, + value: (PyValue(value.0.clone()), PyObjId(value.1.clone())), + conflict: conflict, + }), + am::PatchAction::Insert { index, values } => { + let values: Vec<_> = values + .iter() + .map(|(v, oid, b)| (PyValue(v.clone()), PyObjId(oid.clone()), *b)) + .collect(); + + PyActionType::Insert(Insert { + index: index, + values: values, + }) + } + am::PatchAction::SpliceText { + index, + value, + marks, + } => { + let marks = match marks { + Some(marks) => { + let marks: BTreeMap = marks + .iter() + .map(|(key, value)| (key.to_string(), PyScalarValue(value.clone()))) + .collect(); + Some(marks) + } + None => None, + }; + + PyActionType::SpliceText(SpliceText { + index: index, + value: value.make_string(), + marks: marks, + }) + } + am::PatchAction::Increment { prop, value } => PyActionType::Increment(Increment { + prop: PyProp(prop.clone()), + value: value, + }), + am::PatchAction::Conflict { prop } => PyActionType::Conflict(Conflict { + prop: PyProp(prop.clone()), + }), + am::PatchAction::DeleteMap { key } => { + PyActionType::DeleteMap(DeleteMap { key: key.clone() }) + } + am::PatchAction::DeleteSeq { index, length } => PyActionType::DeleteSeq(DeleteSeq { + index: index, + length: length, + }), + am::PatchAction::Mark { marks } => PyActionType::Mark(MarkAction { + marks: marks + .iter() + .map(|m| PyMark { + start: m.start, + end: m.end, + name: m.name().to_owned(), + value: PyScalarValue(m.value().clone()), + }) + .collect(), + }), + } + } +} + +/// Enum to represent the possible actions in `PyPatchAction`. + +pub enum PyActionType { + Conflict(Conflict), + DeleteMap(DeleteMap), + DeleteSeq(DeleteSeq), + Increment(Increment), + Insert(Insert), + Mark(MarkAction), + PutMap(PutMap), + PutSeq(PutSeq), + SpliceText(SpliceText), } + +/// Individual struct for `PutMap`. +#[pyclass] +#[derive(Debug, Clone)] +pub struct PutMap { + #[pyo3(get)] + pub key: String, + #[pyo3(get)] + pub value: (PyValue<'static>, PyObjId), + #[pyo3(get)] + pub conflict: bool, +} + +/// Individual struct for `PutSeq`. +#[pyclass] +#[derive(Debug, Clone)] +pub struct PutSeq { + #[pyo3(get)] + pub index: usize, + #[pyo3(get)] + pub value: (PyValue<'static>, PyObjId), + #[pyo3(get)] + pub conflict: bool, +} + +/// Struct for `Increment`. +#[pyclass] +#[derive(Debug, Clone)] +pub struct Increment { + #[pyo3(get)] + pub prop: PyProp, + #[pyo3(get)] + pub value: i64, +} + +#[pyclass] +#[derive(Debug, Clone)] +pub struct Insert { + #[pyo3(get)] + index: usize, + #[pyo3(get)] + values: Vec<(PyValue<'static>, PyObjId, bool)>, +} + +/// Struct for `Conflict`. +#[pyclass] +#[derive(Debug, Clone)] +pub struct Conflict { + #[pyo3(get)] + pub prop: PyProp, +} + +/// Struct for `DeleteMap`. +#[pyclass] +#[derive(Debug, Clone)] +pub struct DeleteMap { + #[pyo3(get)] + pub key: String, +} + +/// Struct for `DeleteSeq`. +#[pyclass] +#[derive(Debug, Clone)] +pub struct DeleteSeq { + #[pyo3(get)] + pub index: usize, + #[pyo3(get)] + pub length: usize, +} + +/// Struct for `Mark`. +#[pyclass(name = "Mark")] +#[derive(Debug, Clone)] +pub struct MarkAction { + #[pyo3(get)] + pub marks: Vec, +} + +#[pyclass] +#[derive(Debug, Clone)] +pub struct SpliceText { + #[pyo3(get)] + index: usize, + #[pyo3(get)] + value: String, + #[pyo3(get)] + marks: Option>, +} + +macro_rules! impl_repr { + ( $( $struct_name:ident { $( $field:ident ),* } ),* ) => { + $( + #[pymethods] + impl $struct_name { + pub fn __repr__(slf: &Bound<'_, Self>) -> PyResult { + let class_name: Bound<'_, pyo3::types::PyString> = slf.get_type().qualname()?; + let fields: Vec = vec![ + $( + format!("{}: {:?}", stringify!($field), (&slf.borrow()).$field) + ),* + ]; + let values = fields.join(", "); + + Ok(format!("{}({})", class_name, values)) + } + } + )* + }; +} + +impl_repr!( + Conflict { prop }, + DeleteMap { key }, + DeleteSeq { index, length }, + Increment { prop, value }, + Insert { index, values }, + MarkAction { marks }, + PutMap { + key, + value, + conflict + }, + PutSeq { + index, + value, + conflict + }, + SpliceText { index, value } +);