summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--resources/locales/jvn/en.yml247
-rw-r--r--resources/locales/jvn/zh-CN.yml247
-rw-r--r--src/bin/jvn.rs184
-rw-r--r--src/lib.rs3
-rw-r--r--src/macros.rs1
-rw-r--r--src/macros/command_flag.rs33
-rw-r--r--src/systems.rs1
-rw-r--r--src/systems/cmd/cmd_system.rs83
-rw-r--r--src/systems/cmd/processer.rs31
-rw-r--r--src/systems/debug.rs1
-rw-r--r--src/systems/debug/verbose_logger.rs43
-rw-r--r--templates/_override_renderer_dispatcher.rs.template4
14 files changed, 791 insertions, 89 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7333df6..3a61e3d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1140,6 +1140,7 @@ dependencies = [
"cmd_system_macros",
"colored",
"crossterm",
+ "env_logger",
"erased-serde",
"just_enough_vcs",
"just_fmt",
diff --git a/Cargo.toml b/Cargo.toml
index 5c078ad..8e11a3f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -76,6 +76,7 @@ chrono = "0.4"
# Logging
log = "0.4"
+env_logger = "0.11"
# Async
tokio = { version = "1", features = ["full"] }
diff --git a/resources/locales/jvn/en.yml b/resources/locales/jvn/en.yml
index 801c735..a311eb3 100644
--- a/resources/locales/jvn/en.yml
+++ b/resources/locales/jvn/en.yml
@@ -45,7 +45,7 @@ process_error:
prepare_error:
io: |
I/O error in preparation phase!
- Error: %{error}
+ %{error}
error: |
Unknown error in preparation phase!
@@ -85,7 +85,7 @@ prepare_error:
execute_error:
io: |
I/O error in execution phase!
- Error: %{error}
+ %{error}
error: |
Error in execution phase!
@@ -94,7 +94,7 @@ execute_error:
render_error:
io: |
I/O error in rendering phase!
- Error: %{error}
+ %{error}
error: |
Error in rendering phase!
@@ -113,3 +113,244 @@ render_error:
Please use `jv -v -C` to get detailed version traceback and contact the developers.
github: https://github.com/JustEnoughVCS/CommandLine/
+
+io_error:
+ not_found: |
+ File or directory not found!
+ Please check if the path is correct or if the file exists.
+ %{raw_error}
+
+ permission_denied: |
+ Permission denied!
+ Please check if you have sufficient permissions to access the file or directory.
+ %{raw_error}
+
+ connection_refused: |
+ Connection refused!
+ Please check if the target address and port are correct and if the service is running.
+ %{raw_error}
+
+ connection_reset: |
+ Connection reset!
+ The network connection was unexpectedly interrupted during transmission.
+ %{raw_error}
+
+ host_unreachable: |
+ Host unreachable!
+ Please check your network connection and the status of the target host.
+ %{raw_error}
+
+ network_unreachable: |
+ Network unreachable!
+ Please check your network connection and routing settings.
+ %{raw_error}
+
+ connection_aborted: |
+ Connection aborted!
+ The network connection was interrupted during establishment.
+ %{raw_error}
+
+ not_connected: |
+ Not connected to network!
+ Please check your network connection status.
+ %{raw_error}
+
+ addr_in_use: |
+ Address already in use!
+ Please try a different address or port.
+ %{raw_error}
+
+ addr_not_available: |
+ Address not available!
+ Please check if the address configuration is correct.
+ %{raw_error}
+
+ network_down: |
+ Network is down!
+ Please check your network connection.
+ %{raw_error}
+
+ broken_pipe: |
+ Broken pipe!
+ The communication pipe was interrupted during transmission.
+ %{raw_error}
+
+ already_exists: |
+ File or directory already exists!
+ Please try a different name or delete the existing file.
+ %{raw_error}
+
+ would_block: |
+ Operation would block!
+ The non-blocking operation could not be completed immediately.
+ %{raw_error}
+
+ not_a_directory: |
+ Not a directory!
+ Please check if the path points to a directory.
+ %{raw_error}
+
+ is_a_directory: |
+ Is a directory!
+ Please check if the path points to a file.
+ %{raw_error}
+
+ directory_not_empty: |
+ Directory not empty!
+ Please empty the directory contents first.
+ %{raw_error}
+
+ read_only_filesystem: |
+ Read-only filesystem!
+ Cannot write to a read-only filesystem.
+ %{raw_error}
+
+ stale_network_file_handle: |
+ Stale network file handle!
+ Please re-establish the connection.
+ %{raw_error}
+
+ invalid_input: |
+ Invalid input!
+ Please check if the input parameters are correct.
+ %{raw_error}
+
+ invalid_data: |
+ Invalid data!
+ Please check the data format and content.
+ %{raw_error}
+
+ timed_out: |
+ Operation timed out!
+ Please check your network connection or retry the operation.
+ %{raw_error}
+
+ write_zero: |
+ Write zero bytes!
+ The write operation did not write any data.
+ %{raw_error}
+
+ storage_full: |
+ Storage full!
+ Please free up disk space.
+ %{raw_error}
+
+ not_seekable: |
+ File not seekable!
+ This file does not support seek operations.
+ %{raw_error}
+
+ quota_exceeded: |
+ Quota exceeded!
+ Please check your disk quota settings.
+ %{raw_error}
+
+ file_too_large: |
+ File too large!
+ Please check the file size limit.
+ %{raw_error}
+
+ resource_busy: |
+ Resource busy!
+ Please try again later or release the occupied resource.
+ %{raw_error}
+
+ executable_file_busy: |
+ Executable file busy!
+ Please wait for the program to finish running.
+ %{raw_error}
+
+ deadlock: |
+ Deadlock!
+ Multiple operations are waiting for each other, preventing further execution.
+ %{raw_error}
+
+ crosses_devices: |
+ Cross-device operation!
+ The operation involves multiple devices, which is not supported.
+ %{raw_error}
+
+ too_many_links: |
+ Too many links!
+ The number of filesystem links exceeds the limit.
+ %{raw_error}
+
+ invalid_filename: |
+ Invalid filename!
+ Please check if the filename meets system requirements.
+ %{raw_error}
+
+ argument_list_too_long: |
+ Argument list too long!
+ Please reduce the number or length of arguments.
+ %{raw_error}
+
+ interrupted: |
+ Operation interrupted!
+ The operation was interrupted by a signal. Please retry.
+ %{raw_error}
+
+ unsupported: |
+ Operation not supported!
+ This operation is not supported by the current system or environment.
+ %{raw_error}
+
+ unexpected_eof: |
+ Unexpected end of file!
+ Encountered an unexpected end-of-file marker while reading.
+ %{raw_error}
+
+ out_of_memory: |
+ Out of memory!
+ System memory is insufficient to complete the operation.
+ %{raw_error}
+
+ other: |
+ %{error}
+
+logger:
+ error: |
+ ERROR
+ warn: |
+ WARN
+ info: |
+ INFO
+ debug: |
+ DEBUG
+ trace: |
+ TRACE
+
+verbose:
+ setup_verbose: Starting `Verbose` mode
+ setup_i18n: Setting language to `%{lang}`
+ setup_renderer: Setting renderer to `%{renderer}`
+ no_error_logs: Disabling error output
+ help: Help flag entered, command will not be executed
+ confirmed: Confirmation flag entered, all commands will run without confirmation
+
+ no_arguments: No command entered! Will output help information
+
+ user_input: User input command `%{command}`
+
+ process_success: Command executed successfully!
+ process_fail: Command execution failed!
+
+ print_render_result: Result has been rendered
+
+ cmd_process_start: Starting to search nodes to match command ...
+ cmd_match_no_node: Unable to find any matching command node
+ cmd_match_matched_single: Found matching command node `%{node}`
+ cmd_match_matched_multi: Matched multiple command nodes `%{nodes}`, selecting `${node} (most relevant)`
+
+ cmd_process: Command execution starting!
+ render_with_specific_renderer: Result will be rendered with the default renderer
+ render_with_override_renderer: Result will be rendered with the override renderer (%{renderer})
+
+ cmd_process_parse: Command will be converted to `%{t}`
+ cmd_process_parse_failed: Failed to convert command to `%{t}`!
+
+ cmd_process_prepare: Entering preparation phase, converting to `%{i}` and `%{c}` in parallel
+ cmd_process_prepare_failed: Preparation phase failed!
+
+ cmd_process_exec: Entering execution phase
+ cmd_process_exec_failed: Execution phase failed!
diff --git a/resources/locales/jvn/zh-CN.yml b/resources/locales/jvn/zh-CN.yml
index 71953a9..7d2b52b 100644
--- a/resources/locales/jvn/zh-CN.yml
+++ b/resources/locales/jvn/zh-CN.yml
@@ -44,7 +44,7 @@ process_error:
prepare_error:
io: |
命令在准备阶段发生了 I/O 错误!
- 错误信息:%{error}
+ %{error}
error: |
命令在准备阶段发生未知错误!
@@ -83,7 +83,7 @@ prepare_error:
execute_error:
io: |
命令在运行阶段发生了 I/O 错误!
- 错误信息:%{error}
+ %{error}
error: |
命令在运行阶段发生错误!
@@ -92,7 +92,7 @@ execute_error:
render_error:
io: |
命令在渲染阶段发生了 I/O 错误!
- 错误信息:%{error}
+ %{error}
error: |
命令在渲染阶段发生错误!
@@ -111,3 +111,244 @@ render_error:
请使用 `jv -v -C` 获得详细的版本追溯,并联系开发人员
github: https://github.com/JustEnoughVCS/CommandLine/
+
+io_error:
+ not_found: |
+ 找不到文件或目录!
+ 请检查路径是否正确,或文件是否存在
+ %{raw_error}
+
+ permission_denied: |
+ 权限被拒绝!
+ 请检查您是否有足够的权限访问该文件或目录
+ %{raw_error}
+
+ connection_refused: |
+ 连接被拒绝!
+ 请检查目标地址和端口是否正确,以及服务是否正在运行
+ %{raw_error}
+
+ connection_reset: |
+ 连接被重置!
+ 网络连接在传输过程中被意外中断
+ %{raw_error}
+
+ host_unreachable: |
+ 主机不可达!
+ 请检查网络连接和目标主机状态
+ %{raw_error}
+
+ network_unreachable: |
+ 网络不可达!
+ 请检查网络连接和路由设置
+ %{raw_error}
+
+ connection_aborted: |
+ 连接被中止!
+ 网络连接在建立过程中被中断
+ %{raw_error}
+
+ not_connected: |
+ 未连接到网络!
+ 请检查网络连接状态
+ %{raw_error}
+
+ addr_in_use: |
+ 地址已被占用!
+ 请尝试使用其他地址或端口
+ %{raw_error}
+
+ addr_not_available: |
+ 地址不可用!
+ 请检查地址配置是否正确
+ %{raw_error}
+
+ network_down: |
+ 网络已断开!
+ 请检查网络连接
+ %{raw_error}
+
+ broken_pipe: |
+ 管道已损坏!
+ 通信管道在传输过程中被中断
+ %{raw_error}
+
+ already_exists: |
+ 文件或目录已存在!
+ 请尝试使用其他名称或删除现有文件
+ %{raw_error}
+
+ would_block: |
+ 操作将被阻塞!
+ 非阻塞操作无法立即完成
+ %{raw_error}
+
+ not_a_directory: |
+ 不是目录!
+ 请检查路径是否指向一个目录
+ %{raw_error}
+
+ is_a_directory: |
+ 是一个目录!
+ 请检查路径是否指向一个文件
+ %{raw_error}
+
+ directory_not_empty: |
+ 目录非空!
+ 请先清空目录内容
+ %{raw_error}
+
+ read_only_filesystem: |
+ 文件系统只读!
+ 无法写入只读文件系统
+ %{raw_error}
+
+ stale_network_file_handle: |
+ 网络文件句柄已失效!
+ 请重新建立连接
+ %{raw_error}
+
+ invalid_input: |
+ 输入无效!
+ 请检查输入参数是否正确
+ %{raw_error}
+
+ invalid_data: |
+ 数据无效!
+ 请检查数据格式和内容
+ %{raw_error}
+
+ timed_out: |
+ 操作超时!
+ 请检查网络连接或重试操作
+ %{raw_error}
+
+ write_zero: |
+ 写入零字节!
+ 写入操作未写入任何数据
+ %{raw_error}
+
+ storage_full: |
+ 存储空间已满!
+ 请清理磁盘空间
+ %{raw_error}
+
+ not_seekable: |
+ 文件不可寻址!
+ 该文件不支持寻址操作
+ %{raw_error}
+
+ quota_exceeded: |
+ 超出配额限制!
+ 请检查磁盘配额设置
+ %{raw_error}
+
+ file_too_large: |
+ 文件过大!
+ 请检查文件大小限制
+ %{raw_error}
+
+ resource_busy: |
+ 资源正忙!
+ 请稍后重试或释放占用资源
+ %{raw_error}
+
+ executable_file_busy: |
+ 可执行文件正忙!
+ 请等待程序结束运行
+ %{raw_error}
+
+ deadlock: |
+ 死锁!
+ 多个操作相互等待导致无法继续执行
+ %{raw_error}
+
+ crosses_devices: |
+ 跨设备操作!
+ 操作涉及多个设备,不支持此类操作
+ %{raw_error}
+
+ too_many_links: |
+ 链接过多!
+ 文件系统链接数量超出限制
+ %{raw_error}
+
+ invalid_filename: |
+ 文件名无效!
+ 请检查文件名是否符合系统要求
+ %{raw_error}
+
+ argument_list_too_long: |
+ 参数列表过长!
+ 请减少参数数量或长度
+ %{raw_error}
+
+ interrupted: |
+ 操作被中断!
+ 操作被信号中断,请重试
+ %{raw_error}
+
+ unsupported: |
+ 操作不支持!
+ 当前系统或环境不支持此操作
+ %{raw_error}
+
+ unexpected_eof: |
+ 意外的文件结束!
+ 在读取过程中遇到意外的文件结束符
+ %{raw_error}
+
+ out_of_memory: |
+ 内存不足!
+ 系统内存不足,无法完成操作
+ %{raw_error}
+
+ other: |
+ %{error}
+
+logger:
+ error: |
+ 错误
+ warn: |
+ 警告
+ info: |
+ 信息
+ debug: |
+ 调试
+ trace: |
+ 追踪
+
+verbose:
+ setup_verbose: 启动 `Verbose` 模式
+ setup_i18n: 设置语言为 `%{lang}`
+ setup_renderer: 设置渲染器为 `%{renderer}`
+ no_error_logs: 禁用错误输出
+ help: 帮助符号已输入,将不会执行命令
+ confirmed: 确认符号已输入,所有命令将无需确认即可运行
+
+ no_arguments: 无命令输入!将输出帮助信息
+
+ user_input: 用户输入命令:%{command}
+
+ process_success: 命令执行成功!
+ process_fail: 命令执行失败!
+
+ print_render_result: 结果已渲染完成:
+
+ cmd_process_start: 开始搜寻节点以匹配命令 ...
+ cmd_match_no_node: 无法找到任何相匹配的命令节点
+ cmd_match_matched_single: 找到相匹配的命令节点 `%{node}`
+ cmd_match_matched_multi: 匹配多个命令节点 `%{nodes}`,选择 `${node} (最相关)`
+
+ cmd_process: 命令开始执行!
+ render_with_specific_renderer: 结果将用默认渲染器呈现
+ render_with_override_renderer: 结果将用覆盖渲染器 (%{renderer}) 呈现
+
+ cmd_process_parse: 命令将转换为 `%{t}`
+ cmd_process_parse_failed: 命令转换为 `%{t}` 失败!
+
+ cmd_process_prepare: 进入准备阶段,并行转换为 `%{i}` 和 `%{c}`
+ cmd_process_prepare_failed: 准备阶段失败!
+
+ cmd_process_exec: 进入执行阶段
+ cmd_process_exec_failed: 执行阶段失败!
diff --git a/src/bin/jvn.rs b/src/bin/jvn.rs
index 7ec1f4d..aa438d0 100644
--- a/src/bin/jvn.rs
+++ b/src/bin/jvn.rs
@@ -1,76 +1,87 @@
use std::process::exit;
-use cli_utils::display::md;
-use cli_utils::env::current_locales;
-use cli_utils::levenshtein_distance::levenshtein_distance;
-use just_enough_vcs_cli::systems::cmd::_commands::jv_cmd_nodes;
-use just_enough_vcs_cli::systems::cmd::cmd_system::JVCommandContext;
-use just_enough_vcs_cli::systems::cmd::errors::{CmdExecuteError, CmdPrepareError, CmdRenderError};
-use just_enough_vcs_cli::systems::cmd::{errors::CmdProcessError, processer::jv_cmd_process};
+use cli_utils::{display::md, env::current_locales, levenshtein_distance::levenshtein_distance};
+use just_enough_vcs_cli::{
+ special_argument, special_flag,
+ systems::{
+ cmd::{
+ _commands::jv_cmd_nodes,
+ cmd_system::JVCommandContext,
+ errors::{CmdExecuteError, CmdPrepareError, CmdProcessError, CmdRenderError},
+ processer::jv_cmd_process,
+ },
+ debug::verbose_logger::init_verbose_logger,
+ },
+};
+use log::{LevelFilter, error, info, trace, warn};
use rust_i18n::{set_locale, t};
rust_i18n::i18n!("resources/locales/jvn", fallback = "en");
-macro_rules! special_flag {
- ($args:expr, $flag:expr) => {{
- let flag = $flag;
- let found = $args.iter().any(|arg| arg == flag);
- $args.retain(|arg| arg != flag);
- found
- }};
-}
-
-macro_rules! special_argument {
- ($args:expr, $flag:expr) => {{
- let flag = $flag;
- let mut value: Option<String> = None;
- let mut i = 0;
- while i < $args.len() {
- if $args[i] == flag {
- if i + 1 < $args.len() {
- value = Some($args[i + 1].clone());
- $args.remove(i + 1);
- $args.remove(i);
- } else {
- value = None;
- $args.remove(i);
- }
- break;
- }
- i += 1;
- }
- value
- }};
-}
-
#[tokio::main]
async fn main() {
// Collect arguments
let mut args: Vec<String> = std::env::args().skip(1).collect();
- // Init i18n
+ // Init colored
+ #[cfg(windows)]
+ colored::control::set_virtual_terminal(true).unwrap();
+
+ // Output control flags
+ let quiet = special_flag!(args, "--quiet") || special_flag!(args, "-q");
+ let verbose = special_flag!(args, "--verbose") || special_flag!(args, "-V");
+ let verbose_full = special_flag!(args, "--verbose-full");
+
+ // If `--verbose` or `--verbose-full` is enabled and `--quiet` is not enabled, turn on the logger
+ let filter = if (verbose || verbose_full) && !quiet {
+ let filter = if verbose_full {
+ LevelFilter::Trace
+ } else {
+ LevelFilter::Info
+ };
+ Some(filter)
+ } else {
+ None
+ };
+ init_verbose_logger(filter);
+ trace!("{}", t!("verbose.setup_verbose"));
+
+ // I18n flags
let lang = special_argument!(args, "--lang").unwrap_or(current_locales());
set_locale(&lang);
+ trace!("{}", t!("verbose.setup_i18n", lang = lang));
// Renderer
let renderer_override = special_argument!(args, "--renderer").unwrap_or("default".to_string());
+ trace!(
+ "{}",
+ t!("verbose.setup_renderer", renderer = renderer_override)
+ );
// Other flags
let no_error_logs = special_flag!(args, "--no-error-logs");
- let quiet = special_flag!(args, "--quiet") || special_flag!(args, "-q");
let help = special_flag!(args, "--help") || special_flag!(args, "-h");
let confirmed = special_flag!(args, "--confirm") || special_flag!(args, "-C");
- // Init colored
- #[cfg(windows)]
- colored::control::set_virtual_terminal(true).unwrap();
+ if no_error_logs {
+ trace!("{}", t!("verbose.no_error_logs"));
+ }
+ if help {
+ trace!("{}", t!("verbose.help"));
+ }
+ if confirmed {
+ trace!("{}", t!("verbose.confirmed"));
+ }
// Handle help when no arguments provided
if args.len() < 1 && help {
+ warn!("{}", t!("verbose.no_arguments"));
eprintln!("{}", md(t!("help")));
exit(1);
}
+ info!("{}", t!("verbose.user_input", command = args.join(" ")));
+
// Process commands
let render_result = match jv_cmd_process(
&args,
@@ -79,8 +90,12 @@ async fn main() {
)
.await
{
- Ok(result) => result,
+ Ok(result) => {
+ info!("{}", t!("verbose.process_success"));
+ result
+ }
Err(e) => {
+ error!("{}", t!("verbose.process_fail"));
if !no_error_logs {
match e {
CmdProcessError::Prepare(cmd_prepare_error) => {
@@ -125,6 +140,7 @@ async fn main() {
// Print
if !quiet {
+ info!("{}", t!("verbose.print_render_result"));
print!("{}", render_result);
}
}
@@ -159,7 +175,10 @@ fn handle_no_matching_command_error(args: Vec<String>) {
fn handle_prepare_error(cmd_prepare_error: CmdPrepareError) {
match cmd_prepare_error {
CmdPrepareError::Io(error) => {
- eprintln!("{}", md(t!("prepare_error.io", error = error.to_string())));
+ eprintln!(
+ "{}",
+ md(t!("prepare_error.io", error = display_io_error(error)))
+ );
}
CmdPrepareError::Error(msg) => {
eprintln!("{}", md(t!("prepare_error.error", error = msg)));
@@ -213,7 +232,10 @@ fn handle_prepare_error(cmd_prepare_error: CmdPrepareError) {
fn handle_execute_error(cmd_execute_error: CmdExecuteError) {
match cmd_execute_error {
CmdExecuteError::Io(error) => {
- eprintln!("{}", md(t!("execute_error.io", error = error.to_string())));
+ eprintln!(
+ "{}",
+ md(t!("execute_error.io", error = display_io_error(error)))
+ );
}
CmdExecuteError::Prepare(cmd_prepare_error) => handle_prepare_error(cmd_prepare_error),
CmdExecuteError::Error(msg) => {
@@ -225,7 +247,10 @@ fn handle_execute_error(cmd_execute_error: CmdExecuteError) {
fn handle_render_error(cmd_render_error: CmdRenderError) {
match cmd_render_error {
CmdRenderError::Io(error) => {
- eprintln!("{}", md(t!("render_error.io", error = error.to_string())));
+ eprintln!(
+ "{}",
+ md(t!("render_error.io", error = display_io_error(error)))
+ );
}
CmdRenderError::Prepare(cmd_prepare_error) => handle_prepare_error(cmd_prepare_error),
CmdRenderError::Execute(cmd_execute_error) => handle_execute_error(cmd_execute_error),
@@ -258,3 +283,66 @@ fn handle_render_error(cmd_render_error: CmdRenderError) {
}
}
}
+
+fn display_io_error(error: std::io::Error) -> std::borrow::Cow<'static, str> {
+ match error.kind() {
+ std::io::ErrorKind::NotFound => t!("io_error.not_found", raw_error = error),
+ std::io::ErrorKind::PermissionDenied => t!("io_error.permission_denied", raw_error = error),
+ std::io::ErrorKind::ConnectionRefused => {
+ t!("io_error.connection_refused", raw_error = error)
+ }
+ std::io::ErrorKind::ConnectionReset => t!("io_error.connection_reset", raw_error = error),
+ std::io::ErrorKind::HostUnreachable => t!("io_error.host_unreachable", raw_error = error),
+ std::io::ErrorKind::NetworkUnreachable => {
+ t!("io_error.network_unreachable", raw_error = error)
+ }
+ std::io::ErrorKind::ConnectionAborted => {
+ t!("io_error.connection_aborted", raw_error = error)
+ }
+ std::io::ErrorKind::NotConnected => t!("io_error.not_connected", raw_error = error),
+ std::io::ErrorKind::AddrInUse => t!("io_error.addr_in_use", raw_error = error),
+ std::io::ErrorKind::AddrNotAvailable => {
+ t!("io_error.addr_not_available", raw_error = error)
+ }
+ std::io::ErrorKind::NetworkDown => t!("io_error.network_down", raw_error = error),
+ std::io::ErrorKind::BrokenPipe => t!("io_error.broken_pipe", raw_error = error),
+ std::io::ErrorKind::AlreadyExists => t!("io_error.already_exists", raw_error = error),
+ std::io::ErrorKind::WouldBlock => t!("io_error.would_block", raw_error = error),
+ std::io::ErrorKind::NotADirectory => t!("io_error.not_a_directory", raw_error = error),
+ std::io::ErrorKind::IsADirectory => t!("io_error.is_a_directory", raw_error = error),
+ std::io::ErrorKind::DirectoryNotEmpty => {
+ t!("io_error.directory_not_empty", raw_error = error)
+ }
+ std::io::ErrorKind::ReadOnlyFilesystem => {
+ t!("io_error.read_only_filesystem", raw_error = error)
+ }
+ std::io::ErrorKind::StaleNetworkFileHandle => {
+ t!("io_error.stale_network_file_handle", raw_error = error)
+ }
+ std::io::ErrorKind::InvalidInput => t!("io_error.invalid_input", raw_error = error),
+ std::io::ErrorKind::InvalidData => t!("io_error.invalid_data", raw_error = error),
+ std::io::ErrorKind::TimedOut => t!("io_error.timed_out", raw_error = error),
+ std::io::ErrorKind::WriteZero => t!("io_error.write_zero", raw_error = error),
+ std::io::ErrorKind::StorageFull => t!("io_error.storage_full", raw_error = error),
+ std::io::ErrorKind::NotSeekable => t!("io_error.not_seekable", raw_error = error),
+ std::io::ErrorKind::QuotaExceeded => t!("io_error.quota_exceeded", raw_error = error),
+ std::io::ErrorKind::FileTooLarge => t!("io_error.file_too_large", raw_error = error),
+ std::io::ErrorKind::ResourceBusy => t!("io_error.resource_busy", raw_error = error),
+ std::io::ErrorKind::ExecutableFileBusy => {
+ t!("io_error.executable_file_busy", raw_error = error)
+ }
+ std::io::ErrorKind::Deadlock => t!("io_error.deadlock", raw_error = error),
+ std::io::ErrorKind::CrossesDevices => t!("io_error.crosses_devices", raw_error = error),
+ std::io::ErrorKind::TooManyLinks => t!("io_error.too_many_links", raw_error = error),
+ std::io::ErrorKind::InvalidFilename => t!("io_error.invalid_filename", raw_error = error),
+ std::io::ErrorKind::ArgumentListTooLong => {
+ t!("io_error.argument_list_too_long", raw_error = error)
+ }
+ std::io::ErrorKind::Interrupted => t!("io_error.interrupted", raw_error = error),
+ std::io::ErrorKind::Unsupported => t!("io_error.unsupported", raw_error = error),
+ std::io::ErrorKind::UnexpectedEof => t!("io_error.unexpected_eof", raw_error = error),
+ std::io::ErrorKind::OutOfMemory => t!("io_error.out_of_memory", raw_error = error),
+ std::io::ErrorKind::Other => t!("io_error.other", error = error.to_string()),
+ _ => t!("io_error.other", error = error.to_string()),
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 74b68f1..13629ce 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,3 +13,6 @@ pub mod systems;
/// Commands
pub mod cmds;
+
+/// Macros
+pub mod macros;
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..134b301
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1 @@
+pub mod command_flag;
diff --git a/src/macros/command_flag.rs b/src/macros/command_flag.rs
new file mode 100644
index 0000000..894b3f4
--- /dev/null
+++ b/src/macros/command_flag.rs
@@ -0,0 +1,33 @@
+#[macro_export]
+macro_rules! special_flag {
+ ($args:expr, $flag:expr) => {{
+ let flag = $flag;
+ let found = $args.iter().any(|arg| arg == flag);
+ $args.retain(|arg| arg != flag);
+ found
+ }};
+}
+
+#[macro_export]
+macro_rules! special_argument {
+ ($args:expr, $flag:expr) => {{
+ let flag = $flag;
+ let mut value: Option<String> = None;
+ let mut i = 0;
+ while i < $args.len() {
+ if $args[i] == flag {
+ if i + 1 < $args.len() {
+ value = Some($args[i + 1].clone());
+ $args.remove(i + 1);
+ $args.remove(i);
+ } else {
+ value = None;
+ $args.remove(i);
+ }
+ break;
+ }
+ i += 1;
+ }
+ value
+ }};
+}
diff --git a/src/systems.rs b/src/systems.rs
index 7f77ca5..2ca53be 100644
--- a/src/systems.rs
+++ b/src/systems.rs
@@ -1,2 +1,3 @@
pub mod cmd;
+pub mod debug;
pub mod render;
diff --git a/src/systems/cmd/cmd_system.rs b/src/systems/cmd/cmd_system.rs
index 1fe6e66..57aa15d 100644
--- a/src/systems/cmd/cmd_system.rs
+++ b/src/systems/cmd/cmd_system.rs
@@ -1,3 +1,6 @@
+use log::{error, info};
+use rust_i18n::t;
+
use crate::{
r_println,
systems::{
@@ -6,11 +9,13 @@ use crate::{
},
};
use std::{
- any::{Any, TypeId},
+ any::{Any, TypeId, type_name},
collections::HashMap,
future::Future,
};
+rust_i18n::i18n!("resources/locales/jvn", fallback = "en");
+
pub struct JVCommandContext {
pub help: bool,
pub confirmed: bool,
@@ -42,39 +47,17 @@ where
return Err(CmdProcessError::RendererOverrideButRequestHelp);
}
+ info!("{}", t!("verbose.cmd_process"));
let (data, type_id) = Self::process(args, ctx).await?;
- let renderer_override = renderer_override.as_str();
+ let renderer = renderer_override.as_str();
// Serialize the data based on its concrete type
- let render_result: Result<JVRenderResult, CmdRenderError> = if type_id
- == std::any::TypeId::of::<crate::cmds::out::hex::JVHexOutput>()
- {
- let concrete_data = data
- .downcast::<crate::cmds::out::hex::JVHexOutput>()
- .map_err(|_| CmdProcessError::DowncastFailed)?;
- include!("../render/_override_renderer_dispatcher.rs")
- } else if type_id
- == std::any::TypeId::of::<crate::cmds::out::mappings::JVMappingsOutput>()
- {
- let concrete_data = data
- .downcast::<crate::cmds::out::mappings::JVMappingsOutput>()
- .map_err(|_| CmdProcessError::DowncastFailed)?;
- include!("../render/_override_renderer_dispatcher.rs")
- } else if type_id == std::any::TypeId::of::<crate::cmds::out::status::JVStatusOutput>()
- {
- let concrete_data = data
- .downcast::<crate::cmds::out::status::JVStatusOutput>()
- .map_err(|_| CmdProcessError::DowncastFailed)?;
- include!("../render/_override_renderer_dispatcher.rs")
- } else {
- return Err(CmdProcessError::NoMatchingCommand);
- };
-
- match render_result {
- Ok(r) => Ok(r),
- Err(e) => Err(CmdProcessError::Render(e)),
- }
+ info!(
+ "{}",
+ t!("verbose.render_with_override_renderer", renderer = renderer)
+ );
+ include!("../render/_override_renderer_entry.rs")
}
}
@@ -93,7 +76,10 @@ where
return Ok(r);
}
+ info!("{}", t!("verbose.cmd_process"));
let (data, id) = Self::process(args, ctx).await?;
+
+ info!("{}", t!("verbose.render_with_specific_renderer"));
match render(data, id).await {
Ok(r) => Ok(r),
Err(e) => Err(CmdProcessError::Render(e)),
@@ -111,22 +97,53 @@ where
full_args.extend(args);
+ info!(
+ "{}",
+ t!("verbose.cmd_process_parse", t = type_name::<Argument>())
+ );
+
let parsed_args = match Argument::try_parse_from(full_args) {
Ok(args) => args,
- Err(_) => return Err(CmdProcessError::ParseError(Self::get_help_str())),
+ Err(_) => {
+ error!(
+ "{}",
+ t!(
+ "verbose.cmd_process_parse_failed",
+ t = type_name::<Argument>()
+ )
+ );
+ return Err(CmdProcessError::ParseError(Self::get_help_str()));
+ }
};
+ info!(
+ "{}",
+ t!(
+ "verbose.cmd_process_prepare",
+ i = type_name::<Input>(),
+ c = type_name::<Collect>()
+ )
+ );
+
let (input, collect) = match tokio::try_join!(
Self::prepare(&parsed_args, &ctx),
Self::collect(&parsed_args, &ctx)
) {
Ok((input, collect)) => (input, collect),
- Err(e) => return Err(CmdProcessError::from(e)),
+ Err(e) => {
+ error!("{}", t!("verbose.cmd_process_prepare_failed"));
+ return Err(CmdProcessError::from(e));
+ }
};
+ info!("{}", t!("verbose.cmd_process_exec"));
+
let data = match Self::exec(input, collect).await {
Ok(output) => output,
- Err(e) => return Err(CmdProcessError::from(e)),
+ Err(e) => {
+ error!("{}", t!("verbose.cmd_process_exec_failed"));
+ return Err(CmdProcessError::from(e));
+ }
};
Ok(data)
diff --git a/src/systems/cmd/processer.rs b/src/systems/cmd/processer.rs
index 196764a..0f68afb 100644
--- a/src/systems/cmd/processer.rs
+++ b/src/systems/cmd/processer.rs
@@ -1,13 +1,20 @@
+use log::{error, info, warn};
+use rust_i18n::t;
+
use crate::systems::cmd::_commands::{jv_cmd_nodes, jv_cmd_process_node};
use crate::systems::cmd::cmd_system::JVCommandContext;
use crate::systems::cmd::errors::CmdProcessError;
use crate::systems::render::renderer::JVRenderResult;
+rust_i18n::i18n!("resources/locales/jvn", fallback = "en");
+
pub async fn jv_cmd_process(
args: &Vec<String>,
ctx: JVCommandContext,
renderer_override: String,
) -> Result<JVRenderResult, CmdProcessError> {
+ info!("{}", t!("verbose.cmd_process_start"));
+
let nodes = jv_cmd_nodes();
// Why add a space?
@@ -27,10 +34,18 @@ pub async fn jv_cmd_process(
match matching_nodes.len() {
0 => {
// No matching node found
+ error!("{}", t!("verbose.cmd_match_no_node"));
return Err(CmdProcessError::NoMatchingCommand);
}
1 => {
let matched_prefix = matching_nodes[0];
+
+ // DEBUG
+ info!(
+ "{}",
+ t!("verbose.cmd_match_matched_single", node = matched_prefix)
+ );
+
let prefix_len = matched_prefix.split_whitespace().count();
let trimmed_args: Vec<String> = args.into_iter().cloned().skip(prefix_len).collect();
return jv_cmd_process_node(matched_prefix, trimmed_args, ctx, renderer_override).await;
@@ -39,6 +54,22 @@ pub async fn jv_cmd_process(
// Multiple matching nodes found
// Find the node with the longest length (most specific match)
let matched_prefix = matching_nodes.iter().max_by_key(|node| node.len()).unwrap();
+
+ // DEBUG
+ let nodes_str = matching_nodes
+ .iter()
+ .map(|s| s.as_str())
+ .collect::<Vec<_>>()
+ .join(", ");
+ warn!(
+ "{}",
+ t!(
+ "verbose.cmd_match_matched_multi",
+ nodes = nodes_str,
+ node = matched_prefix
+ )
+ );
+
let prefix_len = matched_prefix.split_whitespace().count();
let trimmed_args: Vec<String> = args.into_iter().cloned().skip(prefix_len).collect();
return jv_cmd_process_node(matched_prefix, trimmed_args, ctx, renderer_override).await;
diff --git a/src/systems/debug.rs b/src/systems/debug.rs
new file mode 100644
index 0000000..be544e8
--- /dev/null
+++ b/src/systems/debug.rs
@@ -0,0 +1 @@
+pub mod verbose_logger;
diff --git a/src/systems/debug/verbose_logger.rs b/src/systems/debug/verbose_logger.rs
new file mode 100644
index 0000000..ceb8bd8
--- /dev/null
+++ b/src/systems/debug/verbose_logger.rs
@@ -0,0 +1,43 @@
+use chrono::Local;
+use env_logger::Builder;
+use log::Level;
+use rust_i18n::t;
+use std::io::Write;
+
+rust_i18n::i18n!("resources/locales/jvn", fallback = "en");
+
+pub fn init_verbose_logger(level_filter: Option<log::LevelFilter>) {
+ let mut builder = match level_filter {
+ Some(f) => {
+ let mut b = Builder::new();
+ b.filter_level(f);
+ b
+ }
+ None => return,
+ };
+
+ builder
+ .format(|buf, record| {
+ let now = Local::now();
+ let timestamp = now.format("%y-%-m-%-d %H:%M:%S");
+ let level = record.level();
+ let args = record.args();
+
+ let (prefix, color_code) = match level {
+ Level::Error => (t!("logger.error").trim().to_string(), "\x1b[31m"),
+ Level::Warn => (t!("logger.warn").trim().to_string(), "\x1b[33m"),
+ Level::Info => (t!("logger.info").trim().to_string(), "\x1b[37m"),
+ Level::Debug => (t!("logger.debug").trim().to_string(), "\x1b[90m"),
+ Level::Trace => (t!("logger.trace").trim().to_string(), "\x1b[36m"),
+ };
+
+ let colored_prefix = if prefix.is_empty() {
+ String::new()
+ } else {
+ format!("{}[{}] {}: \x1b[0m", color_code, timestamp, prefix)
+ };
+
+ writeln!(buf, "{}{}", colored_prefix, args)
+ })
+ .init();
+}
diff --git a/templates/_override_renderer_dispatcher.rs.template b/templates/_override_renderer_dispatcher.rs.template
index b22c957..80cabb6 100644
--- a/templates/_override_renderer_dispatcher.rs.template
+++ b/templates/_override_renderer_dispatcher.rs.template
@@ -1,5 +1,5 @@
// Auto generated by build.rs
-match renderer_override {
+match renderer {
// MATCH
// -- TEMPLATE START --
"<<NAME>>" => {
@@ -8,7 +8,7 @@ match renderer_override {
// -- TEMPLATE END --
_ => {
return Err(CmdProcessError::Render(CmdRenderError::RendererNotFound(
- renderer_override.to_string(),
+ renderer.to_string(),
)));
}
}