Fix CLI commands with --format json not outputting errors in JSON

pull/218/head
Chip Senkbeil 11 months ago
parent eb23b4e1ad
commit 56b3b8f4f1
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Fixed
- CLI commands like `distant manager select` will now output errors in a JSON
format when configured to communicate using JSON
## [0.20.0-alpha.12] ## [0.20.0-alpha.12]
### Changed ### Changed

@ -17,24 +17,54 @@ mod options;
pub mod win_service; pub mod win_service;
pub use cli::Cli; pub use cli::Cli;
pub use options::{Options, OptionsError}; pub use options::{Format, Options, OptionsError};
/// Wrapper around a [`CliResult`] that provides [`Termination`] support /// Wrapper around a [`CliResult`] that provides [`Termination`] support and [`Format`]ing.
pub struct MainResult(CliResult); pub struct MainResult {
inner: CliResult,
format: Format,
}
impl MainResult { impl MainResult {
pub const OK: MainResult = MainResult(Ok(())); pub const OK: MainResult = MainResult {
inner: Ok(()),
format: Format::Shell,
};
/// Creates a new result that performs general shell formatting.
pub fn new(inner: CliResult) -> Self {
Self {
inner,
format: Format::Shell,
}
}
/// Converts to shell formatting for errors.
pub fn shell(self) -> Self {
Self {
inner: self.inner,
format: Format::Shell,
}
}
/// Converts to a JSON formatting for errors.
pub fn json(self) -> Self {
Self {
inner: self.inner,
format: Format::Json,
}
}
} }
impl From<CliResult> for MainResult { impl From<CliResult> for MainResult {
fn from(res: CliResult) -> Self { fn from(res: CliResult) -> Self {
Self(res) Self::new(res)
} }
} }
impl From<OptionsError> for MainResult { impl From<OptionsError> for MainResult {
fn from(x: OptionsError) -> Self { fn from(x: OptionsError) -> Self {
Self(match x { Self::new(match x {
OptionsError::Config(x) => Err(CliError::Error(x)), OptionsError::Config(x) => Err(CliError::Error(x)),
OptionsError::Options(x) => match x.kind() { OptionsError::Options(x) => match x.kind() {
// --help and --version should not actually exit with an error and instead display // --help and --version should not actually exit with an error and instead display
@ -57,13 +87,13 @@ impl From<OptionsError> for MainResult {
impl From<anyhow::Error> for MainResult { impl From<anyhow::Error> for MainResult {
fn from(x: anyhow::Error) -> Self { fn from(x: anyhow::Error) -> Self {
Self(Err(CliError::Error(x))) Self::new(Err(CliError::Error(x)))
} }
} }
impl From<anyhow::Result<()>> for MainResult { impl From<anyhow::Result<()>> for MainResult {
fn from(res: anyhow::Result<()>) -> Self { fn from(res: anyhow::Result<()>) -> Self {
Self(res.map_err(CliError::Error)) Self::new(res.map_err(CliError::Error))
} }
} }
@ -86,12 +116,22 @@ impl CliError {
impl Termination for MainResult { impl Termination for MainResult {
fn report(self) -> ExitCode { fn report(self) -> ExitCode {
match self.0 { match self.inner {
Ok(_) => ExitCode::SUCCESS, Ok(_) => ExitCode::SUCCESS,
Err(x) => match x { Err(x) => match x {
CliError::Exit(code) => ExitCode::from(code), CliError::Exit(code) => ExitCode::from(code),
CliError::Error(x) => { CliError::Error(x) => {
eprintln!("{x:?}"); match self.format {
Format::Shell => eprintln!("{x}"),
Format::Json => println!(
"{}",
serde_json::to_string(&serde_json::json!({
"type": "error",
"msg": x.to_string(),
}),)
.expect("Failed to format error to JSON")
),
}
::log::error!("{x:?}"); ::log::error!("{x:?}");
::log::logger().flush(); ::log::logger().flush();
ExitCode::FAILURE ExitCode::FAILURE

@ -1,4 +1,4 @@
use distant::{Cli, MainResult}; use distant::{Cli, Format, MainResult};
#[cfg(unix)] #[cfg(unix)]
fn main() -> MainResult { fn main() -> MainResult {
@ -8,7 +8,12 @@ fn main() -> MainResult {
}; };
let _logger = cli.init_logger(); let _logger = cli.init_logger();
MainResult::from(cli.run()) let format = cli.options.command.format();
let result = MainResult::from(cli.run());
match format {
Format::Shell => result.shell(),
Format::Json => result.json(),
}
} }
#[cfg(windows)] #[cfg(windows)]
@ -18,6 +23,7 @@ fn main() -> MainResult {
Err(x) => return MainResult::from(x), Err(x) => return MainResult::from(x),
}; };
let _logger = cli.init_logger(); let _logger = cli.init_logger();
let format = cli.options.command.format();
// If we are trying to listen as a manager, try as a service first // If we are trying to listen as a manager, try as a service first
if cli.is_manager_listen_command() { if cli.is_manager_listen_command() {
@ -36,5 +42,9 @@ fn main() -> MainResult {
} }
// Otherwise, execute as a non-service CLI // Otherwise, execute as a non-service CLI
MainResult::from(cli.run()) let result = MainResult::from(cli.run());
match format {
Format::Shell => result.shell(),
Format::Json => result.json(),
}
} }

@ -281,6 +281,19 @@ pub enum DistantSubcommand {
Generate(GenerateSubcommand), Generate(GenerateSubcommand),
} }
impl DistantSubcommand {
/// Format used by the subcommand.
#[inline]
pub fn format(&self) -> Format {
match self {
Self::Client(x) => x.format(),
Self::Manager(x) => x.format(),
Self::Server(x) => x.format(),
Self::Generate(x) => x.format(),
}
}
}
/// Subcommands for `distant client`. /// Subcommands for `distant client`.
#[derive(Debug, PartialEq, Subcommand, IsVariant)] #[derive(Debug, PartialEq, Subcommand, IsVariant)]
pub enum ClientSubcommand { pub enum ClientSubcommand {
@ -539,6 +552,21 @@ impl ClientSubcommand {
Self::Version { network, .. } => network, Self::Version { network, .. } => network,
} }
} }
/// Format used by the subcommand.
#[inline]
pub fn format(&self) -> Format {
match self {
Self::Api { .. } => Format::Json,
Self::Connect { format, .. } => *format,
Self::FileSystem(fs) => fs.format(),
Self::Launch { format, .. } => *format,
Self::Shell { .. } => Format::Shell,
Self::Spawn { .. } => Format::Shell,
Self::SystemInfo { .. } => Format::Shell,
Self::Version { format, .. } => *format,
}
}
} }
/// Subcommands for `distant fs`. /// Subcommands for `distant fs`.
@ -936,6 +964,12 @@ impl ClientFileSystemSubcommand {
Self::Write { network, .. } => network, Self::Write { network, .. } => network,
} }
} }
/// Format used by the subcommand.
#[inline]
pub fn format(&self) -> Format {
Format::Shell
}
} }
/// Subcommands for `distant generate`. /// Subcommands for `distant generate`.
@ -960,6 +994,14 @@ pub enum GenerateSubcommand {
}, },
} }
impl GenerateSubcommand {
/// Format used by the subcommand.
#[inline]
pub fn format(&self) -> Format {
Format::Shell
}
}
/// Subcommands for `distant manager`. /// Subcommands for `distant manager`.
#[derive(Debug, PartialEq, Eq, Subcommand, IsVariant)] #[derive(Debug, PartialEq, Eq, Subcommand, IsVariant)]
pub enum ManagerSubcommand { pub enum ManagerSubcommand {
@ -1056,6 +1098,22 @@ pub enum ManagerSubcommand {
}, },
} }
impl ManagerSubcommand {
/// Format used by the subcommand.
#[inline]
pub fn format(&self) -> Format {
match self {
Self::Select { format, .. } => *format,
Self::Service(_) => Format::Shell,
Self::Listen { .. } => Format::Shell,
Self::Capabilities { format, .. } => *format,
Self::Info { format, .. } => *format,
Self::List { format, .. } => *format,
Self::Kill { format, .. } => *format,
}
}
}
/// Subcommands for `distant manager service`. /// Subcommands for `distant manager service`.
#[derive(Debug, PartialEq, Eq, Subcommand, IsVariant)] #[derive(Debug, PartialEq, Eq, Subcommand, IsVariant)]
pub enum ManagerServiceSubcommand { pub enum ManagerServiceSubcommand {
@ -1172,6 +1230,14 @@ pub enum ServerSubcommand {
}, },
} }
impl ServerSubcommand {
/// Format used by the subcommand.
#[inline]
pub fn format(&self) -> Format {
Format::Shell
}
}
#[derive(Args, Debug, PartialEq)] #[derive(Args, Debug, PartialEq)]
pub struct ServerListenWatchOptions { pub struct ServerListenWatchOptions {
/// If specified, will use the polling-based watcher for filesystem changes /// If specified, will use the polling-based watcher for filesystem changes

Loading…
Cancel
Save