2016-01-20 19:46:42 +00:00
use std ::path ::PathBuf ;
2016-03-21 09:19:03 +00:00
use std ::process ::Command ;
use std ::env ;
2016-03-24 11:09:45 +00:00
use std ::io ::stderr ;
use std ::io ::Write ;
2016-01-20 19:46:42 +00:00
pub use clap ::App ;
use clap ::{ Arg , ArgMatches } ;
use log ;
use log ::LogLevelFilter ;
use configuration ::Configuration ;
2016-01-21 17:43:03 +00:00
use error ::RuntimeError ;
use error ::RuntimeErrorKind ;
2016-01-20 19:46:42 +00:00
use logger ::ImagLogger ;
use libimagstore ::store ::Store ;
pub struct Runtime < ' a > {
rtp : PathBuf ,
2016-03-05 12:01:09 +00:00
configuration : Option < Configuration > ,
2016-02-20 20:15:09 +00:00
cli_matches : ArgMatches < ' a > ,
2016-01-20 19:46:42 +00:00
store : Store ,
}
impl < ' a > Runtime < ' a > {
/**
* Gets the CLI spec for the program and retreives the config file path ( or uses the default on
* in $HOME / . imag / config , $XDG_CONFIG_DIR / imag / config or from env ( " $IMAG_CONFIG " )
* and builds the Runtime object with it .
*
* The cli_spec object should be initially build with the ::get_default_cli_builder ( ) function .
*
* /
2016-02-20 20:15:09 +00:00
pub fn new ( cli_spec : App < ' a , ' a > ) -> Result < Runtime < ' a > , RuntimeError > {
2016-01-29 19:36:37 +00:00
use std ::env ;
2016-03-05 11:36:11 +00:00
use std ::error ::Error ;
2016-01-29 19:36:37 +00:00
2016-03-24 11:09:45 +00:00
use libimagstore ::hook ::position ::HookPosition ;
2016-04-05 14:47:51 +00:00
use libimagstore ::error ::StoreErrorKind ;
2016-03-24 11:09:45 +00:00
use libimagstorestdhook ::debug ::DebugHook ;
use libimagutil ::trace ::trace_error ;
2016-04-05 14:47:51 +00:00
use libimagutil ::trace ::trace_error_dbg ;
2016-03-24 11:09:45 +00:00
2016-03-05 12:01:09 +00:00
use configuration ::error ::ConfigErrorKind ;
2016-01-20 19:46:42 +00:00
let matches = cli_spec . get_matches ( ) ;
2016-04-05 15:46:51 +00:00
2016-03-25 18:42:10 +00:00
let is_debugging = matches . is_present ( " debugging " ) ;
2016-04-05 15:46:51 +00:00
let is_verbose = matches . is_present ( " verbosity " ) ;
Runtime ::init_logger ( is_debugging , is_verbose ) ;
2016-03-25 18:42:10 +00:00
2016-01-29 19:36:37 +00:00
let rtp : PathBuf = matches . value_of ( " runtimepath " )
. map ( PathBuf ::from )
. unwrap_or_else ( | | {
env ::var ( " HOME " )
. map ( PathBuf ::from )
. map ( | mut p | { p . push ( " .imag " ) ; p } )
. unwrap_or_else ( | _ | {
panic! ( " You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways. " ) ;
} )
} ) ;
2016-01-20 19:46:42 +00:00
let storepath = matches . value_of ( " storepath " )
. map ( PathBuf ::from )
. unwrap_or ( {
let mut spath = rtp . clone ( ) ;
2016-01-28 19:05:43 +00:00
spath . push ( " store " ) ;
2016-01-20 19:46:42 +00:00
spath
} ) ;
2016-03-05 11:36:11 +00:00
let cfg = Configuration ::new ( & rtp ) ;
2016-03-05 12:01:09 +00:00
let cfg = if cfg . is_err ( ) {
let e = cfg . err ( ) . unwrap ( ) ;
if e . kind ( ) ! = ConfigErrorKind ::NoConfigFileFound {
let cause : Option < Box < Error > > = Some ( Box ::new ( e ) ) ;
return Err ( RuntimeError ::new ( RuntimeErrorKind ::Instantiate , cause ) ) ;
} else {
2016-03-24 11:09:45 +00:00
trace_error ( & e ) ;
2016-03-05 12:01:09 +00:00
None
}
} else {
Some ( cfg . unwrap ( ) )
} ;
2016-03-05 11:36:11 +00:00
2016-03-05 17:42:59 +00:00
let store_config = {
match & cfg {
& Some ( ref c ) = > c . store_config ( ) . map ( | c | c . clone ( ) ) ,
2016-03-24 11:09:45 +00:00
& None = > None ,
2016-03-05 17:42:59 +00:00
}
} ;
2016-03-24 11:09:45 +00:00
if is_debugging {
write! ( stderr ( ) , " Config: {:?} \n " , cfg ) ;
write! ( stderr ( ) , " Store-config: {:?} \n " , store_config ) ;
}
Store ::new ( storepath , store_config ) . map ( | mut store | {
// If we are debugging, generate hooks for all positions
if is_debugging {
let hooks = vec! [
( DebugHook ::new ( HookPosition ::PreCreate ) , HookPosition ::PreCreate ) ,
( DebugHook ::new ( HookPosition ::PostCreate ) , HookPosition ::PostCreate ) ,
( DebugHook ::new ( HookPosition ::PreRetrieve ) , HookPosition ::PreRetrieve ) ,
( DebugHook ::new ( HookPosition ::PostRetrieve ) , HookPosition ::PostRetrieve ) ,
( DebugHook ::new ( HookPosition ::PreUpdate ) , HookPosition ::PreUpdate ) ,
( DebugHook ::new ( HookPosition ::PostUpdate ) , HookPosition ::PostUpdate ) ,
( DebugHook ::new ( HookPosition ::PreDelete ) , HookPosition ::PreDelete ) ,
( DebugHook ::new ( HookPosition ::PostDelete ) , HookPosition ::PostDelete ) ,
] ;
// Put all debug hooks into the aspect "debug".
// If it fails, trace the error and warn, but continue.
for ( hook , position ) in hooks {
if let Err ( e ) = store . register_hook ( position , & String ::from ( " debug " ) , Box ::new ( hook ) ) {
2016-04-05 14:47:51 +00:00
if e . err_type ( ) = = StoreErrorKind ::HookRegisterError {
trace_error_dbg ( & e ) ;
warn! ( " Registering debug hook with store failed " ) ;
} else {
trace_error ( & e ) ;
} ;
2016-03-24 11:09:45 +00:00
}
}
}
2016-01-21 17:43:03 +00:00
Runtime {
cli_matches : matches ,
2016-03-05 11:36:11 +00:00
configuration : cfg ,
2016-01-21 17:43:03 +00:00
rtp : rtp ,
store : store ,
}
} )
. map_err ( | e | {
RuntimeError ::new ( RuntimeErrorKind ::Instantiate , Some ( Box ::new ( e ) ) )
} )
2016-01-20 19:46:42 +00:00
}
/**
2016-01-21 20:24:20 +00:00
* Get a commandline - interface builder object from ` clap `
*
* This commandline interface builder object already contains some predefined interface flags :
* * - v | - - verbose for verbosity
* * - - debug for debugging
* * - c < file > | - - config < file > for alternative configuration file
* * - r < path > | - - rtp < path > for alternative runtimepath
* * - - store < path > for alternative store path
* Each has the appropriate help text included .
*
* The ` appname ` shall be " imag-<command> " .
2016-01-20 19:46:42 +00:00
* /
pub fn get_default_cli_builder ( appname : & ' a str ,
version : & ' a str ,
about : & ' a str )
2016-02-20 20:15:09 +00:00
-> App < ' a , ' a >
2016-01-20 19:46:42 +00:00
{
App ::new ( appname )
. version ( version )
. author ( " Matthias Beyer <mail@beyermatthias.de> " )
. about ( about )
. arg ( Arg ::with_name ( " verbosity " )
. short ( " v " )
. long ( " verbose " )
. help ( " Enables verbosity " )
. required ( false )
. takes_value ( false ) )
. arg ( Arg ::with_name ( " debugging " )
. long ( " debug " )
. help ( " Enables debugging output " )
. required ( false )
. takes_value ( false ) )
. arg ( Arg ::with_name ( " config " )
. long ( " config " )
. help ( " Path to alternative config file " )
. required ( false )
. takes_value ( true ) )
. arg ( Arg ::with_name ( " runtimepath " )
. long ( " rtp " )
. help ( " Alternative runtimepath " )
. required ( false )
. takes_value ( true ) )
. arg ( Arg ::with_name ( " storepath " )
. long ( " store " )
. help ( " Alternative storepath. Must be specified as full path, can be outside of the RTP " )
. required ( false )
. takes_value ( true ) )
2016-03-21 09:19:03 +00:00
. arg ( Arg ::with_name ( " editor " )
. long ( " editor " )
. help ( " Set editor " )
. required ( false )
. takes_value ( true ) )
2016-01-20 19:46:42 +00:00
}
2016-01-21 20:24:20 +00:00
/**
* Initialize the internal logger
* /
2016-03-25 18:42:10 +00:00
fn init_logger ( is_debugging : bool , is_verbose : bool ) {
let lvl = if is_debugging {
2016-01-20 19:46:42 +00:00
LogLevelFilter ::Debug
2016-03-25 18:42:10 +00:00
} else if is_verbose {
2016-01-20 19:46:42 +00:00
LogLevelFilter ::Info
} else {
LogLevelFilter ::Error
} ;
log ::set_logger ( | max_log_lvl | {
max_log_lvl . set ( lvl ) ;
debug! ( " Init logger with {} " , lvl ) ;
Box ::new ( ImagLogger ::new ( lvl . to_log_level ( ) . unwrap ( ) ) )
} )
. map_err ( | _ | {
panic! ( " Could not setup logger " ) ;
} )
. ok ( ) ;
}
2016-01-21 20:24:20 +00:00
/**
* Get the verbosity flag value
* /
2016-01-20 19:46:42 +00:00
pub fn is_verbose ( & self ) -> bool {
self . cli_matches . is_present ( " verbosity " )
}
2016-01-21 20:24:20 +00:00
/**
* Get the debugging flag value
* /
2016-01-20 19:46:42 +00:00
pub fn is_debugging ( & self ) -> bool {
self . cli_matches . is_present ( " debugging " )
}
2016-01-21 20:24:20 +00:00
/**
* Get the runtimepath
* /
2016-01-20 19:46:42 +00:00
pub fn rtp ( & self ) -> & PathBuf {
& self . rtp
}
2016-01-21 20:24:20 +00:00
/**
* Get the commandline interface matches
* /
2016-01-20 19:46:42 +00:00
pub fn cli ( & self ) -> & ArgMatches {
& self . cli_matches
}
2016-03-26 18:50:13 +00:00
/**
* Get the configuration object
* /
pub fn config ( & self ) -> Option < & Configuration > {
self . configuration . as_ref ( )
}
2016-01-21 20:24:20 +00:00
/**
* Get the store object
* /
2016-01-20 19:46:42 +00:00
pub fn store ( & self ) -> & Store {
& self . store
}
2016-03-21 09:19:03 +00:00
pub fn editor ( & self ) -> Option < Command > {
self . cli ( )
. value_of ( " editor " )
. map ( String ::from )
. or ( {
match & self . configuration {
& Some ( ref c ) = > c . editor ( ) . map ( | s | s . clone ( ) ) ,
_ = > None ,
}
} )
. or ( env ::var ( " EDITOR " ) . ok ( ) )
. map ( Command ::new )
}
2016-01-20 19:46:42 +00:00
}