diff --git a/Cargo.lock b/Cargo.lock index cb78e346..f21609b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" dependencies = [ "cfg-if", "once_cell", @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -720,11 +720,12 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "b5b25e5b3e733153bcab35ee4671b46604b42516163cae442d1601cb716f2ac5" dependencies = [ "cc", + "cfg-if", "libc", "log", "rustversion", @@ -1701,7 +1702,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -3937,11 +3938,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ - "windows-targets 0.48.5", + "windows-core 0.53.0", + "windows-targets 0.52.3", ] [[package]] @@ -3953,6 +3955,25 @@ dependencies = [ "windows-targets 0.52.3", ] +[[package]] +name = "windows-core" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.3", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/weaver_resolved_schema/src/catalog.rs b/crates/weaver_resolved_schema/src/catalog.rs index 1e0e6927..8e190ff9 100644 --- a/crates/weaver_resolved_schema/src/catalog.rs +++ b/crates/weaver_resolved_schema/src/catalog.rs @@ -35,9 +35,9 @@ pub enum Stability { impl Catalog { /// Returns the attribute name from an attribute ref if it exists /// in the catalog or None if it does not exist. - pub fn attribute_name(&self, attribute_ref: &AttributeRef) -> Option { + pub fn attribute_name(&self, attribute_ref: &AttributeRef) -> Option<&str> { self.attributes .get(attribute_ref.0 as usize) - .map(|attr| attr.name.clone()) + .map(|attr| attr.name.as_ref()) } } diff --git a/crates/weaver_resolved_schema/src/registry.rs b/crates/weaver_resolved_schema/src/registry.rs index ac8d5146..b1b9562a 100644 --- a/crates/weaver_resolved_schema/src/registry.rs +++ b/crates/weaver_resolved_schema/src/registry.rs @@ -125,7 +125,7 @@ pub enum TypedGroup { } /// Allow to define additional requirements on the semantic convention. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Hash, Eq)] #[serde(deny_unknown_fields)] pub struct Constraint { /// any_of accepts a list of sequences. Each sequence contains a list of @@ -170,4 +170,12 @@ impl Group { c.include.is_none() || !include_to_remove.contains(c.include.as_ref().unwrap()) }); } + + /// Returns the provenance of the group. + pub fn provenance(&self) -> &str { + match &self.lineage { + Some(lineage) => lineage.provenance(), + None => "unknown", + } + } } diff --git a/crates/weaver_resolver/src/lib.rs b/crates/weaver_resolver/src/lib.rs index 6dfd3fe3..486ce3e8 100644 --- a/crates/weaver_resolver/src/lib.rs +++ b/crates/weaver_resolver/src/lib.rs @@ -19,8 +19,8 @@ use walkdir::DirEntry; use weaver_cache::Cache; use weaver_logger::Logger; -use weaver_resolved_schema::attribute::AttributeRef; use weaver_resolved_schema::catalog::Catalog; +use weaver_resolved_schema::registry::Constraint; use weaver_resolved_schema::ResolvedTelemetrySchema; use weaver_schema::{SemConvImport, TelemetrySchema}; use weaver_semconv::{ResolverConfig, SemConvRegistry, SemConvSpec, SemConvSpecWithProvenance}; @@ -148,22 +148,22 @@ pub enum Error { }, /// The `any_of` constraints are unsatisfied for a group. - #[error("The `any_of` constraints are unsatisfied for the group '{group_id}'.\nGroup attributes: {group_attributes:#?}\n`any_of` constraints: {any_of_constraints:#?}")] - UnsatisfiedAnyOfConstraint { + #[error("Some `any_of` constraints are unsatisfied for the group '{group_id}'.\nUnsatisfied `any_of` constraints: {any_of_constraints:#?}")] + UnsatisfiedAnyOfConstraints { /// The id of the group containing the unsatisfied `any_of` constraint. group_id: String, - /// The attributes of the group. - group_attributes: Vec, - /// The `any_of` constraints of the group. - any_of_constraints: Vec>, + /// The `any_of` constraints of the group that are not satisfied. + any_of_constraints: Vec, }, +} - /// Attribute ref not found in the catalog. - #[error("Attribute ref `{attribute_ref:?}` not found in the catalog.")] - UnresolvedAttribute { - /// The unresolved attribute reference. - attribute_ref: AttributeRef, - }, +/// A constraint that is not satisfied and its missing attributes. +#[derive(Debug)] +pub struct UnsatisfiedAnyOfConstraint { + /// The `any_of` constraint that is not satisfied. + pub any_of: Constraint, + /// The detected missing attributes. + pub missing_attributes: Vec, } impl SchemaResolver { diff --git a/crates/weaver_resolver/src/registry.rs b/crates/weaver_resolver/src/registry.rs index f549e851..2aa2d4f4 100644 --- a/crates/weaver_resolver/src/registry.rs +++ b/crates/weaver_resolver/src/registry.rs @@ -2,9 +2,10 @@ //! Functions to resolve a semantic convention registry. -use serde::Deserialize; use std::collections::{HashMap, HashSet}; +use serde::Deserialize; + use weaver_resolved_schema::attribute::UnresolvedAttribute; use weaver_resolved_schema::lineage::{FieldId, FieldLineage, GroupLineage, ResolutionMode}; use weaver_resolved_schema::registry::{Constraint, Group, Registry, TypedGroup}; @@ -17,7 +18,7 @@ use crate::constraint::resolve_constraints; use crate::metrics::resolve_instrument; use crate::spans::resolve_span_kind; use crate::stability::resolve_stability; -use crate::{Error, UnresolvedReference}; +use crate::{Error, UnresolvedReference, UnsatisfiedAnyOfConstraint}; /// A registry containing unresolved groups. #[derive(Debug, Deserialize)] @@ -161,17 +162,22 @@ pub fn check_any_of_constraints( registry: &Registry, attr_name_index: &[String], ) -> Result<(), Error> { + let mut unresolved_refs = vec![]; + for group in registry.groups.iter() { // Build a list of attribute names for the group. let mut group_attr_names = HashSet::new(); for attr_ref in group.attributes.iter() { - let attr_name = - attr_name_index - .get(attr_ref.0 as usize) - .ok_or(Error::UnresolvedAttribute { - attribute_ref: *attr_ref, - })?; - group_attr_names.insert(attr_name.clone()); + match attr_name_index.get(attr_ref.0 as usize) { + None => unresolved_refs.push(UnresolvedReference::AttributeRef { + group_id: group.id.clone(), + attribute_ref: attr_ref.0.to_string(), + provenance: group.provenance().to_string(), + }), + Some(attr_name) => { + group_attr_names.insert(attr_name.clone()); + } + } } check_group_any_of_constraints( @@ -181,6 +187,12 @@ pub fn check_any_of_constraints( )?; } + if !unresolved_refs.is_empty() { + return Err(Error::UnresolvedReferences { + refs: unresolved_refs, + }); + } + Ok(()) } @@ -190,35 +202,38 @@ fn check_group_any_of_constraints( group_attr_names: HashSet, constraints: &[Constraint], ) -> Result<(), Error> { - let mut any_of_unsatisfied = 0; - let mut any_of_total = 0; - let mut any_of_constraints = vec![]; - 'outer: for constraint in constraints.iter() { + let mut unsatisfied_any_of_constraints: HashMap<&Constraint, Vec> = HashMap::new(); + + for constraint in constraints.iter() { if constraint.any_of.is_empty() { continue; } - any_of_total += 1; // Check if the group satisfies the `any_of` constraint. - any_of_constraints.push(constraint.any_of.clone()); - for attr_name in constraint.any_of.iter() { - if !group_attr_names.contains(attr_name) { - // The any_of constraint is not satisfied. - // Continue to the next constraint. - any_of_unsatisfied += 1; - continue 'outer; - } + if let Some(attr) = constraint + .any_of + .iter() + .find(|name| !group_attr_names.contains(*name)) + { + // The any_of constraint is not satisfied. + // Insert the attribute into the list of missing attributes for the + // constraint. + unsatisfied_any_of_constraints + .entry(constraint) + .or_default() + .push(attr.clone()); } } - if any_of_total > 0 && any_of_total == any_of_unsatisfied { - let group_attributes: Vec = group_attr_names - .iter() - .map(|name| name.to_string()) - .collect(); - return Err(Error::UnsatisfiedAnyOfConstraint { + if !unsatisfied_any_of_constraints.is_empty() { + return Err(Error::UnsatisfiedAnyOfConstraints { group_id: group_id.to_string(), - group_attributes, - any_of_constraints, + any_of_constraints: unsatisfied_any_of_constraints + .into_iter() + .map(|(c, attrs)| UnsatisfiedAnyOfConstraint { + any_of: c.clone(), + missing_attributes: attrs, + }) + .collect(), }); } Ok(())