Skip to main content

command_stream/commands/
dirname.rs

1//! Virtual `dirname` command implementation
2
3use crate::commands::CommandContext;
4use crate::utils::{CommandResult, VirtualUtils};
5use std::path::Path;
6
7/// Execute the dirname command
8///
9/// Strips the last component from filenames.
10pub async fn dirname(ctx: CommandContext) -> CommandResult {
11    if ctx.args.is_empty() {
12        return VirtualUtils::missing_operand_error("dirname");
13    }
14
15    let path = &ctx.args[0];
16    let parent = Path::new(path)
17        .parent()
18        .map(|p| p.to_string_lossy().to_string())
19        .unwrap_or_else(|| ".".to_string());
20
21    // Handle empty parent (file in current directory)
22    let result = if parent.is_empty() { "." } else { &parent };
23
24    CommandResult::success(format!("{}\n", result))
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[tokio::test]
32    async fn test_dirname_simple() {
33        let ctx = CommandContext::new(vec!["/path/to/file.txt".to_string()]);
34        let result = dirname(ctx).await;
35
36        assert!(result.is_success());
37        assert_eq!(result.stdout, "/path/to\n");
38    }
39
40    #[tokio::test]
41    async fn test_dirname_just_file() {
42        let ctx = CommandContext::new(vec!["file.txt".to_string()]);
43        let result = dirname(ctx).await;
44
45        assert!(result.is_success());
46        assert_eq!(result.stdout, ".\n");
47    }
48
49    #[tokio::test]
50    async fn test_dirname_root() {
51        let ctx = CommandContext::new(vec!["/file.txt".to_string()]);
52        let result = dirname(ctx).await;
53
54        assert!(result.is_success());
55        assert_eq!(result.stdout, "/\n");
56    }
57}