diff --git a/Cargo.lock b/Cargo.lock index 2a5a1cd5..58503b36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,9 +494,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "subversion" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8f1b19f5ca2793d08ac42733be26164c11f4d496302a2c81c1613af0024c30" +checksum = "d9ee247990bd6b895efd57136190ca79e2c8af250c7b8a6d0b3db47db6c1d55a" dependencies = [ "apr", "apr-sys", @@ -588,7 +588,7 @@ dependencies = [ "toml_datetime", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -602,18 +602,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.0.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" dependencies = [ - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" [[package]] name = "unicode-ident" @@ -663,3 +663,9 @@ name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" diff --git a/Cargo.toml b/Cargo.toml index 6bc605d7..99280538 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,5 @@ edition = "2021" [workspace.dependencies] pyo3 = { version = "0.27" } #subversion = { version = ">=0.0.5" } -subversion = { version = "0.1.5" } +subversion = { version = "0.1.6" } pyo3-filelike = { version = "0.5" } diff --git a/ra/src/session.rs b/ra/src/session.rs index e007793f..aa24e65e 100644 --- a/ra/src/session.rs +++ b/ra/src/session.rs @@ -213,7 +213,9 @@ impl RemoteAccess { /// Reparent the session to a new URL fn reparent(&mut self, url: &str) -> PyResult<()> { let url = subversion::uri::canonicalize_uri(url).map_err(|e| svn_err_to_py(e))?; - self.session.reparent(&url).map_err(|e| svn_err_to_py(e)) + self.session.reparent(&url).map_err(|e| svn_err_to_py(e))?; + self.url = url; + Ok(()) } /// Check if the repository has a capability diff --git a/subvertpy_util/src/error.rs b/subvertpy_util/src/error.rs index 78b87f0d..c9de2412 100644 --- a/subvertpy_util/src/error.rs +++ b/subvertpy_util/src/error.rs @@ -10,7 +10,7 @@ use subversion::Error as SvnError; pub fn svn_err_to_py(err: SvnError) -> PyErr { Python::attach(|py| { let message = err.message().unwrap_or("Unknown SVN error"); - let code = err.apr_err() as i32; + let code = err.raw_apr_err(); // Get the SubversionException class from the subvertpy module let module = py diff --git a/wc/src/context.rs b/wc/src/context.rs index 4ec3359b..d98f7ca1 100644 --- a/wc/src/context.rs +++ b/wc/src/context.rs @@ -88,6 +88,131 @@ impl Context { self.inner.conflicted(&path_str).map_err(svn_err_to_py) } + /// Schedule a path for addition, optionally with copy history. + #[pyo3(signature = (local_abspath, depth=None, copyfrom_url=None, copyfrom_rev=None))] + fn add( + &mut self, + local_abspath: &Bound, + depth: Option, + copyfrom_url: Option<&str>, + copyfrom_rev: Option, + ) -> PyResult<()> { + let path_str = subvertpy_util::py_to_svn_abspath(local_abspath)?; + let svn_depth = depth + .map(depth_from_py) + .unwrap_or(subversion::Depth::Infinity); + let rev = copyfrom_rev.and_then(subvertpy_util::to_revnum); + self.inner + .add(&path_str, svn_depth, copyfrom_url, rev) + .map_err(svn_err_to_py) + } + + /// Schedule a path for deletion. + #[pyo3(signature = (local_abspath, keep_local=false, delete_unversioned_target=false))] + fn delete( + &mut self, + local_abspath: &Bound, + keep_local: bool, + delete_unversioned_target: bool, + ) -> PyResult<()> { + let path_str = subvertpy_util::py_to_svn_abspath(local_abspath)?; + let path = std::path::Path::new(&path_str); + self.inner + .delete(path, keep_local, delete_unversioned_target, None, None) + .map_err(svn_err_to_py) + } + + /// Copy a working copy path. + #[pyo3(signature = (src_abspath, dst_abspath, metadata_only=false))] + fn copy( + &mut self, + src_abspath: &Bound, + dst_abspath: &Bound, + metadata_only: bool, + ) -> PyResult<()> { + let src = subvertpy_util::py_to_svn_abspath(src_abspath)?; + let dst = subvertpy_util::py_to_svn_abspath(dst_abspath)?; + self.inner + .copy( + std::path::Path::new(&src), + std::path::Path::new(&dst), + metadata_only, + ) + .map_err(svn_err_to_py) + } + + /// Get a versioned property value from a working copy path. + fn prop_get(&mut self, path: &Bound, name: &str) -> PyResult> { + let path_str = subvertpy_util::py_to_svn_abspath(path)?; + let value = self + .inner + .prop_get(std::path::Path::new(&path_str), name) + .map_err(svn_err_to_py)?; + Ok(value.map(|v| { + pyo3::Python::with_gil(|py| pyo3::types::PyBytes::new(py, &v).into_any().unbind()) + })) + } + + /// Set a versioned property on a working copy path. + /// + /// If value is None, deletes the property. + #[pyo3(signature = (name, value, path, depth=None, skip_checks=false))] + fn prop_set( + &mut self, + name: &str, + value: Option<&Bound>, + path: &Bound, + depth: Option, + skip_checks: bool, + ) -> PyResult<()> { + let path_str = subvertpy_util::py_to_svn_abspath(path)?; + let svn_depth = depth.map(depth_from_py).unwrap_or(subversion::Depth::Empty); + let value_bytes: Option> = match value { + None => None, + Some(v) => { + if let Ok(b) = v.cast::() { + Some(b.as_bytes().to_vec()) + } else if let Ok(s) = v.extract::() { + Some(s.into_bytes()) + } else if v.is_none() { + None + } else { + return Err(pyo3::exceptions::PyTypeError::new_err( + "value must be str, bytes, or None", + )); + } + } + }; + self.inner + .prop_set( + std::path::Path::new(&path_str), + name, + value_bytes.as_deref(), + svn_depth, + skip_checks, + None, // changelist_filter + None, // cancel_func + None, // notify_func + ) + .map_err(svn_err_to_py) + } + + /// Read the kind of a node in the working copy. + #[pyo3(signature = (path, show_deleted=false, show_hidden=false))] + fn read_kind( + &mut self, + path: &Bound, + show_deleted: bool, + show_hidden: bool, + ) -> PyResult { + let path_str = subvertpy_util::py_to_svn_abspath(path)?; + let kind = self + .inner + .read_kind(std::path::Path::new(&path_str), show_deleted, show_hidden) + .map_err(svn_err_to_py)?; + Ok(kind as i32) + } + /// Get the status of a path. fn status(&mut self, path: &Bound) -> PyResult { let path_str = subvertpy_util::py_to_svn_abspath(path)?; @@ -177,8 +302,8 @@ impl Context { } Ok(( - orig_dict.into_pyobject(py)?.into_any().unbind(), changes_list.into_pyobject(py)?.into_any().unbind(), + orig_dict.into_pyobject(py)?.into_any().unbind(), )) }