#[cfg(feature = "directories")]
mod app_dir;
#[cfg(all(feature = "hex", feature = "digest", feature = "sha2"))]
mod hashing;
#[cfg(feature = "chrono")]
mod time;
mod process;
pub use itertools;
pub use linked_hash_map;
pub use linked_hash_set;
pub use boolinator;
pub use tap;
#[cfg(feature = "regex")]
pub use regex;
#[cfg(feature = "tempfile")]
pub use tempfile;
#[cfg(feature = "filetime")]
pub use filetime;
#[cfg(all(target_family = "unix", feature = "file-owner"))]
pub use file_owner;
#[cfg(feature = "file-mode")]
pub use file_mode;
#[cfg(feature = "problem")]
pub use problem;
#[cfg(feature = "error-context")]
pub use error_context;
#[cfg(feature = "scopeguard")]
pub use scopeguard;
#[cfg(feature = "assert_matches")]
pub use assert_matches;
#[cfg(feature = "chrono")]
pub use chrono;
#[cfg(feature = "ansi_term")]
pub use ansi_term;
#[cfg(feature = "atty")]
pub use atty;
#[cfg(feature = "zzz")]
pub use zzz;
#[cfg(feature = "term_size")]
pub use term_size;
#[cfg(feature = "clap")]
pub use clap;
#[cfg(feature = "log")]
pub use log;
#[cfg(feature = "stderrlog")]
pub use stderrlog;
#[cfg(feature = "sha2")]
pub use sha2;
#[cfg(feature = "digest")]
pub use digest;
#[cfg(feature = "shellwords")]
pub use shellwords;
#[cfg(all(target_family = "unix", feature = "exec"))]
pub use exec;
#[cfg(feature = "mkargs")]
pub use mkargs;
#[cfg(feature = "cradle")]
pub use cradle;
#[cfg(feature = "hex")]
pub use hex;
pub use maybe_string;
#[cfg(all(target_family = "unix", feature = "signal-hook"))]
pub use signal_hook;
#[cfg(all(target_family = "unix", feature = "uninterruptible"))]
pub use uninterruptible;
#[cfg(feature = "directories")]
pub use directories;
pub mod prelude {
pub use std::fs::{
canonicalize, copy, create_dir, create_dir_all, hard_link, metadata, read, read_dir,
read_link, read_to_string, remove_dir, remove_dir_all, remove_file, rename,
set_permissions, symlink_metadata, write, DirBuilder, DirEntry, File, Metadata,
OpenOptions, Permissions, ReadDir
};
pub use std::io::{
self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Write, Cursor,
Seek, SeekFrom
};
pub use std::process::{Command, ExitStatus};
pub use std::path::{Path, PathBuf};
pub use std::ffi::{OsStr, OsString};
#[cfg(feature = "file-mode")]
pub use file_mode::{ModeParseError, Mode as FileMode, User, FileType, ProtectionBit, Protection, SpecialBit, Special, set_umask};
#[cfg(all(target_family = "unix", feature = "file-mode"))]
pub use file_mode::{ModeError, ModePath, ModeFile, SetMode};
#[cfg(all(target_family = "unix", feature = "file-owner"))]
pub use file_owner::{FileOwnerError, PathExt, group, owner, owner_group, set_group, set_owner, set_owner_group, Group as FileGroup, Owner as FileOwner};
pub use std::hash::Hash;
pub use std::marker::PhantomData;
#[cfg(feature = "regex")]
pub use regex::{Regex, RegexSet};
#[cfg(feature = "tempfile")]
pub use tempfile::{tempdir, tempfile, spooled_tempfile, tempdir_in, tempfile_in};
#[cfg(feature = "filetime")]
pub use filetime::{set_file_atime, set_file_handle_times, set_file_mtime, set_file_times,
set_symlink_file_times, FileTime};
pub use std::borrow::Cow;
pub use std::collections::HashMap;
pub use std::collections::HashSet;
pub use maybe_string::{MaybeString, MaybeStr};
pub use linked_hash_map::LinkedHashMap;
pub use linked_hash_set::LinkedHashSet;
pub use std::convert::Infallible;
pub use std::convert::TryFrom;
pub use std::convert::TryInto;
pub use std::fmt::Write as FmtWrite; pub use std::fmt::{self, Display, Debug};
#[cfg(feature = "clap")]
pub use clap::{self , Parser, Args, ValueEnum, Subcommand};
pub use std::error::Error;
#[cfg(feature = "assert_matches")]
pub use assert_matches::assert_matches;
#[cfg(feature = "problem")]
pub use ::problem::prelude::{problem, in_context_of, in_context_of_with, FailedTo, FailedToIter, Fatal, FatalProblem,
MapProblem, MapProblemOr, OkOrProblem, Problem, ProblemWhile, OkOrLog, OkOrLogIter};
#[cfg(feature = "problem")]
pub use ::problem::result::{FinalResult, Result as PResult};
#[cfg(feature = "error-context")]
pub use ::error_context::{
in_context_of as in_error_context_of, in_context_of_with as in_error_context_of_with, wrap_in_context_of,
wrap_in_context_of_with, ErrorContext, ErrorNoContext, MapErrorNoContext, ResultErrorWhile,
ResultErrorWhileWrap, ToErrorNoContext, WithContext, WrapContext};
#[cfg(feature = "scopeguard")]
pub use scopeguard::{defer, defer_on_success, defer_on_unwind, guard, guard_on_success, guard_on_unwind};
#[cfg(feature = "shellwords")]
pub use shellwords::{escape as shell_escape, join as shell_join, split as shell_split};
pub use crate::process::*;
#[cfg(feature = "mkargs")]
pub use mkargs::{mkargs, MkArgs};
#[cfg(feature = "cradle")]
pub use cradle::prelude::*;
#[cfg(all(feature = "hex", feature = "digest", feature = "sha2"))]
pub use super::hashing::*;
#[cfg(feature = "hex")]
pub use hex::{encode as hex_encode, decode as hex_decode, FromHexError};
#[cfg(feature = "sha2")]
pub use sha2::digest::{self, generic_array::{self, GenericArray}};
#[cfg(feature = "directories")]
pub use super::app_dir::*;
#[cfg(feature = "chrono")]
pub use super::time::*;
pub use itertools::*;
pub use std::iter::FromIterator;
pub use std::iter::{empty, from_fn, once, once_with, repeat, repeat_with, successors};
#[cfg(all(target_family = "unix", feature = "uninterruptible"))]
pub use uninterruptible::Uninterruptible;
#[cfg(all(target_family = "unix", feature = "signal-hook"))]
pub use signal_hook::{consts::signal::*, consts::TERM_SIGNALS, iterator::Signals, flag as signal_flag};
pub use boolinator::Boolinator;
pub use tap::prelude::{Conv, Tap, Pipe, TapFallible, TapOptional, TryConv};
#[cfg(feature = "ansi_term")]
pub use ansi_term::{Colour, Style, ANSIString, ANSIStrings, unstyle};
#[cfg(feature = "zzz")]
pub use zzz::ProgressBarIterExt;
#[cfg(feature = "term_size")]
pub use term_size::dimensions as term_dimensions;
#[cfg(feature = "atty")]
pub fn stdout_is_tty() -> bool {
atty::is(atty::Stream::Stdout)
}
#[cfg(feature = "atty")]
pub fn stderr_is_tty() -> bool {
atty::is(atty::Stream::Stdout)
}
#[cfg(feature = "log")]
pub use log::{debug, error, info, log_enabled, trace, warn};
#[cfg(feature = "clap")]
#[derive(Debug, Args)]
pub struct ArgsDryRun {
#[arg(long = "dry-run", short = 'd')]
pub enabled: bool,
}
#[cfg(all(feature = "clap", feature = "log"))]
impl ArgsDryRun {
pub fn run(&self, msg: impl Display, run: impl FnOnce() -> ()) -> () {
if self.enabled {
info!("[dry run]: {}", msg);
} else {
info!("{}", msg);
run()
}
}
}
#[derive(Debug)]
pub enum FileIoError {
IoError(PathBuf, io::Error),
Utf8Error(PathBuf, std::str::Utf8Error),
}
impl Display for FileIoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FileIoError::IoError(path, _) => write!(f, "I/O error while reading file {:?}", path),
FileIoError::Utf8Error(path, _) => write!(f, "failed to decode content of file {:?} as UTF-8 encoded string", path),
}
}
}
impl Error for FileIoError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
FileIoError::IoError(_, err) => Some(err),
FileIoError::Utf8Error(_, err) => Some(err),
}
}
}
pub fn read_stdin() -> String {
let mut buffer = String::new();
stdin()
.read_to_string(&mut buffer)
.map_err(|err| format!("Failed to read UTF-8 string from stdin due to: {}", err))
.unwrap();
buffer
}
pub fn read_stdin_bytes() -> Vec<u8> {
let mut buffer = Vec::new();
stdin()
.read_to_end(&mut buffer)
.map_err(|err| format!("Failed to read bytes from stdin due to: {}", err))
.unwrap();
buffer
}
pub fn read_stdin_lines() -> impl Iterator<Item = String> {
BufReader::new(stdin())
.lines()
.map(|val| val.map_err(|err| format!("Failed to read UTF-8 lines from stdin due to: {}", err)).unwrap())
}
pub fn read_all(paths: impl IntoIterator<Item = impl AsRef<Path>>) -> Result<String, FileIoError> {
let mut string = String::new();
for path in paths {
let path = path.as_ref();
let mut file = File::open(path).map_err(|err| FileIoError::IoError(path.into(), err))?;
file.read_to_string(&mut string).map_err(|err| FileIoError::IoError(path.into(), err))?;
}
Ok(string)
}
pub fn read_all_bytes(paths: impl IntoIterator<Item = impl AsRef<Path>>) -> Result<Vec<u8>, FileIoError> {
let mut bytes = Vec::new();
for path in paths {
let path = path.as_ref();
let mut file = File::open(path).map_err(|err| FileIoError::IoError(path.into(), err))?;
file.read_to_end(&mut bytes).map_err(|err| FileIoError::IoError(path.into(), err))?;
}
Ok(bytes)
}
#[cfg(all(feature = "clap", feature = "stderrlog"))]
#[derive(Args)]
pub struct ArgsLogger {
#[arg(short = 'v', long, action = clap::ArgAction::Count)]
pub verbose: u8,
#[arg(short = 'q', long, action = clap::ArgAction::Count)]
quiet: u8,
#[arg(long = "force-colors")]
pub force_colors: bool,
}
#[cfg(all(feature = "clap", feature = "stderrlog"))]
pub fn setup_logger(opt: ArgsLogger, module_paths: impl IntoIterator<Item = impl Into<String>>) {
let verbosity = (opt.verbose + 1) as i16 - opt.quiet as i16;
_setup_logger(verbosity, opt.force_colors, module_paths)
}
#[cfg(all(not(feature = "clap"), feature = "stderrlog"))]
pub fn setup_logger(verbosity: i16, force_colors: bool, module_paths: impl IntoIterator<Item = impl Into<String>>) {
_setup_logger(verbosity, force_colors, module_paths)
}
#[cfg(feature = "stderrlog")]
pub fn _setup_logger(verbosity: i16, force_colors: bool, module_paths: impl IntoIterator<Item = impl Into<String>>) {
let mut logger = stderrlog::new();
logger
.quiet(verbosity < 0)
.verbosity(verbosity as usize)
.color(if force_colors { stderrlog::ColorChoice::Always } else { stderrlog::ColorChoice::Auto })
.timestamp(stderrlog::Timestamp::Microsecond)
.module(module_path!())
.module("cotton")
.module("problem");
for module in module_paths {
logger.module(module);
}
logger
.init()
.unwrap();
#[cfg(feature = "problem")]
problem::format_panic_to_error_log();
}
}
#[cfg(test)]
mod tests {
use super::prelude::*;
#[test]
#[should_panic(expected = "Failed to baz due to: while bar got error caused by: foo")]
fn test_problem() {
in_context_of("bar", || {
problem!("foo")?;
Ok(())
}).or_failed_to("baz");
}
}