2019-06-07 16:30:50 +00:00
use crate ::commands ::autoview ;
2019-06-07 06:34:42 +00:00
use crate ::commands ::classified ::SinkCommand ;
use crate ::commands ::command ::sink ;
2019-05-23 04:30:43 +00:00
use crate ::prelude ::* ;
2019-05-24 07:29:16 +00:00
use crate ::commands ::classified ::{
2019-05-26 06:54:41 +00:00
ClassifiedCommand , ClassifiedInputStream , ClassifiedPipeline , ExternalCommand , InternalCommand ,
StreamNext ,
2019-05-24 07:29:16 +00:00
} ;
2019-05-23 04:30:43 +00:00
use crate ::context ::Context ;
crate use crate ::errors ::ShellError ;
2019-05-28 06:45:18 +00:00
use crate ::evaluate ::Scope ;
2019-06-25 04:33:12 +00:00
use crate ::parser ::parse ::span ::Spanned ;
2019-06-22 03:43:37 +00:00
use crate ::parser ::registry ;
2019-06-23 17:35:43 +00:00
use crate ::parser ::{ Pipeline , PipelineElement , TokenNode } ;
2019-06-07 06:34:42 +00:00
2019-06-03 05:11:21 +00:00
use crate ::git ::current_branch ;
2019-05-23 04:30:43 +00:00
use crate ::object ::Value ;
2019-06-22 03:43:37 +00:00
use log ::{ debug , trace } ;
2019-05-23 04:30:43 +00:00
use rustyline ::error ::ReadlineError ;
use rustyline ::{ self , ColorMode , Config , Editor } ;
2019-06-07 06:34:42 +00:00
2019-05-23 04:30:43 +00:00
use std ::error ::Error ;
2019-05-24 07:29:16 +00:00
use std ::iter ::Iterator ;
2019-06-07 00:31:22 +00:00
use std ::sync ::atomic ::{ AtomicBool , Ordering } ;
2019-05-23 04:30:43 +00:00
#[ derive(Debug) ]
pub enum MaybeOwned < ' a , T > {
Owned ( T ) ,
Borrowed ( & ' a T ) ,
}
impl < T > MaybeOwned < ' a , T > {
crate fn borrow ( & self ) -> & T {
match self {
MaybeOwned ::Owned ( v ) = > v ,
MaybeOwned ::Borrowed ( v ) = > v ,
}
}
}
2019-06-03 03:48:58 +00:00
pub async fn cli ( ) -> Result < ( ) , Box < dyn Error > > {
2019-05-23 04:30:43 +00:00
let mut context = Context ::basic ( ) ? ;
{
use crate ::commands ::* ;
context . add_commands ( vec! [
2019-05-28 06:45:18 +00:00
command ( " ps " , ps ::ps ) ,
command ( " ls " , ls ::ls ) ,
2019-06-19 04:53:55 +00:00
command ( " sysinfo " , sysinfo ::sysinfo ) ,
2019-05-28 06:45:18 +00:00
command ( " cd " , cd ::cd ) ,
command ( " view " , view ::view ) ,
command ( " skip " , skip ::skip ) ,
2019-06-02 18:53:30 +00:00
command ( " first " , first ::first ) ,
2019-05-28 06:45:18 +00:00
command ( " size " , size ::size ) ,
2019-06-16 06:43:40 +00:00
command ( " from-ini " , from_ini ::from_ini ) ,
2019-05-28 06:45:18 +00:00
command ( " from-json " , from_json ::from_json ) ,
2019-06-01 07:05:57 +00:00
command ( " from-toml " , from_toml ::from_toml ) ,
2019-06-11 06:26:03 +00:00
command ( " from-xml " , from_xml ::from_xml ) ,
2019-06-03 07:41:28 +00:00
command ( " from-yaml " , from_yaml ::from_yaml ) ,
2019-06-05 01:53:38 +00:00
command ( " get " , get ::get ) ,
2019-06-13 21:47:25 +00:00
command ( " enter " , enter ::enter ) ,
command ( " exit " , exit ::exit ) ,
2019-06-18 00:39:09 +00:00
command ( " lines " , lines ::lines ) ,
2019-06-02 18:53:30 +00:00
command ( " pick " , pick ::pick ) ,
2019-05-31 20:34:15 +00:00
command ( " split-column " , split_column ::split_column ) ,
command ( " split-row " , split_row ::split_row ) ,
2019-06-22 03:43:37 +00:00
command ( " lines " , lines ::lines ) ,
2019-05-28 06:45:18 +00:00
command ( " reject " , reject ::reject ) ,
2019-06-01 03:43:59 +00:00
command ( " trim " , trim ::trim ) ,
2019-05-28 06:45:18 +00:00
command ( " to-array " , to_array ::to_array ) ,
command ( " to-json " , to_json ::to_json ) ,
2019-06-01 18:26:04 +00:00
command ( " to-toml " , to_toml ::to_toml ) ,
2019-06-22 03:43:37 +00:00
command ( " sort-by " , sort_by ::sort_by ) ,
Arc ::new ( Open ) ,
2019-05-28 06:45:18 +00:00
Arc ::new ( Where ) ,
2019-06-01 05:50:16 +00:00
Arc ::new ( Config ) ,
2019-06-18 00:39:09 +00:00
Arc ::new ( SkipWhile ) ,
2019-05-28 06:45:18 +00:00
command ( " sort-by " , sort_by ::sort_by ) ,
2019-07-02 07:56:20 +00:00
command ( " inc " , | x | plugin ::filter_plugin ( " inc " . into ( ) , x ) ) ,
2019-05-23 04:30:43 +00:00
] ) ;
2019-06-07 06:34:42 +00:00
2019-06-07 07:50:26 +00:00
context . add_sinks ( vec! [
sink ( " autoview " , autoview ::autoview ) ,
2019-06-07 16:46:47 +00:00
sink ( " clip " , clip ::clip ) ,
2019-06-07 17:13:38 +00:00
sink ( " save " , save ::save ) ,
2019-06-21 04:20:06 +00:00
sink ( " table " , table ::table ) ,
2019-06-07 07:50:26 +00:00
sink ( " tree " , tree ::tree ) ,
2019-06-21 04:20:06 +00:00
sink ( " vtable " , vtable ::vtable ) ,
2019-07-02 07:56:20 +00:00
sink ( " sum " , | x | plugin ::sink_plugin ( " sum " . into ( ) , x ) ) ,
2019-06-07 07:50:26 +00:00
] ) ;
2019-05-23 04:30:43 +00:00
}
2019-05-26 06:54:41 +00:00
let config = Config ::builder ( ) . color_mode ( ColorMode ::Forced ) . build ( ) ;
let h = crate ::shell ::Helper ::new ( context . clone_commands ( ) ) ;
let mut rl : Editor < crate ::shell ::Helper > = Editor ::with_config ( config ) ;
#[ cfg(windows) ]
{
let _ = ansi_term ::enable_ansi_support ( ) ;
}
rl . set_helper ( Some ( h ) ) ;
2019-06-03 00:03:40 +00:00
let _ = rl . load_history ( " history.txt " ) ;
2019-05-26 06:54:41 +00:00
2019-06-07 00:31:22 +00:00
let ctrl_c = Arc ::new ( AtomicBool ::new ( false ) ) ;
let cc = ctrl_c . clone ( ) ;
ctrlc ::set_handler ( move | | {
cc . store ( true , Ordering ::SeqCst ) ;
} )
. expect ( " Error setting Ctrl-C handler " ) ;
2019-06-15 18:36:17 +00:00
let mut ctrlcbreak = false ;
2019-05-23 04:30:43 +00:00
loop {
2019-06-07 00:31:22 +00:00
if ctrl_c . load ( Ordering ::SeqCst ) {
ctrl_c . store ( false , Ordering ::SeqCst ) ;
continue ;
}
2019-06-13 21:47:25 +00:00
let ( obj , cwd ) = {
let env = context . env . lock ( ) . unwrap ( ) ;
2019-06-15 17:52:55 +00:00
let last = env . back ( ) . unwrap ( ) ;
2019-06-13 21:47:25 +00:00
( last . obj ( ) . clone ( ) , last . path ( ) . display ( ) . to_string ( ) )
} ;
let readline = match obj {
Value ::Filesystem = > rl . readline ( & format! (
" {}{}> " ,
cwd ,
match current_branch ( ) {
Some ( s ) = > format! ( " ( {} ) " , s ) ,
None = > " " . to_string ( ) ,
}
) ) ,
_ = > rl . readline ( & format! ( " {} {} > " , obj . type_name ( ) , cwd ) ) ,
} ;
2019-05-23 04:30:43 +00:00
2019-05-23 07:23:06 +00:00
match process_line ( readline , & mut context ) . await {
2019-05-23 04:30:43 +00:00
LineResult ::Success ( line ) = > {
rl . add_history_entry ( line . clone ( ) ) ;
}
2019-06-15 18:36:17 +00:00
LineResult ::CtrlC = > {
if ctrlcbreak {
std ::process ::exit ( 0 ) ;
} else {
context
. host
. lock ( )
. unwrap ( )
. stdout ( " CTRL-C pressed (again to quit) " ) ;
ctrlcbreak = true ;
continue ;
}
}
2019-06-18 00:39:09 +00:00
LineResult ::Error ( mut line , err ) = > {
rl . add_history_entry ( line . clone ( ) ) ;
2019-06-03 05:11:21 +00:00
2019-06-24 00:55:31 +00:00
let diag = err . to_diagnostic ( ) ;
let host = context . host . lock ( ) . unwrap ( ) ;
let writer = host . err_termcolor ( ) ;
line . push_str ( " " ) ;
let files = crate ::parser ::Files ::new ( line ) ;
language_reporting ::emit (
& mut writer . lock ( ) ,
& files ,
& diag ,
& language_reporting ::DefaultConfig ,
)
. unwrap ( ) ;
2019-06-18 00:39:09 +00:00
}
2019-05-23 04:30:43 +00:00
LineResult ::Break = > {
break ;
}
2019-06-24 00:55:31 +00:00
LineResult ::FatalError ( _ , err ) = > {
2019-05-23 04:30:43 +00:00
context
. host
2019-05-23 07:23:06 +00:00
. lock ( )
. unwrap ( )
2019-05-23 04:30:43 +00:00
. stdout ( & format! ( " A surprising fatal error occurred. \n {:?} " , err ) ) ;
}
}
2019-06-15 18:36:17 +00:00
ctrlcbreak = false ;
2019-05-23 04:30:43 +00:00
}
rl . save_history ( " history.txt " ) . unwrap ( ) ;
Ok ( ( ) )
}
enum LineResult {
Success ( String ) ,
2019-06-07 22:35:07 +00:00
Error ( String , ShellError ) ,
2019-06-15 18:36:17 +00:00
CtrlC ,
2019-05-23 04:30:43 +00:00
Break ,
#[ allow(unused) ]
2019-06-24 00:55:31 +00:00
FatalError ( String , ShellError ) ,
2019-05-23 04:30:43 +00:00
}
2019-05-24 04:34:43 +00:00
impl std ::ops ::Try for LineResult {
type Ok = Option < String > ;
2019-06-24 00:55:31 +00:00
type Error = ( String , ShellError ) ;
2019-05-24 04:34:43 +00:00
2019-06-24 00:55:31 +00:00
fn into_result ( self ) -> Result < Option < String > , ( String , ShellError ) > {
2019-05-24 04:34:43 +00:00
match self {
LineResult ::Success ( s ) = > Ok ( Some ( s ) ) ,
2019-06-24 00:55:31 +00:00
LineResult ::Error ( string , err ) = > Err ( ( string , err ) ) ,
2019-05-24 04:34:43 +00:00
LineResult ::Break = > Ok ( None ) ,
2019-06-15 18:36:17 +00:00
LineResult ::CtrlC = > Ok ( None ) ,
2019-06-24 00:55:31 +00:00
LineResult ::FatalError ( string , err ) = > Err ( ( string , err ) ) ,
2019-05-24 04:34:43 +00:00
}
}
2019-06-24 00:55:31 +00:00
fn from_error ( v : ( String , ShellError ) ) -> Self {
LineResult ::Error ( v . 0 , v . 1 )
2019-05-24 04:34:43 +00:00
}
fn from_ok ( v : Option < String > ) -> Self {
match v {
None = > LineResult ::Break ,
Some ( v ) = > LineResult ::Success ( v ) ,
}
}
}
2019-05-23 07:23:06 +00:00
async fn process_line ( readline : Result < String , ReadlineError > , ctx : & mut Context ) -> LineResult {
2019-05-23 04:30:43 +00:00
match & readline {
Ok ( line ) if line . trim ( ) = = " " = > LineResult ::Success ( line . clone ( ) ) ,
Ok ( line ) = > {
2019-06-02 16:28:40 +00:00
let result = match crate ::parser ::parse ( & line ) {
2019-05-23 04:30:43 +00:00
Err ( err ) = > {
2019-06-09 17:52:56 +00:00
return LineResult ::Error ( line . clone ( ) , err ) ;
2019-05-23 04:30:43 +00:00
}
Ok ( val ) = > val ,
} ;
2019-05-26 06:54:41 +00:00
debug! ( " === Parsed === " ) ;
debug! ( " {:#?} " , result ) ;
2019-05-24 07:29:16 +00:00
2019-06-24 00:55:31 +00:00
let mut pipeline = classify_pipeline ( & result , ctx , & Text ::from ( line ) )
. map_err ( | err | ( line . clone ( ) , err ) ) ? ;
2019-06-07 06:34:42 +00:00
match pipeline . commands . last ( ) {
Some ( ClassifiedCommand ::Sink ( _ ) ) = > { }
2019-06-07 07:54:52 +00:00
Some ( ClassifiedCommand ::External ( _ ) ) = > { }
2019-06-07 06:34:42 +00:00
_ = > pipeline . commands . push ( ClassifiedCommand ::Sink ( SinkCommand {
command : sink ( " autoview " , autoview ::autoview ) ,
2019-06-07 22:35:07 +00:00
name_span : None ,
2019-06-22 03:43:37 +00:00
args : registry ::Args {
positional : None ,
named : None ,
2019-06-07 06:34:42 +00:00
} ,
} ) ) ,
}
2019-05-24 07:29:16 +00:00
let mut input = ClassifiedInputStream ::new ( ) ;
2019-05-26 06:54:41 +00:00
let mut iter = pipeline . commands . into_iter ( ) . peekable ( ) ;
2019-05-24 07:29:16 +00:00
loop {
let item : Option < ClassifiedCommand > = iter . next ( ) ;
let next : Option < & ClassifiedCommand > = iter . peek ( ) ;
input = match ( item , next ) {
( None , _ ) = > break ,
2019-06-04 21:42:31 +00:00
( Some ( ClassifiedCommand ::Expr ( _ ) ) , _ ) = > {
2019-06-15 18:36:17 +00:00
return LineResult ::Error (
line . clone ( ) ,
ShellError ::unimplemented ( " Expression-only commands " ) ,
)
2019-06-04 21:42:31 +00:00
}
( _ , Some ( ClassifiedCommand ::Expr ( _ ) ) ) = > {
2019-06-15 18:36:17 +00:00
return LineResult ::Error (
line . clone ( ) ,
ShellError ::unimplemented ( " Expression-only commands " ) ,
)
2019-06-04 21:42:31 +00:00
}
2019-06-15 18:36:17 +00:00
( Some ( ClassifiedCommand ::Sink ( SinkCommand { name_span , .. } ) ) , Some ( _ ) ) = > {
return LineResult ::Error ( line . clone ( ) , ShellError ::maybe_labeled_error ( " Commands like table, save, and autoview must come last in the pipeline " , " must come last " , name_span ) ) ;
2019-06-07 06:34:42 +00:00
}
( Some ( ClassifiedCommand ::Sink ( left ) ) , None ) = > {
let input_vec : Vec < Value > = input . objects . collect ( ) . await ;
2019-06-15 23:03:49 +00:00
if let Err ( err ) = left . run ( ctx , input_vec ) {
return LineResult ::Error ( line . clone ( ) , err ) ;
}
2019-06-07 06:34:42 +00:00
break ;
}
2019-05-24 07:29:16 +00:00
(
Some ( ClassifiedCommand ::Internal ( left ) ) ,
2019-06-07 06:34:42 +00:00
Some ( ClassifiedCommand ::External ( _ ) ) ,
) = > match left . run ( ctx , input ) . await {
Ok ( val ) = > ClassifiedInputStream ::from_input_stream ( val ) ,
2019-06-09 17:52:56 +00:00
Err ( err ) = > return LineResult ::Error ( line . clone ( ) , err ) ,
2019-06-07 06:34:42 +00:00
} ,
2019-06-15 18:36:17 +00:00
( Some ( ClassifiedCommand ::Internal ( left ) ) , Some ( _ ) ) = > {
match left . run ( ctx , input ) . await {
Ok ( val ) = > ClassifiedInputStream ::from_input_stream ( val ) ,
Err ( err ) = > return LineResult ::Error ( line . clone ( ) , err ) ,
}
}
2019-05-24 07:29:16 +00:00
( Some ( ClassifiedCommand ::Internal ( left ) ) , None ) = > {
match left . run ( ctx , input ) . await {
Ok ( val ) = > ClassifiedInputStream ::from_input_stream ( val ) ,
2019-06-09 17:52:56 +00:00
Err ( err ) = > return LineResult ::Error ( line . clone ( ) , err ) ,
2019-05-24 07:29:16 +00:00
}
}
(
Some ( ClassifiedCommand ::External ( left ) ) ,
Some ( ClassifiedCommand ::External ( _ ) ) ,
2019-05-24 18:48:33 +00:00
) = > match left . run ( ctx , input , StreamNext ::External ) . await {
2019-05-24 07:29:16 +00:00
Ok ( val ) = > val ,
2019-06-09 17:52:56 +00:00
Err ( err ) = > return LineResult ::Error ( line . clone ( ) , err ) ,
2019-05-24 07:29:16 +00:00
} ,
2019-06-15 18:36:17 +00:00
( Some ( ClassifiedCommand ::External ( left ) ) , Some ( _ ) ) = > {
match left . run ( ctx , input , StreamNext ::Internal ) . await {
Ok ( val ) = > val ,
Err ( err ) = > return LineResult ::Error ( line . clone ( ) , err ) ,
}
}
2019-05-24 07:29:16 +00:00
( Some ( ClassifiedCommand ::External ( left ) ) , None ) = > {
2019-05-24 18:48:33 +00:00
match left . run ( ctx , input , StreamNext ::Last ) . await {
2019-05-24 07:29:16 +00:00
Ok ( val ) = > val ,
2019-06-09 17:52:56 +00:00
Err ( err ) = > return LineResult ::Error ( line . clone ( ) , err ) ,
2019-05-24 07:29:16 +00:00
}
}
}
2019-05-23 04:30:43 +00:00
}
2019-06-09 17:52:56 +00:00
LineResult ::Success ( line . clone ( ) )
2019-05-23 04:30:43 +00:00
}
2019-06-15 18:36:17 +00:00
Err ( ReadlineError ::Interrupted ) = > LineResult ::CtrlC ,
2019-05-23 04:30:43 +00:00
Err ( ReadlineError ::Eof ) = > {
println! ( " CTRL-D " ) ;
LineResult ::Break
}
Err ( err ) = > {
println! ( " Error: {:?} " , err ) ;
LineResult ::Break
}
}
}
2019-05-26 06:54:41 +00:00
fn classify_pipeline (
2019-06-22 03:43:37 +00:00
pipeline : & TokenNode ,
2019-05-26 06:54:41 +00:00
context : & Context ,
2019-06-22 20:46:16 +00:00
source : & Text ,
2019-05-26 06:54:41 +00:00
) -> Result < ClassifiedPipeline , ShellError > {
2019-06-22 03:43:37 +00:00
let pipeline = pipeline . as_pipeline ( ) ? ;
2019-06-23 17:35:43 +00:00
let Pipeline { parts , .. } = pipeline ;
let commands : Result < Vec < _ > , ShellError > = parts
2019-05-26 06:54:41 +00:00
. iter ( )
2019-06-22 20:46:16 +00:00
. map ( | item | classify_command ( & item , context , & source ) )
2019-05-26 06:54:41 +00:00
. collect ( ) ;
Ok ( ClassifiedPipeline {
commands : commands ? ,
} )
}
2019-05-23 04:30:43 +00:00
fn classify_command (
2019-06-22 03:43:37 +00:00
command : & PipelineElement ,
2019-05-23 04:30:43 +00:00
context : & Context ,
2019-06-22 20:46:16 +00:00
source : & Text ,
2019-05-23 04:30:43 +00:00
) -> Result < ClassifiedCommand , ShellError > {
2019-06-22 03:43:37 +00:00
let call = command . call ( ) ;
2019-06-04 21:42:31 +00:00
2019-06-22 03:43:37 +00:00
match call {
call if call . head ( ) . is_bare ( ) = > {
let head = call . head ( ) ;
let name = head . source ( source ) ;
match context . has_command ( name ) {
2019-06-06 06:34:59 +00:00
true = > {
2019-06-22 03:43:37 +00:00
let command = context . get_command ( name ) ;
2019-06-06 06:34:59 +00:00
let config = command . config ( ) ;
let scope = Scope ::empty ( ) ;
2019-06-22 03:43:37 +00:00
trace! ( " classifying {:?} " , config ) ;
let args = config . evaluate_args ( call , context , & scope , source ) ? ;
2019-06-06 06:34:59 +00:00
Ok ( ClassifiedCommand ::Internal ( InternalCommand {
command ,
2019-06-22 03:43:37 +00:00
name_span : Some ( head . span ( ) . clone ( ) ) ,
2019-06-06 06:34:59 +00:00
args ,
} ) )
2019-06-04 21:42:31 +00:00
}
2019-06-22 03:43:37 +00:00
false = > match context . has_sink ( name ) {
2019-06-07 06:34:42 +00:00
true = > {
2019-06-22 03:43:37 +00:00
let command = context . get_sink ( name ) ;
2019-06-07 06:34:42 +00:00
let config = command . config ( ) ;
let scope = Scope ::empty ( ) ;
2019-06-22 03:43:37 +00:00
let args = config . evaluate_args ( call , context , & scope , source ) ? ;
2019-06-07 06:34:42 +00:00
2019-06-07 22:35:07 +00:00
Ok ( ClassifiedCommand ::Sink ( SinkCommand {
command ,
2019-06-22 03:43:37 +00:00
name_span : Some ( head . span ( ) . clone ( ) ) ,
2019-06-07 22:35:07 +00:00
args ,
} ) )
2019-06-07 06:34:42 +00:00
}
false = > {
2019-06-22 03:43:37 +00:00
let arg_list_strings : Vec < Spanned < String > > = match call . children ( ) {
//Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(),
2019-06-15 04:20:58 +00:00
Some ( args ) = > args
. iter ( )
2019-06-24 00:55:31 +00:00
. filter_map ( | i | match i {
TokenNode ::Whitespace ( _ ) = > None ,
other = > Some ( Spanned ::from_item (
other . as_external_arg ( source ) ,
other . span ( ) ,
) ) ,
} )
2019-06-15 04:20:58 +00:00
. collect ( ) ,
2019-06-07 06:34:42 +00:00
None = > vec! [ ] ,
} ;
Ok ( ClassifiedCommand ::External ( ExternalCommand {
name : name . to_string ( ) ,
2019-06-22 03:43:37 +00:00
name_span : Some ( head . span ( ) . clone ( ) ) ,
2019-06-07 06:34:42 +00:00
args : arg_list_strings ,
} ) )
}
} ,
2019-06-22 03:43:37 +00:00
}
2019-06-04 21:42:31 +00:00
}
2019-06-22 03:43:37 +00:00
2019-06-24 00:55:31 +00:00
call = > Err ( ShellError ::diagnostic (
language_reporting ::Diagnostic ::new (
language_reporting ::Severity ::Error ,
" Invalid command " ,
)
. with_label ( language_reporting ::Label ::new_primary ( call . head ( ) . span ( ) ) ) ,
2019-06-22 03:43:37 +00:00
) ) ,
2019-05-23 04:30:43 +00:00
}
}