use memchr::memchr;
use rand::Rng;
pub fn chunks_to_lines<'a, I, E>(mut chunks: I) -> impl Iterator<Item = Result<Vec<u8>, E>>
where
I: Iterator<Item = Result<&'a [u8], E>> + 'a,
E: std::fmt::Debug,
{
let mut tail: Option<Vec<u8>> = None;
std::iter::from_fn(move || -> Option<Result<Vec<u8>, E>> {
loop {
if let Some(mut chunk) = tail.take() {
if let Some(newline) = memchr(b'\n', &chunk) {
if newline == chunk.len() - 1 {
assert!(!chunk.is_empty());
return Some(Ok(chunk));
} else {
let line = chunk[..=newline].to_vec();
assert!(!chunk.is_empty());
tail = Some(chunk[newline + 1..].to_vec());
return Some(Ok(line));
}
} else {
if let Some(next_chunk) = chunks.next() {
if let Err(e) = next_chunk {
return Some(Err(e));
}
chunk.extend_from_slice(next_chunk.unwrap());
} else {
assert!(!chunk.is_empty());
return Some(Ok(chunk));
}
if !chunk.is_empty() {
tail = Some(chunk);
}
}
} else if let Some(next_chunk) = chunks.next() {
if let Err(e) = next_chunk {
return Some(Err(e));
}
let next_chunk = next_chunk.unwrap();
if !next_chunk.is_empty() {
tail = Some(next_chunk.to_vec());
}
} else {
return None;
}
}
})
}
pub fn set_or_unset_env(
env_variable: &str,
value: Option<&str>,
) -> Result<Option<String>, std::env::VarError> {
let orig_val = std::env::var(env_variable);
let ret: Option<String>;
if let Err(std::env::VarError::NotPresent) = orig_val {
ret = None;
if let Some(value) = value {
std::env::set_var(env_variable, value);
}
} else if let Err(e) = orig_val {
return Err(e);
} else {
assert!(orig_val.is_ok());
ret = Some(orig_val.unwrap());
match value {
None => std::env::remove_var(env_variable),
Some(val) => std::env::set_var(env_variable, val),
}
}
Ok(ret)
}
const ALNUM: &str = "0123456789abcdefghijklmnopqrstuvwxyz";
pub fn rand_chars(num: usize) -> String {
let mut rng = rand::thread_rng();
let mut s = String::new();
for _ in 0..num {
let raw_byte = rng.gen_range(0..256);
s.push(ALNUM.chars().nth(raw_byte % 36).unwrap());
}
s
}
#[cfg(unix)]
use nix::sys::stat::{umask, Mode};
#[cfg(unix)]
pub fn get_umask() -> Mode {
let mask = umask(Mode::empty());
umask(mask);
mask
}
pub fn kind_marker(kind: &str) -> &str {
match kind {
"file" => "",
"directory" => "/",
"symlink" => "@",
"tree-reference" => "+",
_ => "",
}
}
pub fn get_host_name() -> std::io::Result<String> {
hostname::get().map(|h| h.to_string_lossy().to_string())
}
pub fn local_concurrency(use_cache: bool) -> usize {
unsafe {
static mut _CACHED_LOCAL_CONCURRENCY: Option<usize> = None;
if use_cache {
if let Some(concurrency) = _CACHED_LOCAL_CONCURRENCY {
return concurrency;
}
}
let concurrency = std::env::var("BRZ_CONCURRENCY")
.map(|s| s.parse::<usize>().unwrap_or(1))
.unwrap_or_else(|_| num_cpus::get());
if use_cache {
_CACHED_LOCAL_CONCURRENCY = Some(concurrency);
}
concurrency
}
}
pub fn pumpfile(
mut reader: impl std::io::Read,
mut writer: impl std::io::Write,
num_bytes: Option<u64>,
) -> std::io::Result<u64> {
Ok(if let Some(num_bytes) = num_bytes {
std::io::copy(&mut reader.take(num_bytes), &mut writer)?
} else {
std::io::copy(&mut reader, &mut writer)?
})
}
pub fn contains_whitespace(s: &str) -> bool {
let ws = " \t\n\r\u{000B}\u{000C}";
for ch in ws.chars() {
if s.contains(ch) {
return true;
}
}
false
}
pub fn contains_whitespace_bytes(s: &[u8]) -> bool {
let ws = b" \t\n\r\x0C\x0B";
for ch in ws {
if s.contains(ch) {
return true;
}
}
false
}
pub fn contains_linebreaks(s: &str) -> bool {
for ch in "\n\r\x0C".chars() {
if s.contains(ch) {
return true;
}
}
false
}
pub mod file;
pub mod iterablefile;
pub mod path;
pub mod sha;
pub mod textfile;
pub mod time;
#[cfg(unix)]
#[path = "mounts-unix.rs"]
pub mod mounts;
#[cfg(windows)]
#[path = "mounts-win32.rs"]
pub mod mounts;
#[cfg(test)]
mod tests;