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 ) * ) ;
}
}
}
} ;
}