use std::borrow::Cow;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::str;
use std::thread;
use std::time;
use std::str::FromStr;
use crate::config::{Config, Delimiter};
#[allow(dead_code)]
pub fn version() -> String {
let (maj, min, pat) = (
option_env!("CARGO_PKG_VERSION_MAJOR"),
option_env!("CARGO_PKG_VERSION_MINOR"),
option_env!("CARGO_PKG_VERSION_PATCH"),
);
match (maj, min, pat) {
(Some(maj), Some(min), Some(pat)) => format!("{}.{}.{}", maj, min, pat),
_ => "".to_owned(),
}
}
pub fn many_configs(
inps: &[String],
delim: Option<Delimiter>,
no_headers: bool,
) -> Result<Vec<Config>, String> {
let mut inps = inps.to_vec();
if inps.is_empty() {
inps.push("-".to_owned()); }
let confs = inps
.into_iter()
.map(|p| {
Config::new(&Some(p))
.delimiter(delim)
.no_headers(no_headers)
})
.collect::<Vec<_>>();
errif_greater_one_stdin(&confs)?;
Ok(confs)
}
pub fn errif_greater_one_stdin(inps: &[Config]) -> Result<(), String> {
let nstd = inps.iter().filter(|inp| inp.is_std()).count();
if nstd > 1 {
return Err("At most one <stdin> input is allowed.".to_owned());
}
Ok(())
}
pub fn chunk_size(nitems: usize, njobs: usize) -> usize {
if nitems < njobs {
nitems
} else {
nitems / njobs
}
}
pub fn num_of_chunks(nitems: usize, chunk_size: usize) -> usize {
if chunk_size == 0 {
return nitems;
}
let mut n = nitems / chunk_size;
if nitems % chunk_size != 0 {
n += 1;
}
n
}
pub fn last_modified(md: &fs::Metadata) -> u64 {
use filetime::FileTime;
FileTime::from_last_modification_time(md).unix_seconds() as u64
}
pub fn condense<'a>(val: Cow<'a, [u8]>, n: Option<usize>) -> Cow<'a, [u8]> {
match n {
None => val,
Some(n) => {
let mut is_short_utf8 = false;
if let Ok(s) = str::from_utf8(&val) {
if n >= s.chars().count() {
is_short_utf8 = true;
} else {
let mut s = s.chars().take(n).collect::<String>();
s.push_str("...");
return Cow::Owned(s.into_bytes());
}
}
if is_short_utf8 || n >= (*val).len() {
val
} else {
let mut s = val[0..n].to_vec();
s.extend(b"...".iter().cloned());
Cow::Owned(s)
}
}
}
}
pub fn idx_path(csv_path: &Path) -> PathBuf {
let mut p = csv_path
.to_path_buf()
.into_os_string()
.into_string()
.unwrap();
p.push_str(".idx");
PathBuf::from(&p)
}
pub type Idx = Option<usize>;
pub fn range(start: Idx, end: Idx, len: Idx, index: Idx) -> Result<(usize, usize), String> {
match (start, end, len, index) {
(None, None, None, Some(i)) => Ok((i, i + 1)),
(_, _, _, Some(_)) => Err("--index cannot be used with --start, --end or --len".to_owned()),
(_, Some(_), Some(_), None) => {
Err("--end and --len cannot be used at the same time.".to_owned())
}
(_, None, None, None) => Ok((start.unwrap_or(0), usize::MAX)),
(_, Some(e), None, None) => {
let s = start.unwrap_or(0);
if s > e {
Err(format!(
"The end of the range ({}) must be greater than or\n\
equal to the start of the range ({}).",
e, s
))
} else {
Ok((s, e))
}
}
(_, None, Some(l), None) => {
let s = start.unwrap_or(0);
Ok((s, s + l))
}
}
}
fn create_dir_all_threadsafe(path: &Path) -> io::Result<()> {
for _ in 0..20 {
match fs::create_dir_all(path) {
Err(ref err) if err.kind() == io::ErrorKind::AlreadyExists => {}
other => return other,
}
thread::sleep(time::Duration::from_millis(25));
}
fs::create_dir_all(path)
}
#[derive(Clone, Debug)]
pub struct FilenameTemplate {
prefix: String,
suffix: String,
}
impl FilenameTemplate {
pub fn filename(&self, unique_value: &str) -> String {
format!("{}{}{}", self.prefix, unique_value, self.suffix)
}
pub fn writer<P>(
&self,
path: P,
unique_value: &str,
) -> io::Result<csv::Writer<Box<dyn io::Write + 'static>>>
where
P: AsRef<Path>,
{
let filename = self.filename(unique_value);
let full_path = path.as_ref().join(filename);
if let Some(parent) = full_path.parent() {
create_dir_all_threadsafe(parent)?;
}
let spath = Some(full_path.display().to_string());
Config::new(&spath).writer()
}
}
impl FromStr for FilenameTemplate {
type Err = String;
fn from_str(s: &str) -> Result<FilenameTemplate, String> {
let chunks = s.split("{}").collect::<Vec<_>>();
if chunks.len() == 2 {
Ok(FilenameTemplate {
prefix: chunks[0].to_owned(),
suffix: chunks[1].to_owned(),
})
} else {
Err("The --filename argument must contain one '{}'.".to_owned())
}
}
}