Let zebrad revhex read from stdin (#648)
* Log at warn level for commands that use stdout * Let zebrad revhex read from stdin Most unix tools support reading from stdin, so they can be used in pipelines. Part of #564.
This commit is contained in:
parent
e452ba1c13
commit
12b9fa8ae2
|
@ -79,6 +79,7 @@ impl Application for ZebradApp {
|
|||
&mut self.state
|
||||
}
|
||||
|
||||
/// Returns the framework components used by this application.
|
||||
fn framework_components(
|
||||
&mut self,
|
||||
command: &Self::Cmd,
|
||||
|
@ -135,10 +136,29 @@ impl Application for ZebradApp {
|
|||
|
||||
impl ZebradApp {
|
||||
fn level(&self, command: &EntryPoint<ZebradCmd>) -> String {
|
||||
if let Ok(level) = std::env::var("ZEBRAD_LOG") {
|
||||
level
|
||||
} else if command.verbose {
|
||||
// `None` outputs zebrad usage information to stdout
|
||||
let command_uses_stdout = match &command.command {
|
||||
None => true,
|
||||
Some(c) => c.uses_stdout(),
|
||||
};
|
||||
|
||||
// Allow users to:
|
||||
// - override all other configs and defaults using the command line
|
||||
// - see command outputs without spurious log messages, by default
|
||||
// - override the config file using an environmental variable
|
||||
if command.verbose {
|
||||
"debug".to_string()
|
||||
} else if command_uses_stdout {
|
||||
// Tracing sends output to stdout, so we disable info-level logs for
|
||||
// some commands.
|
||||
//
|
||||
// TODO: send tracing output to stderr. This change requires an abscissa
|
||||
// update, because `abscissa_core::component::Tracing` uses
|
||||
// `tracing_subscriber::fmt::Formatter`, which has `Stdout` as a
|
||||
// type parameter. We need `MakeWriter` or a similar type.
|
||||
"warn".to_string()
|
||||
} else if let Ok(level) = std::env::var("ZEBRAD_LOG") {
|
||||
level
|
||||
} else if let Some(ZebradConfig {
|
||||
tracing:
|
||||
crate::config::TracingSection {
|
||||
|
|
|
@ -52,6 +52,24 @@ pub enum ZebradCmd {
|
|||
Version(VersionCmd),
|
||||
}
|
||||
|
||||
impl ZebradCmd {
|
||||
/// Returns true if this command sends output to stdout.
|
||||
///
|
||||
/// For example, `Generate` sends a default config file to stdout.
|
||||
///
|
||||
/// Usage note: `abscissa_core::EntryPoint` stores an `Option<ZerbradCmd>`.
|
||||
/// If the command is `None`, then abscissa writes zebrad usage information
|
||||
/// to stdout.
|
||||
pub(crate) fn uses_stdout(&self) -> bool {
|
||||
use ZebradCmd::*;
|
||||
match self {
|
||||
// List all the commands, so new commands have to make a choice here
|
||||
Generate(_) | Help(_) | Revhex(_) | Version(_) => true,
|
||||
Connect(_) | Seed(_) | Start(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait allows you to define how application configuration is loaded.
|
||||
impl Configurable<ZebradConfig> for ZebradCmd {
|
||||
/// Location of the configuration file
|
||||
|
|
|
@ -3,31 +3,44 @@
|
|||
#![allow(clippy::never_loop)]
|
||||
|
||||
use abscissa_core::{Command, Options, Runnable};
|
||||
use std::io::stdin;
|
||||
|
||||
/// `revhex` subcommand
|
||||
#[derive(Command, Debug, Default, Options)]
|
||||
pub struct RevhexCmd {
|
||||
/// The hex string whose endianness will be reversed.
|
||||
///
|
||||
/// When input is "-" or empty, reads lines from standard input, and
|
||||
/// reverses each line.
|
||||
#[options(free)]
|
||||
input: String,
|
||||
}
|
||||
|
||||
/// Returns the hexadecimal-encoded string `s` in byte-reversed order.
|
||||
fn byte_reverse_hex(s: &str) -> String {
|
||||
String::from_utf8(
|
||||
s.as_bytes()
|
||||
.chunks(2)
|
||||
.rev()
|
||||
.map(|c| c.iter())
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.expect("input should be ascii")
|
||||
}
|
||||
|
||||
impl Runnable for RevhexCmd {
|
||||
/// Print endian-reversed hex string.
|
||||
fn run(&self) {
|
||||
println!(
|
||||
"{}",
|
||||
String::from_utf8(
|
||||
self.input
|
||||
.as_bytes()
|
||||
.chunks(2)
|
||||
.rev()
|
||||
.map(|c| c.iter())
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.expect("input should be ascii")
|
||||
);
|
||||
if self.input.is_empty() || self.input == "-" {
|
||||
// "-" is a typical command-line argument for "read standard input"
|
||||
let mut input = String::new();
|
||||
while stdin().read_line(&mut input).is_ok() {
|
||||
println!("{}", byte_reverse_hex(&input.trim()));
|
||||
}
|
||||
} else {
|
||||
println!("{}", byte_reverse_hex(&self.input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue