diff --git a/crates/weaver_forge/src/extensions/otel.rs b/crates/weaver_forge/src/extensions/otel.rs index 0ca85d7d..45557d91 100644 --- a/crates/weaver_forge/src/extensions/otel.rs +++ b/crates/weaver_forge/src/extensions/otel.rs @@ -666,6 +666,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "rec.b".into(), @@ -680,6 +681,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "crec.a".into(), @@ -694,6 +696,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "crec.b".into(), @@ -708,6 +711,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "rec.c".into(), @@ -722,6 +726,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "rec.d".into(), @@ -736,6 +741,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "opt.a".into(), @@ -750,6 +756,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "opt.b".into(), @@ -764,6 +771,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "req.a".into(), @@ -778,6 +786,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "req.b".into(), @@ -792,6 +801,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, ]; let json = @@ -849,6 +859,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "attr2".to_owned(), @@ -863,6 +874,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, Attribute { name: "attr3".to_owned(), @@ -877,6 +889,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }, ]; @@ -1163,6 +1176,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }; add_tests_and_filters(&mut env); @@ -1189,6 +1203,7 @@ mod tests { deprecated: None, tags: None, value: None, + prefix: false, }; add_tests_and_filters(&mut env); diff --git a/crates/weaver_resolved_schema/src/attribute.rs b/crates/weaver_resolved_schema/src/attribute.rs index 09acd37f..8326a9c1 100644 --- a/crates/weaver_resolved_schema/src/attribute.rs +++ b/crates/weaver_resolved_schema/src/attribute.rs @@ -9,6 +9,7 @@ use crate::value::Value; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::Display; +use std::ops::Not; use weaver_semconv::attribute::{AttributeSpec, AttributeType, Examples, RequirementLevel}; use weaver_semconv::stability::Stability; @@ -64,6 +65,13 @@ pub struct Attribute { /// to use instead. See also stability. #[serde(skip_serializing_if = "Option::is_none")] pub deprecated: Option, + /// Specifies the prefix of the attribute. + /// If this parameter is set, the resolved id of the referenced attribute will + /// have group prefix added to it. + /// It defaults to false. + #[serde(default)] + #[serde(skip_serializing_if = "<&bool>::not")] + pub prefix: bool, /// A set of tags for the attribute. #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option, diff --git a/crates/weaver_resolved_schema/src/lineage.rs b/crates/weaver_resolved_schema/src/lineage.rs index 9dca09c5..32b127f3 100644 --- a/crates/weaver_resolved_schema/src/lineage.rs +++ b/crates/weaver_resolved_schema/src/lineage.rs @@ -74,6 +74,7 @@ impl AttributeLineage { note, stability, deprecated, + prefix, .. } => { if brief.is_some() { @@ -106,6 +107,9 @@ impl AttributeLineage { .inherited_fields .insert("deprecated".to_owned()); } + if *prefix { + _ = attr_lineage.inherited_fields.insert("prefix".to_owned()); + } } AttributeSpec::Id { brief, @@ -394,6 +398,21 @@ impl AttributeLineage { *parent_value } } + + /// This method updates the lineage information for the prefix field to + /// reflect the source of its value. + pub fn prefix(&mut self, local_value: &bool, parent_value: &bool) -> bool { + if *local_value { + _ = self.locally_overridden_fields.insert("prefix".to_owned()); + _ = self.inherited_fields.remove("prefix"); + *local_value + } else { + if *parent_value { + _ = self.inherited_fields.insert("prefix".to_owned()); + } + *parent_value + } + } } impl GroupLineage { diff --git a/crates/weaver_resolver/data/registry-test-10-prefix-refs/README.md b/crates/weaver_resolver/data/registry-test-10-prefix-refs/README.md new file mode 100644 index 00000000..e3ccb3ec --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-10-prefix-refs/README.md @@ -0,0 +1 @@ +Test adding a prefix to the references in attributes. \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-10-prefix-refs/expected-attribute-catalog.json b/crates/weaver_resolver/data/registry-test-10-prefix-refs/expected-attribute-catalog.json new file mode 100644 index 00000000..28db740d --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-10-prefix-refs/expected-attribute-catalog.json @@ -0,0 +1,63 @@ +[ + { + "name": "client.id", + "type": "string", + "brief": "some client id\n", + "examples": [ + "a.einstein" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.lat", + "type": "string", + "brief": "latitude of the geo coordinates.\n", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.lon", + "type": "string", + "brief": "longitude of the geo coordinates.\n", + "examples": [ + "234.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.lat", + "type": "string", + "brief": "this is ref attribute", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "client.geo.lat", + "type": "string", + "brief": "My own latitude", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental", + "prefix": true + }, + { + "name": "client.geo.lat", + "type": "string", + "brief": "this is embedded attribute", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + } +] \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-10-prefix-refs/expected-registry.json b/crates/weaver_resolver/data/registry-test-10-prefix-refs/expected-registry.json new file mode 100644 index 00000000..95fd52de --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-10-prefix-refs/expected-registry.json @@ -0,0 +1,106 @@ +{ + "registry_url": "https://127.0.0.1", + "groups": [ + { + "id": "registry.client", + "type": "attribute_group", + "brief": "Describes information about the client.", + "prefix": "client", + "attributes": [ + 0, + 4 + ], + "lineage": { + "source_file": "data/registry-test-10-prefix-refs/registry/client.yaml", + "attributes": { + "client.geo.lat": { + "source_group": "registry.geo", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + } + } + } + }, + { + "id": "registry.geo", + "type": "attribute_group", + "brief": "Attributes describing geo.", + "prefix": "geo", + "attributes": [ + 1, + 2 + ], + "lineage": { + "source_file": "data/registry-test-10-prefix-refs/registry/geo.yaml" + } + }, + { + "id": "usage", + "type": "attribute_group", + "brief": "Attributes for test.", + "prefix": "usage", + "attributes": [ + 0, + 2, + 3, + 5 + ], + "lineage": { + "source_file": "data/registry-test-10-prefix-refs/registry/usage.yaml", + "attributes": { + "client.geo.lat": { + "source_group": "registry.client", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "client.id": { + "source_group": "registry.client", + "inherited_fields": [ + "brief", + "examples", + "note", + "requirement_level", + "stability" + ] + }, + "geo.lat": { + "source_group": "registry.geo", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "geo.lon": { + "source_group": "registry.geo", + "inherited_fields": [ + "brief", + "examples", + "note", + "requirement_level", + "stability" + ] + } + } + } + } + ] +} \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/client.yaml b/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/client.yaml new file mode 100644 index 00000000..97d88f68 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/client.yaml @@ -0,0 +1,15 @@ +groups: + - id: registry.client + prefix: client + type: attribute_group + brief: "Describes information about the client." + attributes: + - id: id + type: string + stability: experimental + brief: > + some client id + examples: ['a.einstein'] + - ref: geo.lat + prefix: true + brief: My own latitude \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/geo.yaml b/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/geo.yaml new file mode 100644 index 00000000..f826fbf3 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/geo.yaml @@ -0,0 +1,18 @@ +groups: + - id: registry.geo + brief: Attributes describing geo. + type: attribute_group + prefix: geo + attributes: + - id: lat + type: string + stability: experimental + brief: > + latitude of the geo coordinates. + examples: ["123.456"] + - id: lon + type: string + stability: experimental + brief: > + longitude of the geo coordinates. + examples: [ "234.456" ] diff --git a/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/usage.yaml b/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/usage.yaml new file mode 100644 index 00000000..4c1beda0 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-10-prefix-refs/registry/usage.yaml @@ -0,0 +1,12 @@ +groups: + - id: usage + brief: Attributes for test. + type: attribute_group + prefix: usage + attributes: + - ref: geo.lat + brief: this is ref attribute + - ref: client.id + - ref: geo.lon + - ref: client.geo.lat + brief: this is embedded attribute diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/README.md b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/README.md new file mode 100644 index 00000000..8f28dd72 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/README.md @@ -0,0 +1 @@ +Test adding a prefix to the references in attributes together with extends keyword. \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/expected-attribute-catalog.json b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/expected-attribute-catalog.json new file mode 100644 index 00000000..12e05769 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/expected-attribute-catalog.json @@ -0,0 +1,83 @@ +[ + { + "name": "client.id", + "type": "string", + "brief": "some client id\n", + "examples": [ + "a.einstein" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.lat", + "type": "string", + "brief": "latitude of the geo coordinates.\n", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.lon", + "type": "string", + "brief": "longitude of the geo coordinates.\n", + "examples": [ + "234.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.country", + "type": "string", + "brief": "country of the geo coordinates.\n", + "examples": [ + "US" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.lat", + "type": "string", + "brief": "this is ref attribute", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "geo.country", + "type": "string", + "brief": "this is ref country attribute", + "examples": [ + "US" + ], + "requirement_level": "recommended", + "stability": "experimental" + }, + { + "name": "client.geo.lat", + "type": "string", + "brief": "My own latitude", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental", + "prefix": true + }, + { + "name": "client.geo.lat", + "type": "string", + "brief": "this is embedded attribute", + "examples": [ + "123.456" + ], + "requirement_level": "recommended", + "stability": "experimental" + } +] \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/expected-registry.json b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/expected-registry.json new file mode 100644 index 00000000..61032781 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/expected-registry.json @@ -0,0 +1,181 @@ +{ + "registry_url": "https://127.0.0.1", + "groups": [ + { + "id": "registry.client", + "type": "attribute_group", + "brief": "Describes information about the client.", + "prefix": "client", + "attributes": [ + 0, + 6 + ], + "lineage": { + "source_file": "data/registry-test-11-prefix-refs-extends/registry/client.yaml", + "attributes": { + "client.geo.lat": { + "source_group": "registry.geo", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + } + } + } + }, + { + "id": "registry.geo", + "type": "attribute_group", + "brief": "Attributes describing geo.", + "prefix": "geo", + "attributes": [ + 1, + 2, + 3 + ], + "lineage": { + "source_file": "data/registry-test-11-prefix-refs-extends/registry/geo.yaml" + } + }, + { + "id": "usage", + "type": "attribute_group", + "brief": "Attributes for test.", + "prefix": "usage", + "attributes": [ + 0, + 2, + 4, + 7 + ], + "lineage": { + "source_file": "data/registry-test-11-prefix-refs-extends/registry/usage.yaml", + "attributes": { + "client.geo.lat": { + "source_group": "registry.client", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "client.id": { + "source_group": "registry.client", + "inherited_fields": [ + "brief", + "examples", + "note", + "requirement_level", + "stability" + ] + }, + "geo.lat": { + "source_group": "registry.geo", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "geo.lon": { + "source_group": "registry.geo", + "inherited_fields": [ + "brief", + "examples", + "note", + "requirement_level", + "stability" + ] + } + } + } + }, + { + "id": "usage2", + "type": "attribute_group", + "brief": "Attributes for test.", + "prefix": "usage2", + "attributes": [ + 0, + 2, + 4, + 5, + 7 + ], + "lineage": { + "source_file": "data/registry-test-11-prefix-refs-extends/registry/usage2.yaml", + "attributes": { + "client.geo.lat": { + "source_group": "registry.client", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "client.id": { + "source_group": "registry.client", + "inherited_fields": [ + "brief", + "examples", + "note", + "requirement_level", + "stability" + ] + }, + "geo.country": { + "source_group": "registry.geo", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "geo.lat": { + "source_group": "registry.geo", + "inherited_fields": [ + "examples", + "note", + "requirement_level", + "stability" + ], + "locally_overridden_fields": [ + "brief" + ] + }, + "geo.lon": { + "source_group": "registry.geo", + "inherited_fields": [ + "brief", + "examples", + "note", + "requirement_level", + "stability" + ] + } + } + } + } + ] +} \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/client.yaml b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/client.yaml new file mode 100644 index 00000000..97d88f68 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/client.yaml @@ -0,0 +1,15 @@ +groups: + - id: registry.client + prefix: client + type: attribute_group + brief: "Describes information about the client." + attributes: + - id: id + type: string + stability: experimental + brief: > + some client id + examples: ['a.einstein'] + - ref: geo.lat + prefix: true + brief: My own latitude \ No newline at end of file diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/geo.yaml b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/geo.yaml new file mode 100644 index 00000000..51693c48 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/geo.yaml @@ -0,0 +1,24 @@ +groups: + - id: registry.geo + brief: Attributes describing geo. + type: attribute_group + prefix: geo + attributes: + - id: lat + type: string + stability: experimental + brief: > + latitude of the geo coordinates. + examples: ["123.456"] + - id: lon + type: string + stability: experimental + brief: > + longitude of the geo coordinates. + examples: [ "234.456" ] + - id: country + type: string + stability: experimental + brief: > + country of the geo coordinates. + examples: [ "US" ] diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/usage.yaml b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/usage.yaml new file mode 100644 index 00000000..4c1beda0 --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/usage.yaml @@ -0,0 +1,12 @@ +groups: + - id: usage + brief: Attributes for test. + type: attribute_group + prefix: usage + attributes: + - ref: geo.lat + brief: this is ref attribute + - ref: client.id + - ref: geo.lon + - ref: client.geo.lat + brief: this is embedded attribute diff --git a/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/usage2.yaml b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/usage2.yaml new file mode 100644 index 00000000..a6a559ca --- /dev/null +++ b/crates/weaver_resolver/data/registry-test-11-prefix-refs-extends/registry/usage2.yaml @@ -0,0 +1,9 @@ +groups: + - id: usage2 + brief: Attributes for test. + type: attribute_group + prefix: usage2 + extends: usage + attributes: + - ref: geo.country + brief: this is ref country attribute diff --git a/crates/weaver_resolver/src/attribute.rs b/crates/weaver_resolver/src/attribute.rs index dd745d44..bd617df5 100644 --- a/crates/weaver_resolver/src/attribute.rs +++ b/crates/weaver_resolver/src/attribute.rs @@ -66,6 +66,7 @@ impl AttributeCatalog { pub fn resolve( &mut self, group_id: &str, + group_prefix: &str, attr: &AttributeSpec, lineage: Option<&mut GroupLineage>, ) -> Option { @@ -80,16 +81,25 @@ impl AttributeCatalog { note, stability, deprecated, + prefix, } => { + let name; let root_attr = self.root_attributes.get(r#ref); if let Some(root_attr) = root_attr { let mut attr_lineage = AttributeLineage::new(&root_attr.group_id); + if *prefix { + // depending on the prefix we either create embedded attribute or normal reference + name = format!("{}.{}", group_prefix, r#ref); + } else { + name = r#ref.clone(); + } + // Create a fully resolved attribute from an attribute spec // (ref) and override the root attribute with the new // values if they are present. let resolved_attr = attribute::Attribute { - name: r#ref.clone(), + name: name.clone(), r#type: root_attr.attribute.r#type.clone(), brief: attr_lineage.brief(brief, &root_attr.attribute.brief), examples: attr_lineage.examples(examples, &root_attr.attribute.examples), @@ -109,14 +119,27 @@ impl AttributeCatalog { .deprecated(deprecated, &root_attr.attribute.deprecated), tags: root_attr.attribute.tags.clone(), value: root_attr.attribute.value.clone(), + prefix: *prefix, }; - let attr_ref = self.attribute_ref(resolved_attr); + let attr_ref = self.attribute_ref(resolved_attr.clone()); // Update the lineage based on the inherited fields. // Note: the lineage is only updated if a group lineage is provided. if let Some(lineage) = lineage { - lineage.add_attribute_lineage(r#ref.to_owned(), attr_lineage); + lineage.add_attribute_lineage(name.clone(), attr_lineage); + } + + if *prefix { + // if it's a prefix with reference + // we need to add it to the dictionary of resolved attributes + _ = self.root_attributes.insert( + name, + AttributeWithGroupId { + attribute: resolved_attr, + group_id: group_id.to_owned(), + }, + ); } Some(attr_ref) @@ -153,6 +176,7 @@ impl AttributeCatalog { deprecated: deprecated.clone(), tags: None, value: None, + prefix: false, }; _ = self.root_attributes.insert( diff --git a/crates/weaver_resolver/src/registry.rs b/crates/weaver_resolver/src/registry.rs index 3df9bf2f..fa7c6241 100644 --- a/crates/weaver_resolver/src/registry.rs +++ b/crates/weaver_resolver/src/registry.rs @@ -318,6 +318,7 @@ fn resolve_attribute_references( .filter_map(|attr| { let attr_ref = attr_catalog.resolve( &unresolved_group.group.id, + &unresolved_group.group.prefix, &attr.spec, unresolved_group.group.lineage.as_mut(), ); @@ -616,6 +617,7 @@ fn resolve_inheritance_attr( note, stability, deprecated, + prefix, } => { match parent_attr { AttributeSpec::Ref { @@ -627,6 +629,7 @@ fn resolve_inheritance_attr( note: parent_note, stability: parent_stability, deprecated: parent_deprecated, + prefix: parent_prefix, .. } => { // attr and attr_parent are both references. @@ -644,6 +647,7 @@ fn resolve_inheritance_attr( note: lineage.optional_note(note, parent_note), stability: lineage.stability(stability, parent_stability), deprecated: lineage.deprecated(deprecated, parent_deprecated), + prefix: lineage.prefix(prefix, parent_prefix), } } AttributeSpec::Id { diff --git a/crates/weaver_semconv/src/attribute.rs b/crates/weaver_semconv/src/attribute.rs index 0bb2084a..83dfe7c3 100644 --- a/crates/weaver_semconv/src/attribute.rs +++ b/crates/weaver_semconv/src/attribute.rs @@ -8,6 +8,7 @@ use ordered_float::OrderedFloat; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; +use std::ops::Not; use crate::stability::Stability; @@ -67,6 +68,13 @@ pub enum AttributeSpec { /// to use instead. See also stability. #[serde(skip_serializing_if = "Option::is_none")] deprecated: Option, + /// Specifies the prefix of the attribute. + /// If this parameter is set, the resolved id of the referenced attribute will + /// have group prefix added to it. + /// It defaults to false. + #[serde(default)] + #[serde(skip_serializing_if = "<&bool>::not")] + prefix: bool, }, /// Attribute definition. Id { @@ -777,6 +785,7 @@ mod tests { note: Some("note".to_owned()), stability: Some(Stability::Stable), deprecated: Some("deprecated".to_owned()), + prefix: false, }; assert_eq!(attr.id(), "ref"); assert_eq!(attr.brief(), "brief");