#[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 {
ptr: *mut subversion_sys::svn_wc_adm_access_t,
_pool: apr::Pool<'static>,
}
#[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<Self, 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: pool,
})
}
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_svn = value.map(|v| crate::string::BStr::from_bytes(v, &self._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 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
}
#[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 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();
with_tmp_pool(|scratch_pool| {
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()),
scratch_pool.as_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() {
unsafe {
subversion_sys::svn_wc_adm_close2(self.ptr, self._pool.as_mut_ptr());
}
self.ptr = std::ptr::null_mut();
}
}
}
#[allow(deprecated)]
impl Drop for Adm {
fn drop(&mut self) {
self.close();
}
}