1#[cfg(feature = "directories")]
162mod app_dir;
163#[cfg(all(feature = "hex", feature = "digest", feature = "sha2"))]
164mod hashing;
165#[cfg(feature = "chrono")]
166mod time;
167mod process;
168
169pub use itertools;
173pub use linked_hash_map;
174pub use linked_hash_set;
175pub use boolinator;
176pub use tap;
177
178#[cfg(feature = "regex")]
179pub use regex;
180
181#[cfg(feature = "tempfile")]
183pub use tempfile;
184#[cfg(feature = "filetime")]
185pub use filetime;
186#[cfg(all(target_family = "unix", feature = "file-owner"))]
187pub use file_owner;
188#[cfg(feature = "file-mode")]
189pub use file_mode;
190
191#[cfg(feature = "problem")]
193pub use problem;
194#[cfg(feature = "error-context")]
195pub use error_context;
196#[cfg(feature = "scopeguard")]
197pub use scopeguard;
198#[cfg(feature = "assert_matches")]
199pub use assert_matches;
200
201#[cfg(feature = "chrono")]
203pub use chrono;
204
205#[cfg(feature = "ansi_term")]
207pub use ansi_term;
208#[cfg(feature = "atty")]
209pub use atty;
210#[cfg(feature = "zzz")]
211pub use zzz;
212#[cfg(feature = "term_size")]
213pub use term_size;
214
215#[cfg(feature = "clap")]
217pub use clap;
218
219#[cfg(feature = "log")]
221pub use log;
222#[cfg(feature = "stderrlog")]
223pub use stderrlog;
224
225#[cfg(feature = "sha2")]
227pub use sha2;
228#[cfg(feature = "digest")]
229pub use digest;
230
231#[cfg(feature = "shellwords")]
233pub use shellwords;
234#[cfg(all(target_family = "unix", feature = "exec"))]
235pub use exec;
236#[cfg(feature = "mkargs")]
237pub use mkargs;
238#[cfg(feature = "cradle")]
239pub use cradle;
240
241#[cfg(feature = "hex")]
243pub use hex;
244pub use maybe_string;
245
246#[cfg(all(target_family = "unix", feature = "signal-hook"))]
248pub use signal_hook;
249#[cfg(all(target_family = "unix", feature = "uninterruptible"))]
250pub use uninterruptible;
251
252#[cfg(feature = "directories")]
254pub use directories;
255
256pub mod prelude {
257 pub use std::fs::{
259 canonicalize, copy, create_dir, create_dir_all, hard_link, metadata, read, read_dir,
260 read_link, read_to_string, remove_dir, remove_dir_all, remove_file, rename,
261 set_permissions, symlink_metadata, write, DirBuilder, DirEntry, File, Metadata,
262 OpenOptions, Permissions, ReadDir
263 };
264 pub use std::io::{
265 self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Write, Cursor,
266 Seek, SeekFrom
267 };
268 pub use std::process::{Command, ExitStatus};
269 pub use std::path::{Path, PathBuf};
270 pub use std::ffi::{OsStr, OsString};
271
272 #[cfg(feature = "file-mode")]
274 pub use file_mode::{ModeParseError, Mode as FileMode, User, FileType, ProtectionBit, Protection, SpecialBit, Special, set_umask};
275 #[cfg(all(target_family = "unix", feature = "file-mode"))]
276 pub use file_mode::{ModeError, ModePath, ModeFile, SetMode};
277 #[cfg(all(target_family = "unix", feature = "file-owner"))]
278 pub use file_owner::{FileOwnerError, PathExt, group, owner, owner_group, set_group, set_owner, set_owner_group, Group as FileGroup, Owner as FileOwner};
279
280 pub use std::hash::Hash;
282 pub use std::marker::PhantomData;
283
284 #[cfg(feature = "regex")]
286 pub use regex::{Regex, RegexSet};
287
288 #[cfg(feature = "tempfile")]
290 pub use tempfile::{tempdir, tempfile, spooled_tempfile, tempdir_in, tempfile_in};
291
292 #[cfg(feature = "filetime")]
294 pub use filetime::{set_file_atime, set_file_handle_times, set_file_mtime, set_file_times,
295 set_symlink_file_times, FileTime};
296
297 pub use std::borrow::Cow;
299 pub use std::collections::HashMap;
300 pub use std::collections::HashSet;
301
302 pub use maybe_string::{MaybeString, MaybeStr};
304
305 pub use linked_hash_map::LinkedHashMap;
307 pub use linked_hash_set::LinkedHashSet;
308
309 pub use std::convert::Infallible;
311 pub use std::convert::TryFrom;
312 pub use std::convert::TryInto; pub use std::fmt::Write as FmtWrite; pub use std::fmt::{self, Display, Debug};
317
318 #[cfg(feature = "clap")]
320 pub use clap::{self , Parser, Args, ValueEnum, Subcommand};
321
322 pub use std::error::Error;
324 #[cfg(feature = "assert_matches")]
325 pub use assert_matches::assert_matches;
326 #[cfg(feature = "problem")]
327 pub use ::problem::prelude::{problem, in_context_of, in_context_of_with, FailedTo, FailedToIter, Fatal, FatalProblem,
328 MapProblem, MapProblemOr, OkOrProblem, Problem, ProblemWhile, OkOrLog, OkOrLogIter};
329 #[cfg(feature = "problem")]
330 pub use ::problem::result::{FinalResult, Result as PResult};
331 #[cfg(feature = "error-context")]
332 pub use ::error_context::{
333 in_context_of as in_error_context_of, in_context_of_with as in_error_context_of_with, wrap_in_context_of,
334 wrap_in_context_of_with, ErrorContext, ErrorNoContext, MapErrorNoContext, ResultErrorWhile,
335 ResultErrorWhileWrap, ToErrorNoContext, WithContext, WrapContext};
336 #[cfg(feature = "scopeguard")]
337 pub use scopeguard::{defer, defer_on_success, defer_on_unwind, guard, guard_on_success, guard_on_unwind};
338
339 #[cfg(feature = "shellwords")]
341 pub use shellwords::{escape as shell_escape, join as shell_join, split as shell_split};
342 pub use crate::process::*;
343 #[cfg(feature = "mkargs")]
344 pub use mkargs::{mkargs, MkArgs};
345 #[cfg(feature = "cradle")]
346 pub use cradle::prelude::*;
347
348 #[cfg(all(feature = "hex", feature = "digest", feature = "sha2"))]
350 pub use super::hashing::*;
351
352 #[cfg(feature = "hex")]
353 pub use hex::{encode as hex_encode, decode as hex_decode, FromHexError};
354 #[cfg(feature = "sha2")]
355 pub use sha2::digest::{self, generic_array::{self, GenericArray}};
356
357 #[cfg(feature = "directories")]
359 pub use super::app_dir::*;
360
361 #[cfg(feature = "chrono")]
363 pub use super::time::*;
364
365 pub use itertools::*;
367 pub use std::iter::FromIterator;
368 pub use std::iter::{empty, from_fn, once, once_with, repeat, repeat_with, successors};
369
370 #[cfg(all(target_family = "unix", feature = "uninterruptible"))]
372 pub use uninterruptible::Uninterruptible;
373 #[cfg(all(target_family = "unix", feature = "signal-hook"))]
374 pub use signal_hook::{consts::signal::*, consts::TERM_SIGNALS, iterator::Signals, flag as signal_flag};
375
376 pub use boolinator::Boolinator;
378 pub use tap::prelude::{Conv, Tap, Pipe, TapFallible, TapOptional, TryConv};
379
380 #[cfg(feature = "ansi_term")]
382 pub use ansi_term::{Colour, Style, ANSIString, ANSIStrings, unstyle};
383 #[cfg(feature = "zzz")]
384 pub use zzz::ProgressBarIterExt;
385 #[cfg(feature = "term_size")]
386 pub use term_size::dimensions as term_dimensions;
387
388 #[cfg(feature = "atty")]
390 pub fn stdout_is_tty() -> bool {
391 atty::is(atty::Stream::Stdout)
392 }
393
394 #[cfg(feature = "atty")]
396 pub fn stderr_is_tty() -> bool {
397 atty::is(atty::Stream::Stdout)
398 }
399
400 #[cfg(feature = "log")]
402 pub use log::{debug, error, info, log_enabled, trace, warn};
403
404 #[cfg(feature = "clap")]
405 #[derive(Debug, Args)]
406 pub struct ArgsDryRun {
407 #[arg(long = "dry-run", short = 'd')]
409 pub enabled: bool,
410 }
411
412 #[cfg(all(feature = "clap", feature = "log"))]
413 impl ArgsDryRun {
414 pub fn run(&self, msg: impl Display, run: impl FnOnce() -> ()) -> () {
415 if self.enabled {
416 info!("[dry run]: {}", msg);
417 } else {
418 info!("{}", msg);
419 run()
420 }
421 }
422 }
423
424 #[derive(Debug)]
425 pub enum FileIoError {
426 IoError(PathBuf, io::Error),
427 Utf8Error(PathBuf, std::str::Utf8Error),
428 }
429
430 impl Display for FileIoError {
431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 match self {
433 FileIoError::IoError(path, _) => write!(f, "I/O error while reading file {:?}", path),
434 FileIoError::Utf8Error(path, _) => write!(f, "failed to decode content of file {:?} as UTF-8 encoded string", path),
435 }
436 }
437 }
438
439 impl Error for FileIoError {
440 fn source(&self) -> Option<&(dyn Error + 'static)> {
441 match self {
442 FileIoError::IoError(_, err) => Some(err),
443 FileIoError::Utf8Error(_, err) => Some(err),
444 }
445 }
446 }
447
448 pub fn read_stdin() -> String {
449 let mut buffer = String::new();
450 stdin()
451 .read_to_string(&mut buffer)
452 .map_err(|err| format!("Failed to read UTF-8 string from stdin due to: {}", err))
453 .unwrap();
454 buffer
455 }
456
457 pub fn read_stdin_bytes() -> Vec<u8> {
458 let mut buffer = Vec::new();
459 stdin()
460 .read_to_end(&mut buffer)
461 .map_err(|err| format!("Failed to read bytes from stdin due to: {}", err))
462 .unwrap();
463 buffer
464 }
465
466 pub fn read_stdin_lines() -> impl Iterator<Item = String> {
467 BufReader::new(stdin())
468 .lines()
469 .map(|val| val.map_err(|err| format!("Failed to read UTF-8 lines from stdin due to: {}", err)).unwrap())
470 }
471
472 pub fn read_all(paths: impl IntoIterator<Item = impl AsRef<Path>>) -> Result<String, FileIoError> {
474 let mut string = String::new();
475
476 for path in paths {
477 let path = path.as_ref();
478 let mut file = File::open(path).map_err(|err| FileIoError::IoError(path.into(), err))?;
479 file.read_to_string(&mut string).map_err(|err| FileIoError::IoError(path.into(), err))?;
480 }
481
482 Ok(string)
483 }
484
485 pub fn read_all_bytes(paths: impl IntoIterator<Item = impl AsRef<Path>>) -> Result<Vec<u8>, FileIoError> {
487 let mut bytes = Vec::new();
488
489 for path in paths {
490 let path = path.as_ref();
491 let mut file = File::open(path).map_err(|err| FileIoError::IoError(path.into(), err))?;
492 file.read_to_end(&mut bytes).map_err(|err| FileIoError::IoError(path.into(), err))?;
493 }
494
495 Ok(bytes)
496 }
497
498 #[cfg(all(feature = "clap", feature = "stderrlog"))]
499 #[derive(Args)]
500 pub struct ArgsLogger {
501 #[arg(short = 'v', long, action = clap::ArgAction::Count)]
503 pub verbose: u8,
504
505 #[arg(short = 'q', long, action = clap::ArgAction::Count)]
507 quiet: u8,
508
509 #[arg(long = "force-colors")]
511 pub force_colors: bool,
512 }
513
514 #[cfg(all(feature = "clap", feature = "stderrlog"))]
515 pub fn setup_logger(opt: ArgsLogger, module_paths: impl IntoIterator<Item = impl Into<String>>) {
516 let verbosity = (opt.verbose + 1) as i16 - opt.quiet as i16;
517 _setup_logger(verbosity, opt.force_colors, module_paths)
518 }
519
520 #[cfg(all(not(feature = "clap"), feature = "stderrlog"))]
521 pub fn setup_logger(verbosity: i16, force_colors: bool, module_paths: impl IntoIterator<Item = impl Into<String>>) {
522 _setup_logger(verbosity, force_colors, module_paths)
523 }
524
525 #[cfg(feature = "stderrlog")]
526 pub fn _setup_logger(verbosity: i16, force_colors: bool, module_paths: impl IntoIterator<Item = impl Into<String>>) {
527 let mut logger = stderrlog::new();
528
529 logger
530 .quiet(verbosity < 0)
531 .verbosity(verbosity as usize)
532 .color(if force_colors { stderrlog::ColorChoice::Always } else { stderrlog::ColorChoice::Auto })
533 .timestamp(stderrlog::Timestamp::Microsecond)
534 .module(module_path!())
535 .module("cotton")
536 .module("problem");
537
538 for module in module_paths {
539 logger.module(module);
540 }
541
542 logger
543 .init()
544 .unwrap();
545
546 #[cfg(feature = "problem")]
547 problem::format_panic_to_error_log();
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::prelude::*;
554
555 #[test]
556 #[should_panic(expected = "Failed to baz due to: while bar got error caused by: foo")]
557 fn test_problem() {
558 in_context_of("bar", || {
559 problem!("foo")?;
560 Ok(())
561 }).or_failed_to("baz");
562 }
563}