use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::evaluate::Scope; use crate::object::Value; use crate::parser::hir; use crate::parser::{registry, ConfigDeserializer, Span, Spanned}; use crate::prelude::*; use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; use std::fmt; use std::ops::Deref; use std::path::PathBuf; use uuid::Uuid; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct UnevaluatedCallInfo { pub args: hir::Call, pub source: Text, pub source_map: SourceMap, pub name_span: Option, } impl ToDebug for UnevaluatedCallInfo { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { self.args.fmt_debug(f, source) } } impl UnevaluatedCallInfo { fn evaluate( self, registry: ®istry::CommandRegistry, scope: &Scope, ) -> Result { let args = self.args.evaluate(registry, scope, &self.source)?; Ok(CallInfo { args, source_map: self.source_map, name_span: self.name_span, }) } } #[derive(Deserialize, Serialize, Debug)] pub struct CallInfo { pub args: registry::EvaluatedArgs, pub source_map: SourceMap, pub name_span: Option, } #[derive(Getters)] #[get = "crate"] pub struct CommandArgs { pub host: Arc>, pub env: Arc>, pub call_info: UnevaluatedCallInfo, pub input: InputStream, } impl ToDebug for CommandArgs { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { self.call_info.fmt_debug(f, source) } } impl CommandArgs { pub fn evaluate_once( self, registry: ®istry::CommandRegistry, ) -> Result { let host = self.host.clone(); let env = self.env.clone(); let input = self.input; let call_info = self.call_info.evaluate(registry, &Scope::empty())?; Ok(EvaluatedStaticCommandArgs::new(host, env, call_info, input)) } pub fn name_span(&self) -> Option { self.call_info.name_span } pub fn process<'de, T: Deserialize<'de>>( self, registry: &CommandRegistry, callback: fn(T, RunnableContext) -> Result, ) -> Result, ShellError> { let env = self.env.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); let name_span = args.call_info.name_span; let mut deserializer = ConfigDeserializer::from_call_node(args); Ok(RunnableArgs { args: T::deserialize(&mut deserializer)?, context: RunnableContext { input: input, env, name: name_span, }, callback, }) } } pub struct SinkContext { pub input: Vec>, pub env: Arc>, pub name: Option, } pub struct SinkArgs { args: T, context: SinkContext, callback: fn(T, SinkContext) -> Result<(), ShellError>, } pub struct RunnableContext { pub input: InputStream, pub env: Arc>, pub name: Option, } impl RunnableContext { pub fn cwd(&self) -> PathBuf { let env = self.env.clone(); let env = env.lock().unwrap(); env.path.clone() } } pub struct RunnableArgs { args: T, context: RunnableContext, callback: fn(T, RunnableContext) -> Result, } impl RunnableArgs { pub fn run(self) -> Result { (self.callback)(self.args, self.context) } } pub struct EvaluatedStaticCommandArgs { pub args: EvaluatedCommandArgs, pub input: InputStream, } impl Deref for EvaluatedStaticCommandArgs { type Target = EvaluatedCommandArgs; fn deref(&self) -> &Self::Target { &self.args } } impl EvaluatedStaticCommandArgs { pub fn new( host: Arc>, env: Arc>, call_info: CallInfo, input: impl Into, ) -> EvaluatedStaticCommandArgs { EvaluatedStaticCommandArgs { args: EvaluatedCommandArgs { host, env, call_info, }, input: input.into(), } } pub fn name_span(&self) -> Option { self.args.call_info.name_span } pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { let EvaluatedStaticCommandArgs { args, input } = self; (input, args.call_info.args) } pub fn split(self) -> (InputStream, EvaluatedCommandArgs) { let EvaluatedStaticCommandArgs { args, input } = self; (input, args) } } #[derive(Getters)] #[get = "pub"] pub struct EvaluatedFilterCommandArgs { args: EvaluatedCommandArgs, #[allow(unused)] input: Spanned, } impl Deref for EvaluatedFilterCommandArgs { type Target = EvaluatedCommandArgs; fn deref(&self) -> &Self::Target { &self.args } } impl EvaluatedFilterCommandArgs { pub fn new( host: Arc>, env: Arc>, call_info: CallInfo, input: Spanned, ) -> EvaluatedFilterCommandArgs { EvaluatedFilterCommandArgs { args: EvaluatedCommandArgs { host, env, call_info, }, input, } } } #[derive(Getters, new)] #[get = "crate"] pub struct EvaluatedCommandArgs { pub host: Arc>, pub env: Arc>, pub call_info: CallInfo, } impl EvaluatedCommandArgs { pub fn call_args(&self) -> ®istry::EvaluatedArgs { &self.call_info.args } pub fn nth(&self, pos: usize) -> Option<&Spanned> { self.call_info.args.nth(pos) } pub fn expect_nth(&self, pos: usize) -> Result<&Spanned, ShellError> { self.call_info.args.expect_nth(pos) } pub fn len(&self) -> usize { self.call_info.args.len() } pub fn get(&self, name: &str) -> Option<&Spanned> { self.call_info.args.get(name) } pub fn slice_from(&self, from: usize) -> Vec> { let positional = &self.call_info.args.positional; match positional { None => vec![], Some(list) => list[from..].to_vec(), } } #[allow(unused)] pub fn has(&self, name: &str) -> bool { self.call_info.args.has(name) } } #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), AddSpanSource(Uuid, SpanSource), Exit, } #[derive(Debug, Serialize, Deserialize)] pub enum ReturnSuccess { Value(Spanned), Action(CommandAction), } pub type ReturnValue = Result; impl From> for ReturnValue { fn from(input: Spanned) -> ReturnValue { Ok(ReturnSuccess::Value(input)) } } impl ReturnSuccess { pub fn change_cwd(path: PathBuf) -> ReturnValue { Ok(ReturnSuccess::Action(CommandAction::ChangePath(path))) } pub fn value(input: impl Into>) -> ReturnValue { Ok(ReturnSuccess::Value(input.into())) } pub fn action(input: CommandAction) -> ReturnValue { Ok(ReturnSuccess::Action(input)) } pub fn spanned_value(input: Value, span: Span) -> ReturnValue { Ok(ReturnSuccess::Value(Spanned::from_item(input, span))) } } pub trait StaticCommand: Send + Sync { fn name(&self) -> &str; fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result; fn signature(&self) -> Signature { Signature { name: self.name().to_string(), positional: vec![], rest_positional: true, named: indexmap::IndexMap::new(), is_filter: true, } } } pub enum Command { Static(Arc), } impl Command { pub fn name(&self) -> &str { match self { Command::Static(command) => command.name(), } } pub fn is_sink(&self) -> bool { match self { Command::Static(..) => false, } } pub fn signature(&self) -> Signature { match self { Command::Static(command) => command.signature(), } } pub async fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { match self { Command::Static(command) => command.run(args, registry), } } } #[allow(unused)] pub struct FnFilterCommand { name: String, func: fn(EvaluatedFilterCommandArgs) -> Result, } impl StaticCommand for FnFilterCommand { fn name(&self) -> &str { &self.name } fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { let CommandArgs { host, env, call_info, input, } = args; let host: Arc> = host.clone(); let env: Arc> = env.clone(); let registry: registry::CommandRegistry = registry.clone(); let func = self.func; let result = input.values.map(move |it| { let registry = registry.clone(); let call_info = match call_info .clone() .evaluate(®istry, &Scope::it_value(it.clone())) { Err(err) => return OutputStream::from(vec![Err(err)]).values, Ok(args) => args, }; let args = EvaluatedFilterCommandArgs::new(host.clone(), env.clone(), call_info, it); match func(args) { Err(err) => return OutputStream::from(vec![Err(err)]).values, Ok(stream) => stream.values, } }); let result = result.flatten(); let result: BoxStream = result.boxed(); Ok(result.into()) } } pub struct FnRawCommand { name: String, func: Box< dyn Fn(CommandArgs, ®istry::CommandRegistry) -> Result + Send + Sync, >, } impl StaticCommand for FnRawCommand { fn name(&self) -> &str { &self.name } fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { (self.func)(args, registry) } } pub fn command( name: &str, func: Box< dyn Fn(CommandArgs, ®istry::CommandRegistry) -> Result + Send + Sync, >, ) -> Arc { Arc::new(Command::Static(Arc::new(FnRawCommand { name: name.to_string(), func, }))) } pub fn static_command(command: impl StaticCommand + 'static) -> Arc { Arc::new(Command::Static(Arc::new(command))) } #[allow(unused)] pub fn filter( name: &str, func: fn(EvaluatedFilterCommandArgs) -> Result, ) -> Arc { Arc::new(Command::Static(Arc::new(FnFilterCommand { name: name.to_string(), func, }))) }