Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod cd;
mod echo;
mod environment;
mod exit;
mod introspection;
mod pwd;
mod source;
mod status;
Expand Down Expand Up @@ -55,11 +56,13 @@ pub(crate) fn run(
) -> Result<Option<BuiltinResult>> {
let result = match name {
"cd" => cd::run(shell, argv)?,
"command" => introspection::command(shell, argv),
"pwd" => pwd::run()?,
"exit" => exit::run(argv),
"export" => environment::export(shell, argv, env_overlay),
"unset" => environment::unset(shell, argv),
"set" => environment::set(shell)?,
"type" => introspection::type_(shell, argv),
"true" => status::true_(),
"false" => status::false_(),
"echo" => echo::run(argv)?,
Expand All @@ -69,3 +72,21 @@ pub(crate) fn run(

Ok(Some(result))
}

pub(crate) fn is_builtin(name: &str) -> bool {
matches!(
name,
"." | "cd"
| "command"
| "echo"
| "exit"
| "export"
| "false"
| "pwd"
| "set"
| "source"
| "true"
| "type"
| "unset"
)
}
62 changes: 62 additions & 0 deletions src/commands/introspection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::commands::{BuiltinResult, is_builtin};
use crate::path::display_path;
use crate::runtime::Shell;

pub(crate) fn command(shell: &Shell, argv: &[String]) -> BuiltinResult {
match argv.first().map(String::as_str) {
Some("-v") => command_v(shell, &argv[1..]),
Some(option) if option.starts_with('-') => BuiltinResult::stderr(
2,
format!("command: unsupported option: {option}\n").into_bytes(),
),
Some(_) => BuiltinResult::stderr(
2,
b"command: only command -v is currently supported\n".to_vec(),
),
None => BuiltinResult::success(),
}
}

pub(crate) fn type_(shell: &Shell, argv: &[String]) -> BuiltinResult {
let mut stdout = Vec::new();
let mut stderr = Vec::new();
let mut status = 0;

for name in argv {
if is_builtin(name) {
stdout.extend_from_slice(format!("{name} is a shell builtin\n").as_bytes());
} else if let Some(path) = shell.resolve_program(name) {
stdout.extend_from_slice(format!("{name} is {}\n", display_path(&path)).as_bytes());
} else {
status = 1;
stderr.extend_from_slice(format!("type: {name}: not found\n").as_bytes());
}
}

BuiltinResult {
status,
stdout,
stderr,
}
}

fn command_v(shell: &Shell, names: &[String]) -> BuiltinResult {
let mut stdout = Vec::new();
let mut status = 0;

for name in names {
if is_builtin(name) {
stdout.extend_from_slice(format!("{name}\n").as_bytes());
} else if let Some(path) = shell.resolve_program(name) {
stdout.extend_from_slice(format!("{}\n", display_path(&path)).as_bytes());
} else {
status = 1;
}
}

BuiltinResult {
status,
stdout,
stderr: Vec::new(),
}
}
4 changes: 4 additions & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ impl Shell {
Ok((status, stdout))
}

pub(crate) fn resolve_program(&self, name: &str) -> Option<PathBuf> {
resolve_program(name, &self.vars).ok()
}

fn run_pipeline(&mut self, pipeline: &Pipeline) -> Result<i32> {
Ok(self.run_pipeline_inner(pipeline, false)?.status)
}
Expand Down
25 changes: 25 additions & 0 deletions tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,28 @@ fn failed_cd_does_not_run_argument_as_command() {
assert_eq!(output.status.code(), Some(1));
assert!(output.stdout.is_empty());
}

#[test]
fn command_v_reports_builtins_and_missing_commands() {
let output = Command::new(env!("CARGO_BIN_EXE_rysh"))
.args(["-c", "command -v cd definitely_missing_rysh_command"])
.output()
.unwrap();

assert_eq!(output.status.code(), Some(1));
assert_eq!(String::from_utf8_lossy(&output.stdout), "cd\n");
}

#[test]
fn type_reports_builtins() {
let output = Command::new(env!("CARGO_BIN_EXE_rysh"))
.args(["-c", "type cd"])
.output()
.unwrap();

assert!(output.status.success());
assert_eq!(
String::from_utf8_lossy(&output.stdout),
"cd is a shell builtin\n"
);
}
Loading