use crate::common::CancelChecker;
use crate::env::EnvDyn;
use crate::env::{EnvStack, Environment};
use crate::parser::Parser;
use crate::proc::JobGroupRef;
use crate::reader::read_generation_count;
use crate::signal::signal_check_cancel;
pub fn no_cancel() -> bool {
false
}
pub const EXPANSION_LIMIT_DEFAULT: usize = 512 * 1024;
pub const EXPANSION_LIMIT_BACKGROUND: usize = 512;
#[allow(clippy::enum_variant_names)]
enum Vars<'a> {
Parser(&'a Parser),
Vars(&'a dyn Environment),
TestOnly(&'a Parser, &'a dyn Environment),
}
pub struct OperationContext<'a> {
vars: Vars<'a>,
pub expansion_limit: usize,
pub job_group: Option<JobGroupRef>,
pub cancel_checker: CancelChecker,
}
impl<'a> OperationContext<'a> {
pub fn vars(&self) -> &dyn Environment {
match &self.vars {
Vars::Parser(parser) => &parser.variables,
Vars::Vars(vars) => *vars,
Vars::TestOnly(_, vars) => *vars,
}
}
pub fn empty() -> OperationContext<'static> {
use std::sync::OnceLock;
static NULL_ENV: OnceLock<EnvStack> = OnceLock::new();
let null_env = NULL_ENV.get_or_init(EnvStack::new);
OperationContext::background(null_env, EXPANSION_LIMIT_DEFAULT)
}
pub fn globals() -> OperationContext<'static> {
OperationContext::background(EnvStack::globals(), EXPANSION_LIMIT_DEFAULT)
}
pub fn foreground(
parser: &'a Parser,
cancel_checker: CancelChecker,
expansion_limit: usize,
) -> OperationContext<'a> {
OperationContext {
vars: Vars::Parser(parser),
expansion_limit,
job_group: None,
cancel_checker,
}
}
pub fn test_only_foreground(
parser: &'a Parser,
vars: &'a dyn Environment,
cancel_checker: CancelChecker,
) -> OperationContext<'a> {
OperationContext {
vars: Vars::TestOnly(parser, vars),
expansion_limit: EXPANSION_LIMIT_DEFAULT,
job_group: None,
cancel_checker,
}
}
pub fn background(vars: &'a dyn Environment, expansion_limit: usize) -> OperationContext<'a> {
OperationContext {
vars: Vars::Vars(vars),
expansion_limit,
job_group: None,
cancel_checker: Box::new(no_cancel),
}
}
pub fn background_with_cancel_checker(
vars: &'a dyn Environment,
cancel_checker: CancelChecker,
expansion_limit: usize,
) -> OperationContext<'a> {
OperationContext {
vars: Vars::Vars(vars),
expansion_limit,
job_group: None,
cancel_checker,
}
}
pub fn background_interruptible(env: &dyn Environment) -> OperationContext<'_> {
OperationContext::background_with_cancel_checker(
env,
Box::new(|| signal_check_cancel() != 0),
EXPANSION_LIMIT_BACKGROUND,
)
}
pub fn has_parser(&self) -> bool {
matches!(self.vars, Vars::Parser(_) | Vars::TestOnly(_, _))
}
pub fn maybe_parser(&self) -> Option<&Parser> {
match &self.vars {
Vars::Parser(parser) => Some(parser),
Vars::Vars(_) => None,
Vars::TestOnly(parser, _) => Some(parser),
}
}
pub fn parser(&self) -> &Parser {
match &self.vars {
Vars::Parser(parser) => parser,
Vars::Vars(_) => panic!(),
Vars::TestOnly(parser, _) => parser,
}
}
pub fn check_cancel(&self) -> bool {
(self.cancel_checker)()
}
}
pub fn get_bg_context(env: &EnvDyn, generation_count: u32) -> OperationContext<'_> {
let cancel_checker = move || {
generation_count != read_generation_count()
};
OperationContext::background_with_cancel_checker(
env,
Box::new(cancel_checker),
EXPANSION_LIMIT_BACKGROUND,
)
}