From 74bc8902be593796eb6292151e08374072766e3e Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Wed, 18 Mar 2026 22:50:03 +0800 Subject: Add workspace sheet command with help and localization --- src/cmds/arg/workspace_sheet.rs | 22 +++++ src/cmds/cmd/workspace_sheet.rs | 109 ++++++++++++++++++++++++ src/cmds/collect/workspace.rs | 5 ++ src/cmds/comp/workspace_sheet.rs | 20 +++++ src/cmds/converter/make_sheet_error.rs | 22 +++++ src/cmds/converter/workspace_operation_error.rs | 59 ++++++++----- src/cmds/in/workspace_sheet.rs | 6 ++ 7 files changed, 224 insertions(+), 19 deletions(-) create mode 100644 src/cmds/arg/workspace_sheet.rs create mode 100644 src/cmds/cmd/workspace_sheet.rs create mode 100644 src/cmds/collect/workspace.rs create mode 100644 src/cmds/comp/workspace_sheet.rs create mode 100644 src/cmds/converter/make_sheet_error.rs create mode 100644 src/cmds/in/workspace_sheet.rs (limited to 'src') diff --git a/src/cmds/arg/workspace_sheet.rs b/src/cmds/arg/workspace_sheet.rs new file mode 100644 index 0000000..7cf65e4 --- /dev/null +++ b/src/cmds/arg/workspace_sheet.rs @@ -0,0 +1,22 @@ +// Why not directly design new, delete, print_path as Option? +// Because the former would only allow the following syntax: +// jvn workspace sheet --new sheet +// But by separating name and operation, it can simultaneously support: +// jvn workspace sheet sheet --new + +#[derive(clap::Parser)] +pub struct JVWorkspaceSheetArgument { + pub name: Option, + + #[arg(short = 'n', long = "new")] + pub new: bool, + + #[arg(short = 'd', long = "delete")] + pub delete: bool, + + #[arg(short = 'A', long = "list-all")] + pub list_all: bool, + + #[arg(short = 'p', long = "print-path")] + pub print_path: bool, +} diff --git a/src/cmds/cmd/workspace_sheet.rs b/src/cmds/cmd/workspace_sheet.rs new file mode 100644 index 0000000..a360203 --- /dev/null +++ b/src/cmds/cmd/workspace_sheet.rs @@ -0,0 +1,109 @@ +use crate::{ + cmd_output, + cmds::{ + arg::workspace_sheet::JVWorkspaceSheetArgument, collect::workspace::JVWorkspaceCollect, + converter::make_sheet_error::MakeSheetErrorConverter, + r#in::workspace_sheet::JVWorkspaceSheetInput, out::none::JVNoneOutput, + }, + systems::{ + cmd::{ + cmd_system::{AnyOutput, JVCommandContext}, + errors::{CmdExecuteError, CmdPrepareError}, + }, + helpdoc::helpdoc_viewer, + }, +}; +use cmd_system_macros::exec; +use just_enough_vcs::system::workspace::workspace::manager::WorkspaceManager; +use rust_i18n::t; + +pub struct JVWorkspaceSheetCommand; +type Cmd = JVWorkspaceSheetCommand; +type Arg = JVWorkspaceSheetArgument; +type In = JVWorkspaceSheetInput; +type Collect = JVWorkspaceCollect; + +async fn help_str() -> String { + helpdoc_viewer::display("commands/workspace/sheet").await; + String::new() +} + +async fn prepare(_args: &Arg, _ctx: &JVCommandContext) -> Result { + let input = match (_args.new, _args.delete, _args.list_all, _args.print_path) { + (true, false, false, false) => { + let name = _args.name.as_ref().ok_or_else(|| { + CmdPrepareError::Error( + t!("workspace_sheet.error.sheet_name_required_for_new") + .trim() + .to_string(), + ) + })?; + JVWorkspaceSheetInput::Add(name.clone()) + } + (false, true, false, false) => { + let name = _args.name.as_ref().ok_or_else(|| { + CmdPrepareError::Error( + t!("workspace_sheet.error.sheet_name_required_for_delete") + .trim() + .to_string(), + ) + })?; + JVWorkspaceSheetInput::Delete(name.clone()) + } + (false, false, true, false) => JVWorkspaceSheetInput::ListAll, + (false, false, false, true) => { + let name = _args.name.as_ref().ok_or_else(|| { + CmdPrepareError::Error( + t!("workspace_sheet.error.sheet_name_required_for_print_path") + .trim() + .to_string(), + ) + })?; + JVWorkspaceSheetInput::PrintPath(name.clone()) + } + _ => { + return Err(CmdPrepareError::Error( + t!("workspace_sheet.error.exactly_one_required") + .trim() + .to_string(), + )); + } + }; + Ok(input) +} + +async fn collect(_args: &Arg, _ctx: &JVCommandContext) -> Result { + Ok(JVWorkspaceCollect { + manager: WorkspaceManager::new(), + }) +} + +#[exec] +async fn exec(input: In, collect: Collect) -> Result { + match input { + JVWorkspaceSheetInput::Add(sheet_name) => { + if let Err(e) = collect.manager.make_sheet(sheet_name).await { + return Err(MakeSheetErrorConverter::to_exec_error(e)); + } + } + JVWorkspaceSheetInput::Delete(sheet_name) => { + if let Err(e) = collect.manager.drop_sheet(sheet_name).await { + return Err(MakeSheetErrorConverter::to_exec_error(e)); + } + } + JVWorkspaceSheetInput::ListAll => { + collect + .manager + .list_sheet_names() + .await + .iter() + .for_each(|name| println!("{}", name)); + } + JVWorkspaceSheetInput::PrintPath(sheet_name) => { + println!("{}", collect.manager.get_sheet_path(sheet_name).display()) + } + } + cmd_output!(JVNoneOutput => JVNoneOutput) +} + +crate::command_template!(); diff --git a/src/cmds/collect/workspace.rs b/src/cmds/collect/workspace.rs new file mode 100644 index 0000000..5759a09 --- /dev/null +++ b/src/cmds/collect/workspace.rs @@ -0,0 +1,5 @@ +use just_enough_vcs::system::workspace::workspace::manager::WorkspaceManager; + +pub struct JVWorkspaceCollect { + pub manager: WorkspaceManager, +} diff --git a/src/cmds/comp/workspace_sheet.rs b/src/cmds/comp/workspace_sheet.rs new file mode 100644 index 0000000..3d7e6d5 --- /dev/null +++ b/src/cmds/comp/workspace_sheet.rs @@ -0,0 +1,20 @@ +use crate::systems::comp::context::CompletionContext; +use just_enough_vcs::system::workspace::workspace::manager::WorkspaceManager; + +pub fn comp(ctx: CompletionContext) -> Option> { + if ctx.current_word.starts_with('-') { + return Some(vec![ + "--list-all".to_string(), + "--print-path".to_string(), + "--new".to_string(), + "--delete".to_string(), + ]); + } + + if ctx.previous_word == "--new" || ctx.previous_word == "-n" { + return Some(vec![]); + } + + let rt = tokio::runtime::Runtime::new().unwrap(); + Some(rt.block_on(WorkspaceManager::new().list_sheet_names())) +} diff --git a/src/cmds/converter/make_sheet_error.rs b/src/cmds/converter/make_sheet_error.rs new file mode 100644 index 0000000..8bbc45d --- /dev/null +++ b/src/cmds/converter/make_sheet_error.rs @@ -0,0 +1,22 @@ +use crate::systems::cmd::errors::CmdExecuteError; +use just_enough_vcs::system::workspace::workspace::manager::sheet_state::error::MakeSheetError; +use rust_i18n::t; + +pub struct MakeSheetErrorConverter; + +impl MakeSheetErrorConverter { + pub fn to_exec_error(err: MakeSheetError) -> CmdExecuteError { + match err { + MakeSheetError::SheetAlreadyExists => { + CmdExecuteError::Error(t!("make_sheet_error.sheet_already_exists").to_string()) + } + MakeSheetError::SheetNotFound => { + CmdExecuteError::Error(t!("make_sheet_error.sheet_not_found").to_string()) + } + MakeSheetError::Io(error) => CmdExecuteError::Io(error), + MakeSheetError::Other(error) => CmdExecuteError::Error( + t!("make_sheet_error.other", error = error.to_string()).to_string(), + ), + } + } +} diff --git a/src/cmds/converter/workspace_operation_error.rs b/src/cmds/converter/workspace_operation_error.rs index f2f71d0..3e6252d 100644 --- a/src/cmds/converter/workspace_operation_error.rs +++ b/src/cmds/converter/workspace_operation_error.rs @@ -1,5 +1,6 @@ use crate::systems::cmd::errors::CmdExecuteError; use just_enough_vcs::system::workspace::workspace::error::WorkspaceOperationError; +use rust_i18n::t; pub struct JVWorkspaceOperationErrorConverter; @@ -9,26 +10,46 @@ impl JVWorkspaceOperationErrorConverter { WorkspaceOperationError::Io(error) => CmdExecuteError::Io(error), WorkspaceOperationError::Other(msg) => CmdExecuteError::Error(msg), WorkspaceOperationError::ConfigNotFound => { - CmdExecuteError::Error("Config not found".to_string()) - } - WorkspaceOperationError::WorkspaceNotFound => { - CmdExecuteError::Error("Workspace not found".to_string()) - } - WorkspaceOperationError::HandleLock(handle_lock_error) => { - CmdExecuteError::Error(format!("Handle lock error: {}", handle_lock_error)) - } - WorkspaceOperationError::DataRead(data_read_error) => { - CmdExecuteError::Error(format!("Data read error: {}", data_read_error)) - } - WorkspaceOperationError::DataWrite(data_write_error) => { - CmdExecuteError::Error(format!("Data write error: {}", data_write_error)) - } - WorkspaceOperationError::DataApply(data_apply_error) => { - CmdExecuteError::Error(format!("Data apply error: {}", data_apply_error)) - } - WorkspaceOperationError::IDAliasError(id_alias_error) => { - CmdExecuteError::Error(format!("ID alias error: {}", id_alias_error)) + CmdExecuteError::Error(t!("workspace_operation_error.config_not_found").to_string()) } + WorkspaceOperationError::WorkspaceNotFound => CmdExecuteError::Error( + t!("workspace_operation_error.workspace_not_found").to_string(), + ), + WorkspaceOperationError::HandleLock(handle_lock_error) => CmdExecuteError::Error( + t!( + "workspace_operation_error.handle_lock", + error = handle_lock_error + ) + .to_string(), + ), + WorkspaceOperationError::DataRead(data_read_error) => CmdExecuteError::Error( + t!( + "workspace_operation_error.data_read", + error = data_read_error + ) + .to_string(), + ), + WorkspaceOperationError::DataWrite(data_write_error) => CmdExecuteError::Error( + t!( + "workspace_operation_error.data_write", + error = data_write_error + ) + .to_string(), + ), + WorkspaceOperationError::DataApply(data_apply_error) => CmdExecuteError::Error( + t!( + "workspace_operation_error.data_apply", + error = data_apply_error + ) + .to_string(), + ), + WorkspaceOperationError::IDAliasError(id_alias_error) => CmdExecuteError::Error( + t!( + "workspace_operation_error.id_alias_error", + error = id_alias_error + ) + .to_string(), + ), } } } diff --git a/src/cmds/in/workspace_sheet.rs b/src/cmds/in/workspace_sheet.rs new file mode 100644 index 0000000..78159ac --- /dev/null +++ b/src/cmds/in/workspace_sheet.rs @@ -0,0 +1,6 @@ +pub enum JVWorkspaceSheetInput { + Add(String), + Delete(String), + ListAll, + PrintPath(String), +} -- cgit