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.
311 lines
6.5 KiB
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)*);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|