From 1e9c97c21f8a4e55420712b054895ff8b4f9a849 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Fri, 19 Jun 2026 01:40:38 +0800 Subject: feat(rola-bucket): add bucket bind management Implement bucket bind CRUD operations and config loading, along with CLI integration for listing, setting, and removing bucket bindings. --- rola-cli/Cargo.toml | 10 +- rola-cli/locales/errors/common.toml | 25 +++ rola-cli/locales/errors/i18n_space_error.toml | 23 +++ rola-cli/locales/i18n_bucket_manager.toml | 16 ++ rola-cli/src/bin/rola.rs | 16 +- rola-cli/src/bucket_mgr.rs | 2 + rola-cli/src/bucket_mgr/bind.rs | 262 ++++++++++++++++++++++++++ rola-cli/src/bucket_mgr/creation.rs | 27 +-- rola-cli/src/error.rs | 4 + rola-cli/src/error/io.rs | 12 +- rola-cli/src/error/require_overwrite.rs | 19 ++ rola-cli/src/error/space.rs | 64 +++++++ rola-cli/src/lib.rs | 21 ++- rola-cli/src/output/setup.rs | 16 +- rola-cli/src/res.rs | 2 + rola-cli/src/res/bucket.rs | 25 +++ rola-cli/src/res/overwrite.rs | 9 + 17 files changed, 529 insertions(+), 24 deletions(-) create mode 100644 rola-cli/locales/errors/common.toml create mode 100644 rola-cli/locales/errors/i18n_space_error.toml create mode 100644 rola-cli/src/bucket_mgr/bind.rs create mode 100644 rola-cli/src/error/require_overwrite.rs create mode 100644 rola-cli/src/error/space.rs create mode 100644 rola-cli/src/res/bucket.rs create mode 100644 rola-cli/src/res/overwrite.rs (limited to 'rola-cli') diff --git a/rola-cli/Cargo.toml b/rola-cli/Cargo.toml index 0ed9a3b..c9cf562 100644 --- a/rola-cli/Cargo.toml +++ b/rola-cli/Cargo.toml @@ -13,6 +13,7 @@ shared_macros.workspace = true space-system.workspace = true +serde.workspace = true tokio.workspace = true colored = "3.1.1" @@ -20,20 +21,23 @@ chrono = "0.4.45" env_logger = "0.11.10" log = "0.4.32" shakehand = "0.1.3" +clap = { version = "4.6.1", features = ["derive"] } [dependencies.mingling] git = "https://github.com/mingling-rs/mingling.git" -rev = "002f3fd390f64b1d7632f8530a0db81d45edf6c2" +rev = "4be889ac2dc5263ce03bb014de24916bee2e9aa8" features = [ "parser", "extra_macros", "dispatch_tree", - "comp" + "comp", + "clap", + "general_renderer" ] [build-dependencies.mingling] git = "https://github.com/mingling-rs/mingling.git" -rev = "002f3fd390f64b1d7632f8530a0db81d45edf6c2" +rev = "4be889ac2dc5263ce03bb014de24916bee2e9aa8" features = [ "builds", "comp" diff --git a/rola-cli/locales/errors/common.toml b/rola-cli/locales/errors/common.toml new file mode 100644 index 0000000..8c0acf8 --- /dev/null +++ b/rola-cli/locales/errors/common.toml @@ -0,0 +1,25 @@ +[en] +command_not_found = """ +[[b_red]]**Error:**[[/]] Command not found: \"%{command}\"! + +[[b_yellow]]**Hint:**[[/]] You can use `rola -h` to query help. +""" + +require_overwrite = """ +[[b_red]]**Operation blocked:**[[/]] This operation would overwrite an existing resource + +[[b_yellow]]**Hint:**[[/]] Explicitly specify [[b_red]]**--overwrite**[[/]]! +""" + +[zh_CN] +command_not_found = """ +[[b_red]]**错误:**[[/]]未找到命令 \"%{command}\"! + +[[b_yellow]]**提示:**[[/]]您可以使用 `rola -h` 来查询帮助。 +""" + +require_overwrite = """ +[[b_red]]**操作已阻止:**[[/]]该操作将会覆写已有资源 + +[[b_yellow]]**提示:**[[/]]请显式指定 [[b_red]]**--overwrite**[[/]]! +""" diff --git a/rola-cli/locales/errors/i18n_space_error.toml b/rola-cli/locales/errors/i18n_space_error.toml new file mode 100644 index 0000000..48c5aa1 --- /dev/null +++ b/rola-cli/locales/errors/i18n_space_error.toml @@ -0,0 +1,23 @@ +[en] +space_not_found = "Space not found" +path_format = "Path format error: {info}" +require_empty_directory = "The specified directory is not empty" +config_file_already_exist = "Configuration file already exists" +io_error_name = "Space IO error" +io_not_found = "{info}" +io_permission_denied = "{info}" +io_already_exists = "{info}" +io_invalid_input = "{info}" +io_other = "{info}" + +[zh_CN] +space_not_found = "未找到空间" +path_format = "路径格式错误: {info}" +require_empty_directory = "指定目录不为空" +config_file_already_exist = "配置文件已存在" +io_error_name = "空间 IO 错误" +io_not_found = "{info}" +io_permission_denied = "{info}" +io_already_exists = "{info}" +io_invalid_input = "{info}" +io_other = "{info}" diff --git a/rola-cli/locales/i18n_bucket_manager.toml b/rola-cli/locales/i18n_bucket_manager.toml index 09fae75..0e4f3c1 100644 --- a/rola-cli/locales/i18n_bucket_manager.toml +++ b/rola-cli/locales/i18n_bucket_manager.toml @@ -13,6 +13,14 @@ error_bucket_path_not_directory = "The path \"%{path}\" is not a directory!" error_bucket_nested = "Cannot create a nested Bucket directory inside an existing Bucket space" +error_bind_index_not_provided = """ +[[b_red]]**Error:**[[/]] Bucket bind index not provided +""" + +error_bind_index_not_found = """ +[[b_red]]**Error:**[[/]] Bucket bind index not found: %{index} +""" + [zh_CN] created = "桶已被创建于 \"%{path}\"" @@ -27,3 +35,11 @@ error_bucket_path_not_provided = """ error_bucket_path_not_directory = "路径 \"%{path}\" 不是一个目录!" error_bucket_nested = "无法在已有的桶内创建嵌套的桶目录" + +error_bind_index_not_provided = """ +[[b_red]]**错误:**[[/]]未提供桶绑定 +""" + +error_bind_index_not_found = """ +[[b_red]]**错误:**[[/]]未找到桶绑定:%{index} +""" diff --git a/rola-cli/src/bin/rola.rs b/rola-cli/src/bin/rola.rs index a201953..3e97468 100644 --- a/rola-cli/src/bin/rola.rs +++ b/rola-cli/src/bin/rola.rs @@ -1,13 +1,14 @@ use std::{env::current_dir, process::exit}; use mingling::{ - Program, + LazyInit, Program, macros::program_setup, - setup::{ExitCodeSetup, HelpFlagSetup, QuietFlagSetup}, + setup::{ExitCodeSetup, GeneralRendererSetup, HelpFlagSetup, QuietFlagSetup}, }; use rola_cli::{ - ThisProgram, locale, output::ColorOutputSetup, output::EnvLoggerSetup, - res::current_dir::ResCurrentDir, + ThisProgram, locale, + output::{ColorOutputSetup, EnvLoggerSetup}, + res::{bucket::ResBucketWithoutProtocol, current_dir::ResCurrentDir, overwrite::ResOverwrite}, }; fn main() { @@ -31,7 +32,14 @@ fn main() { cwd: current_dir().unwrap(), }); + let overwrite = program.pick_global_flag("--overwrite"); + program.with_resource(ResOverwrite { overwrite }); + + // LazyResources + program.with_resource(ResBucketWithoutProtocol::lazy_default()); + // Setup + program.with_setup(GeneralRendererSetup); program.with_setup(HelpFlagSetup::new(["-h", "--help"])); program.with_setup(StandardOutputSetup); program.with_setup(ExitCodeSetup::default()); diff --git a/rola-cli/src/bucket_mgr.rs b/rola-cli/src/bucket_mgr.rs index 9aa8847..82a76d4 100644 --- a/rola-cli/src/bucket_mgr.rs +++ b/rola-cli/src/bucket_mgr.rs @@ -1,2 +1,4 @@ mod creation; pub use creation::*; +mod bind; +pub use bind::*; diff --git a/rola-cli/src/bucket_mgr/bind.rs b/rola-cli/src/bucket_mgr/bind.rs new file mode 100644 index 0000000..48186a2 --- /dev/null +++ b/rola-cli/src/bucket_mgr/bind.rs @@ -0,0 +1,262 @@ +use colored::Colorize; +use mingling::{ + Groupped, LazyRes, + macros::{chain, dispatcher_clap, pack, pack_err, r_println, renderer}, + res::ResExitCode, +}; +use rorolala::bucket::{self, bind::BucketBind}; +use serde::Serialize; +use shared_functions::info; + +use crate::{ + Next, + bucket_mgr::ResultEnumBucketOperation::Set, + error::{ErrorRequireOverwrite, ErrorSpace}, + locale::I18nBucketManager, + output::display::markdown, + res::{bucket::ResBucketWithoutProtocol, overwrite::ResOverwrite}, +}; + +pub const EC_BUCKET_BIND_INDEX_NOT_PROVIDED: i32 = 251; +pub const EC_BUCKET_BIND_INDEX_NOT_FOUND: i32 = 252; + +#[derive(Default, clap::Parser, Serialize, Groupped)] +#[dispatcher_clap("bucket.bind", CMDBucketBind)] +pub struct EntryBucketBind { + index: Option, + + #[arg(long, conflicts_with_all = ["set"])] + remove: bool, + + #[arg(long, conflicts_with_all = ["remove"])] + set: Option, +} + +pack!(StateBucketBindListAll = ()); +pack!(StateBucketBindListSpecified = u8); +pack!(StateBucketBindRemove = u8); +pack!(StateBucketBindSet = (u8, String)); // idx, url + +#[derive(Default, Serialize, Groupped)] +pub struct ResultAllBucketBind { + bind_list: Vec, +} + +#[derive(Default, Serialize, Groupped)] +pub struct ResultOneBucketBind { + index: u8, + url: String, +} + +#[derive(Default, Serialize, Groupped)] +pub struct ResultBucketOperated { + pub operation: ResultEnumBucketOperation, + pub index: u8, + pub url: Option, +} + +#[derive(Default, Serialize)] +pub enum ResultEnumBucketOperation { + #[serde(rename = "none")] + #[default] + None, + #[serde(rename = "set_bind")] + Set, + #[serde(rename = "remove_bind")] + Remove, +} + +pack_err!(ErrorBucketBindIndexNotProvided); +pack_err!(ErrorBucketBindIndexNotFound = u8); + +// Chains + +#[chain] +pub fn handle_bucket_bind(args: EntryBucketBind) -> Next { + match args.index { + Some(op_idx) => { + if let Some(url) = args.set { + // bind idx --set url + return StateBucketBindSet::new((op_idx, url)).to_chain(); + } else if args.remove { + // bind idx --remove + return StateBucketBindRemove::new(op_idx).to_chain(); + } else { + // bind idx + return StateBucketBindListSpecified::new(op_idx).to_chain(); + } + } + None => { + if !args.remove && args.set.is_none() { + // bind + return StateBucketBindListAll::new(()).to_chain(); + } else { + // index not provided + // bind --set url + // or + // bind --remove + return ErrorBucketBindIndexNotProvided::default().to_render(); + } + } + } +} + +#[chain] +pub fn handle_state_bucket_bind_set( + set: StateBucketBindSet, + bucket: &mut LazyRes, + overwrite: &ResOverwrite, +) -> Next { + let (index, url) = set.inner; + let bucket_space_ref = &bucket.get_ref().space; + + let exist = match bucket::bind::check_bucket_bind_exists(bucket_space_ref, index) { + Ok(exists) => exists, + Err(e) => return ErrorSpace::from(e).to_chain(), + }; + + // When the bind already exists but overwrite is not specified + // Dispatch to ErrorRequireOverwrite + if exist && !overwrite.overwrite { + return ErrorRequireOverwrite::default().to_render(); + } + + if let Err(space_error) = + bucket::bind::write_bucket_bind(bucket_space_ref, index, url.clone().as_str()) + { + ErrorSpace::from(space_error).to_chain(); + } + + ResultBucketOperated { + operation: Set, + index, + url: Some(url), + } + .to_render() +} + +#[chain] +pub fn handle_state_bucket_bind_remove( + remove: StateBucketBindRemove, + bucket: &mut LazyRes, +) -> Next { + let index = remove.inner; + let bucket_space_ref = &bucket.get_ref().space; + + if let Err(space_error) = bucket::bind::remove_bucket_bind(bucket_space_ref, index) { + return ErrorSpace::from(space_error).to_chain(); + } + + ResultBucketOperated { + operation: ResultEnumBucketOperation::Remove, + index, + url: None, + } + .to_render() +} + +#[chain] +pub fn handle_state_bucket_bind_list_specified( + index: StateBucketBindListSpecified, + bucket: &mut LazyRes, +) -> Next { + let index = index.inner; + let bucket_space_ref = &bucket.get_ref().space; + let bind_info = match bucket::bind::read_bucket_bind(bucket_space_ref, index) { + Err(e) => return ErrorSpace::from(e).to_chain(), + Ok(None) => return ErrorBucketBindIndexNotFound::new(index).to_render(), + Ok(Some(r)) => r, + }; + + ResultOneBucketBind { + index: bind_info.get_index(), + url: bind_info.get_url().to_string(), + } + .to_render() +} + +#[chain] +pub fn handle_state_bucket_bind_list_all( + _p: StateBucketBindListAll, + bucket: &mut LazyRes, +) -> Next { + let bucket_space_ref = &bucket.get_ref().space; + info!("Reading all bucket binds from space"); + let bind_list = match bucket::bind::read_bucket_binds(bucket_space_ref) { + Err(e) => return ErrorSpace::from(e).to_chain(), + Ok(r) => r, + }; + info!("Read {} bucket binds", bind_list.len()); + ResultAllBucketBind { bind_list }.to_render() +} + +// Result Renderers + +#[renderer] +pub fn render_result_one_bucket_bind(result: ResultOneBucketBind) { + r_println!("BIND_{} => \"{}\"", result.index, result.url.trim().bold()); +} + +#[renderer] +pub fn render_result_all_bucket_bind(result: ResultAllBucketBind) { + let list: Vec = result + .bind_list + .iter() + .map(|b| { + format!( + "BIND_{} => \"{}\"", + b.get_index(), + b.get_url().trim().bold() + ) + }) + .collect(); + for item in list { + r_println!("{}", item); + } +} + +#[renderer] +pub fn render_result_bucket_operated(result: ResultBucketOperated) { + let index = result.index; + let url = result.url.unwrap_or("".to_string()); + match result.operation { + ResultEnumBucketOperation::Set => { + r_println!( + "{} BIND_{} => \"{}\"", + "+++".bold().bright_green(), + index, + url.bold().trim() + ) + } + ResultEnumBucketOperation::Remove => { + r_println!("{} BIND_{}", "---".bold().bright_red(), index) + } + _ => {} + } +} + +// Error Renderers + +#[renderer] +pub fn render_error_bucket_bind_index_not_provided( + _err: ErrorBucketBindIndexNotProvided, + ec: &mut ResExitCode, +) { + r_println!( + "{}", + markdown(I18nBucketManager::error_bind_index_not_provided().trim()) + ); + ec.exit_code = EC_BUCKET_BIND_INDEX_NOT_PROVIDED; +} + +#[renderer] +pub fn render_error_bucket_bind_index_not_found( + err: ErrorBucketBindIndexNotFound, + ec: &mut ResExitCode, +) { + r_println!( + "{}", + markdown(I18nBucketManager::error_bind_index_not_found(err.info.to_string()).trim()) + ); + ec.exit_code = EC_BUCKET_BIND_INDEX_NOT_FOUND; +} diff --git a/rola-cli/src/bucket_mgr/creation.rs b/rola-cli/src/bucket_mgr/creation.rs index c363773..2394228 100644 --- a/rola-cli/src/bucket_mgr/creation.rs +++ b/rola-cli/src/bucket_mgr/creation.rs @@ -1,16 +1,16 @@ use std::{fs::create_dir_all, path::PathBuf}; use mingling::{ - macros::{chain, dispatcher, pack, r_println, renderer, route}, + Groupped, + macros::{chain, dispatcher, pack, pack_err, r_println, renderer, route}, parser::AsPicker, res::ResExitCode, }; use rorolala::bucket::{Bucket, NoProtocol}; +use serde::Serialize; use space_system::{Space, SpaceError, SpaceRoot, find_space_root_with}; -use crate::{ - Next, error::ErrorIo, locale::I18nBucketManager, res::current_dir::ResCurrentDir, tkr, -}; +use crate::{Next, error::ErrorIo, locale::I18nBucketManager, res::current_dir::ResCurrentDir}; pub const EC_BUCKET_CREATE_DIR_NOT_EMPTY: i32 = 2400; pub const EC_BUCKET_PATH_NOT_PROVIDED: i32 = 2401; @@ -23,12 +23,15 @@ dispatcher!("bucket.create"); pack!(StateBucketCreationPrecheck = PathBuf); pack!(StateBucketCreation = PathBuf); -pack!(ResultBucketCreated = PathBuf); +#[derive(Debug, Groupped, Serialize)] +pub struct ResultBucketCreated { + bucket_path: PathBuf, +} -pack!(ErrorDirectoryNotEmpty = PathBuf); -pack!(ErrorBucketPathNotProvided = ()); -pack!(ErrorBucketPathNotDirectory = PathBuf); -pack!(ErrorBucketPathNested = PathBuf); +pack_err!(ErrorDirectoryNotEmpty = PathBuf); +pack_err!(ErrorBucketPathNotProvided = ()); +pack_err!(ErrorBucketPathNotDirectory = PathBuf); +pack_err!(ErrorBucketPathNested = PathBuf); #[chain] pub fn handle_bucket_init(_args: EntryBucketInit, cwd: &mut ResCurrentDir) -> Next { @@ -86,16 +89,16 @@ pub fn handle_state_bucket_creation(create: StateBucketCreation) -> Next { // Initialize the Space and capture any SpaceError::Io let path_to_init = path.clone(); - if let Err(SpaceError::Io(error)) = tkr! { bucket_space.init(path_to_init).await } { + if let Err(SpaceError::Io(error)) = bucket_space.init(path_to_init) { return ErrorIo::from(error).to_render(); } - ResultBucketCreated::new(path).to_render() + ResultBucketCreated { bucket_path: path }.to_render() } #[renderer] pub fn render_result_bucket_created(result: ResultBucketCreated) { - let path = result.inner.to_string_lossy(); + let path = result.bucket_path.to_string_lossy(); r_println!("{}", I18nBucketManager::created(path)); } diff --git a/rola-cli/src/error.rs b/rola-cli/src/error.rs index 608d4e1..15bdc70 100644 --- a/rola-cli/src/error.rs +++ b/rola-cli/src/error.rs @@ -1,2 +1,6 @@ mod io; pub use io::*; +mod space; +pub use space::*; +mod require_overwrite; +pub use require_overwrite::*; diff --git a/rola-cli/src/error/io.rs b/rola-cli/src/error/io.rs index d65b765..7f31824 100644 --- a/rola-cli/src/error/io.rs +++ b/rola-cli/src/error/io.rs @@ -3,6 +3,7 @@ use mingling::{ macros::{r_println, renderer}, res::ResExitCode, }; +use serde::Serialize; use crate::locale::errors::I18nIoError; @@ -46,7 +47,7 @@ pub const EC_IOERR_UNEXPECTED_EOF: i32 = 2536; pub const EC_IOERR_OUT_OF_MEMORY: i32 = 2537; pub const EC_IOERR_OTHER: i32 = 2538; -#[derive(Default, Groupped)] +#[derive(Default, Serialize, Groupped)] pub enum ErrorIo { #[default] /// DONT USE IT: This variant is only used to provide a Default derive for ErrorIo @@ -54,7 +55,14 @@ pub enum ErrorIo { /// In normal creation flow, you should directly use ErrorIo::from(/* std::io::Error */) DontUse, - Error(std::io::Error), + Error(#[serde(serialize_with = "serialize_io_error")] std::io::Error), +} + +fn serialize_io_error( + err: &std::io::Error, + serializer: S, +) -> Result { + serializer.serialize_str(&format!("{:?}", err)) } #[renderer] diff --git a/rola-cli/src/error/require_overwrite.rs b/rola-cli/src/error/require_overwrite.rs new file mode 100644 index 0000000..c84dcb1 --- /dev/null +++ b/rola-cli/src/error/require_overwrite.rs @@ -0,0 +1,19 @@ +use mingling::{ + macros::{pack_err, r_println, renderer}, + res::ResExitCode, +}; + +use crate::{locale, output::display::markdown}; + +pub const EC_REQUIRE_OVERWRITE: i32 = 1001; + +pack_err!(ErrorRequireOverwrite); + +#[renderer] +pub fn render_error_require_overwrite(_err: ErrorRequireOverwrite, ec: &mut ResExitCode) { + r_println!( + "{}", + markdown(locale::errors::Common::require_overwrite().trim()) + ); + ec.exit_code = EC_REQUIRE_OVERWRITE; +} diff --git a/rola-cli/src/error/space.rs b/rola-cli/src/error/space.rs new file mode 100644 index 0000000..fb0a560 --- /dev/null +++ b/rola-cli/src/error/space.rs @@ -0,0 +1,64 @@ +use mingling::{ + Groupped, + macros::{chain, r_println, renderer}, + res::ResExitCode, +}; +use serde::Serialize; +use space_system::SpaceError; + +use crate::{Next, error::ErrorIo, locale::errors::I18nSpaceError}; + +pub const EC_SPACE_NOT_FOUND: i32 = 2600; +pub const EC_SPACE_PATH_FORMAT: i32 = 2601; +pub const EC_SPACE_REQUIRE_EMPTY_DIR: i32 = 2602; +pub const EC_SPACE_CONFIG_ALREADY_EXIST: i32 = 2603; + +#[derive(Serialize, Groupped)] +pub struct ErrorSpace { + pub error: SpaceError, +} + +#[chain] +pub fn handle_error_space(err: ErrorSpace) -> Next { + match err.error { + // Forward to ErrorIo + SpaceError::Io(error) => ErrorIo::from(error).to_render(), + + _ => err.to_render(), + } +} + +#[renderer] +pub fn render_error_space(err: ErrorSpace, ec: &mut ResExitCode) { + match &err.error { + SpaceError::SpaceNotFound => { + r_println!("{}", I18nSpaceError::space_not_found().trim()); + ec.exit_code = EC_SPACE_NOT_FOUND; + } + SpaceError::PathFormatError(_) => { + r_println!("{}", I18nSpaceError::path_format().trim()); + ec.exit_code = EC_SPACE_PATH_FORMAT; + } + SpaceError::RequireEmptyDirectory => { + r_println!("{}", I18nSpaceError::require_empty_directory().trim()); + ec.exit_code = EC_SPACE_REQUIRE_EMPTY_DIR; + } + SpaceError::ConfigFileAlreadyExist => { + r_println!("{}", I18nSpaceError::config_file_already_exist().trim()); + ec.exit_code = EC_SPACE_CONFIG_ALREADY_EXIST; + } + SpaceError::Io(_) => { + // Forwarded to ErrorIo via handle_error_space chain + } + SpaceError::Other(_) => { + r_println!("{}", I18nSpaceError::space_not_found().trim()); + ec.exit_code = EC_SPACE_NOT_FOUND; + } + } +} + +impl From for ErrorSpace { + fn from(error: SpaceError) -> Self { + Self { error } + } +} diff --git a/rola-cli/src/lib.rs b/rola-cli/src/lib.rs index b3587e2..b472169 100644 --- a/rola-cli/src/lib.rs +++ b/rola-cli/src/lib.rs @@ -1,6 +1,9 @@ use std::process::exit; -use mingling::macros::{gen_program, help}; +use mingling::{ + macros::{gen_program, help, r_println, renderer}, + res::ResExitCode, +}; pub mod output; pub mod res; @@ -14,8 +17,10 @@ use error::*; use crate::output::display::markdown; +pub const EC_COMMAND_NOT_FOUND: i32 = 1000; + #[help] -fn handle_error_dispatch_not_found(_err: ErrorDispatcherNotFound) { +fn help_global(_err: ErrorDispatcherNotFound) { let help = locale::helps::Basic::help().trim(); // Print directly to stderr and exit with code 0 @@ -23,6 +28,18 @@ fn handle_error_dispatch_not_found(_err: ErrorDispatcherNotFound) { exit(0) } +#[renderer] +fn render_error_dispatch_not_found(err: ErrorDispatcherNotFound, ec: &mut ResExitCode) { + if !err.inner.is_empty() { + let cmd_str = err.inner.join(" "); + r_println!( + "{}", + markdown(locale::errors::Common::command_not_found(cmd_str).trim()) + ); + ec.exit_code = EC_COMMAND_NOT_FOUND; + } +} + gen_program!(); pub mod locale { diff --git a/rola-cli/src/output/setup.rs b/rola-cli/src/output/setup.rs index 824348b..880b236 100644 --- a/rola-cli/src/output/setup.rs +++ b/rola-cli/src/output/setup.rs @@ -1,4 +1,4 @@ -use mingling::{Program, macros::program_setup}; +use mingling::{Program, hook::ProgramHook, macros::program_setup}; use shared_functions::info; use crate::{ @@ -35,6 +35,20 @@ pub fn env_logger_setup(program: &mut Program) { _ => log::Level::Info, }, }); + + // Add Hook + program.with_hook( + ProgramHook::::empty() + .on_begin(|| info!("[INFO] Program is begin")) + .on_pre_dispatch(|args| info!("[INFO] Pre dispatch: {args:?}")) + .on_post_dispatch(|c: &_| info!("[INFO] Post dispatch: {c:?}")) + .on_pre_chain(|c: &_, _| { + info!("[INFO] Pre chain: {c}"); + }) + .on_post_chain(|any_output| info!("[INFO] Post chain: {}", any_output.member_id)) + .on_pre_render(|c: &_, _| info!("[INFO] Pre render: {c}")) + .on_post_render(|_| info!("[INFO] Post render")), + ); } info!("Verbose mode enabled!"); diff --git a/rola-cli/src/res.rs b/rola-cli/src/res.rs index 2ff9b75..f85a889 100644 --- a/rola-cli/src/res.rs +++ b/rola-cli/src/res.rs @@ -1 +1,3 @@ +pub mod bucket; pub mod current_dir; +pub mod overwrite; diff --git a/rola-cli/src/res/bucket.rs b/rola-cli/src/res/bucket.rs new file mode 100644 index 0000000..16b8fc9 --- /dev/null +++ b/rola-cli/src/res/bucket.rs @@ -0,0 +1,25 @@ +use std::env::current_dir; + +use rorolala::bucket::{Bucket, NoProtocol}; +use space_system::Space; + +/// A resource holding a local filesystem bucket without a protocol. +/// +/// This struct wraps a [`Space>`] that provides access to a +/// local filesystem bucket. It automatically initializes the bucket's current +/// directory from the [`ResCurrentDir`] resource injected into [`ThisProgram`]. +#[derive(Clone)] +pub struct ResBucketWithoutProtocol { + /// The space containing the protocol-less local bucket. + pub space: Space>, +} + +impl Default for ResBucketWithoutProtocol { + fn default() -> Self { + let current_dir = current_dir().unwrap(); + let mut space = Space::new(Bucket::::new_local()); + space.set_current_dir(current_dir).unwrap(); + + Self { space } + } +} diff --git a/rola-cli/src/res/overwrite.rs b/rola-cli/src/res/overwrite.rs new file mode 100644 index 0000000..cef0932 --- /dev/null +++ b/rola-cli/src/res/overwrite.rs @@ -0,0 +1,9 @@ +/// A flag indicating whether to overwrite existing resources. +/// +/// This struct encapsulates a boolean value that indicates whether to overwrite +/// existing files or data during resource processing. +#[derive(Debug, Default, Clone)] +pub struct ResOverwrite { + /// Boolean flag, `true` means overwrite is allowed, `false` means it is not. + pub overwrite: bool, +} -- cgit