summaryrefslogtreecommitdiff
path: root/rola-cli
diff options
context:
space:
mode:
Diffstat (limited to 'rola-cli')
-rw-r--r--rola-cli/Cargo.toml10
-rw-r--r--rola-cli/locales/errors/common.toml25
-rw-r--r--rola-cli/locales/errors/i18n_space_error.toml23
-rw-r--r--rola-cli/locales/i18n_bucket_manager.toml16
-rw-r--r--rola-cli/src/bin/rola.rs16
-rw-r--r--rola-cli/src/bucket_mgr.rs2
-rw-r--r--rola-cli/src/bucket_mgr/bind.rs262
-rw-r--r--rola-cli/src/bucket_mgr/creation.rs27
-rw-r--r--rola-cli/src/error.rs4
-rw-r--r--rola-cli/src/error/io.rs12
-rw-r--r--rola-cli/src/error/require_overwrite.rs19
-rw-r--r--rola-cli/src/error/space.rs64
-rw-r--r--rola-cli/src/lib.rs21
-rw-r--r--rola-cli/src/output/setup.rs16
-rw-r--r--rola-cli/src/res.rs2
-rw-r--r--rola-cli/src/res/bucket.rs25
-rw-r--r--rola-cli/src/res/overwrite.rs9
17 files changed, 529 insertions, 24 deletions
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<u8>,
+
+ #[arg(long, conflicts_with_all = ["set"])]
+ remove: bool,
+
+ #[arg(long, conflicts_with_all = ["remove"])]
+ set: Option<String>,
+}
+
+pack!(StateBucketBindListAll = ());
+pack!(StateBucketBindListSpecified = u8);
+pack!(StateBucketBindRemove = u8);
+pack!(StateBucketBindSet = (u8, String)); // idx, url
+
+#[derive(Default, Serialize, Groupped)]
+pub struct ResultAllBucketBind {
+ bind_list: Vec<BucketBind>,
+}
+
+#[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<String>,
+}
+
+#[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<ResBucketWithoutProtocol>,
+ 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<ResBucketWithoutProtocol>,
+) -> 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<ResBucketWithoutProtocol>,
+) -> 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<ResBucketWithoutProtocol>,
+) -> 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<String> = 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<S: serde::Serializer>(
+ err: &std::io::Error,
+ serializer: S,
+) -> Result<S::Ok, S::Error> {
+ 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<SpaceError> 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<ThisProgram>) {
_ => log::Level::Info,
},
});
+
+ // Add Hook
+ program.with_hook(
+ ProgramHook::<ThisProgram>::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<Bucket<NoProtocol>>`] 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<Bucket<NoProtocol>>,
+}
+
+impl Default for ResBucketWithoutProtocol {
+ fn default() -> Self {
+ let current_dir = current_dir().unwrap();
+ let mut space = Space::new(Bucket::<NoProtocol>::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,
+}