use super::Category;
use super::Search;
use std::ffi::CStr;
use std::rc::Rc;
use yash_env::Env;
use yash_env::builtin::Builtin;
use yash_env::function::Function;
use yash_env::system::IsExecutableFile;
use yash_env::system::Sysconf;
use yash_env::variable::Expansion;
#[derive(Debug)]
pub struct SearchEnv<'a, S> {
pub env: &'a mut Env<S>,
pub params: &'a Search,
}
impl<S> yash_env::semantics::command::search::PathEnv for SearchEnv<'_, S>
where
S: IsExecutableFile + Sysconf,
{
fn path(&self) -> Expansion<'_> {
if self.params.standard_path {
match self.env.system.confstr_path() {
Ok(path) => match path.into_string() {
Ok(path) => path.into(),
Err(_) => Expansion::Unset,
},
Err(_) => Expansion::Unset,
}
} else {
self.env.path()
}
}
#[inline]
fn is_executable_file(&self, path: &CStr) -> bool {
self.env.is_executable_file(path)
}
}
impl<S> yash_env::semantics::command::search::ClassifyEnv<S> for SearchEnv<'_, S> {
fn builtin(&self, name: &str) -> Option<Builtin<S>> {
if self.params.categories.contains(Category::Builtin) {
self.env.builtin(name)
} else {
None
}
}
fn function(&self, name: &str) -> Option<&Rc<Function<S>>> {
if self.params.categories.contains(Category::Function) {
self.env.function(name)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use enumset::EnumSet;
use yash_env::builtin::Type::Special;
use yash_env::semantics::command::search::{ClassifyEnv as _, PathEnv as _};
use yash_env::source::Location;
use yash_env::str::UnixString;
use yash_env::system::r#virtual::VirtualSystem;
use yash_env::variable::PATH;
use yash_env::variable::Scope;
use yash_env_test_helper::function::FunctionBodyStub;
#[test]
fn standard_path() {
let system = VirtualSystem::new();
system.state.borrow_mut().path = "/bin:/usr/bin:/std".into();
let env = &mut Env::with_system(system);
env.variables
.get_or_new(PATH, Scope::Global)
.assign("/usr/local/bin:/bin", None)
.unwrap();
let params = &Search {
standard_path: true,
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.path();
assert_eq!(result, Expansion::from("/bin:/usr/bin:/std"));
}
#[test]
fn standard_path_with_confstr_error() {
let system = VirtualSystem::new();
system.state.borrow_mut().path = "".into();
let env = &mut Env::with_system(system);
let params = &Search {
standard_path: true,
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.path();
assert_eq!(result, Expansion::Unset);
}
#[test]
fn standard_path_with_invalid_utf8() {
let system = VirtualSystem::new();
system.state.borrow_mut().path = UnixString::from_vec(vec![0x80]);
let env = &mut Env::with_system(system);
let params = &Search {
standard_path: true,
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.path();
assert_eq!(result, Expansion::Unset);
}
#[test]
fn non_standard_path_scalar() {
let system = VirtualSystem::new();
system.state.borrow_mut().path = "/bin:/usr/bin:/std".into();
let env = &mut Env::with_system(system);
env.variables
.get_or_new(PATH, Scope::Global)
.assign("/usr/local/bin:/bin", None)
.unwrap();
let params = &Search {
standard_path: false,
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.path();
assert_eq!(result, Expansion::from("/usr/local/bin:/bin"));
}
#[test]
fn non_standard_path_array() {
let array = vec!["/usr/local/bin".to_owned(), "/bin".to_owned()];
let system = VirtualSystem::new();
system.state.borrow_mut().path = "/bin:/usr/bin:/std".into();
let env = &mut Env::with_system(system);
env.variables
.get_or_new(PATH, Scope::Global)
.assign(array.clone(), None)
.unwrap();
let params = &Search {
standard_path: false,
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.path();
assert_eq!(result, Expansion::from(array));
}
#[test]
fn builtin_on() {
let env = &mut Env::new_virtual();
let builtin = Builtin::new(Special, |_, _| unreachable!());
env.builtins.insert(":", builtin);
let params = &Search {
categories: Category::Builtin.into(),
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.builtin(":");
assert_eq!(result, Some(builtin));
}
#[test]
fn builtin_off() {
let env = &mut Env::new_virtual();
let builtin = Builtin::new(Special, |_, _| unreachable!());
env.builtins.insert(":", builtin);
let params = &Search {
categories: EnumSet::empty(),
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.builtin(":");
assert_eq!(result, None);
}
#[test]
fn function_on() {
let env = &mut Env::new_virtual();
let location = Location::dummy("f");
let function = Rc::new(Function::new("f", FunctionBodyStub::rc_dyn(), location));
env.functions.define(Rc::clone(&function)).unwrap();
let params = &Search {
categories: Category::Function.into(),
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.function("f");
assert_eq!(result, Some(&function));
}
#[test]
fn function_off() {
let env = &mut Env::new_virtual();
let location = Location::dummy("f");
env.functions
.define(Function::new("f", FunctionBodyStub::rc_dyn(), location))
.unwrap();
let params = &Search {
categories: EnumSet::empty(),
..Search::default_for_invoke()
};
let search_env = SearchEnv { env, params };
let result = search_env.function("f");
assert_eq!(result, None);
}
}