Skip to main content

command_stream/commands/
basename.rs

1//! Virtual `basename` command implementation
2
3use crate::commands::CommandContext;
4use crate::utils::{CommandResult, VirtualUtils};
5use std::path::Path;
6
7/// Execute the basename command
8///
9/// Strips directory and suffix from filenames.
10pub async fn basename(ctx: CommandContext) -> CommandResult {
11    if ctx.args.is_empty() {
12        return VirtualUtils::missing_operand_error("basename");
13    }
14
15    let path = &ctx.args[0];
16    let suffix = ctx.args.get(1);
17
18    let base = Path::new(path)
19        .file_name()
20        .map(|n| n.to_string_lossy().to_string())
21        .unwrap_or_default();
22
23    let result = if let Some(suf) = suffix {
24        if base.ends_with(suf.as_str()) {
25            base[..base.len() - suf.len()].to_string()
26        } else {
27            base
28        }
29    } else {
30        base
31    };
32
33    CommandResult::success(format!("{}\n", result))
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[tokio::test]
41    async fn test_basename_simple() {
42        let ctx = CommandContext::new(vec!["/path/to/file.txt".to_string()]);
43        let result = basename(ctx).await;
44
45        assert!(result.is_success());
46        assert_eq!(result.stdout, "file.txt\n");
47    }
48
49    #[tokio::test]
50    async fn test_basename_with_suffix() {
51        let ctx = CommandContext::new(vec!["/path/to/file.txt".to_string(), ".txt".to_string()]);
52        let result = basename(ctx).await;
53
54        assert!(result.is_success());
55        assert_eq!(result.stdout, "file\n");
56    }
57
58    #[tokio::test]
59    async fn test_basename_no_path() {
60        let ctx = CommandContext::new(vec!["file.txt".to_string()]);
61        let result = basename(ctx).await;
62
63        assert!(result.is_success());
64        assert_eq!(result.stdout, "file.txt\n");
65    }
66}