diff --git a/Cargo.lock b/Cargo.lock index ade2fdb6..d548b4fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3373,9 +3373,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -3760,6 +3760,7 @@ dependencies = [ "include_dir", "miette", "rayon", + "schemars", "serde", "serde_json", "serde_yaml", diff --git a/Cargo.toml b/Cargo.toml index 173a712f..be733843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ walkdir.workspace = true include_dir.workspace = true thiserror.workspace = true miette.workspace = true +schemars.workspace = true rayon = "1.10.0" diff --git a/crates/weaver_common/src/in_memory.rs b/crates/weaver_common/src/in_memory.rs index 19d8a6c7..eccefef4 100644 --- a/crates/weaver_common/src/in_memory.rs +++ b/crates/weaver_common/src/in_memory.rs @@ -151,4 +151,9 @@ impl crate::Logger for Logger { .expect("Failed to lock messages") .push(LogMessage::Log(message.to_owned())); } + + /// Mute all the messages except for the warnings and errors. + fn mute(&self) { + // We do not mute the logger in this implementation. + } } diff --git a/crates/weaver_common/src/lib.rs b/crates/weaver_common/src/lib.rs index c2e74b1e..b1a830e4 100644 --- a/crates/weaver_common/src/lib.rs +++ b/crates/weaver_common/src/lib.rs @@ -47,6 +47,9 @@ pub trait Logger { /// Logs a message without icon. fn log(&self, message: &str); + + /// Mute all the messages except for the warnings and errors. + fn mute(&self); } /// A generic logger that can be used to log messages to the console. @@ -55,6 +58,7 @@ pub trait Logger { pub struct ConsoleLogger { logger: Arc>>, debug_level: u8, + mute: Arc>, } impl ConsoleLogger { @@ -64,6 +68,7 @@ impl ConsoleLogger { ConsoleLogger { logger: Arc::new(Mutex::new(paris::Logger::new())), debug_level, + mute: Arc::new(Mutex::new(false)), } } } @@ -71,7 +76,8 @@ impl ConsoleLogger { impl Logger for ConsoleLogger { /// Logs an trace message (only with debug enabled). fn trace(&self, message: &str) { - if self.debug_level > 0 { + let mute = *self.mute.lock().expect("Failed to lock mute"); + if self.debug_level > 0 && !mute { _ = self .logger .lock() @@ -82,6 +88,10 @@ impl Logger for ConsoleLogger { /// Logs an info message. fn info(&self, message: &str) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self .logger .lock() @@ -109,6 +119,10 @@ impl Logger for ConsoleLogger { /// Logs a success message. fn success(&self, message: &str) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self .logger .lock() @@ -118,6 +132,10 @@ impl Logger for ConsoleLogger { /// Logs a newline. fn newline(&self, count: usize) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self .logger .lock() @@ -127,6 +145,10 @@ impl Logger for ConsoleLogger { /// Indents the logger. fn indent(&self, count: usize) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self .logger .lock() @@ -136,11 +158,19 @@ impl Logger for ConsoleLogger { /// Stops a loading message. fn done(&self) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self.logger.lock().expect("Failed to lock logger").done(); } /// Adds a style to the logger. fn add_style(&self, name: &str, styles: Vec<&'static str>) -> &Self { + if *self.mute.lock().expect("Failed to lock mute") { + return self; + } + _ = self .logger .lock() @@ -151,6 +181,10 @@ impl Logger for ConsoleLogger { /// Logs a loading message with a spinner. fn loading(&self, message: &str) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self .logger .lock() @@ -160,18 +194,31 @@ impl Logger for ConsoleLogger { /// Forces the logger to not print a newline for the next message. fn same(&self) -> &Self { + if *self.mute.lock().expect("Failed to lock mute") { + return self; + } + _ = self.logger.lock().expect("Failed to lock logger").same(); self } /// Logs a message without icon. fn log(&self, message: &str) { + if *self.mute.lock().expect("Failed to lock mute") { + return; + } + _ = self .logger .lock() .expect("Failed to lock logger") .log(message); } + + /// Mute all the messages except for the warnings and errors. + fn mute(&self) { + *self.mute.lock().expect("Failed to lock mute") = true; + } } /// A logger that does not log anything. @@ -226,6 +273,9 @@ impl Logger for NullLogger { /// Logs a message without icon. fn log(&self, _: &str) {} + + /// Mute all the messages except for the warnings and errors. + fn mute(&self) {} } /// A logger that can be used in unit or integration tests. @@ -363,4 +413,9 @@ impl Logger for TestLogger { .expect("Failed to lock logger") .log(message); } + + /// Mute all the messages except for the warnings and errors. + fn mute(&self) { + // We do not need to mute the logger in the tests. + } } diff --git a/crates/weaver_common/src/quiet.rs b/crates/weaver_common/src/quiet.rs index b549c90a..f5165753 100644 --- a/crates/weaver_common/src/quiet.rs +++ b/crates/weaver_common/src/quiet.rs @@ -78,4 +78,9 @@ impl Logger for QuietLogger { /// Logs a message without icon. fn log(&self, _message: &str) {} + + /// Mute all the messages except for the warnings and errors. + fn mute(&self) { + // Do nothing + } } diff --git a/src/diagnostic/init.rs b/src/diagnostic/init.rs index 6b044796..8be9bdc4 100644 --- a/src/diagnostic/init.rs +++ b/src/diagnostic/init.rs @@ -3,7 +3,7 @@ //! Initializes a `diagnostic_templates` directory to define or override diagnostic output formats. use crate::diagnostic::{Error, DEFAULT_DIAGNOSTIC_TEMPLATES}; -use crate::DiagnosticArgs; +use crate::{DiagnosticArgs, ExitDirectives}; use clap::Args; use include_dir::DirEntry; use std::fs; @@ -32,7 +32,7 @@ pub struct DiagnosticInitArgs { pub(crate) fn command( logger: impl Logger + Sync + Clone, args: &DiagnosticInitArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { extract(args.diagnostic_templates_dir.clone(), &args.target).map_err(|e| { Error::InitDiagnosticError { path: args.diagnostic_templates_dir.clone(), @@ -44,7 +44,10 @@ pub(crate) fn command( "Diagnostic templates initialized at {:?}", args.diagnostic_templates_dir )); - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: false, + }) } /// Extracts the diagnostic templates to the specified path for the given target. @@ -104,9 +107,9 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); // Check the presence of 3 subdirectories in the temp_output directory let subdirs = fs::read_dir(&temp_output).unwrap().count(); @@ -128,9 +131,9 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); // Check the presence of 3 subdirectories in the temp_output directory let subdirs = fs::read_dir(&temp_output).unwrap().count(); diff --git a/src/main.rs b/src/main.rs index 5b355a44..e7cbfe94 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,14 +49,23 @@ impl Default for DiagnosticArgs { /// Result of a command execution. #[derive(Debug)] pub(crate) struct CmdResult { - pub(crate) command_result: Result<(), DiagnosticMessages>, + pub(crate) command_result: Result, pub(crate) diagnostic_args: Option, } +/// Exit directives. +#[derive(Debug, Clone)] +pub(crate) struct ExitDirectives { + /// Exit code. + exit_code: i32, + /// Quiet mode. + quiet_mode: bool, +} + impl CmdResult { /// Create a new command result. pub(crate) fn new( - command_result: Result<(), DiagnosticMessages>, + command_result: Result, diagnostic_args: Option, ) -> Self { Self { @@ -71,7 +80,7 @@ fn main() { let cli = Cli::parse(); let start = std::time::Instant::now(); - let exit_code = if cli.quiet { + let exit_directives = if cli.quiet { let log = QuietLogger::new(); run_command(&cli, log) } else { @@ -79,37 +88,53 @@ fn main() { run_command(&cli, log) }; - if !cli.quiet { + if !cli.quiet && !exit_directives.quiet_mode { let elapsed = start.elapsed(); - println!("Total execution time: {:?}s", elapsed.as_secs_f64()); + println!("\nTotal execution time: {:?}s", elapsed.as_secs_f64()); } // Exit the process with the exit code provided by the `run_command` function. #[allow(clippy::exit)] - std::process::exit(exit_code); + std::process::exit(exit_directives.exit_code); } -/// Run the command specified by the CLI arguments and return the exit code. +/// Run the command specified by the CLI arguments and return the exit directives. #[cfg(not(tarpaulin_include))] -fn run_command(cli: &Cli, log: impl Logger + Sync + Clone) -> i32 { +fn run_command(cli: &Cli, log: impl Logger + Sync + Clone) -> ExitDirectives { let cmd_result = match &cli.command { Some(Commands::Registry(params)) => semconv_registry(log.clone(), params), Some(Commands::Schema(params)) => telemetry_schema(log.clone(), params), Some(Commands::Diagnostic(params)) => diagnostic::diagnostic(log.clone(), params), - None => return 0, + None => { + return ExitDirectives { + exit_code: 0, + quiet_mode: false, + } + } }; process_diagnostics(cmd_result, log.clone()) } -/// Render the diagnostic messages based on the diagnostic configuration and return the exit code -/// based on the diagnostic messages. -fn process_diagnostics(cmd_result: CmdResult, logger: impl Logger + Sync + Clone) -> i32 { +/// Render the diagnostic messages based on the diagnostic configuration and return the exit +/// directives based on the diagnostic messages and the CmdResult quiet mode. +fn process_diagnostics( + cmd_result: CmdResult, + logger: impl Logger + Sync + Clone, +) -> ExitDirectives { let diagnostic_args = if let Some(diagnostic_args) = cmd_result.diagnostic_args { diagnostic_args } else { DiagnosticArgs::default() // Default diagnostic arguments; }; + let mut exit_directives = if let Ok(exit_directives) = &cmd_result.command_result { + exit_directives.clone() + } else { + ExitDirectives { + exit_code: 0, + quiet_mode: false, + } + }; if let Err(diagnostic_messages) = cmd_result.command_result { let loader = EmbeddedFileLoader::try_new( @@ -132,22 +157,21 @@ fn process_diagnostics(cmd_result: CmdResult, logger: impl Logger + Sync + Clone "Failed to render the diagnostic messages. Error: {}", e )); - return 1; + exit_directives.exit_code = 1; + return exit_directives; } } } Err(e) => { logger.error(&format!("Failed to create the template engine to render the diagnostic messages. Error: {}", e)); - return 1; + exit_directives.exit_code = 1; + return exit_directives; } } - return if diagnostic_messages.has_error() { - 1 - } else { - 0 - }; + if diagnostic_messages.has_error() { + exit_directives.exit_code = 1; + } } - // Return 0 if there are no diagnostic messages - 0 + exit_directives } diff --git a/src/registry/check.rs b/src/registry/check.rs index 78a78a86..13d556b8 100644 --- a/src/registry/check.rs +++ b/src/registry/check.rs @@ -6,7 +6,7 @@ use crate::registry::RegistryArgs; use crate::util::{ check_policies, load_semconv_specs, resolve_semconv_specs, semconv_registry_path_from, }; -use crate::DiagnosticArgs; +use crate::{DiagnosticArgs, ExitDirectives}; use clap::Args; use std::path::PathBuf; use weaver_cache::Cache; @@ -41,7 +41,7 @@ pub(crate) fn command( logger: impl Logger + Sync + Clone, cache: &Cache, args: &RegistryCheckArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { logger.loading(&format!("Checking registry `{}`", args.registry.registry)); let registry_id = "default"; @@ -65,7 +65,10 @@ pub(crate) fn command( let mut registry = SemConvRegistry::from_semconv_specs(registry_id, semconv_specs); _ = resolve_semconv_specs(&mut registry, logger.clone())?; - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: false, + }) } #[cfg(test)] @@ -98,9 +101,9 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); // Now, let's run the command again with the policy checks enabled. let cli = Cli { @@ -121,8 +124,8 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger); + let exit_directive = run_command(&cli, logger); // The command should exit with an error code. - assert_eq!(exit_code, 1); + assert_eq!(exit_directive.exit_code, 1); } } diff --git a/src/registry/generate.rs b/src/registry/generate.rs index 731ed015..f7bc91d5 100644 --- a/src/registry/generate.rs +++ b/src/registry/generate.rs @@ -18,7 +18,7 @@ use crate::registry::RegistryArgs; use crate::util::{ check_policies, load_semconv_specs, resolve_semconv_specs, semconv_registry_path_from, }; -use crate::DiagnosticArgs; +use crate::{DiagnosticArgs, ExitDirectives}; /// Parameters for the `registry generate` sub-command #[derive(Debug, Args)] @@ -59,7 +59,7 @@ pub(crate) fn command( logger: impl Logger + Sync + Clone, cache: &Cache, args: &RegistryGenerateArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { logger.loading(&format!( "Generating artifacts for the registry `{}`", args.registry.registry @@ -102,7 +102,10 @@ pub(crate) fn command( )?; logger.success("Artifacts generated successfully"); - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: false, + }) } #[cfg(test)] @@ -145,9 +148,9 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); // Hashset containing recursively all the relative paths of rust files in the // output directory. @@ -212,8 +215,8 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger); + let exit_directive = run_command(&cli, logger); // The command should exit with an error code. - assert_eq!(exit_code, 1); + assert_eq!(exit_directive.exit_code, 1); } } diff --git a/src/registry/json_schema.rs b/src/registry/json_schema.rs new file mode 100644 index 00000000..2a3653aa --- /dev/null +++ b/src/registry/json_schema.rs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Generate the JSON Schema of the resolved registry documents consumed by the template generator +//! and the policy engine. + +use crate::{DiagnosticArgs, ExitDirectives}; +use clap::Args; +use miette::Diagnostic; +use schemars::schema_for; +use serde::Serialize; +use serde_json::to_string_pretty; +use std::path::PathBuf; +use weaver_cache::Cache; +use weaver_common::diagnostic::{DiagnosticMessage, DiagnosticMessages}; +use weaver_common::Logger; +use weaver_forge::registry::ResolvedRegistry; + +/// Parameters for the `registry json-schema` sub-command +#[derive(Debug, Args)] +pub struct RegistryJsonSchemaArgs { + /// Output file to write the JSON schema to + /// If not specified, the JSON schema is printed to stdout + #[arg(short, long)] + output: Option, + + /// Parameters to specify the diagnostic format. + #[command(flatten)] + pub diagnostic: DiagnosticArgs, +} + +/// An error that can occur while generating a JSON Schema. +#[derive(thiserror::Error, Debug, Clone, PartialEq, Serialize, Diagnostic)] +#[non_exhaustive] +pub enum Error { + /// The serialization of the JSON schema failed. + #[error("The serialization of the JSON schema failed. Error: {error}")] + SerializationError { + /// The error that occurred. + error: String, + }, + + /// Writing to the file failed. + #[error("Writing to the file ‘{file}’ failed for the following reason: {error}")] + WriteError { + /// The path to the output file. + file: PathBuf, + /// The error that occurred. + error: String, + }, +} + +impl From for DiagnosticMessages { + fn from(error: Error) -> Self { + DiagnosticMessages::new(vec![DiagnosticMessage::new(error)]) + } +} + +/// Generate the JSON Schema of a ResolvedRegistry and write the JSON schema to a +/// file or print it to stdout. +#[cfg(not(tarpaulin_include))] +pub(crate) fn command( + logger: impl Logger + Sync + Clone, + _cache: &Cache, + args: &RegistryJsonSchemaArgs, +) -> Result { + let json_schema = schema_for!(ResolvedRegistry); + + let json_schema_str = + to_string_pretty(&json_schema).map_err(|e| Error::SerializationError { + error: e.to_string(), + })?; + + if let Some(output) = &args.output { + logger.loading(&format!("Writing JSON schema to `{}`", output.display())); + std::fs::write(output, json_schema_str).map_err(|e| Error::WriteError { + file: output.clone(), + error: e.to_string(), + })?; + } else { + logger.log(&json_schema_str); + } + + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: args.output.is_none(), + }) +} + +#[cfg(test)] +mod tests { + use weaver_common::in_memory; + use weaver_common::in_memory::LogMessage; + + use crate::cli::{Cli, Commands}; + use crate::registry::json_schema::RegistryJsonSchemaArgs; + use crate::registry::{RegistryCommand, RegistrySubCommand}; + use crate::run_command; + + #[test] + fn test_registry_json_schema() { + let logger = in_memory::Logger::new(0); + let cli = Cli { + debug: 0, + quiet: false, + command: Some(Commands::Registry(RegistryCommand { + command: RegistrySubCommand::JsonSchema(RegistryJsonSchemaArgs { + output: None, + diagnostic: Default::default(), + }), + })), + }; + + let exit_directive = run_command(&cli, logger.clone()); + // The command should succeed. + assert_eq!(exit_directive.exit_code, 0); + + // We should have a single log message with the JSON schema. + let messages = logger.messages(); + assert_eq!(messages.len(), 1); + + let message = &messages[0]; + if let LogMessage::Log(log) = message { + let value = + serde_json::from_str::(log).expect("Failed to parse JSON"); + let definitions = value + .as_object() + .expect("Expected a JSON object") + .get("definitions"); + assert!( + definitions.is_some(), + "Expected a 'definitions' key in the JSON schema" + ); + } else { + panic!("Expected a log message, but got: {:?}", message); + } + } +} diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 72649762..81f1dd03 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -8,6 +8,7 @@ use std::str::FromStr; use clap::{Args, Subcommand}; use crate::registry::generate::RegistryGenerateArgs; +use crate::registry::json_schema::RegistryJsonSchemaArgs; use crate::registry::resolve::RegistryResolveArgs; use crate::registry::search::RegistrySearchArgs; use crate::registry::stats::RegistryStatsArgs; @@ -19,6 +20,7 @@ use weaver_common::Logger; mod check; mod generate; +mod json_schema; mod resolve; mod search; mod stats; @@ -73,6 +75,11 @@ pub enum RegistrySubCommand { Stats(RegistryStatsArgs), /// Update markdown files that contain markers indicating the templates used to update the specified sections. UpdateMarkdown(RegistryUpdateMarkdownArgs), + /// Generate the JSON Schema of the resolved registry documents consumed by the template generator and the policy engine. + /// + /// The produced JSON Schema can be used to generate documentation of the resolved registry format or to generate code in your language of choice if you need to interact with the resolved registry format for any reason. + #[clap(verbatim_doc_comment)] + JsonSchema(RegistryJsonSchemaArgs), } /// Path to a semantic convention registry. @@ -156,5 +163,9 @@ pub fn semconv_registry(log: impl Logger + Sync + Clone, command: &RegistryComma update_markdown::command(log.clone(), &cache, args), Some(args.diagnostic.clone()), ), + RegistrySubCommand::JsonSchema(args) => CmdResult::new( + json_schema::command(log.clone(), &cache, args), + Some(args.diagnostic.clone()), + ), } } diff --git a/src/registry/resolve.rs b/src/registry/resolve.rs index 2009533b..2713108d 100644 --- a/src/registry/resolve.rs +++ b/src/registry/resolve.rs @@ -17,7 +17,7 @@ use crate::registry::RegistryArgs; use crate::util::{ check_policies, load_semconv_specs, resolve_semconv_specs, semconv_registry_path_from, }; -use crate::DiagnosticArgs; +use crate::{DiagnosticArgs, ExitDirectives}; /// Parameters for the `registry resolve` sub-command #[derive(Debug, Args)] @@ -65,7 +65,10 @@ pub(crate) fn command( logger: impl Logger + Sync + Clone, cache: &Cache, args: &RegistryResolveArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { + if args.output.is_none() { + logger.mute(); + } logger.loading(&format!("Resolving registry `{}`", args.registry.registry)); let registry_id = "default"; @@ -116,7 +119,10 @@ pub(crate) fn command( panic!("{}", e); }); - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: args.output.is_none(), + }) } #[cfg(test)] @@ -153,9 +159,9 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); // Now, let's run the command again with the policy checks enabled. let cli = Cli { @@ -179,8 +185,8 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger); + let exit_directive = run_command(&cli, logger); // The command should exit with an error code. - assert_eq!(exit_code, 1); + assert_eq!(exit_directive.exit_code, 1); } } diff --git a/src/registry/stats.rs b/src/registry/stats.rs index cbc8a3a4..bd1c1564 100644 --- a/src/registry/stats.rs +++ b/src/registry/stats.rs @@ -4,7 +4,7 @@ use crate::registry::RegistryArgs; use crate::util::{load_semconv_specs, resolve_semconv_specs, semconv_registry_path_from}; -use crate::DiagnosticArgs; +use crate::{DiagnosticArgs, ExitDirectives}; use clap::Args; use weaver_cache::Cache; use weaver_common::diagnostic::DiagnosticMessages; @@ -32,7 +32,7 @@ pub(crate) fn command( logger: impl Logger + Sync + Clone, cache: &Cache, args: &RegistryStatsArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { logger.loading(&format!( "Compute statistics on the registry `{}`", args.registry.registry @@ -52,7 +52,10 @@ pub(crate) fn command( let resolved_schema = resolve_semconv_specs(&mut registry, logger)?; display_schema_stats(&resolved_schema); - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: false, + }) } #[cfg(not(tarpaulin_include))] diff --git a/src/registry/update_markdown.rs b/src/registry/update_markdown.rs index b919bea6..7c07aa05 100644 --- a/src/registry/update_markdown.rs +++ b/src/registry/update_markdown.rs @@ -5,7 +5,7 @@ use crate::registry::RegistryArgs; use crate::util::semconv_registry_path_from; -use crate::DiagnosticArgs; +use crate::{DiagnosticArgs, ExitDirectives}; use clap::Args; use weaver_cache::Cache; use weaver_common::diagnostic::DiagnosticMessages; @@ -56,7 +56,7 @@ pub(crate) fn command( log: impl Logger + Sync + Clone, cache: &Cache, args: &RegistryUpdateMarkdownArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { fn is_markdown(entry: &walkdir::DirEntry) -> bool { let path = entry.path(); let extension = path.extension().unwrap_or_else(|| std::ffi::OsStr::new("")); @@ -108,7 +108,10 @@ pub(crate) fn command( panic!("weaver registry update-markdown failed."); } - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: false, + }) } #[cfg(test)] @@ -142,8 +145,8 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); } } diff --git a/src/schema/resolve.rs b/src/schema/resolve.rs index 9e2a38f3..55866345 100644 --- a/src/schema/resolve.rs +++ b/src/schema/resolve.rs @@ -7,7 +7,7 @@ use crate::registry::RegistryArgs; use crate::util::{ check_policies, load_semconv_specs, resolve_semconv_specs, semconv_registry_path_from, }; -use crate::{format, DiagnosticArgs}; +use crate::{format, DiagnosticArgs, ExitDirectives}; use clap::Args; use std::path::PathBuf; use weaver_cache::Cache; @@ -56,7 +56,7 @@ pub(crate) fn command( logger: impl Logger + Sync + Clone, cache: &Cache, args: &SchemaResolveArgs, -) -> Result<(), DiagnosticMessages> { +) -> Result { logger.loading(&format!("Resolving schema `{}`", args.registry.registry)); let registry_id = "default"; @@ -99,7 +99,10 @@ pub(crate) fn command( panic!("{}", e); }); - Ok(()) + Ok(ExitDirectives { + exit_code: 0, + quiet_mode: args.output.is_none(), + }) } #[cfg(test)] @@ -136,9 +139,9 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger.clone()); + let exit_directive = run_command(&cli, logger.clone()); // The command should succeed. - assert_eq!(exit_code, 0); + assert_eq!(exit_directive.exit_code, 0); // Now, let's run the command again with the policy checks enabled. let cli = Cli { @@ -161,8 +164,8 @@ mod tests { })), }; - let exit_code = run_command(&cli, logger); + let exit_directive = run_command(&cli, logger); // The command should exit with an error code. - assert_eq!(exit_code, 1); + assert_eq!(exit_directive.exit_code, 1); } }