From e957fd08db24edd5cdfe637e89e6ae582ecfa57d Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Fri, 10 Apr 2026 13:40:20 +0200 Subject: [PATCH] implement rm and rmdir commands in command line shell --- .../command_line/src/commands/directory.rs | 91 +++++++++++++++++-- .../shell/command_line/src/commands/mod.rs | 21 ++++- 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/executables/shell/command_line/src/commands/directory.rs b/executables/shell/command_line/src/commands/directory.rs index 271e4cec..1cedc15e 100644 --- a/executables/shell/command_line/src/commands/directory.rs +++ b/executables/shell/command_line/src/commands/directory.rs @@ -3,14 +3,15 @@ use alloc::borrow::ToOwned; use executable_macros::GetArgs; use getargs::Options; use xila::{ - file_system::Path, + file_system::{Kind, Path}, virtual_file_system::{self, Directory}, }; use super::{CommandContext, UserCommand}; pub struct CreateDirectoryCommand; -pub struct RemoveCommand; +pub struct RemoveFileCommand; +pub struct RemoveDirectoryCommand; impl UserCommand for CreateDirectoryCommand { async fn execute<'a, I, C>( @@ -27,7 +28,7 @@ impl UserCommand for CreateDirectoryCommand { } } -impl UserCommand for RemoveCommand { +impl UserCommand for RemoveFileCommand { async fn execute<'a, I, C>( &self, context: &mut C, @@ -38,7 +39,22 @@ impl UserCommand for RemoveCommand { I: Iterator, C: CommandContext, { - execute_remove(context, options).await + execute_remove_file(context, options).await + } +} + +impl UserCommand for RemoveDirectoryCommand { + async fn execute<'a, I, C>( + &self, + context: &mut C, + options: &mut Options<&'a str, I>, + _paths: &[&Path], + ) -> Result<()> + where + I: Iterator, + C: CommandContext, + { + execute_remove_directory(context, options).await } } @@ -73,6 +89,14 @@ fn resolve_path( } } +fn can_remove_with_rm(kind: Kind) -> bool { + kind != Kind::Directory +} + +fn can_remove_with_rmdir(kind: Kind) -> bool { + kind == Kind::Directory +} + async fn execute_create_directory<'a, I, C>( context: &mut C, options: &mut Options<&'a str, I>, @@ -94,15 +118,52 @@ where .map_err(Error::FailedToCreateDirectory) } -async fn execute_remove<'a, I, C>(context: &mut C, options: &mut Options<&'a str, I>) -> Result<()> +async fn execute_remove_file<'a, I, C>( + context: &mut C, + options: &mut Options<&'a str, I>, +) -> Result<()> where I: Iterator, C: CommandContext, { let DirectoryRemoveArguments { path } = DirectoryRemoveArguments::parse(options)?; + let path = resolve_path(context, path)?; + + let statistics = virtual_file_system::get_instance() + .get_statistics(&path) + .await + .map_err(Error::FailedToGetMetadata)?; + + if !can_remove_with_rm(statistics.kind) { + return Err(Error::InvalidArgument); + } + + virtual_file_system::get_instance() + .remove(context.task_id(), &path) + .await + .map_err(Error::FailedToRemoveDirectory) +} +async fn execute_remove_directory<'a, I, C>( + context: &mut C, + options: &mut Options<&'a str, I>, +) -> Result<()> +where + I: Iterator, + C: CommandContext, +{ + let DirectoryRemoveArguments { path } = DirectoryRemoveArguments::parse(options)?; let path = resolve_path(context, path)?; + let statistics = virtual_file_system::get_instance() + .get_statistics(&path) + .await + .map_err(Error::FailedToGetMetadata)?; + + if !can_remove_with_rmdir(statistics.kind) { + return Err(Error::InvalidArgument); + } + virtual_file_system::get_instance() .remove(context.task_id(), &path) .await @@ -111,13 +172,13 @@ where #[cfg(test)] mod tests { - use super::resolve_path; + use super::{can_remove_with_rm, can_remove_with_rmdir, resolve_path}; use crate::{Error, Result}; use alloc::borrow::ToOwned; use core::fmt; use xila::{ executable::Standard, - file_system::{Path, PathOwned}, + file_system::{Kind, Path, PathOwned}, task::TaskIdentifier, }; @@ -185,4 +246,20 @@ mod tests { assert!(matches!(result, Err(Error::InvalidPath))); } + + #[test] + fn rm_rejects_directories() { + assert!(!can_remove_with_rm(Kind::Directory)); + } + + #[test] + fn rm_accepts_files() { + assert!(can_remove_with_rm(Kind::File)); + } + + #[test] + fn rmdir_accepts_directories_only() { + assert!(can_remove_with_rmdir(Kind::Directory)); + assert!(!can_remove_with_rmdir(Kind::File)); + } } diff --git a/executables/shell/command_line/src/commands/mod.rs b/executables/shell/command_line/src/commands/mod.rs index afe1d9bf..21794262 100644 --- a/executables/shell/command_line/src/commands/mod.rs +++ b/executables/shell/command_line/src/commands/mod.rs @@ -29,7 +29,7 @@ use self::{ change_directory::ChangeDirectoryCommand, clear::ClearCommand, concatenate::ConcatenateCommand, - directory::{CreateDirectoryCommand, RemoveCommand}, + directory::{CreateDirectoryCommand, RemoveDirectoryCommand, RemoveFileCommand}, dns::DnsResolveCommand, echo::EchoCommand, environment_variables::{ @@ -87,7 +87,8 @@ pub enum UserCommandKind { CreateDirectory, SetEnvironmentVariable, RemoveEnvironmentVariable, - Remove, + RemoveFile, + RemoveDirectory, WebRequest, DnsResolve, Ping, @@ -112,7 +113,8 @@ pub fn resolve_user_command(name: &str) -> Option { "mkdir" => Some(UserCommandKind::CreateDirectory), "export" => Some(UserCommandKind::SetEnvironmentVariable), "unset" => Some(UserCommandKind::RemoveEnvironmentVariable), - "rm" => Some(UserCommandKind::Remove), + "rm" => Some(UserCommandKind::RemoveFile), + "rmdir" => Some(UserCommandKind::RemoveDirectory), "web_request" => Some(UserCommandKind::WebRequest), "dns_resolve" => Some(UserCommandKind::DnsResolve), "ping" => Some(UserCommandKind::Ping), @@ -164,7 +166,12 @@ where .execute(context, options, paths) .await } - UserCommandKind::Remove => RemoveCommand.execute(context, options, paths).await, + UserCommandKind::RemoveFile => RemoveFileCommand.execute(context, options, paths).await, + UserCommandKind::RemoveDirectory => { + RemoveDirectoryCommand + .execute(context, options, paths) + .await + } UserCommandKind::WebRequest => WebRequestCommand.execute(context, options, paths).await, UserCommandKind::DnsResolve => DnsResolveCommand.execute(context, options, paths).await, UserCommandKind::Ping => PingCommand.execute(context, options, paths).await, @@ -245,7 +252,11 @@ mod tests { )); assert!(matches!( resolve_user_command("rm"), - Some(UserCommandKind::Remove) + Some(UserCommandKind::RemoveFile) + )); + assert!(matches!( + resolve_user_command("rmdir"), + Some(UserCommandKind::RemoveDirectory) )); assert!(matches!( resolve_user_command("web_request"),