diff --git a/crates/weaver_forge/README.md b/crates/weaver_forge/README.md index 1b7406e1..74557574 100644 --- a/crates/weaver_forge/README.md +++ b/crates/weaver_forge/README.md @@ -34,9 +34,9 @@ templates/ html/ <-- Templates to generate the semantic conventions in HTML ... markdown/ <-- Templates to generate the semantic conventions in markdown - ... + ... rust/ <-- Templates to generate the semantic conventions in Rust - ... + ... go/ <-- Templates to generate the semantic conventions in Go ... schema/ @@ -78,6 +78,7 @@ documentation for more details. ## Global Variables All templates have access to the following global variables: + - `ctx`: The context object that contains the resolved registry or the output of the JQ filter if defined in the `weaver.yaml` configuration file. - `params`: The parameters defined in the `weaver.yaml` configuration file or overridden @@ -130,12 +131,13 @@ Jinja templates can also access the parameters: ## Jinja Filters All the filters available in the MiniJinja template engine are available (see -this online [documentation](https://docs.rs/minijinja/latest/minijinja/filters/index.html)). +this online [documentation](https://docs.rs/minijinja/latest/minijinja/filters/index.html)). In addition, OTel Weaver provides a set of custom filters to facilitate the generation of documentation and code. The following filters are available: + - `lower_case`: Converts a string to lowercase. - `upper_case`: Converts a string to UPPERCASE. - `title_case`: Converts a string to TitleCase. @@ -146,7 +148,7 @@ The following filters are available: - `kebab_case`: Converts a string to kebab-case. - `screaming_kebab_case`: Converts a string to SCREAMING-KEBAB-CASE. - `capitalize_first`: Capitalizes the first letter of a string. -- `kebab_case_const`: Generates kebab-case constants which follow semantic convention namespacing rules (underscores are ignored, but . is meaningful). +- `kebab_case_const`: Generates kebab-case constants which follow semantic convention namespacing rules (underscores are ignored, but . is meaningful). - `pascal_case_const`: Generates PascalCase constants which follow semantic convention namespacing rules (underscores are ignored, but . is meaningful). - `camel_case_const`: Generates camelCase constants which follow semantic convention namespacing rules (underscores are ignored, but . is meaningful). - `snake_case_const`: Generates snake_case constants which follow semantic convention namespacing rules (underscores are ignored, but . is meaningful). @@ -165,7 +167,7 @@ e.g. \[\[a,b\],\[c\]\] => \[a,b,c\] - `attribute_namespace`: Converts {namespace}.{attribute_id} to {namespace}. - `required`: Filters a list of `Attribute`s to include only the required attributes. The "conditionally_required" attributes are not returned by this filter. - `not_required`: Filters a list of `Attribute`s to only include non-required attributes. The "conditionally_required" attributes are returned by this filter. -- `instantiated_type`: Filters a type to return the instantiated type. +- `instantiated_type`: Filters a type to return the instantiated type. - `enum_type`: Filters a type to return the enum type or an error if the type is not an enum. - `markdown_to_html`: Converts a markdown string to an HTML string. - `map_text`: Converts an input into a string based on the `text_maps` section of the `weaver.yaml` configuration file @@ -212,7 +214,7 @@ value if the name of the text map or the input are not found in the `text_maps` ## Jinja Functions -All the functions available in the MiniJinja template engine are available (see +All the functions available in the MiniJinja template engine are available (see this online [documentation](https://docs.rs/minijinja/latest/minijinja/functions/index.html)). Right now, OTel Weaver does not provide any custom functions but feel free to diff --git a/crates/weaver_forge/src/config.rs b/crates/weaver_forge/src/config.rs index d1894394..545ba608 100644 --- a/crates/weaver_forge/src/config.rs +++ b/crates/weaver_forge/src/config.rs @@ -77,6 +77,9 @@ pub(crate) struct TargetConfig { /// Configuration for the template syntax. #[serde(default)] pub(crate) template_syntax: TemplateSyntax, + /// Configuration for the whitespace behavior on the template engine. + #[serde(default)] + pub(crate) whitespace_control: WhitespaceControl, /// Parameters for the templates. /// These parameters can be overridden by parameters passed to the CLI. @@ -298,6 +301,20 @@ impl Default for TemplateSyntax { } } +/// Whitespace control configuration for the template engine. +#[derive(Deserialize, Debug, Clone, Default)] +pub struct WhitespaceControl { + /// Configures the behavior of the first newline after a block. + /// See + pub trim_blocks: bool, + /// Configures the behavior of leading spaces and tabs from the start of a line to a block. + /// See + pub lstrip_blocks: bool, + /// Configures whether trailing newline are preserved when rendering templates. + /// See + pub keep_trailing_newline: bool, +} + impl Default for CaseConvention { /// Default case convention is PascalCase fn default() -> Self { diff --git a/crates/weaver_forge/src/lib.rs b/crates/weaver_forge/src/lib.rs index 228eef98..77b8a051 100644 --- a/crates/weaver_forge/src/lib.rs +++ b/crates/weaver_forge/src/lib.rs @@ -456,6 +456,13 @@ impl TemplateEngine { }); env.set_syntax(syntax); + // Jinja whitespace control + // https://docs.rs/minijinja/latest/minijinja/syntax/index.html#whitespace-control + let whitespace_control = self.target_config.whitespace_control.clone(); + env.set_trim_blocks(whitespace_control.trim_blocks); + env.set_lstrip_blocks(whitespace_control.lstrip_blocks); + env.set_keep_trailing_newline(whitespace_control.keep_trailing_newline); + code::add_filters(&mut env, &self.target_config); ansi::add_filters(&mut env); case::add_filters(&mut env, &self.target_config); @@ -682,4 +689,48 @@ mod tests { assert!(diff_dir("expected_output", "observed_output").unwrap()); } + + #[test] + fn test_whitespace_control() { + let logger = TestLogger::default(); + let loader = FileSystemFileLoader::try_new("whitespace_control_templates".into(), "test") + .expect("Failed to create file system loader"); + let engine = super::TemplateEngine::try_new(loader, Params::default()) + .expect("Failed to create template engine"); + + let registry_id = "default"; + let mut registry = SemConvRegistry::try_from_path_pattern(registry_id, "data/*.yaml") + .expect("Failed to load registry"); + let schema = SchemaResolver::resolve_semantic_convention_registry(&mut registry) + .expect("Failed to resolve registry"); + + let template_registry = ResolvedRegistry::try_from_resolved_registry( + schema.registry(registry_id).expect("registry not found"), + schema.catalog(), + ) + .unwrap_or_else(|e| { + panic!( + "Failed to create the context for the template evaluation: {:?}", + e + ) + }); + + engine + .generate( + logger.clone(), + &template_registry, + Path::new("whitespace_control_templates/test/observed_output"), + &OutputDirective::File, + ) + .inspect_err(|e| { + print_dedup_errors(logger.clone(), e.clone()); + }) + .expect("Failed to generate registry assets"); + + assert!(diff_dir( + "whitespace_control_templates/test/expected_output", + "whitespace_control_templates/test/observed_output" + ) + .unwrap()); + } } diff --git a/crates/weaver_forge/whitespace_control_templates/test/expected_output/registry.md b/crates/weaver_forge/whitespace_control_templates/test/expected_output/registry.md new file mode 100644 index 00000000..e2d11600 --- /dev/null +++ b/crates/weaver_forge/whitespace_control_templates/test/expected_output/registry.md @@ -0,0 +1,56 @@ +# Semantic Convention Registry + +Url: + +## Attribute Groups + +- [attributes.jvm.memory](attribute_group/attributes_jvm_memory.md) +- [registry.db](attribute_group/registry_db.md) +- [registry.http](attribute_group/registry_http.md) +- [registry.network](attribute_group/registry_network.md) +- [server](attribute_group/server.md) +- [registry.url](attribute_group/registry_url.md) +- [registry.user_agent](attribute_group/registry_user_agent.md) + +## Events + +- [ios.lifecycle.events](event/ios_lifecycle_events.md) +- [android.lifecycle.events](event/android_lifecycle_events.md) + +## Metrics + +- [metric.jvm.memory.used](metric/metric_jvm_memory_used.md) +- [metric.jvm.memory.committed](metric/metric_jvm_memory_committed.md) +- [metric.jvm.memory.limit](metric/metric_jvm_memory_limit.md) +- [metric.jvm.memory.used_after_last_gc](metric/metric_jvm_memory_used_after_last_gc.md) +- [metric.jvm.gc.duration](metric/metric_jvm_gc_duration.md) +- [metric.jvm.thread.count](metric/metric_jvm_thread_count.md) +- [metric.jvm.class.loaded](metric/metric_jvm_class_loaded.md) +- [metric.jvm.class.unloaded](metric/metric_jvm_class_unloaded.md) +- [metric.jvm.class.count](metric/metric_jvm_class_count.md) +- [metric.jvm.cpu.count](metric/metric_jvm_cpu_count.md) +- [metric.jvm.cpu.time](metric/metric_jvm_cpu_time.md) +- [metric.jvm.cpu.recent_utilization](metric/metric_jvm_cpu_recent_utilization.md) + +## Metric Groups + +## Resource + +- [otel.scope](resource/otel_scope.md) +- [otel.library](resource/otel_library.md) + +## Scope + +## Span + +- [db](span/db.md) +- [db.mssql](span/db_mssql.md) +- [db.cassandra](span/db_cassandra.md) +- [db.hbase](span/db_hbase.md) +- [db.couchdb](span/db_couchdb.md) +- [db.redis](span/db_redis.md) +- [db.mongodb](span/db_mongodb.md) +- [db.elasticsearch](span/db_elasticsearch.md) +- [db.sql](span/db_sql.md) +- [db.cosmosdb](span/db_cosmosdb.md) +- [db.tech](span/db_tech.md) diff --git a/crates/weaver_forge/whitespace_control_templates/test/registry.md b/crates/weaver_forge/whitespace_control_templates/test/registry.md new file mode 100644 index 00000000..73f1c62c --- /dev/null +++ b/crates/weaver_forge/whitespace_control_templates/test/registry.md @@ -0,0 +1,57 @@ +# Semantic Convention Registry + +Url:{{ registry_url }} + +## Attribute Groups + +{% for group in ctx.groups %} +{% if group.type == "attribute_group" %} +- [{{ group.id }}](attribute_group/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} + +## Events + +{% for group in ctx.groups %} +{% if group.type == "event" %} +- [{{ group.id }}](event/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} + +## Metrics + +{% for group in ctx.groups %} +{% if group.type == "metric" %} +- [{{ group.id }}](metric/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} + +## Metric Groups +{% for group in ctx.groups %} +{% if group.type == "metric_group" %} +- [{{ group.id }}](metric_group/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} + +## Resource + +{% for group in ctx.groups %} +{% if group.type == "resource" %} +- [{{ group.id }}](resource/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} + +## Scope +{% for group in ctx.groups %} +{% if group.type == "scope" %} +- [{{ group.id }}](scope/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} + +## Span + +{% for group in ctx.groups %} +{% if group.type == "span" %} +- [{{ group.id }}](span/{{ group.id | file_name }}.md) +{% endif %} +{% endfor %} diff --git a/crates/weaver_forge/whitespace_control_templates/test/weaver.yaml b/crates/weaver_forge/whitespace_control_templates/test/weaver.yaml new file mode 100644 index 00000000..618921bd --- /dev/null +++ b/crates/weaver_forge/whitespace_control_templates/test/weaver.yaml @@ -0,0 +1,22 @@ +file_name: snake_case +function_name: PascalCase +arg_name: camelCase +struct_name: PascalCase +field_name: PascalCase + +type_mapping: + int: int64 + double: double + boolean: bool + string: string + "int[]": "[]int64" + "double[]": "[]double" + "boolean[]": "[]bool" + "string[]": "[]string" + +acronyms: ["iOS", "API", "URL"] + +whitespace_control: + trim_blocks: true + lstrip_blocks: true + keep_trailing_newline: true diff --git a/crates/weaver_semconv/src/semconv.rs b/crates/weaver_semconv/src/semconv.rs index b0cb41cd..f4d17d91 100644 --- a/crates/weaver_semconv/src/semconv.rs +++ b/crates/weaver_semconv/src/semconv.rs @@ -302,10 +302,7 @@ mod tests { let semconv_url = "http://unknown.com/unknown-semconv.yaml"; let semconv_spec = SemConvSpec::from_url(semconv_url); assert!(semconv_spec.is_err()); - assert!(matches!( - semconv_spec.unwrap_err(), - InvalidSemConvSpec { .. } - )); + assert!(matches!(semconv_spec.unwrap_err(), RegistryNotFound { .. })); } #[test] diff --git a/docs/weaver-config.md b/docs/weaver-config.md index 36f33e72..91626ba9 100644 --- a/docs/weaver-config.md +++ b/docs/weaver-config.md @@ -16,7 +16,7 @@ following options: # double: doubleKey # boolean: booleanKey # string: stringKey - + # Deprecated, please use text_maps instead # Configuration of the type mapping. This is useful to generate code in a # specific language. This is optional. @@ -33,7 +33,8 @@ following options: # "string[]": "[]string" # ... -# Uncomment this section to specify the configuration of the Jinja template syntax. +# Uncomment this section to specify the configuration of the Jinja template syntax +# and control whitespace behavior. # Note: The default syntax is strongly recommended. #template_syntax: # block_start: "{%" @@ -43,6 +44,13 @@ following options: # comment_start: "{#" # comment_end: "#}" +# Uncomment this section to specify the whitespace behavior of the Jinja template engine. +# For more info, see: https://docs.rs/minijinja/latest/minijinja/syntax/index.html#whitespace-control +# whitespace_control: +# trim_blocks: true +# lstrip_blocks: true +# keep_trailing_newline: true + # Uncomment the following section to specify a list of acronyms that # will be interpreted by the acronym filter. This is optional. # acronyms: ["iOS", "HTTP", "API", "SDK", "CLI", "URL", "JSON", "XML", "HTML"] @@ -52,7 +60,7 @@ following options: # params: # param1: val1 # param2: val2 - + # Uncomment the following templates to override the default template # mapping. Each template mapping specifies a jaq filter (compatible with jq) # to apply to every file matching the pattern. The application_mode specifies