diff options
| -rw-r--r-- | Cargo.lock | 23 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | resources/locales/jvn/en.yml | 8 | ||||
| -rw-r--r-- | resources/locales/jvn/zh-CN.yml | 10 | ||||
| -rw-r--r-- | src/bin/jvn.rs | 50 | ||||
| -rw-r--r-- | src/cmds/arg/single_file.rs | 2 | ||||
| -rw-r--r-- | src/cmds/cmd/hexdump.rs | 21 | ||||
| -rw-r--r-- | src/systems/cmd/cmd_system.rs | 3 |
8 files changed, 102 insertions, 16 deletions
@@ -176,6 +176,17 @@ dependencies = [ ] [[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1036,6 +1047,15 @@ dependencies = [ ] [[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] name = "hex_display" version = "0.1.0" @@ -1211,6 +1231,7 @@ dependencies = [ name = "just_enough_vcs_cli" version = "0.0.0" dependencies = [ + "atty", "chrono", "clap", "cli_utils", @@ -1602,6 +1623,8 @@ dependencies = [ name = "protocol" version = "0.1.0" dependencies = [ + "constants", + "dirs", "framework", "serde", "sheet_system", @@ -67,6 +67,7 @@ log.workspace = true serde.workspace = true thiserror.workspace = true +atty = "0.2" chrono = "0.4" clap = { version = "4.5", features = ["derive"] } colored.workspace = true diff --git a/resources/locales/jvn/en.yml b/resources/locales/jvn/en.yml index f118566..46cd0c5 100644 --- a/resources/locales/jvn/en.yml +++ b/resources/locales/jvn/en.yml @@ -65,7 +65,7 @@ prepare_error: error: | Unknown error in preparation phase! - Error: %{error} + %{error} local_workspace_not_found: | Local workspace not found! @@ -105,7 +105,7 @@ execute_error: error: | Error in execution phase! - Error: %{error} + %{error} render_error: io: | @@ -114,11 +114,11 @@ render_error: error: | Error in rendering phase! - Error: %{error} + %{error} serialize_failed: | Data serialization error! - Error: %{error} + %{error} renderer_not_found: | Renderer `%{renderer_name}` not found! diff --git a/resources/locales/jvn/zh-CN.yml b/resources/locales/jvn/zh-CN.yml index 8a7bdea..37f45a7 100644 --- a/resources/locales/jvn/zh-CN.yml +++ b/resources/locales/jvn/zh-CN.yml @@ -44,8 +44,8 @@ prepare_error: %{error} error: | - 命令在准备阶段发生未知错误! - 错误信息:%{error} + 命令在准备阶段发生错误! + %{error} local_workspace_not_found: | 无法找到本地工作区! @@ -84,7 +84,7 @@ execute_error: error: | 命令在运行阶段发生错误! - 错误信息:%{error} + %{error} render_error: io: | @@ -93,11 +93,11 @@ render_error: error: | 命令在渲染阶段发生错误! - 错误信息:%{error} + %{error} serialize_failed: | 数据在序列化时发生了错误! - 错误信息:%{error} + %{error} renderer_not_found: | 无法找到渲染器 `%{renderer_name}`! diff --git a/src/bin/jvn.rs b/src/bin/jvn.rs index 062eab8..fd01431 100644 --- a/src/bin/jvn.rs +++ b/src/bin/jvn.rs @@ -1,4 +1,8 @@ -use std::{ops::Deref, process::exit}; +use std::{ + ops::Deref, + path::{Path, PathBuf}, + process::exit, +}; use cli_utils::legacy::{display::md, env::current_locales, levenshtein_distance}; use just_enough_vcs_cli::{ @@ -23,6 +27,7 @@ use just_progress::{ }; use log::{LevelFilter, error, info, trace, warn}; use rust_i18n::{set_locale, t}; +use tokio::io::AsyncReadExt; rust_i18n::i18n!("resources/locales/jvn", fallback = "en"); @@ -115,6 +120,18 @@ async fn main() { info!("{}", t!("verbose.user_input", command = args.join(" "))); + // Read pipe inpuit + let (stdin_path, stdin_data) = match read_all_from_stdin().await { + Ok((path, data)) => { + if data.is_empty() { + (None, None) + } else { + (path, Some(data)) + } + } + Err(_) => (None, None), + }; + // Build process future let args_clone = args.clone(); let process_future = jv_cmd_process( @@ -124,6 +141,8 @@ async fn main() { confirmed, args: args.clone(), lang, + stdin_path, + stdin_data, }, renderer_override, ); @@ -198,6 +217,35 @@ async fn main() { } } +/// Read path or raw information from standard input +async fn read_all_from_stdin() -> tokio::io::Result<(Option<PathBuf>, Vec<u8>)> { + if atty::is(atty::Stream::Stdin) { + return Ok((None, Vec::new())); + } + + let mut stdin = tokio::io::stdin(); + let mut buffer = Vec::new(); + + stdin.read_to_end(&mut buffer).await?; + + if buffer.is_empty() { + return Ok((None, Vec::new())); + } + + let path = if let Ok(input_str) = String::from_utf8(buffer.clone()) { + let trimmed = input_str.trim(); + if !trimmed.is_empty() && Path::new(trimmed).exists() { + Some(PathBuf::from(trimmed)) + } else { + None + } + } else { + None + }; + + Ok((path, buffer)) +} + fn handle_no_matching_command_error(args: Vec<String>) { let mut similar_nodes: Vec<String> = Vec::new(); for node in jv_cmd_nodes() { diff --git a/src/cmds/arg/single_file.rs b/src/cmds/arg/single_file.rs index 4ed9715..42927fc 100644 --- a/src/cmds/arg/single_file.rs +++ b/src/cmds/arg/single_file.rs @@ -4,5 +4,5 @@ use clap::Parser; #[derive(Parser, Debug)] pub struct JVSingleFileArgument { - pub file: PathBuf, + pub file: Option<PathBuf>, } diff --git a/src/cmds/cmd/hexdump.rs b/src/cmds/cmd/hexdump.rs index 346fffe..34df45c 100644 --- a/src/cmds/cmd/hexdump.rs +++ b/src/cmds/cmd/hexdump.rs @@ -1,9 +1,12 @@ use crate::{ cmd_output, cmds::{ - arg::single_file::JVSingleFileArgument, collect::single_file::JVSingleFileCollect, - r#in::empty::JVEmptyInput, out::hex::JVHexOutput, + arg::single_file::JVSingleFileArgument, + collect::single_file::JVSingleFileCollect, + r#in::empty::JVEmptyInput, + out::{hex::JVHexOutput, none::JVNoneOutput}, }, + early_cmd_output, systems::{ cmd::{ cmd_system::{AnyOutput, JVCommandContext}, @@ -30,9 +33,17 @@ async fn prepare(_args: &Arg, _ctx: &JVCommandContext) -> Result<In, CmdPrepareE Ok(In {}) } -async fn collect(args: &Arg, _ctx: &JVCommandContext) -> Result<Collect, CmdPrepareError> { - let file = &args.file; - let data = fs::read(file).await?; +async fn collect(args: &Arg, ctx: &JVCommandContext) -> Result<Collect, CmdPrepareError> { + let data = if let Some(ref stdin_path) = ctx.stdin_path { + fs::read(stdin_path).await? + } else if let Some(ref stdin_data) = ctx.stdin_data { + stdin_data.clone() + } else if let Some(path) = &args.file { + fs::read(&path).await? + } else { + // No path input, exit early + return early_cmd_output!(JVNoneOutput => JVNoneOutput); + }; Ok(Collect { data }) } diff --git a/src/systems/cmd/cmd_system.rs b/src/systems/cmd/cmd_system.rs index 43d5187..a89842b 100644 --- a/src/systems/cmd/cmd_system.rs +++ b/src/systems/cmd/cmd_system.rs @@ -12,6 +12,7 @@ use std::{ any::{TypeId, type_name}, collections::HashMap, future::Future, + path::PathBuf, }; pub type AnyOutput = (Box<dyn std::any::Any + Send + 'static>, TypeId); @@ -21,6 +22,8 @@ pub struct JVCommandContext { pub confirmed: bool, pub args: Vec<String>, pub lang: String, + pub stdin_path: Option<PathBuf>, + pub stdin_data: Option<Vec<u8>>, } pub trait JVCommand<Argument, Input, Collect> |
