pub mod alias;
pub mod bg;
pub mod r#break;
pub mod cd;
pub mod colon;
#[cfg(feature = "yash-semantics")]
pub mod command;
pub mod common;
pub mod r#continue;
#[cfg(feature = "yash-semantics")]
pub mod eval;
#[cfg(feature = "yash-semantics")]
pub mod exec;
pub mod exit;
pub mod export;
pub mod r#false;
pub mod fg;
pub mod getopts;
pub mod jobs;
pub mod kill;
pub mod pwd;
#[cfg(feature = "yash-semantics")]
pub mod read;
pub mod readonly;
pub mod r#return;
pub mod set;
pub mod shift;
#[cfg(feature = "yash-semantics")]
pub mod source;
pub mod times;
pub mod trap;
pub mod r#true;
#[cfg(feature = "yash-semantics")]
pub mod r#type;
pub mod typeset;
pub mod ulimit;
pub mod umask;
pub mod unalias;
pub mod unset;
#[cfg(feature = "yash-semantics")]
pub mod wait;
#[doc(no_inline)]
pub use yash_env::builtin::*;
#[cfg(doc)]
use yash_env::stack::{Frame, Stack};
#[cfg(doc)]
use yash_env::Env;
use std::future::ready;
use Type::{Elective, Mandatory, Special};
pub const BUILTINS: &[(&str, Builtin)] = &[
#[cfg(feature = "yash-semantics")]
(
".",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(source::main(env, args)),
},
),
(
":",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(ready(colon::main(env, args))),
},
),
(
"alias",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(alias::main(env, args)),
},
),
(
"bg",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(bg::main(env, args)),
},
),
(
"break",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(r#break::main(env, args)),
},
),
(
"cd",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(cd::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"command",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(command::main(env, args)),
},
),
(
"continue",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(r#continue::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"eval",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(eval::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"exec",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(exec::main(env, args)),
},
),
(
"exit",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(exit::main(env, args)),
},
),
(
"export",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(export::main(env, args)),
},
),
(
"false",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(r#false::main(env, args)),
},
),
(
"fg",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(fg::main(env, args)),
},
),
(
"getopts",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(getopts::main(env, args)),
},
),
(
"jobs",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(jobs::main(env, args)),
},
),
(
"kill",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(kill::main(env, args)),
},
),
(
"pwd",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(pwd::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"read",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(read::main(env, args)),
},
),
(
"readonly",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(readonly::main(env, args)),
},
),
(
"return",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(r#return::main(env, args)),
},
),
(
"set",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(set::main(env, args)),
},
),
(
"shift",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(shift::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"source",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(source::main(env, args)),
},
),
(
"times",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(times::main(env, args)),
},
),
(
"trap",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(trap::main(env, args)),
},
),
(
"true",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(r#true::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"type",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(r#type::main(env, args)),
},
),
(
"typeset",
Builtin {
r#type: Elective,
execute: |env, args| Box::pin(typeset::main(env, args)),
},
),
(
"ulimit",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(ulimit::main(env, args)),
},
),
(
"umask",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(umask::main(env, args)),
},
),
(
"unalias",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(unalias::main(env, args)),
},
),
(
"unset",
Builtin {
r#type: Special,
execute: |env, args| Box::pin(unset::main(env, args)),
},
),
#[cfg(feature = "yash-semantics")]
(
"wait",
Builtin {
r#type: Mandatory,
execute: |env, args| Box::pin(wait::main(env, args)),
},
),
];
#[cfg(test)]
pub(crate) mod tests {
use assert_matches::assert_matches;
use futures_executor::LocalSpawner;
use futures_util::task::LocalSpawnExt as _;
use futures_util::FutureExt as _;
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::str::from_utf8;
use yash_env::system::r#virtual::FileBody;
use yash_env::system::r#virtual::INode;
use yash_env::system::r#virtual::SystemState;
use yash_env::Env;
use yash_env::VirtualSystem;
#[derive(Clone, Debug)]
pub struct LocalExecutor(pub LocalSpawner);
impl yash_env::system::r#virtual::Executor for LocalExecutor {
fn spawn(
&self,
task: Pin<Box<dyn Future<Output = ()>>>,
) -> Result<(), Box<dyn std::error::Error>> {
self.0
.spawn_local(task)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}
}
pub fn in_virtual_system<F, Fut, T>(f: F) -> T
where
F: FnOnce(Env, Rc<RefCell<SystemState>>) -> Fut,
Fut: Future<Output = T> + 'static,
T: 'static,
{
let system = VirtualSystem::new();
let state = Rc::clone(&system.state);
let mut executor = futures_executor::LocalPool::new();
state.borrow_mut().executor = Some(Rc::new(LocalExecutor(executor.spawner())));
let env = Env::with_system(Box::new(system));
let shared_system = env.system.clone();
let task = f(env, Rc::clone(&state));
let mut task = executor.spawner().spawn_local_with_handle(task).unwrap();
loop {
if let Some(result) = (&mut task).now_or_never() {
return result;
}
executor.run_until_stalled();
shared_system.select(false).unwrap();
SystemState::select_all(&state);
}
}
pub fn stub_tty(state: &RefCell<SystemState>) {
state
.borrow_mut()
.file_system
.save("/dev/tty", Rc::new(RefCell::new(INode::new([]))))
.unwrap();
}
pub fn assert_stdout<F, T>(state: &RefCell<SystemState>, f: F) -> T
where
F: FnOnce(&str) -> T,
{
let stdout = state.borrow().file_system.get("/dev/stdout").unwrap();
let stdout = stdout.borrow();
assert_matches!(&stdout.body, FileBody::Regular { content, .. } => {
f(from_utf8(content).unwrap())
})
}
pub fn assert_stderr<F, T>(state: &RefCell<SystemState>, f: F) -> T
where
F: FnOnce(&str) -> T,
{
let stderr = state.borrow().file_system.get("/dev/stderr").unwrap();
let stderr = stderr.borrow();
assert_matches!(&stderr.body, FileBody::Regular { content, .. } => {
f(from_utf8(content).unwrap())
})
}
#[test]
fn builtins_are_sorted() {
super::BUILTINS
.windows(2)
.for_each(|pair| assert!(pair[0].0 < pair[1].0, "disordered pair: {pair:?}"))
}
}