use crate::{with_tmp_pool, Error};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EolStyle {
Unknown,
None,
Native,
Fixed,
}
impl From<subversion_sys::svn_subst_eol_style_t> for EolStyle {
fn from(style: subversion_sys::svn_subst_eol_style_t) -> Self {
match style {
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_unknown => EolStyle::Unknown,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_none => EolStyle::None,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_native => EolStyle::Native,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_fixed => EolStyle::Fixed,
_ => unreachable!("unknown svn_subst_eol_style_t value: {}", style),
}
}
}
impl From<EolStyle> for subversion_sys::svn_subst_eol_style_t {
fn from(style: EolStyle) -> Self {
match style {
EolStyle::Unknown => subversion_sys::svn_subst_eol_style_svn_subst_eol_style_unknown,
EolStyle::None => subversion_sys::svn_subst_eol_style_svn_subst_eol_style_none,
EolStyle::Native => subversion_sys::svn_subst_eol_style_svn_subst_eol_style_native,
EolStyle::Fixed => subversion_sys::svn_subst_eol_style_svn_subst_eol_style_fixed,
}
}
}
pub fn eol_style_from_value(value: &str) -> (EolStyle, Option<String>) {
let value_cstr = std::ffi::CString::new(value).unwrap();
unsafe {
let mut style: subversion_sys::svn_subst_eol_style_t =
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_unknown;
let mut eol_ptr: *const std::ffi::c_char = std::ptr::null();
subversion_sys::svn_subst_eol_style_from_value(
&mut style,
&mut eol_ptr,
value_cstr.as_ptr(),
);
let eol_str = if eol_ptr.is_null() {
None
} else {
Some(
std::ffi::CStr::from_ptr(eol_ptr)
.to_str()
.unwrap()
.to_string(),
)
};
(EolStyle::from(style), eol_str)
}
}
pub fn translation_required(
style: EolStyle,
eol: Option<&str>,
keywords: Option<&HashMap<String, String>>,
special: bool,
force_eol_check: bool,
) -> bool {
with_tmp_pool(|pool| {
let eol_cstr = eol.map(|s| std::ffi::CString::new(s).unwrap());
let eol_ptr = eol_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null());
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(pool);
for (k, v) in kw.iter() {
unsafe {
let k_pooled = apr::strings::pstrdup_raw(k, pool).unwrap();
let v_cstr = std::ffi::CString::new(v.as_str()).unwrap();
let svn_str =
subversion_sys::svn_string_create(v_cstr.as_ptr(), pool.as_mut_ptr());
let key_slice =
std::slice::from_raw_parts(k_pooled as *const u8, k.len() + 1);
hash.insert(key_slice, svn_str as *mut std::ffi::c_void);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
unsafe {
subversion_sys::svn_subst_translation_required(
style.into(),
eol_ptr,
keywords_hash,
if special { 1 } else { 0 },
if force_eol_check { 1 } else { 0 },
) != 0
}
})
}
pub fn build_keywords(
keywords_string: &str,
rev: Option<&str>,
url: Option<&str>,
date: Option<apr::time::Time>,
author: Option<&str>,
repos_root_url: Option<&str>,
) -> Result<HashMap<String, String>, Error<'static>> {
with_tmp_pool(|pool| {
let keywords_cstr = std::ffi::CString::new(keywords_string).unwrap();
let rev_cstr = rev.map(|s| std::ffi::CString::new(s).unwrap());
let url_cstr = url.map(|s| std::ffi::CString::new(s).unwrap());
let author_cstr = author.map(|s| std::ffi::CString::new(s).unwrap());
let repos_root_cstr = repos_root_url.map(|s| std::ffi::CString::new(s).unwrap());
let mut keywords_hash: *mut apr_sys::apr_hash_t = std::ptr::null_mut();
unsafe {
let err = subversion_sys::svn_subst_build_keywords3(
&mut keywords_hash,
keywords_cstr.as_ptr(),
rev_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null()),
url_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null()),
repos_root_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null()),
date.map(|d| d.into()).unwrap_or(0),
author_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null()),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
}
if keywords_hash.is_null() {
return Ok(HashMap::new());
}
let prop_hash = unsafe { crate::props::PropHash::from_ptr(keywords_hash) };
Ok(prop_hash.to_string_hashmap())
})
}
pub fn keywords_differ(
a: Option<&HashMap<String, String>>,
b: Option<&HashMap<String, String>>,
compare_values: bool,
) -> Result<bool, Error<'static>> {
with_tmp_pool(|pool| {
let mut c_strings_a: Vec<_> = match a {
Some(kw) => kw
.iter()
.map(|(k, v)| (k.as_str(), std::ffi::CString::new(v.as_str()).unwrap()))
.collect(),
None => Vec::new(),
};
c_strings_a.sort_by(|a, b| a.0.cmp(b.0));
let hash_a = match a {
Some(_kw) => {
let mut hash = apr::hash::Hash::new(pool);
for (k, v_cstr) in c_strings_a.iter() {
unsafe {
let svn_str =
subversion_sys::svn_string_create(v_cstr.as_ptr(), pool.as_mut_ptr());
hash.insert(k.as_bytes(), svn_str as *mut std::ffi::c_void);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
let mut c_strings_b: Vec<_> = match b {
Some(kw) => kw
.iter()
.map(|(k, v)| (k.as_str(), std::ffi::CString::new(v.as_str()).unwrap()))
.collect(),
None => Vec::new(),
};
c_strings_b.sort_by(|a, b| a.0.cmp(b.0));
let hash_b = match b {
Some(_kw) => {
let mut hash = apr::hash::Hash::new(pool);
for (k, v_cstr) in c_strings_b.iter() {
unsafe {
let svn_str =
subversion_sys::svn_string_create(v_cstr.as_ptr(), pool.as_mut_ptr());
hash.insert(k.as_bytes(), svn_str as *mut std::ffi::c_void);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
unsafe {
let result = subversion_sys::svn_subst_keywords_differ2(
hash_a,
hash_b,
if compare_values { 1 } else { 0 },
pool.as_mut_ptr(),
);
Ok(result != 0)
}
})
}
pub fn stream_translated(
mut source_stream: crate::io::Stream,
eol_str: Option<&str>,
repair: bool,
keywords: Option<&HashMap<String, String>>,
expand: bool,
) -> Result<crate::io::Stream, Error<'static>> {
let result_pool = apr::Pool::new();
let eol_ptr = match eol_str {
Some(s) => apr::strings::pstrdup_raw(s, &result_pool)?,
None => std::ptr::null(),
};
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(&result_pool);
for (k, v) in kw.iter() {
unsafe {
let key_ptr = apr::strings::pstrdup_raw(k, &result_pool)?;
let val_ptr = apr::strings::pstrdup_raw(v, &result_pool)?;
hash.insert(
std::slice::from_raw_parts(key_ptr as *const u8, k.len()),
val_ptr as *mut std::ffi::c_void,
);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
unsafe {
let translated_stream = subversion_sys::svn_subst_stream_translated(
source_stream.as_mut_ptr(),
eol_ptr,
if repair { 1 } else { 0 },
keywords_hash,
if expand { 1 } else { 0 },
result_pool.as_mut_ptr(),
);
Ok(crate::io::Stream::from_ptr(translated_stream, result_pool))
}
}
pub fn stream_translated_to_normal_form(
mut source_stream: crate::io::Stream,
eol_style: EolStyle,
eol_str: Option<&str>,
always_repair_eols: bool,
keywords: Option<&HashMap<String, String>>,
) -> Result<crate::io::Stream, Error<'static>> {
let result_pool = apr::Pool::new();
let eol_ptr = match eol_str {
Some(s) => apr::strings::pstrdup_raw(s, &result_pool)?,
None => std::ptr::null(),
};
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(&result_pool);
for (k, v) in kw.iter() {
unsafe {
let key_ptr = apr::strings::pstrdup_raw(k, &result_pool)?;
let val_ptr = apr::strings::pstrdup_raw(v, &result_pool)?;
hash.insert(
std::slice::from_raw_parts(key_ptr as *const u8, k.len()),
val_ptr as *mut std::ffi::c_void,
);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
unsafe {
let mut translated_stream: *mut subversion_sys::svn_stream_t = std::ptr::null_mut();
let err = subversion_sys::svn_subst_stream_translated_to_normal_form(
&mut translated_stream,
source_stream.as_mut_ptr(),
eol_style.into(),
eol_ptr,
if always_repair_eols { 1 } else { 0 },
keywords_hash,
result_pool.as_mut_ptr(),
);
Error::from_raw(err)?;
Ok(crate::io::Stream::from_ptr(translated_stream, result_pool))
}
}
pub fn translate_stream(
src_stream: &mut crate::io::Stream,
dst_stream: &mut crate::io::Stream,
eol_str: Option<&str>,
repair: bool,
keywords: Option<&HashMap<String, String>>,
expand: bool,
) -> Result<(), Error<'static>> {
with_tmp_pool(|pool| {
let eol_cstr = eol_str.map(|s| std::ffi::CString::new(s).unwrap());
let eol_ptr = eol_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null());
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(pool);
for (k, v) in kw.iter() {
unsafe {
let k_pooled = apr::strings::pstrdup_raw(k, pool).unwrap();
let v_cstr = std::ffi::CString::new(v.as_str()).unwrap();
let svn_str =
subversion_sys::svn_string_create(v_cstr.as_ptr(), pool.as_mut_ptr());
let key_slice =
std::slice::from_raw_parts(k_pooled as *const u8, k.len() + 1);
hash.insert(key_slice, svn_str as *mut std::ffi::c_void);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
unsafe {
let err = subversion_sys::svn_subst_translate_stream3(
src_stream.as_mut_ptr(),
dst_stream.as_mut_ptr(),
eol_ptr,
if repair { 1 } else { 0 },
keywords_hash,
if expand { 1 } else { 0 },
pool.as_mut_ptr(),
);
Error::from_raw(err)
}
})
}
pub fn copy_and_translate(
src: &std::path::Path,
dst: &std::path::Path,
eol_str: Option<&str>,
repair: bool,
keywords: Option<&HashMap<String, String>>,
expand: bool,
special: bool,
) -> Result<(), Error<'static>> {
with_tmp_pool(|pool| {
let src_cstr = std::ffi::CString::new(
src.to_str()
.ok_or_else(|| Error::from_message("Invalid source path encoding"))?,
)?;
let dst_cstr = std::ffi::CString::new(
dst.to_str()
.ok_or_else(|| Error::from_message("Invalid destination path encoding"))?,
)?;
let eol_cstr = eol_str.map(|s| std::ffi::CString::new(s).unwrap());
let eol_ptr = eol_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null());
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(pool);
for (k, v) in kw.iter() {
unsafe {
let k_pooled = apr::strings::pstrdup_raw(k, pool).unwrap();
let v_cstr = std::ffi::CString::new(v.as_str()).unwrap();
let svn_str =
subversion_sys::svn_string_create(v_cstr.as_ptr(), pool.as_mut_ptr());
let key_slice =
std::slice::from_raw_parts(k_pooled as *const u8, k.len() + 1);
hash.insert(key_slice, svn_str as *mut std::ffi::c_void);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
unsafe {
let err = subversion_sys::svn_subst_copy_and_translate4(
src_cstr.as_ptr(),
dst_cstr.as_ptr(),
eol_ptr,
if repair { 1 } else { 0 },
keywords_hash,
if expand { 1 } else { 0 },
if special { 1 } else { 0 },
None,
std::ptr::null_mut(),
pool.as_mut_ptr(),
);
Error::from_raw(err)
}
})
}
pub fn translate_cstring(
src: &str,
eol_str: Option<&str>,
repair: bool,
keywords: Option<&HashMap<String, String>>,
expand: bool,
) -> Result<String, Error<'static>> {
with_tmp_pool(|pool| {
let src_cstr = std::ffi::CString::new(src)?;
let eol_cstr = eol_str.map(|s| std::ffi::CString::new(s).unwrap());
let eol_ptr = eol_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null());
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(pool);
for (k, v) in kw.iter() {
unsafe {
let k_pooled = apr::strings::pstrdup_raw(k, pool).unwrap();
let v_cstr = std::ffi::CString::new(v.as_str()).unwrap();
let svn_str =
subversion_sys::svn_string_create(v_cstr.as_ptr(), pool.as_mut_ptr());
let key_slice =
std::slice::from_raw_parts(k_pooled as *const u8, k.len() + 1);
hash.insert(key_slice, svn_str as *mut std::ffi::c_void);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
let mut dst_ptr: *const std::ffi::c_char = std::ptr::null();
unsafe {
let err = subversion_sys::svn_subst_translate_cstring2(
src_cstr.as_ptr(),
&mut dst_ptr,
eol_ptr,
if repair { 1 } else { 0 },
keywords_hash,
if expand { 1 } else { 0 },
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
if dst_ptr.is_null() {
return Ok(String::new());
}
Ok(std::ffi::CStr::from_ptr(dst_ptr)
.to_str()
.map_err(|_| Error::from_message("Translated string is not valid UTF-8"))?
.to_string())
}
})
}
pub fn translate_string(
value: &[u8],
encoding: Option<&str>,
repair: bool,
) -> Result<(Vec<u8>, bool, bool), Error<'static>> {
with_tmp_pool(|pool| {
let encoding_cstr = encoding.map(|s| std::ffi::CString::new(s).unwrap());
let encoding_ptr = encoding_cstr
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(std::ptr::null());
let input_str = unsafe {
subversion_sys::svn_string_ncreate(
value.as_ptr() as *const std::ffi::c_char,
value.len(),
pool.as_mut_ptr(),
)
};
let mut new_value: *mut subversion_sys::svn_string_t = std::ptr::null_mut();
let mut translated_to_utf8: i32 = 0;
let mut translated_line_endings: i32 = 0;
unsafe {
let err = subversion_sys::svn_subst_translate_string2(
&mut new_value,
&mut translated_to_utf8,
&mut translated_line_endings,
input_str,
encoding_ptr,
if repair { 1 } else { 0 },
pool.as_mut_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
if new_value.is_null() || (*new_value).data.is_null() {
return Ok((Vec::new(), false, false));
}
let data = std::slice::from_raw_parts((*new_value).data as *const u8, (*new_value).len);
Ok((
data.to_vec(),
translated_to_utf8 != 0,
translated_line_endings != 0,
))
}
})
}
pub fn read_specialfile(path: &std::path::Path) -> Result<crate::io::Stream, Error<'static>> {
let pool = apr::Pool::new();
let path_cstr = std::ffi::CString::new(
path.to_str()
.ok_or_else(|| Error::from_message("Invalid path encoding"))?,
)?;
let mut stream: *mut subversion_sys::svn_stream_t = std::ptr::null_mut();
unsafe {
let err = subversion_sys::svn_subst_read_specialfile(
&mut stream,
path_cstr.as_ptr(),
pool.as_mut_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
Ok(crate::io::Stream::from_ptr(stream, pool))
}
}
pub fn create_specialfile(path: &std::path::Path) -> Result<crate::io::Stream, Error<'static>> {
let pool = apr::Pool::new();
let path_cstr = std::ffi::CString::new(
path.to_str()
.ok_or_else(|| Error::from_message("Invalid path encoding"))?,
)?;
let mut stream: *mut subversion_sys::svn_stream_t = std::ptr::null_mut();
unsafe {
let err = subversion_sys::svn_subst_create_specialfile(
&mut stream,
path_cstr.as_ptr(),
pool.as_mut_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
Ok(crate::io::Stream::from_ptr(stream, pool))
}
}
pub fn stream_detranslated(
src_path: &std::path::Path,
eol_style: EolStyle,
eol_str: Option<&str>,
always_repair_eols: bool,
keywords: Option<&HashMap<String, String>>,
special: bool,
) -> Result<crate::io::Stream, Error<'static>> {
let pool = apr::Pool::new();
let path_cstr = std::ffi::CString::new(
src_path
.to_str()
.ok_or_else(|| Error::from_message("Invalid path encoding"))?,
)?;
let eol_ptr = match eol_str {
Some(s) => apr::strings::pstrdup_raw(s, &pool)?,
None => std::ptr::null(),
};
let keywords_hash = match keywords {
Some(kw) => {
let mut hash = apr::hash::Hash::new(&pool);
for (k, v) in kw.iter() {
unsafe {
let key_ptr = apr::strings::pstrdup_raw(k, &pool)?;
let val_ptr = apr::strings::pstrdup_raw(v, &pool)?;
hash.insert(
std::slice::from_raw_parts(key_ptr as *const u8, k.len()),
val_ptr as *mut std::ffi::c_void,
);
}
}
unsafe { hash.as_mut_ptr() }
}
None => std::ptr::null_mut(),
};
let mut stream: *mut subversion_sys::svn_stream_t = std::ptr::null_mut();
unsafe {
let err = subversion_sys::svn_subst_stream_detranslated(
&mut stream,
path_cstr.as_ptr(),
eol_style.into(),
eol_ptr,
if always_repair_eols { 1 } else { 0 },
keywords_hash,
if special { 1 } else { 0 },
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
Ok(crate::io::Stream::from_ptr(stream, pool))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_eol_style_from_value() {
let (style, eol) = eol_style_from_value("native");
assert_eq!(style, EolStyle::Native);
assert!(eol.is_some());
let (style, eol) = eol_style_from_value("LF");
assert_eq!(style, EolStyle::Fixed);
assert_eq!(eol.as_deref(), Some("\n"));
let (style, eol) = eol_style_from_value("CRLF");
assert_eq!(style, EolStyle::Fixed);
assert_eq!(eol.as_deref(), Some("\r\n"));
let (style, eol) = eol_style_from_value("CR");
assert_eq!(style, EolStyle::Fixed);
assert_eq!(eol.as_deref(), Some("\r"));
let (style, eol) = eol_style_from_value("invalid");
assert_eq!(style, EolStyle::Unknown);
assert!(eol.is_none());
}
#[test]
fn test_translation_required() {
assert!(!translation_required(
EolStyle::None,
None,
None,
false,
false
));
let _result = translation_required(EolStyle::Native, Some("\n"), None, false, false);
let mut keywords = HashMap::new();
keywords.insert("Id".to_string(), "$Id$".to_string());
assert!(translation_required(
EolStyle::None,
None,
Some(&keywords),
false,
false
));
}
#[test]
fn test_build_keywords() {
let keywords = build_keywords(
"Id Rev Author Date",
Some("123"),
Some("http://example.com/repo/file.txt"),
Some(apr::time::Time::now()),
Some("testuser"),
Some("http://example.com/repo"),
)
.unwrap();
assert!(keywords.contains_key("Id"));
assert!(keywords.contains_key("Rev"));
assert!(keywords.contains_key("Author"));
assert!(keywords.contains_key("Date"));
let empty_keywords = build_keywords("", None, None, None, None, None).unwrap();
assert!(empty_keywords.is_empty());
}
#[test]
fn test_keywords_differ() {
let mut kw1 = HashMap::new();
kw1.insert("Id".to_string(), "$Id$".to_string());
kw1.insert("Rev".to_string(), "$Rev$".to_string());
let mut kw2 = HashMap::new();
kw2.insert("Id".to_string(), "$Id$".to_string());
kw2.insert("Rev".to_string(), "$Rev$".to_string());
assert!(!keywords_differ(Some(&kw1), Some(&kw2), true).unwrap());
assert!(!keywords_differ(Some(&kw1), Some(&kw2), false).unwrap());
kw2.insert("Rev".to_string(), "$Rev: 456$".to_string());
assert!(keywords_differ(Some(&kw1), Some(&kw2), true).unwrap());
assert!(!keywords_differ(Some(&kw1), Some(&kw2), false).unwrap());
kw2.insert("Date".to_string(), "$Date$".to_string());
assert!(keywords_differ(Some(&kw1), Some(&kw2), true).unwrap());
assert!(keywords_differ(Some(&kw1), Some(&kw2), false).unwrap());
assert!(keywords_differ(Some(&kw1), None, false).unwrap());
assert!(keywords_differ(None, Some(&kw2), false).unwrap());
assert!(!keywords_differ(None, None, false).unwrap());
}
#[test]
fn test_eol_style_conversions() {
let unknown: EolStyle =
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_unknown.into();
assert_eq!(unknown, EolStyle::Unknown);
let none: EolStyle = subversion_sys::svn_subst_eol_style_svn_subst_eol_style_none.into();
assert_eq!(none, EolStyle::None);
let native: EolStyle =
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_native.into();
assert_eq!(native, EolStyle::Native);
let fixed: EolStyle = subversion_sys::svn_subst_eol_style_svn_subst_eol_style_fixed.into();
assert_eq!(fixed, EolStyle::Fixed);
let unknown_raw: subversion_sys::svn_subst_eol_style_t = EolStyle::Unknown.into();
assert_eq!(
unknown_raw,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_unknown
);
let none_raw: subversion_sys::svn_subst_eol_style_t = EolStyle::None.into();
assert_eq!(
none_raw,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_none
);
let native_raw: subversion_sys::svn_subst_eol_style_t = EolStyle::Native.into();
assert_eq!(
native_raw,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_native
);
let fixed_raw: subversion_sys::svn_subst_eol_style_t = EolStyle::Fixed.into();
assert_eq!(
fixed_raw,
subversion_sys::svn_subst_eol_style_svn_subst_eol_style_fixed
);
}
#[test]
fn test_build_keywords_with_nulls() {
let keywords = build_keywords("Id", None, None, None, None, None).unwrap();
assert!(keywords.contains_key("Id"));
let keywords = build_keywords("Rev", Some("100"), None, None, None, None).unwrap();
assert!(keywords.contains_key("Rev"));
}
#[test]
fn test_stream_translated() {
let content = b"Test content with $Id$ keyword\n";
let source = crate::io::Stream::from(&content[..]);
stream_translated(source, Some("\n"), false, None, true).unwrap();
}
#[test]
fn test_stream_translated_with_keywords() {
let content = b"Test content with $Id$ keyword\n";
let source = crate::io::Stream::from(&content[..]);
let mut keywords = HashMap::new();
keywords.insert("Id".to_string(), "test-id".to_string());
stream_translated(source, Some("\n"), false, Some(&keywords), true).unwrap();
}
#[test]
fn test_stream_translated_to_normal_form() {
let content = b"Test content\r\n";
let source = crate::io::Stream::from(&content[..]);
stream_translated_to_normal_form(source, EolStyle::Native, Some("\n"), false, None)
.unwrap();
}
#[test]
fn test_stream_translated_to_normal_form_with_keywords() {
let content = b"Test $Rev$ content\n";
let source = crate::io::Stream::from(&content[..]);
let mut keywords = HashMap::new();
keywords.insert("Rev".to_string(), "123".to_string());
stream_translated_to_normal_form(
source,
EolStyle::Fixed,
Some("\n"),
false,
Some(&keywords),
)
.unwrap();
}
#[test]
fn test_translate_stream() {
let content = b"Test content\n";
let mut source = crate::io::Stream::from(&content[..]);
let mut dest = crate::io::Stream::empty();
translate_stream(&mut source, &mut dest, Some("\n"), false, None, false).unwrap();
}
#[test]
fn test_translate_stream_with_keywords() {
let content = b"Test $Id$ content\n";
let mut source = crate::io::Stream::from(&content[..]);
let mut dest = crate::io::Stream::empty();
let mut keywords = HashMap::new();
keywords.insert("Id".to_string(), "test-id".to_string());
translate_stream(
&mut source,
&mut dest,
Some("\n"),
false,
Some(&keywords),
true,
)
.unwrap();
}
#[test]
fn test_translate_cstring() {
let src = "Hello\nWorld\n";
let result = translate_cstring(src, Some("\n"), false, None, false).unwrap();
assert_eq!(result, "Hello\nWorld\n");
}
#[test]
fn test_translate_cstring_with_eol() {
let src = "Hello\nWorld\n";
let result = translate_cstring(src, Some("\r\n"), false, None, false).unwrap();
assert_eq!(result, "Hello\r\nWorld\r\n");
}
#[test]
fn test_translate_string() {
let value = b"Hello\nWorld\n";
let (result, to_utf8, line_endings) = translate_string(value, None, false).unwrap();
assert_eq!(result, b"Hello\nWorld\n");
assert!(!to_utf8);
assert!(!line_endings);
}
#[test]
fn test_copy_and_translate() {
let td = tempfile::tempdir().unwrap();
let src = td.path().join("src.txt");
let dst = td.path().join("dst.txt");
std::fs::write(&src, "Hello\nWorld\n").unwrap();
copy_and_translate(&src, &dst, Some("\n"), false, None, false, false).unwrap();
let content = std::fs::read_to_string(&dst).unwrap();
assert_eq!(content, "Hello\nWorld\n");
}
}