#[cfg(feature = "ra")]
use super::{box_notify_baton_borrowed, drop_notify_baton_borrowed, wrap_notify_func, Notify};
use super::{CommittedQueue, ConflictChoice, Lock, PropChange};
use crate::{svn_result, with_tmp_pool};
use std::collections::HashMap;
unsafe fn cstr_to_option(ptr: *const std::ffi::c_char) -> Option<String> {
if ptr.is_null() {
None
} else {
Some(
std::ffi::CStr::from_ptr(ptr)
.to_str()
.expect("expected valid UTF-8")
.to_string(),
)
}
}
#[derive(Debug, Clone)]
pub struct Entry {
pub name: Option<String>,
pub revision: crate::Revnum,
pub url: Option<String>,
pub repos: Option<String>,
pub uuid: Option<String>,
pub kind: crate::NodeKind,
pub schedule: u32,
pub copied: bool,
pub deleted: bool,
pub absent: bool,
pub incomplete: bool,
pub copyfrom_url: Option<String>,
pub copyfrom_rev: crate::Revnum,
pub conflict_old: Option<String>,
pub conflict_new: Option<String>,
pub conflict_wrk: Option<String>,
pub prejfile: Option<String>,
pub text_time: i64,
pub prop_time: i64,
pub checksum: Option<String>,
pub cmt_rev: crate::Revnum,
pub cmt_date: i64,
pub cmt_author: Option<String>,
pub lock_token: Option<String>,
pub lock_owner: Option<String>,
pub lock_comment: Option<String>,
pub lock_creation_date: i64,
pub has_props: bool,
pub has_prop_mods: bool,
pub changelist: Option<String>,
pub working_size: i64,
pub keep_local: bool,
pub depth: crate::Depth,
}
impl Entry {
pub unsafe fn from_raw(ptr: *const subversion_sys::svn_wc_entry_t) -> Self {
let e = &*ptr;
Entry {
name: cstr_to_option(e.name),
revision: crate::Revnum(e.revision),
url: cstr_to_option(e.url),
repos: cstr_to_option(e.repos),
uuid: cstr_to_option(e.uuid),
kind: crate::NodeKind::from(e.kind),
schedule: e.schedule as u32,
copied: e.copied != 0,
deleted: e.deleted != 0,
absent: e.absent != 0,
incomplete: e.incomplete != 0,
copyfrom_url: cstr_to_option(e.copyfrom_url),
copyfrom_rev: crate::Revnum(e.copyfrom_rev),
conflict_old: cstr_to_option(e.conflict_old),
conflict_new: cstr_to_option(e.conflict_new),
conflict_wrk: cstr_to_option(e.conflict_wrk),
prejfile: cstr_to_option(e.prejfile),
text_time: e.text_time,
prop_time: e.prop_time,
checksum: cstr_to_option(e.checksum),
cmt_rev: crate::Revnum(e.cmt_rev),
cmt_date: e.cmt_date,
cmt_author: cstr_to_option(e.cmt_author),
lock_token: cstr_to_option(e.lock_token),
lock_owner: cstr_to_option(e.lock_owner),
lock_comment: cstr_to_option(e.lock_comment),
lock_creation_date: e.lock_creation_date,
has_props: e.has_props != 0,
has_prop_mods: e.has_prop_mods != 0,
changelist: cstr_to_option(e.changelist),
working_size: e.working_size,
keep_local: e.keep_local != 0,
depth: crate::Depth::from(e.depth),
}
}
}
#[derive(Debug, Clone)]
pub struct Status {
pub entry: Option<Entry>,
pub text_status: u32,
pub prop_status: u32,
pub locked: bool,
pub copied: bool,
pub switched: bool,
pub repos_text_status: u32,
pub repos_prop_status: u32,
pub url: Option<String>,
pub ood_last_cmt_rev: crate::Revnum,
pub ood_last_cmt_date: i64,
pub ood_kind: u32,
pub ood_last_cmt_author: Option<String>,
pub file_external: bool,
pub pristine_text_status: u32,
pub pristine_prop_status: u32,
}
impl Status {
pub unsafe fn from_raw(ptr: *const subversion_sys::svn_wc_status2_t) -> Self {
let s = &*ptr;
let entry = if s.entry.is_null() {
None
} else {
Some(Entry::from_raw(s.entry))
};
Status {
entry,
text_status: s.text_status as u32,
prop_status: s.prop_status as u32,
locked: s.locked != 0,
copied: s.copied != 0,
switched: s.switched != 0,
repos_text_status: s.repos_text_status as u32,
repos_prop_status: s.repos_prop_status as u32,
url: cstr_to_option(s.url),
ood_last_cmt_rev: crate::Revnum(s.ood_last_cmt_rev),
ood_last_cmt_date: s.ood_last_cmt_date,
ood_kind: s.ood_kind as u32,
ood_last_cmt_author: cstr_to_option(s.ood_last_cmt_author),
file_external: s.file_external != 0,
pristine_text_status: s.pristine_text_status as u32,
pristine_prop_status: s.pristine_prop_status as u32,
}
}
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub struct Adm<'a> {
ptr: *mut subversion_sys::svn_wc_adm_access_t,
_pool: Option<apr::Pool<'static>>,
_marker: std::marker::PhantomData<&'a ()>,
}
#[allow(deprecated)]
impl Adm<'_> {
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn open(
path: &str,
write_lock: bool,
levels_to_lock: i32,
) -> Result<Adm<'static>, crate::Error<'static>> {
let pool = apr::Pool::new();
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut adm_access: *mut subversion_sys::svn_wc_adm_access_t = std::ptr::null_mut();
with_tmp_pool(|_scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_adm_open3(
&mut adm_access,
std::ptr::null_mut(), path_cstr.as_ptr(),
if write_lock { 1 } else { 0 },
levels_to_lock,
None, std::ptr::null_mut(), pool.as_mut_ptr(),
)
};
svn_result(err)
})?;
Ok(Adm {
ptr: adm_access,
_pool: Some(pool),
_marker: std::marker::PhantomData,
})
}
pub fn is_locked(&self) -> bool {
unsafe { subversion_sys::svn_wc_adm_locked(self.ptr) != 0 }
}
pub fn access_path(&self) -> &str {
let cstr = unsafe { subversion_sys::svn_wc_adm_access_path(self.ptr) };
unsafe { std::ffi::CStr::from_ptr(cstr) }
.to_str()
.expect("access path should be valid UTF-8")
}
pub fn as_ptr(&self) -> *mut subversion_sys::svn_wc_adm_access_t {
self.ptr
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn prop_set(
&self,
name: &str,
value: Option<&[u8]>,
path: &str,
) -> Result<(), crate::Error<'static>> {
let name_cstr = std::ffi::CString::new(name).unwrap();
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let value_pool = apr::Pool::new();
let value_svn = value.map(|v| crate::string::BStr::from_bytes(v, &value_pool));
let value_ptr = value_svn
.as_ref()
.map(|v| v.as_ptr() as *const subversion_sys::svn_string_t)
.unwrap_or(std::ptr::null());
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_prop_set3(
name_cstr.as_ptr(),
value_ptr,
path_cstr.as_ptr(),
self.ptr,
0, None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn prop_get(
&self,
name: &str,
path: &str,
) -> Result<Option<Vec<u8>>, crate::Error<'static>> {
let name_cstr = std::ffi::CString::new(name).unwrap();
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut value: *const subversion_sys::svn_string_t = std::ptr::null();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_prop_get(
&mut value,
name_cstr.as_ptr(),
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
if value.is_null() {
Ok(None)
} else {
let svn_str = unsafe { &*value };
let data = unsafe {
std::slice::from_raw_parts(svn_str.data as *const u8, svn_str.len as usize)
};
Ok(Some(data.to_vec()))
}
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn prop_list(&self, path: &str) -> Result<HashMap<String, Vec<u8>>, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut props: *mut apr::hash::apr_hash_t = std::ptr::null_mut();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_prop_list(
&mut props,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
let mut result = HashMap::new();
if !props.is_null() {
let mut hi = unsafe { apr_sys::apr_hash_first(scratch_pool.as_mut_ptr(), props) };
while !hi.is_null() {
let mut key: *const std::ffi::c_void = std::ptr::null();
let mut val: *mut std::ffi::c_void = std::ptr::null_mut();
let mut klen: apr_sys::apr_ssize_t = 0;
unsafe {
apr_sys::apr_hash_this(hi, &mut key, &mut klen, &mut val);
}
let name = unsafe { std::ffi::CStr::from_ptr(key as *const std::ffi::c_char) }
.to_str()
.expect("property name should be valid UTF-8")
.to_string();
let svn_str = unsafe { &*(val as *const subversion_sys::svn_string_t) };
let data = unsafe {
std::slice::from_raw_parts(svn_str.data as *const u8, svn_str.len as usize)
};
result.insert(name, data.to_vec());
hi = unsafe { apr_sys::apr_hash_next(hi) };
}
}
Ok(result)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn add(
&self,
path: &str,
copyfrom_url: Option<&str>,
copyfrom_rev: Option<crate::Revnum>,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let copyfrom_url_cstr = copyfrom_url.map(std::ffi::CString::new).transpose()?;
let copyfrom_rev_raw = copyfrom_rev.map(|r| r.0).unwrap_or(-1);
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_add3(
path_cstr.as_ptr(),
self.ptr,
subversion_sys::svn_depth_t_svn_depth_infinity,
copyfrom_url_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
copyfrom_rev_raw,
None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn add_repos_file(
&self,
path: &str,
new_base_contents: &mut crate::io::Stream,
new_contents: Option<&mut crate::io::Stream>,
new_base_props: &std::collections::HashMap<String, Vec<u8>>,
new_props: Option<&std::collections::HashMap<String, Vec<u8>>>,
copyfrom_url: Option<&str>,
copyfrom_rev: Option<crate::Revnum>,
) -> Result<(), crate::Error<'static>> {
let path_cstr = std::ffi::CString::new(path)?;
let copyfrom_url_cstr = copyfrom_url.map(std::ffi::CString::new).transpose()?;
let copyfrom_rev_raw = copyfrom_rev.map(|r| r.0).unwrap_or(-1);
with_tmp_pool(|scratch_pool| {
let mut base_props_hash = apr::hash::Hash::new(scratch_pool);
for (key, value) in new_base_props {
let svn_str = crate::string::BStr::from_bytes(value, scratch_pool);
unsafe {
base_props_hash
.insert(key.as_bytes(), svn_str.as_ptr() as *mut std::ffi::c_void);
}
}
let mut props_hash_storage;
let props_ptr = if let Some(props) = new_props {
props_hash_storage = apr::hash::Hash::new(scratch_pool);
for (key, value) in props {
let svn_str = crate::string::BStr::from_bytes(value, scratch_pool);
unsafe {
props_hash_storage
.insert(key.as_bytes(), svn_str.as_ptr() as *mut std::ffi::c_void);
}
}
unsafe { props_hash_storage.as_mut_ptr() }
} else {
std::ptr::null_mut()
};
let err = unsafe {
subversion_sys::svn_wc_add_repos_file3(
path_cstr.as_ptr(),
self.ptr,
new_base_contents.as_mut_ptr(),
new_contents
.map(|s| s.as_mut_ptr())
.unwrap_or(std::ptr::null_mut()),
base_props_hash.as_mut_ptr(),
props_ptr,
copyfrom_url_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
copyfrom_rev_raw,
None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn delete(&self, path: &str, keep_local: bool) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_delete3(
path_cstr.as_ptr(),
self.ptr,
None, std::ptr::null_mut(), None, std::ptr::null_mut(), if keep_local { 1 } else { 0 },
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn copy(&self, src: &str, dst_basename: &str) -> Result<(), crate::Error<'static>> {
let src_cstr = crate::dirent::to_absolute_cstring(src)?;
let dst_cstr = std::ffi::CString::new(dst_basename)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_copy2(
src_cstr.as_ptr(),
self.ptr,
dst_cstr.as_ptr(),
None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn has_binary_prop(&self, path: &str) -> Result<bool, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut has_binary: subversion_sys::svn_boolean_t = 0;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_has_binary_prop(
&mut has_binary,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(has_binary != 0)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn text_modified(
&self,
path: &str,
force_comparison: bool,
) -> Result<bool, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut modified: subversion_sys::svn_boolean_t = 0;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_text_modified_p(
&mut modified,
path_cstr.as_ptr(),
if force_comparison { 1 } else { 0 },
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(modified != 0)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn props_modified(&self, path: &str) -> Result<bool, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut modified: subversion_sys::svn_boolean_t = 0;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_props_modified_p(
&mut modified,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(modified != 0)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn is_wc_root(&self, path: &str) -> Result<bool, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut wc_root: subversion_sys::svn_boolean_t = 0;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_is_wc_root(
&mut wc_root,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(wc_root != 0)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn conflicted(&self, path: &str) -> Result<(bool, bool, bool), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut text: subversion_sys::svn_boolean_t = 0;
let mut prop: subversion_sys::svn_boolean_t = 0;
let mut tree: subversion_sys::svn_boolean_t = 0;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_conflicted_p2(
&mut text,
&mut prop,
&mut tree,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok((text != 0, prop != 0, tree != 0))
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn get_ancestry(
&self,
path: &str,
) -> Result<(Option<String>, crate::Revnum), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut url: *mut std::ffi::c_char = std::ptr::null_mut();
let mut rev: subversion_sys::svn_revnum_t = -1;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_get_ancestry(
&mut url,
&mut rev,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
let url_str = if url.is_null() {
None
} else {
Some(
unsafe { std::ffi::CStr::from_ptr(url) }
.to_str()
.expect("URL should be valid UTF-8")
.to_string(),
)
};
Ok((url_str, crate::Revnum(rev)))
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn get_prop_diffs(
&self,
path: &str,
) -> Result<(Vec<PropChange>, Option<HashMap<String, Vec<u8>>>), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let mut propchanges: *mut apr::tables::apr_array_header_t = std::ptr::null_mut();
let mut original_props: *mut apr::hash::apr_hash_t = std::ptr::null_mut();
let err = unsafe {
subversion_sys::svn_wc_get_prop_diffs(
&mut propchanges,
&mut original_props,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
let changes = if propchanges.is_null() {
Vec::new()
} else {
let array = unsafe {
apr::tables::TypedArray::<subversion_sys::svn_prop_t>::from_ptr(propchanges)
};
array
.iter()
.map(|prop| unsafe {
let name = std::ffi::CStr::from_ptr(prop.name)
.to_str()
.expect("Property name is not valid UTF-8")
.to_owned();
let value = if prop.value.is_null() {
None
} else {
Some(crate::svn_string_helpers::to_vec(&*prop.value))
};
PropChange { name, value }
})
.collect()
};
let original = if original_props.is_null() {
None
} else {
let prop_hash = unsafe { crate::props::PropHash::from_ptr(original_props) };
Some(prop_hash.to_hashmap())
};
Ok((changes, original))
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn mark_missing_deleted(&self, path: &str) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_mark_missing_deleted(
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn maybe_set_repos_root(
&self,
path: &str,
repos: &str,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let repos_cstr = std::ffi::CString::new(repos)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_maybe_set_repos_root(
self.ptr,
path_cstr.as_ptr(),
repos_cstr.as_ptr(),
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn add_lock(&self, path: &str, lock: &Lock) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_add_lock(
path_cstr.as_ptr(),
lock.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn remove_lock(&self, path: &str) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_remove_lock(
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn remove_from_revision_control(
&self,
name: &str,
destroy_wf: bool,
instant_error: bool,
) -> Result<(), crate::Error<'static>> {
let name_cstr = std::ffi::CString::new(name)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_remove_from_revision_control(
self.ptr,
name_cstr.as_ptr(),
if destroy_wf { 1 } else { 0 },
if instant_error { 1 } else { 0 },
None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn resolved_conflict(
&self,
path: &str,
resolve_text: bool,
resolve_props: bool,
resolve_tree: bool,
depth: crate::Depth,
conflict_choice: ConflictChoice,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_resolved_conflict4(
path_cstr.as_ptr(),
self.ptr,
if resolve_text { 1 } else { 0 },
if resolve_props { 1 } else { 0 },
if resolve_tree { 1 } else { 0 },
depth.into(),
conflict_choice.into(),
None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn revert(
&self,
path: &str,
depth: crate::Depth,
use_commit_times: bool,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_revert3(
path_cstr.as_ptr(),
self.ptr,
depth.into(),
if use_commit_times { 1 } else { 0 },
std::ptr::null(), None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn set_changelist(
&self,
path: &str,
changelist: Option<&str>,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let changelist_cstr = changelist.map(std::ffi::CString::new).transpose()?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_set_changelist(
path_cstr.as_ptr(),
changelist_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
self.ptr,
None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn crop_tree(
&self,
target: &str,
depth: crate::Depth,
) -> Result<(), crate::Error<'static>> {
let target_cstr = std::ffi::CString::new(target)?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_crop_tree(
self.ptr,
target_cstr.as_ptr(),
depth.into(),
None, std::ptr::null_mut(), None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn merge_prop_diffs(
&self,
path: &str,
propchanges: &[PropChange],
base_merge: bool,
dry_run: bool,
) -> Result<u32, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut state: subversion_sys::svn_wc_notify_state_t = 0;
with_tmp_pool(|scratch_pool| {
let arr = unsafe {
apr_sys::apr_array_make(
scratch_pool.as_mut_ptr(),
propchanges.len() as i32,
std::mem::size_of::<subversion_sys::svn_prop_t>() as i32,
)
};
for pc in propchanges {
let name_cstr = std::ffi::CString::new(pc.name.as_str()).unwrap();
let name_ptr =
unsafe { apr_sys::apr_pstrdup(scratch_pool.as_mut_ptr(), name_cstr.as_ptr()) };
let value_ptr = match &pc.value {
Some(v) => {
let svn_str = crate::string::BStr::from_bytes(v, scratch_pool);
svn_str.as_ptr() as *const subversion_sys::svn_string_t
}
None => std::ptr::null(),
};
let prop = subversion_sys::svn_prop_t {
name: name_ptr,
value: value_ptr,
};
unsafe {
let dest = apr_sys::apr_array_push(arr) as *mut subversion_sys::svn_prop_t;
*dest = prop;
}
}
let err = unsafe {
subversion_sys::svn_wc_merge_prop_diffs(
&mut state,
path_cstr.as_ptr(),
self.ptr,
arr,
if base_merge { 1 } else { 0 },
if dry_run { 1 } else { 0 },
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(state as u32)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn translated_file(
&self,
src: &str,
versioned_file: &str,
flags: u32,
) -> Result<String, crate::Error<'static>> {
let src_cstr = crate::dirent::to_absolute_cstring(src)?;
let versioned_cstr = crate::dirent::to_absolute_cstring(versioned_file)?;
let mut xlated_path: *const std::ffi::c_char = std::ptr::null();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_translated_file2(
&mut xlated_path,
src_cstr.as_ptr(),
versioned_cstr.as_ptr(),
self.ptr,
flags,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(unsafe { std::ffi::CStr::from_ptr(xlated_path) }
.to_str()
.expect("path should be valid UTF-8")
.to_string())
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn relocate(
&self,
path: &str,
from: &str,
to: &str,
recurse: bool,
validator: Option<&dyn Fn(&str, &str, &str) -> Result<(), crate::Error<'static>>>,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let from_cstr = std::ffi::CString::new(from)?;
let to_cstr = std::ffi::CString::new(to)?;
unsafe extern "C" fn validator_trampoline(
baton: *mut std::ffi::c_void,
uuid: *const std::ffi::c_char,
url: *const std::ffi::c_char,
root_url: *const std::ffi::c_char,
_pool: *mut apr_sys::apr_pool_t,
) -> *mut subversion_sys::svn_error_t {
let validator =
&*(baton as *const &dyn Fn(&str, &str, &str) -> Result<(), crate::Error<'static>>);
let uuid_str = std::ffi::CStr::from_ptr(uuid).to_str().unwrap();
let url_str = std::ffi::CStr::from_ptr(url).to_str().unwrap();
let root_str = std::ffi::CStr::from_ptr(root_url).to_str().unwrap();
match validator(uuid_str, url_str, root_str) {
Ok(()) => std::ptr::null_mut(),
Err(e) => e.into_raw(),
}
}
unsafe extern "C" fn noop_validator(
_baton: *mut std::ffi::c_void,
_uuid: *const std::ffi::c_char,
_url: *const std::ffi::c_char,
_root_url: *const std::ffi::c_char,
_pool: *mut apr_sys::apr_pool_t,
) -> *mut subversion_sys::svn_error_t {
std::ptr::null_mut()
}
let (func, baton): (
subversion_sys::svn_wc_relocation_validator3_t,
*mut std::ffi::c_void,
) = match &validator {
Some(v) => (
Some(validator_trampoline),
v as *const _ as *mut std::ffi::c_void,
),
None => (Some(noop_validator), std::ptr::null_mut()),
};
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_relocate3(
path_cstr.as_ptr(),
self.ptr,
from_cstr.as_ptr(),
to_cstr.as_ptr(),
if recurse { 1 } else { 0 },
func,
baton,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn entry(
&self,
path: &str,
show_hidden: bool,
) -> Result<Option<Entry>, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut entry_ptr: *const subversion_sys::svn_wc_entry_t = std::ptr::null();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_entry(
&mut entry_ptr,
path_cstr.as_ptr(),
self.ptr,
if show_hidden { 1 } else { 0 },
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
if entry_ptr.is_null() {
Ok(None)
} else {
Ok(Some(unsafe { Entry::from_raw(entry_ptr) }))
}
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn entries_read(
&self,
show_hidden: bool,
) -> Result<HashMap<String, Entry>, crate::Error<'static>> {
let mut entries: *mut apr::hash::apr_hash_t = std::ptr::null_mut();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_entries_read(
&mut entries,
self.ptr,
if show_hidden { 1 } else { 0 },
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
let mut result = HashMap::new();
if !entries.is_null() {
let mut hi = unsafe { apr_sys::apr_hash_first(scratch_pool.as_mut_ptr(), entries) };
while !hi.is_null() {
let mut key: *const std::ffi::c_void = std::ptr::null();
let mut val: *mut std::ffi::c_void = std::ptr::null_mut();
let mut klen: apr_sys::apr_ssize_t = 0;
unsafe { apr_sys::apr_hash_this(hi, &mut key, &mut klen, &mut val) };
let name = unsafe { std::ffi::CStr::from_ptr(key as *const std::ffi::c_char) }
.to_str()
.expect("entry name should be valid UTF-8")
.to_string();
let entry =
unsafe { Entry::from_raw(val as *const subversion_sys::svn_wc_entry_t) };
result.insert(name, entry);
hi = unsafe { apr_sys::apr_hash_next(hi) };
}
}
Ok(result)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn status(&self, path: &str) -> Result<Status, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut status_ptr: *mut subversion_sys::svn_wc_status2_t = std::ptr::null_mut();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_status2(
&mut status_ptr,
path_cstr.as_ptr(),
self.ptr,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(unsafe { Status::from_raw(status_ptr) })
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn walk_entries<F>(
&self,
path: &str,
mut callback: F,
depth: crate::Depth,
show_hidden: bool,
) -> Result<(), crate::Error<'static>>
where
F: FnMut(&str, &Entry) -> Result<(), crate::Error<'static>>,
{
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
unsafe extern "C" fn found_entry_trampoline<F>(
path: *const std::ffi::c_char,
entry: *const subversion_sys::svn_wc_entry_t,
walk_baton: *mut std::ffi::c_void,
_pool: *mut apr_sys::apr_pool_t,
) -> *mut subversion_sys::svn_error_t
where
F: FnMut(&str, &Entry) -> Result<(), crate::Error<'static>>,
{
let callback = &mut *(walk_baton as *mut F);
let path_str = std::ffi::CStr::from_ptr(path).to_str().unwrap();
let entry = Entry::from_raw(entry);
match callback(path_str, &entry) {
Ok(()) => std::ptr::null_mut(),
Err(e) => e.into_raw(),
}
}
unsafe extern "C" fn handle_error_trampoline(
_path: *const std::ffi::c_char,
err: *mut subversion_sys::svn_error_t,
_walk_baton: *mut std::ffi::c_void,
_pool: *mut apr_sys::apr_pool_t,
) -> *mut subversion_sys::svn_error_t {
err
}
let callbacks = subversion_sys::svn_wc_entry_callbacks2_t {
found_entry: Some(found_entry_trampoline::<F>),
handle_error: Some(handle_error_trampoline),
};
let baton = &mut callback as *mut F as *mut std::ffi::c_void;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_walk_entries3(
path_cstr.as_ptr(),
self.ptr,
&callbacks,
baton,
depth.into(),
if show_hidden { 1 } else { 0 },
None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
#[cfg(feature = "ra")]
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn crawl_revisions(
&self,
path: &str,
reporter: &crate::ra::WrapReporter,
restore_files: bool,
depth: crate::Depth,
honor_depth_exclude: bool,
depth_compatibility_trick: bool,
use_commit_times: bool,
notify_func: Option<&dyn Fn(&Notify)>,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let notify_baton = notify_func
.map(|f| box_notify_baton_borrowed(f))
.unwrap_or(std::ptr::null_mut());
let result = with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_crawl_revisions4(
path_cstr.as_ptr(),
self.ptr,
reporter.as_ptr(),
reporter.as_baton(),
if restore_files { 1 } else { 0 },
depth.into(),
if honor_depth_exclude { 1 } else { 0 },
if depth_compatibility_trick { 1 } else { 0 },
if use_commit_times { 1 } else { 0 },
if notify_func.is_some() {
Some(wrap_notify_func)
} else {
None
},
notify_baton,
std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
});
if !notify_baton.is_null() {
unsafe { drop_notify_baton_borrowed(notify_baton) };
}
result
}
#[allow(clippy::too_many_arguments)]
pub fn get_switch_editor(
&self,
target: &str,
switch_url: &str,
use_commit_times: bool,
depth: crate::Depth,
depth_is_sticky: bool,
allow_unver_obstructions: bool,
diff3_cmd: Option<&str>,
) -> Result<(crate::delta::WrapEditor<'static>, crate::Revnum), crate::Error<'static>> {
let target_cstr = std::ffi::CString::new(target)?;
let switch_url_cstr = std::ffi::CString::new(switch_url)?;
let diff3_cmd_cstr = diff3_cmd.map(std::ffi::CString::new).transpose()?;
let result_pool = apr::Pool::new();
let mut target_revision: subversion_sys::svn_revnum_t = 0;
let mut editor_ptr: *const subversion_sys::svn_delta_editor_t = std::ptr::null();
let mut edit_baton: *mut std::ffi::c_void = std::ptr::null_mut();
unsafe {
let err = subversion_sys::svn_wc_get_switch_editor3(
&mut target_revision,
self.ptr,
target_cstr.as_ptr(),
switch_url_cstr.as_ptr(),
if use_commit_times { 1 } else { 0 },
depth.into(),
if depth_is_sticky { 1 } else { 0 },
if allow_unver_obstructions { 1 } else { 0 },
None, std::ptr::null_mut(), None, std::ptr::null_mut(), None, std::ptr::null_mut(), diff3_cmd_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
std::ptr::null(), &mut editor_ptr,
&mut edit_baton,
std::ptr::null_mut(), result_pool.as_mut_ptr(),
);
svn_result(err)?;
}
let editor = crate::delta::WrapEditor {
editor: editor_ptr,
baton: edit_baton,
_pool: apr::PoolHandle::owned(result_pool),
callback_batons: Vec::new(),
};
Ok((editor, crate::Revnum(target_revision)))
}
#[cfg(feature = "delta")]
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn transmit_text_deltas(
&self,
path: &str,
fulltext: bool,
file_editor: &crate::delta::WrapFileEditor<'_>,
) -> Result<(Option<String>, [u8; 16]), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut tempfile: *const std::ffi::c_char = std::ptr::null();
let mut digest = [0u8; 16]; let (editor, file_baton) = file_editor.as_raw_parts();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_transmit_text_deltas2(
&mut tempfile,
digest.as_mut_ptr(),
path_cstr.as_ptr(),
self.ptr,
if fulltext { 1 } else { 0 },
editor,
file_baton,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
let tempfile_str = unsafe { cstr_to_option(tempfile) };
Ok((tempfile_str, digest))
})
}
#[cfg(feature = "delta")]
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn transmit_prop_deltas(
&self,
path: &str,
file_editor: &crate::delta::WrapFileEditor<'_>,
) -> Result<Option<String>, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut tempfile: *const std::ffi::c_char = std::ptr::null();
let (editor, baton) = file_editor.as_raw_parts();
with_tmp_pool(|scratch_pool| {
let mut entry_ptr: *const subversion_sys::svn_wc_entry_t = std::ptr::null();
let err = unsafe {
subversion_sys::svn_wc_entry(
&mut entry_ptr,
path_cstr.as_ptr(),
self.ptr,
0, scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
if entry_ptr.is_null() {
return Err(crate::Error::with_raw_status(
subversion_sys::svn_errno_t_SVN_ERR_ENTRY_NOT_FOUND as i32,
None,
&format!("No entry for '{path}'"),
));
}
let err = unsafe {
subversion_sys::svn_wc_transmit_prop_deltas(
path_cstr.as_ptr(),
self.ptr,
entry_ptr,
editor,
baton,
&mut tempfile,
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(unsafe { cstr_to_option(tempfile) })
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn merge(
&self,
left: &str,
right: &str,
merge_target: &str,
left_label: Option<&str>,
right_label: Option<&str>,
target_label: Option<&str>,
dry_run: bool,
diff3_cmd: Option<&str>,
merge_options: &[&str],
prop_diff: &[PropChange],
) -> Result<u32, crate::Error<'static>> {
let left_cstr = crate::dirent::to_absolute_cstring(left)?;
let right_cstr = crate::dirent::to_absolute_cstring(right)?;
let target_cstr = crate::dirent::to_absolute_cstring(merge_target)?;
let left_label_cstr = left_label.map(std::ffi::CString::new).transpose()?;
let right_label_cstr = right_label.map(std::ffi::CString::new).transpose()?;
let target_label_cstr = target_label.map(std::ffi::CString::new).transpose()?;
let diff3_cmd_cstr = diff3_cmd.map(std::ffi::CString::new).transpose()?;
let mut merge_outcome: subversion_sys::svn_wc_merge_outcome_t = 0;
with_tmp_pool(|scratch_pool| {
let merge_opts_arr = if merge_options.is_empty() {
std::ptr::null()
} else {
let arr = unsafe {
apr_sys::apr_array_make(
scratch_pool.as_mut_ptr(),
merge_options.len() as i32,
std::mem::size_of::<*const std::ffi::c_char>() as i32,
)
};
for opt in merge_options {
let cstr = std::ffi::CString::new(*opt).unwrap();
let ptr =
unsafe { apr_sys::apr_pstrdup(scratch_pool.as_mut_ptr(), cstr.as_ptr()) };
unsafe {
let dest = apr_sys::apr_array_push(arr) as *mut *const std::ffi::c_char;
*dest = ptr;
}
}
arr as *const _
};
let prop_diff_arr = unsafe {
apr_sys::apr_array_make(
scratch_pool.as_mut_ptr(),
prop_diff.len() as i32,
std::mem::size_of::<subversion_sys::svn_prop_t>() as i32,
)
};
for pc in prop_diff {
let name_cstr = std::ffi::CString::new(pc.name.as_str()).unwrap();
let name_ptr =
unsafe { apr_sys::apr_pstrdup(scratch_pool.as_mut_ptr(), name_cstr.as_ptr()) };
let value_ptr = match &pc.value {
Some(v) => {
let svn_str = crate::string::BStr::from_bytes(v, scratch_pool);
svn_str.as_ptr() as *const subversion_sys::svn_string_t
}
None => std::ptr::null(),
};
unsafe {
let dest =
apr_sys::apr_array_push(prop_diff_arr) as *mut subversion_sys::svn_prop_t;
*dest = subversion_sys::svn_prop_t {
name: name_ptr,
value: value_ptr,
};
}
}
let err = unsafe {
subversion_sys::svn_wc_merge3(
&mut merge_outcome,
left_cstr.as_ptr(),
right_cstr.as_ptr(),
target_cstr.as_ptr(),
self.ptr,
left_label_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
right_label_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
target_label_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
if dry_run { 1 } else { 0 },
diff3_cmd_cstr
.as_ref()
.map_or(std::ptr::null(), |c| c.as_ptr()),
merge_opts_arr,
prop_diff_arr,
None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(merge_outcome as u32)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn merge_props(
&self,
path: &str,
baseprops: &HashMap<String, Vec<u8>>,
propchanges: &[PropChange],
base_merge: bool,
dry_run: bool,
) -> Result<u32, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut state: subversion_sys::svn_wc_notify_state_t = 0;
with_tmp_pool(|scratch_pool| {
let baseprops_hash = unsafe { apr_sys::apr_hash_make(scratch_pool.as_mut_ptr()) };
for (name, value) in baseprops {
let name_cstr = std::ffi::CString::new(name.as_str()).unwrap();
let name_ptr =
unsafe { apr_sys::apr_pstrdup(scratch_pool.as_mut_ptr(), name_cstr.as_ptr()) };
let svn_str = crate::string::BStr::from_bytes(value, scratch_pool);
unsafe {
apr_sys::apr_hash_set(
baseprops_hash,
name_ptr as *const _,
apr_sys::APR_HASH_KEY_STRING as apr_sys::apr_ssize_t,
svn_str.as_ptr() as *const _,
);
}
}
let arr = unsafe {
apr_sys::apr_array_make(
scratch_pool.as_mut_ptr(),
propchanges.len() as i32,
std::mem::size_of::<subversion_sys::svn_prop_t>() as i32,
)
};
for pc in propchanges {
let name_cstr = std::ffi::CString::new(pc.name.as_str()).unwrap();
let name_ptr =
unsafe { apr_sys::apr_pstrdup(scratch_pool.as_mut_ptr(), name_cstr.as_ptr()) };
let value_ptr = match &pc.value {
Some(v) => {
let svn_str = crate::string::BStr::from_bytes(v, scratch_pool);
svn_str.as_ptr() as *const subversion_sys::svn_string_t
}
None => std::ptr::null(),
};
unsafe {
let dest = apr_sys::apr_array_push(arr) as *mut subversion_sys::svn_prop_t;
*dest = subversion_sys::svn_prop_t {
name: name_ptr,
value: value_ptr,
};
}
}
let err = unsafe {
subversion_sys::svn_wc_merge_props2(
&mut state,
path_cstr.as_ptr(),
self.ptr,
baseprops_hash,
arr,
if base_merge { 1 } else { 0 },
if dry_run { 1 } else { 0 },
None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
Ok(state as u32)
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn probe_try(
&mut self,
path: &str,
write_lock: bool,
levels_to_lock: i32,
) -> Result<Option<Adm<'_>>, crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut adm_access: *mut subversion_sys::svn_wc_adm_access_t = std::ptr::null_mut();
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_adm_probe_try3(
&mut adm_access,
self.ptr,
path_cstr.as_ptr(),
if write_lock { 1 } else { 0 },
levels_to_lock,
None, std::ptr::null_mut(), scratch_pool.as_mut_ptr(),
)
};
svn_result(err)?;
if adm_access.is_null() {
Ok(None)
} else {
Ok(Some(Adm {
ptr: adm_access,
_pool: None,
_marker: std::marker::PhantomData,
}))
}
})
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn queue_committed(
&self,
path: &str,
committed_queue: &mut CommittedQueue,
recurse: bool,
remove_lock: bool,
remove_changelist: bool,
digest: Option<&[u8; 16]>,
) -> Result<(), crate::Error<'static>> {
let path_cstr = crate::dirent::to_absolute_cstring(path)?;
let mut queue_ptr = committed_queue.as_mut_ptr();
let err = unsafe {
subversion_sys::svn_wc_queue_committed(
&mut queue_ptr,
path_cstr.as_ptr(),
self.ptr,
if recurse { 1 } else { 0 },
std::ptr::null(), if remove_lock { 1 } else { 0 },
if remove_changelist { 1 } else { 0 },
digest.map(|d| d.as_ptr()).unwrap_or(std::ptr::null()),
committed_queue.pool_mut_ptr(),
)
};
svn_result(err)
}
#[deprecated(note = "Use svn_wc_context_t based APIs where possible")]
pub fn process_committed_queue(
&self,
committed_queue: &mut CommittedQueue,
new_revnum: crate::Revnum,
rev_date: Option<&str>,
rev_author: Option<&str>,
) -> Result<(), crate::Error<'static>> {
let rev_date_cstr = rev_date.map(std::ffi::CString::new).transpose()?;
let rev_author_cstr = rev_author.map(std::ffi::CString::new).transpose()?;
with_tmp_pool(|scratch_pool| {
let err = unsafe {
subversion_sys::svn_wc_process_committed_queue(
committed_queue.as_mut_ptr(),
self.ptr,
new_revnum.0,
rev_date_cstr
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
rev_author_cstr
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
scratch_pool.as_mut_ptr(),
)
};
svn_result(err)
})
}
}
#[allow(deprecated)]
impl Adm<'_> {
pub fn close(&mut self) {
if !self.ptr.is_null() {
match &mut self._pool {
Some(pool) => unsafe {
subversion_sys::svn_wc_adm_close2(self.ptr, pool.as_mut_ptr());
},
None => {
}
}
self.ptr = std::ptr::null_mut();
}
}
}
#[allow(deprecated)]
impl Drop for Adm<'_> {
fn drop(&mut self) {
self.close();
}
}
#[cfg(all(test, feature = "client"))]
#[allow(deprecated)]
mod tests {
use super::*;
fn checkout_wc() -> (tempfile::TempDir, std::path::PathBuf) {
let temp = tempfile::TempDir::new().unwrap();
let repos_path = temp.path().join("repos");
let wc_path = temp.path().join("wc");
crate::repos::Repos::create(&repos_path).unwrap();
let url = crate::path_to_file_url(&repos_path);
let uri = crate::uri::Uri::new(&url).unwrap();
let mut ctx = crate::client::Context::new().unwrap();
ctx.checkout(
uri,
&wc_path,
&crate::client::CheckoutOptions {
peg_revision: crate::Revision::Head,
revision: crate::Revision::Head,
depth: crate::Depth::Infinity,
ignore_externals: false,
allow_unver_obstructions: false,
},
)
.unwrap();
(temp, wc_path)
}
#[test]
fn test_adm_open_lifecycle() {
let (_temp, wc) = checkout_wc();
let wc_str = wc.to_str().unwrap();
let ro = Adm::open(wc_str, false, 0).unwrap();
assert!(!ro.is_locked());
drop(ro);
let mut rw = Adm::open(wc_str, true, 0).unwrap();
assert!(rw.is_locked());
let expected = crate::dirent::to_absolute_cstring(wc_str).unwrap();
assert_eq!(rw.access_path(), expected.to_str().unwrap());
rw.close();
}
#[test]
fn test_adm_entry_and_is_wc_root() {
let (_temp, wc) = checkout_wc();
let wc_str = wc.to_str().unwrap();
let adm = Adm::open(wc_str, false, 0).unwrap();
assert!(adm.is_wc_root(wc_str).unwrap());
assert_eq!(adm.conflicted(wc_str).unwrap(), (false, false, false));
let entry = adm
.entry(wc_str, false)
.unwrap()
.expect("root is versioned");
assert_eq!(entry.kind, crate::NodeKind::Dir);
assert!(entry.url.is_some());
let entries = adm.entries_read(false).unwrap();
assert_eq!(entries.len(), 1);
}
#[test]
fn test_adm_prop_round_trip() {
let (_temp, wc) = checkout_wc();
let wc_str = wc.to_str().unwrap();
let adm = Adm::open(wc_str, true, 0).unwrap();
assert!(!adm.props_modified(wc_str).unwrap());
assert_eq!(adm.prop_get("my:prop", wc_str).unwrap(), None);
adm.prop_set("my:prop", Some(b"hello"), wc_str).unwrap();
assert_eq!(
adm.prop_get("my:prop", wc_str).unwrap(),
Some(b"hello".to_vec())
);
assert!(adm.props_modified(wc_str).unwrap());
assert_eq!(
adm.prop_list(wc_str).unwrap().get("my:prop"),
Some(&b"hello".to_vec())
);
}
#[test]
fn test_adm_status() {
let (_temp, wc) = checkout_wc();
let wc_str = wc.to_str().unwrap();
let adm = Adm::open(wc_str, false, 0).unwrap();
let status = adm.status(wc_str).unwrap();
let entry = status.entry.expect("versioned path has an entry");
assert_eq!(entry.kind, crate::NodeKind::Dir);
}
#[test]
fn test_adm_get_ancestry() {
let (_temp, wc) = checkout_wc();
let wc_str = wc.to_str().unwrap();
let adm = Adm::open(wc_str, false, 0).unwrap();
let (url, rev) = adm.get_ancestry(wc_str).unwrap();
assert_eq!(rev, crate::Revnum(0));
assert!(url.expect("root has a URL").ends_with("repos"));
}
#[test]
fn test_adm_add_file_and_query() {
let (_temp, wc) = checkout_wc();
let wc_str = wc.to_str().unwrap();
let adm = Adm::open(wc_str, true, 0).unwrap();
let file = wc.join("hello.txt");
std::fs::write(&file, b"hello\n").unwrap();
let file_str = file.to_str().unwrap();
adm.add(file_str, None, None).unwrap();
let entry = adm
.entry(file_str, false)
.unwrap()
.expect("file is versioned");
assert_eq!(entry.kind, crate::NodeKind::File);
assert!(!adm.is_wc_root(file_str).unwrap());
assert!(adm.text_modified(file_str, false).unwrap());
assert!(!adm.has_binary_prop(file_str).unwrap());
}
#[test]
fn test_add_repos_file() {
use std::io::Cursor;
let (_temp, wc_path) = checkout_wc();
let file_path = wc_path.join("test.txt");
let content = b"hello world";
std::fs::write(&file_path, content).unwrap();
let mut adm = Adm::open(wc_path.to_str().unwrap(), true, -1).unwrap();
let backend = crate::io::ReadOnlyBackend::new(Cursor::new(content.to_vec()));
let mut stream = crate::io::Stream::from_backend(backend).unwrap();
let base_props = std::collections::HashMap::new();
adm.add_repos_file(
file_path.to_str().unwrap(),
&mut stream,
None,
&base_props,
None,
None,
None,
)
.unwrap();
adm.close();
}
#[test]
fn test_probe_try_versioned_dir() {
let (_temp, wc_path) = checkout_wc();
let mut adm = Adm::open(wc_path.to_str().unwrap(), true, -1).unwrap();
let sub_dir = wc_path.join("subdir");
std::fs::create_dir(&sub_dir).unwrap();
adm.add(sub_dir.to_str().unwrap(), None, None).unwrap();
let sub_adm = adm.probe_try(sub_dir.to_str().unwrap(), true, 0).unwrap();
assert!(sub_adm.is_some());
drop(sub_adm);
adm.close();
}
#[test]
fn test_probe_try_unversioned_file() {
let (_temp, wc_path) = checkout_wc();
let mut adm = Adm::open(wc_path.to_str().unwrap(), true, -1).unwrap();
let probed = adm
.probe_try(wc_path.join("nonexistent").to_str().unwrap(), false, 0)
.unwrap();
assert!(probed.is_some());
drop(probed);
adm.close();
}
}