summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-03-18 22:49:50 +0800
committer魏曹先生 <1992414357@qq.com>2026-03-18 22:49:50 +0800
commit5372793a49567dcba7315bf8e7bc5a1cab2d5a76 (patch)
tree96d13d527835c23b978eae470e54a1d5fd15bc6d /src
parent2609dfe338b9bace6ff74c5efc93f684ba55a44e (diff)
Add support for reading from stdin and improve error messages
Diffstat (limited to 'src')
-rw-r--r--src/bin/jvn.rs50
-rw-r--r--src/cmds/arg/single_file.rs2
-rw-r--r--src/cmds/cmd/hexdump.rs21
-rw-r--r--src/systems/cmd/cmd_system.rs3
4 files changed, 69 insertions, 7 deletions
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>