use std::path::{Path, PathBuf};
use crate::ast::{
AndOrList, Assignment, BinOpType, Command as AstCommand, IoRedirect, ParameterOp, Pipeline,
Program, Word,
};
use crate::policy::VariableAttributes;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CommandResolutionError {
NotExecutable(PathBuf),
NotFound,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeferredReason {
NeedsCurrentShellState,
ShortCircuitRhs,
}
#[derive(Debug, Clone, Copy)]
pub enum PlannedLazyAst<'ast> {
AndOr(&'ast AndOrList),
Command(&'ast AstCommand),
}
#[derive(Debug, Clone)]
pub struct PlannedLazyNode<'ast> {
pub(crate) ast: PlannedLazyAst<'ast>,
pub(crate) reason: DeferredReason,
pub(crate) work_id: Option<String>,
pub(crate) work: Option<DeferredPipelineStageWork<'ast>>,
}
impl<'ast> PlannedLazyNode<'ast> {
pub fn ast(&self) -> PlannedLazyAst<'ast> {
self.ast
}
pub fn and_or_list(&self) -> Option<&'ast AndOrList> {
match self.ast {
PlannedLazyAst::AndOr(and_or) => Some(and_or),
PlannedLazyAst::Command(_) => None,
}
}
pub fn command(&self) -> Option<&'ast AstCommand> {
match self.ast {
PlannedLazyAst::AndOr(_) => None,
PlannedLazyAst::Command(command) => Some(command),
}
}
pub fn reason(&self) -> DeferredReason {
self.reason
}
pub fn work_id(&self) -> Option<&str> {
self.work_id.as_deref()
}
pub fn deferred_work(&self) -> Option<DeferredPipelineStageWork<'ast>> {
self.work
}
}
#[derive(Debug)]
pub struct PlannedProgram<'ast> {
pub(crate) body: Vec<PlannedCommandList<'ast>>,
}
impl<'ast> PlannedProgram<'ast> {
pub fn command_lists(&self) -> &[PlannedCommandList<'ast>] {
&self.body
}
}
#[derive(Debug)]
pub struct PlannedCommandList<'ast> {
pub(crate) raw_and_or_list: &'ast AndOrList,
pub(crate) and_or_list: PlannedAndOr<'ast>,
pub(crate) ampersand: bool,
}
impl<'ast> PlannedCommandList<'ast> {
pub fn raw_and_or_list(&self) -> &'ast AndOrList {
self.raw_and_or_list
}
pub fn and_or_list(&self) -> &PlannedAndOr<'ast> {
&self.and_or_list
}
pub fn ampersand(&self) -> bool {
self.ampersand
}
}
#[derive(Debug)]
pub enum PlannedAndOr<'ast> {
Pipeline(PlannedPipeline<'ast>),
BinOp {
op: BinOpType,
left: Box<PlannedAndOr<'ast>>,
right: PlannedLazyNode<'ast>,
},
}
#[derive(Debug)]
pub struct PlannedPipeline<'ast> {
pub(crate) bang: bool,
pub(crate) stages: Vec<PlannedPipelineStage<'ast>>,
}
impl<'ast> PlannedPipeline<'ast> {
pub fn bang(&self) -> bool {
self.bang
}
pub fn stages(&self) -> &[PlannedPipelineStage<'ast>] {
&self.stages
}
}
#[derive(Debug, Clone)]
pub struct PreparedExternalStagePlan {
pub(crate) program: String,
pub(crate) argv: Vec<String>,
pub(crate) env: Vec<(String, String)>,
pub(crate) cwd: PathBuf,
}
impl PreparedExternalStagePlan {
pub fn program(&self) -> &str {
&self.program
}
pub fn argv(&self) -> &[String] {
&self.argv
}
pub fn env(&self) -> &[(String, String)] {
&self.env
}
pub fn cwd(&self) -> &Path {
&self.cwd
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DeferredExpansion<'ast> {
CommandSubstitution { word: &'ast Word },
ParameterExpansion { word: &'ast Word, op: ParameterOp },
ArithmeticExpansion { word: &'ast Word },
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DeferredPipelineStageWork<'ast> {
CommandDispatch,
CompoundCommand,
Expansion(DeferredExpansion<'ast>),
}
#[derive(Debug)]
pub enum PlannedPipelineStage<'ast> {
PreparedExternal(PreparedExternalStagePlan),
Failure {
status: i32,
},
SimpleCommand(PlannedSimpleCommand<'ast>),
Lazy(PlannedLazyNode<'ast>),
}
impl<'ast> PlannedPipelineStage<'ast> {
pub fn lazy(&self) -> Option<&PlannedLazyNode<'ast>> {
match self {
Self::Lazy(lazy) => Some(lazy),
Self::PreparedExternal(_) | Self::Failure { .. } | Self::SimpleCommand(_) => None,
}
}
pub fn work_id(&self) -> Option<&str> {
match self {
Self::Lazy(lazy) => lazy.work_id(),
Self::PreparedExternal(_) | Self::Failure { .. } | Self::SimpleCommand(_) => None,
}
}
pub fn deferred_work(&self) -> Option<DeferredPipelineStageWork<'ast>> {
match self {
Self::Lazy(lazy) => lazy.deferred_work(),
Self::PreparedExternal(_) | Self::Failure { .. } | Self::SimpleCommand(_) => None,
}
}
}
#[derive(Debug, Clone)]
pub enum PlannedSimpleCommandKind {
AssignmentsOnly {
has_command_substitution: bool,
},
Function {
command_name: String,
argv: Vec<String>,
},
Builtin {
argv: Vec<String>,
},
ShellOverride {
argv: Vec<String>,
},
External {
program: String,
argv: Vec<String>,
},
UnspecifiedUtility {
command_name: String,
},
ResolutionFailure {
command_name: String,
resolution: CommandResolutionError,
},
CommandNotFoundHandler {
argv: Vec<String>,
},
}
#[derive(Debug, Clone)]
pub struct PlannedSimpleCommand<'ast> {
pub(crate) assignments: &'ast [Assignment],
pub(crate) redirects: &'ast [IoRedirect],
pub(crate) assignment_attrib: VariableAttributes,
pub(crate) restore_assignments: bool,
pub(crate) resolve_external_after_assignments: bool,
pub(crate) kind: PlannedSimpleCommandKind,
pub(crate) source_line: Option<u32>,
}
impl<'ast> PlannedSimpleCommand<'ast> {
pub fn assignments(&self) -> &'ast [Assignment] {
self.assignments
}
pub fn redirects(&self) -> &'ast [IoRedirect] {
self.redirects
}
pub fn assignment_attributes(&self) -> VariableAttributes {
self.assignment_attrib
}
pub fn restore_assignments(&self) -> bool {
self.restore_assignments
}
pub fn kind(&self) -> &PlannedSimpleCommandKind {
&self.kind
}
pub fn source_line(&self) -> Option<u32> {
self.source_line
}
pub fn command_name(&self) -> Option<&str> {
match &self.kind {
PlannedSimpleCommandKind::AssignmentsOnly { .. } => None,
PlannedSimpleCommandKind::Function { command_name, .. }
| PlannedSimpleCommandKind::UnspecifiedUtility { command_name }
| PlannedSimpleCommandKind::ResolutionFailure { command_name, .. } => {
Some(command_name.as_str())
}
PlannedSimpleCommandKind::Builtin { argv }
| PlannedSimpleCommandKind::ShellOverride { argv }
| PlannedSimpleCommandKind::CommandNotFoundHandler { argv }
| PlannedSimpleCommandKind::External { argv, .. } => argv.first().map(String::as_str),
}
}
pub fn argv(&self) -> &[String] {
match &self.kind {
PlannedSimpleCommandKind::AssignmentsOnly { .. } => &[],
PlannedSimpleCommandKind::Function { argv, .. }
| PlannedSimpleCommandKind::Builtin { argv }
| PlannedSimpleCommandKind::ShellOverride { argv }
| PlannedSimpleCommandKind::CommandNotFoundHandler { argv }
| PlannedSimpleCommandKind::External { argv, .. } => argv,
PlannedSimpleCommandKind::UnspecifiedUtility { .. }
| PlannedSimpleCommandKind::ResolutionFailure { .. } => &[],
}
}
}
#[derive(Debug)]
pub struct ExecutionPlan<'ast> {
pub(crate) program: &'ast Program,
pub(crate) inner: PlannedProgram<'ast>,
}
impl<'ast> ExecutionPlan<'ast> {
pub fn program(&self) -> &'ast Program {
self.program
}
pub fn plan(&self) -> &PlannedProgram<'ast> {
&self.inner
}
pub fn command_lists(&self) -> &[PlannedCommandList<'ast>] {
self.inner.command_lists()
}
}
#[derive(Debug)]
pub struct PipelineExecutionPlan<'ast> {
pub(crate) pipeline: &'ast Pipeline,
pub(crate) inner: PlannedPipeline<'ast>,
}
impl<'ast> PipelineExecutionPlan<'ast> {
pub fn pipeline(&self) -> &'ast Pipeline {
self.pipeline
}
pub fn plan(&self) -> &PlannedPipeline<'ast> {
&self.inner
}
pub fn stages(&self) -> &[PlannedPipelineStage<'ast>] {
self.inner.stages()
}
}