diff --git a/src/cli.rs b/src/cli.rs index 05da6218..ad7357f2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,7 +4,6 @@ use crate::diagnostic::DiagnosticCommand; use crate::registry::RegistryCommand; -use crate::schema::SchemaCommand; use clap::{Parser, Subcommand}; /// Command line arguments. @@ -36,8 +35,6 @@ pub struct Cli { pub enum Commands { /// Manage Semantic Convention Registry Registry(RegistryCommand), - /// Manage Telemetry Schema - Schema(SchemaCommand), /// Manage Diagnostic Messages Diagnostic(DiagnosticCommand), } diff --git a/src/main.rs b/src/main.rs index e7cbfe94..449ebe5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,13 +15,11 @@ use weaver_forge::{OutputDirective, TemplateEngine}; use crate::cli::{Cli, Commands}; use crate::diagnostic::DEFAULT_DIAGNOSTIC_TEMPLATES; -use crate::schema::telemetry_schema; mod cli; mod diagnostic; mod format; mod registry; -mod schema; mod util; /// Set of parameters used to specify the diagnostic format. @@ -103,7 +101,6 @@ fn main() { 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 ExitDirectives { diff --git a/src/schema/json_schema.rs b/src/schema/json_schema.rs deleted file mode 100644 index 20e5ce47..00000000 --- a/src/schema/json_schema.rs +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -//! Generate the JSON Schema of the resolved telemetry schema. - -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_resolved_schema::ResolvedTelemetrySchema; - -/// Parameters for the `schema json-schema` sub-command -#[derive(Debug, Args)] -pub struct SchemaJsonSchemaArgs { - /// 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 Telemetry Schema 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: &SchemaJsonSchemaArgs, -) -> Result { - let json_schema = schema_for!(ResolvedTelemetrySchema); - - 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::run_command; - use crate::schema::json_schema::SchemaJsonSchemaArgs; - use crate::schema::{SchemaCommand, SchemaSubCommand}; - - #[test] - fn test_registry_json_schema() { - let logger = in_memory::Logger::new(0); - let cli = Cli { - debug: 0, - quiet: false, - command: Some(Commands::Schema(SchemaCommand { - command: SchemaSubCommand::JsonSchema(SchemaJsonSchemaArgs { - 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/schema/mod.rs b/src/schema/mod.rs deleted file mode 100644 index 74fef02e..00000000 --- a/src/schema/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -//! Commands to manage a telemetry schema. - -mod json_schema; -mod resolve; - -use crate::schema::json_schema::SchemaJsonSchemaArgs; -use crate::schema::resolve::SchemaResolveArgs; -use crate::CmdResult; -use clap::{Args, Subcommand}; -use weaver_cache::Cache; -use weaver_common::Logger; - -/// Parameters for the `registry` command -#[derive(Debug, Args)] -pub struct SchemaCommand { - /// Define the sub-commands for the `schema` command - #[clap(subcommand)] - pub command: SchemaSubCommand, -} - -/// Sub-commands to manage a `schema`. -#[derive(Debug, Subcommand)] -#[clap(verbatim_doc_comment)] -pub enum SchemaSubCommand { - /// Resolves a telemetry schema. - /// - /// Rego policies present in the registry or specified using -p or --policy will be automatically validated by the policy engine before the artifact generation phase. - /// - /// Note: The `-d` and `--registry-git-sub-dir` options are only used when the registry is a Git URL otherwise these options are ignored. - /// - /// The process exits with a code of 0 if the resolution is successful. - #[clap(verbatim_doc_comment)] - Resolve(SchemaResolveArgs), - /// Generate the JSON Schema of a resolved telemetry schema. - /// - /// 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 telemetry schema format for any reason. - #[clap(verbatim_doc_comment)] - JsonSchema(SchemaJsonSchemaArgs), -} - -/// Manage a telemetry schema and return the exit code. -pub fn telemetry_schema(log: impl Logger + Sync + Clone, command: &SchemaCommand) -> CmdResult { - let cache = match Cache::try_new() { - Ok(cache) => cache, - Err(e) => return CmdResult::new(Err(e.into()), None), - }; - - match &command.command { - SchemaSubCommand::Resolve(args) => CmdResult::new( - resolve::command(log.clone(), &cache, args), - Some(args.diagnostic.clone()), - ), - SchemaSubCommand::JsonSchema(args) => CmdResult::new( - json_schema::command(log.clone(), &cache, args), - Some(args.diagnostic.clone()), - ), - } -} diff --git a/src/schema/resolve.rs b/src/schema/resolve.rs deleted file mode 100644 index 55866345..00000000 --- a/src/schema/resolve.rs +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -//! Resolve a telemetry schema. - -use crate::format::Format; -use crate::registry::RegistryArgs; -use crate::util::{ - check_policies, load_semconv_specs, resolve_semconv_specs, semconv_registry_path_from, -}; -use crate::{format, DiagnosticArgs, ExitDirectives}; -use clap::Args; -use std::path::PathBuf; -use weaver_cache::Cache; -use weaver_common::diagnostic::DiagnosticMessages; -use weaver_common::Logger; -use weaver_semconv::registry::SemConvRegistry; - -/// Parameters for the `schema resolve` sub-command -#[derive(Debug, Args)] -pub struct SchemaResolveArgs { - /// Parameters to specify the semantic convention registry - #[command(flatten)] - registry: RegistryArgs, - - /// Output file to write the resolved schema to - /// If not specified, the resolved schema is printed to stdout - #[arg(short, long)] - output: Option, - - /// Output format for the resolved schema - /// If not specified, the resolved schema is printed in YAML format - /// Supported formats: yaml, json - /// Default format: yaml - /// Example: `--format json` - #[arg(short, long, default_value = "yaml")] - format: Format, - - /// Optional list of policy files to check against the files of the semantic - /// convention registry. - #[arg(short = 'p', long = "policy")] - pub policies: Vec, - - /// Skip the policy checks. - #[arg(long, default_value = "false")] - pub skip_policies: bool, - - /// Parameters to specify the diagnostic format. - #[command(flatten)] - pub diagnostic: DiagnosticArgs, -} - -/// Resolve a telemetry schema and write the resolved 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: &SchemaResolveArgs, -) -> Result { - logger.loading(&format!("Resolving schema `{}`", args.registry.registry)); - - let registry_id = "default"; - let registry_path = - semconv_registry_path_from(&args.registry.registry, &args.registry.registry_git_sub_dir); - - // Load the semantic convention registry into a local cache. - let semconv_specs = load_semconv_specs(®istry_path, cache, logger.clone())?; - - if !args.skip_policies { - check_policies( - ®istry_path, - cache, - &args.policies, - &semconv_specs, - logger.clone(), - )?; - } - - let mut registry = SemConvRegistry::from_semconv_specs(registry_id, semconv_specs); - let schema = resolve_semconv_specs(&mut registry, logger.clone())?; - - // Serialize the resolved schema and write it - // to a file or print it to stdout. - format::apply_format(&args.format, &schema) - .map_err(|e| format!("Failed to serialize the registry: {e:?}")) - .and_then(|s| { - if let Some(ref path) = args.output { - // Write the resolved registry to a file. - std::fs::write(path, s) - .map_err(|e| format!("Failed to write the resolved registry to file: {e:?}")) - } else { - // Print the resolved registry to stdout. - println!("{}", s); - Ok(()) - } - }) - .unwrap_or_else(|e| { - // Capture all the errors - panic!("{}", e); - }); - - Ok(ExitDirectives { - exit_code: 0, - quiet_mode: args.output.is_none(), - }) -} - -#[cfg(test)] -mod tests { - use weaver_common::TestLogger; - - use crate::cli::{Cli, Commands}; - use crate::format::Format; - use crate::registry::{RegistryArgs, RegistryPath}; - use crate::run_command; - use crate::schema::resolve::SchemaResolveArgs; - use crate::schema::{SchemaCommand, SchemaSubCommand}; - - #[test] - fn test_schema_resolve() { - let logger = TestLogger::new(); - let cli = Cli { - debug: 0, - quiet: false, - command: Some(Commands::Schema(SchemaCommand { - command: SchemaSubCommand::Resolve(SchemaResolveArgs { - registry: RegistryArgs { - registry: RegistryPath::Local( - "crates/weaver_codegen_test/semconv_registry/".to_owned(), - ), - registry_git_sub_dir: None, - }, - output: None, - format: Format::Yaml, - policies: vec![], - skip_policies: true, - diagnostic: Default::default(), - }), - })), - }; - - let exit_directive = run_command(&cli, logger.clone()); - // The command should succeed. - assert_eq!(exit_directive.exit_code, 0); - - // Now, let's run the command again with the policy checks enabled. - let cli = Cli { - debug: 0, - quiet: false, - command: Some(Commands::Schema(SchemaCommand { - command: SchemaSubCommand::Resolve(SchemaResolveArgs { - registry: RegistryArgs { - registry: RegistryPath::Local( - "crates/weaver_codegen_test/semconv_registry/".to_owned(), - ), - registry_git_sub_dir: None, - }, - output: None, - format: Format::Json, - policies: vec![], - skip_policies: false, - diagnostic: Default::default(), - }), - })), - }; - - let exit_directive = run_command(&cli, logger); - // The command should exit with an error code. - assert_eq!(exit_directive.exit_code, 1); - } -}