You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dirstat/src/config.rs

311 lines
6.5 KiB

use std::path::PathBuf;
use std::num::NonZeroUsize;
use std::{fmt,error};
use once_cell::sync::OnceCell;
static GLOBAL: OnceCell<Config> = OnceCell::new();
/// Get the global config instance, if it has been set.
#[inline] pub fn try_get_global() -> Option<&'static Config>
{
GLOBAL.get()
}
/// Get the global config instance.
/// # Panics
/// If one has not been set yet.
pub fn get_global() -> &'static Config
{
try_get_global().expect("Tried to access global config when it had not been initialised")
}
/// Try to set the global config instance, if it hasn't been already.
#[inline] pub fn try_set_global(cfg: Config) -> Result<(), Config>
{
GLOBAL.set(cfg)
}
/// Set the global config instance.
/// # Panics
/// If one has already been set.
pub fn set_global(cfg: Config)
{
try_set_global(cfg).expect("Tried to set global config more than once")
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord)]
#[repr(u32)]
pub enum OutputLevel
{
Silent = 0,
Quiet = 1,
Noisy = 2,
Verbose = 3,
}
impl Default for OutputLevel
{
#[inline]
fn default() -> Self
{
Self::Noisy
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Recursion
{
None,
Limited(NonZeroUsize),
Unlimited,
}
impl Default for Recursion
{
#[inline]
fn default() -> Self
{
Self::None
}
}
impl Recursion
{
/// Can we run at this depth?
pub fn can_run(&self, depth: usize) -> bool
{
debug_assert!(depth > 0, "Depth of 0 is invalid");
match self {
Self::None => depth == 1,
Self::Limited(limit) => depth <= limit.get(),
Self::Unlimited => true
}
}
}
impl From<usize> for Recursion
{
fn from(from: usize) -> Self
{
match from {
0 => Self::Unlimited,
1 => Self::None,
x => Self::Limited(unsafe {NonZeroUsize::new_unchecked(x)}),
}
}
}
#[cfg(feature="inspect")]
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum OutputSerialisationMode
{
Stdout,
File(PathBuf),
RawFile(PathBuf),
RawStdout,
#[cfg(feature="prealloc")] PreallocFile(PathBuf),
}
#[cfg(feature="inspect")]
impl OutputSerialisationMode
{
/// Should this serialisation mode be compressed?
#[inline] pub fn should_compress(&self) -> bool
{
match self {
Self::File(_) | Self::Stdout => true,
_ => false,
}
}
}
/// What to do with the graph afterwards?
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Inspection
{
pub treemap: Option<(u64, u64)>, //w and h
}
impl Default for Inspection
{
#[inline]
fn default() -> Self
{
Self {
treemap: None
}
}
}
/// Configuration for this run
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config
{
pub paths: Vec<PathBuf>,
pub recursive: Recursion,
pub max_tasks: Option<NonZeroUsize>,
pub output_level: OutputLevel,
#[cfg(feature="inspect")]
pub serialise_output: Option<OutputSerialisationMode>,
pub inspection: Inspection,
}
impl Config
{
/// Try to make this the global config instance, if one does not already exist.
#[inline] pub fn try_make_global(self) -> Result<&'static Self, Self>
{
try_set_global(self).map(|_| get_global())
}
/// Make this the global config instance.
/// # Panics
/// If a global config instance has already been set.
#[inline] pub fn make_global(self) -> &'static Self
{
set_global(self);
get_global()
//GLOBAL.get_or_init(move || self) // This isn't okay to do here. As it would silently fail if there was already an instance, when we want it to panic in that case.
}
/// Are we expected to dump data to `stdout`?
#[inline] pub fn is_using_stdout(&self) -> bool
{
#[cfg(feature="inspect")] {
return match self.serialise_output {
Some(OutputSerialisationMode::Stdout) |
Some(OutputSerialisationMode::RawStdout) => true,
_ => false,
}
}
#[cfg(not(feature="inspect"))] {
false
}
}
}
/// The default `max_tasks`
#[inline(always)] pub fn max_tasks_cpus() -> Option<NonZeroUsize>
{
lazy_static! {
static ref CPUS: usize = num_cpus::get();
}
NonZeroUsize::new(*CPUS)
}
impl Default for Config
{
#[inline]
fn default() -> Self
{
Self {
paths: Vec::new(),
recursive: Default::default(),
max_tasks: None, //max_tasks_cpus(),
output_level: Default::default(),
#[cfg(feature="inspect")]
serialise_output: None,
inspection: Default::default(),
}
}
}
impl Config
{
/// Validate this configuration instance.
pub fn validate(self) -> Result<Self, InvalidConfigError>
{
if self.paths.len() < 1 {
return Err(InvalidConfigError::NoPaths);
}
let paths: Result<Vec<_>, _> = self.paths.into_iter()
.map(|path| if !path.exists() { Err(InvalidConfigError::PathNotFound(path)) } else { Ok(path) })
.collect();
Ok(Self{
paths: paths?,
..self
})
}
}
/// Error type for an invalid instance of `Config`.
#[derive(Debug)]
#[non_exhaustive]
pub enum InvalidConfigError
{
/// No paths were given.
NoPaths,
/// Non-existant path was given.
PathNotFound(PathBuf),
/// Unknown error
Other,
}
impl error::Error for InvalidConfigError{}
impl fmt::Display for InvalidConfigError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::NoPaths => write!(f, "No input paths were given. Cannot do anything"),
Self::PathNotFound(path) => write!(f, "Root path {:?} not found", path),
_ => write!(f, "Unknown error"),
}
}
}
/// Print an error line in accordance with `Config`'s output directives.
#[macro_export] macro_rules! cfg_eprintln {
($cfg:expr, $fmt:literal $($tt:tt)*) => {
{
if $cfg.output_level > $crate::config::OutputLevel::Silent {
eprintln!($fmt $($tt)*);
}
}
};
($level:ident; $cfg:expr, $fmt:literal $($tt:tt)*) => {
{
if $cfg.output_level >= $crate::config::OutputLevel::$level {
eprintln!($fmt $($tt)*);
}
}
}
}
/// Print a line in accordance with `Config`'s output directives.
#[macro_export] macro_rules! cfg_println {
($cfg:expr, $fmt:literal $($tt:tt)*) => {
{
let cfg = &$cfg;
if cfg.output_level > $crate::config::OutputLevel::Quiet {
if cfg.is_using_stdout() {
eprintln!($fmt $($tt)*);
} else {
println!($fmt $($tt)*);
}
}
}
};
($level:ident; $cfg:expr, $fmt:literal $($tt:tt)*) => {
{
let cfg = &$cfg;
if cfg.output_level >= $crate::config::OutputLevel::$level {
if cfg.is_using_stdout() {
eprintln!($fmt $($tt)*);
} else {
println!($fmt $($tt)*);
}
}
}
};
}