Skip to content

Commit

Permalink
Merge pull request #200 from lquerel/semconv_const
Browse files Browse the repository at this point in the history
feat(forge): Add `<case>_const` filters to support semantic convention namespacing rules for constant (underscores are ignored, but . is meaningful).
  • Loading branch information
lquerel authored Jun 7, 2024
2 parents 24965a1 + fb90c08 commit 7f675c7
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 21 deletions.
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/weaver_forge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ 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).
- `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).
- `screaming_snake_case_const`: Generates SCREAMING_SNAKE_CASE constants which follow semantic convention namespacing rules (underscores are ignored, but . is meaningful).
- `acronym`: Replaces acronyms in the input string with the full name defined in the `acronyms` section of the `weaver.yaml` configuration file.
- `split_ids`: Splits a string by '.' creating a list of nested ids.
- `type_mapping`: Converts a semantic convention type to a target type (see weaver.yaml section `type_mapping`).
Expand Down
7 changes: 7 additions & 0 deletions crates/weaver_forge/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ pub enum Error {
error: String,
},

/// Invalid case convention.
#[error("`{case}` is not a valid case convention. Valid case conventions are: lower_case, upper_case, title_case, snake_case, kebab_case, camel_case, pascal_case, screaming_snake_case, and screaming_kebab_case.")]
InvalidCaseConvention {
/// The invalid case
case: String,
},

/// A generic container for multiple errors.
#[error("Errors:\n{0:#?}")]
CompoundError(Vec<Error>),
Expand Down
23 changes: 12 additions & 11 deletions crates/weaver_forge/src/extensions/case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ pub(crate) fn add_filters(env: &mut Environment<'_>, target_config: &TargetConfi
);
}

/// Converts input string to the specified case convention.
/// Converts a `CaseConvention` to a function that converts a string to the specified case
/// convention.
#[must_use]
pub fn case_converter(case_convention: CaseConvention) -> fn(&str) -> String {
match case_convention {
Expand All @@ -58,52 +59,52 @@ pub fn case_converter(case_convention: CaseConvention) -> fn(&str) -> String {
}

/// Converts input string to lower case
fn lower_case(input: &str) -> String {
pub(crate) fn lower_case(input: &str) -> String {
CaseConvention::LowerCase.convert(input)
}

/// Converts input string to upper case
fn upper_case(input: &str) -> String {
pub(crate) fn upper_case(input: &str) -> String {
CaseConvention::UpperCase.convert(input)
}

/// Converts input string to title case
fn title_case(input: &str) -> String {
pub(crate) fn title_case(input: &str) -> String {
CaseConvention::TitleCase.convert(input)
}

/// Converts input string to camel case
fn camel_case(input: &str) -> String {
pub(crate) fn camel_case(input: &str) -> String {
CaseConvention::CamelCase.convert(input)
}

/// Converts input string to pascal case
fn pascal_case(input: &str) -> String {
pub(crate) fn pascal_case(input: &str) -> String {
CaseConvention::PascalCase.convert(input)
}

/// Converts input string to snake case
fn snake_case(input: &str) -> String {
pub(crate) fn snake_case(input: &str) -> String {
CaseConvention::SnakeCase.convert(input)
}

/// Converts input string to screaming snake case
fn screaming_snake_case(input: &str) -> String {
pub(crate) fn screaming_snake_case(input: &str) -> String {
CaseConvention::ScreamingSnakeCase.convert(input)
}

/// Converts input string to kebab case
fn kebab_case(input: &str) -> String {
pub(crate) fn kebab_case(input: &str) -> String {
CaseConvention::KebabCase.convert(input)
}

/// Converts input string to screaming kebab case
fn screaming_kebab_case(input: &str) -> String {
pub(crate) fn screaming_kebab_case(input: &str) -> String {
CaseConvention::ScreamingKebabCase.convert(input)
}

/// Capitalize the first character of a string.
fn capitalize_first(input: &str) -> String {
pub(crate) fn capitalize_first(input: &str) -> String {
let mut chars = input.chars();
let mut result = String::with_capacity(input.len());

Expand Down
125 changes: 125 additions & 0 deletions crates/weaver_forge/src/extensions/otel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ use minijinja::{ErrorKind, Value};
use serde::de::Error;

use crate::config::CaseConvention;
use crate::extensions::case::{
camel_case, kebab_case, pascal_case, screaming_snake_case, snake_case,
};

const TEMPLATE_PREFIX: &str = "template[";
const TEMPLATE_SUFFIX: &str = "]";
Expand All @@ -24,6 +27,11 @@ pub(crate) fn add_tests_and_filters(env: &mut minijinja::Environment<'_>) {
env.add_filter("not_required", not_required);
env.add_filter("instantiated_type", instantiated_type);
env.add_filter("enum_type", enum_type);
env.add_filter("kebab_case_const", kebab_case_const);
env.add_filter("pascal_case_const", pascal_case_const);
env.add_filter("camel_case_const", camel_case_const);
env.add_filter("snake_case_const", snake_case_const);
env.add_filter("screaming_snake_case_const", screaming_snake_case_const);

env.add_test("stable", is_stable);
env.add_test("experimental", is_experimental);
Expand Down Expand Up @@ -141,6 +149,41 @@ pub(crate) fn attribute_namespace(input: &str) -> Result<String, minijinja::Erro
Ok(parts[0].to_owned())
}

/// Converts a semconv id into semconv constant following the namespacing rules and the
/// kebab case convention.
pub(crate) fn kebab_case_const(input: &str) -> String {
// Remove all _ and convert to the kebab case
kebab_case(&input.replace('_', ""))
}

/// Converts a semconv id into semconv constant following the namespacing rules and the
/// pascal case convention.
pub(crate) fn pascal_case_const(input: &str) -> String {
// Remove all _ and convert to the pascal case
pascal_case(&input.replace('_', ""))
}

/// Converts a semconv id into semconv constant following the namespacing rules and the
/// camel case convention.
pub(crate) fn camel_case_const(input: &str) -> String {
// Remove all _ and convert to the camel case
camel_case(&input.replace('_', ""))
}

/// Converts a semconv id into semconv constant following the namespacing rules and the
/// snake case convention.
pub(crate) fn snake_case_const(input: &str) -> String {
// Remove all _ and convert to the snake case
snake_case(&input.replace('_', ""))
}

/// Converts a semconv id into semconv constant following the namespacing rules and the
/// screaming snake case convention.
pub(crate) fn screaming_snake_case_const(input: &str) -> String {
// Remove all _ and convert to the screaming snake case
screaming_snake_case(&input.replace('_', ""))
}

/// Compares two attributes by their requirement_level, then name.
fn compare_requirement_level(
lhs: &Value,
Expand Down Expand Up @@ -1180,4 +1223,86 @@ mod tests {
members,
}
}

#[test]
fn test_semconv_const() {
let mut env = Environment::new();
let ctx = serde_json::Value::Null;

add_tests_and_filters(&mut env);

assert_eq!(
env.render_str(
"{{ 'messaging.client_id' | screaming_snake_case_const }}",
&ctx,
)
.unwrap(),
"MESSAGING_CLIENTID"
);

assert_eq!(
env.render_str("{{ 'messaging.client_id' | pascal_case_const }}", &ctx,)
.unwrap(),
"MessagingClientid"
);

assert_eq!(
env.render_str(
"{{ 'messaging.client.id' | screaming_snake_case_const }}",
&ctx,
)
.unwrap(),
"MESSAGING_CLIENT_ID"
);

assert_eq!(
env.render_str("{{ 'messaging.client.id' | pascal_case_const }}", &ctx,)
.unwrap(),
"MessagingClientId"
);

assert_eq!(
env.render_str("{{ 'messaging.client.id' | kebab_case_const }}", &ctx,)
.unwrap(),
"messaging-client-id"
);

assert_eq!(
env.render_str("{{ 'messaging.client_id' | kebab_case_const }}", &ctx,)
.unwrap(),
"messaging-clientid"
);

assert_eq!(
env.render_str("{{ 'messaging.client.id' | camel_case_const }}", &ctx,)
.unwrap(),
"messagingClientId"
);

assert_eq!(
env.render_str("{{ 'messaging.client_id' | camel_case_const }}", &ctx,)
.unwrap(),
"messagingClientid"
);

assert_eq!(
env.render_str("{{ 'messaging.client.id' | snake_case_const }}", &ctx,)
.unwrap(),
"messaging_client_id"
);

assert_eq!(
env.render_str("{{ 'messaging.client_id' | snake_case_const }}", &ctx,)
.unwrap(),
"messaging_clientid"
);

assert!(env
.render_str("{{ 'messaging.client.id' | invalid_case_const }}", &ctx,)
.is_err());

assert!(env
.render_str("{{ 123 | pascal_case_const }}", &ctx,)
.is_err());
}
}

0 comments on commit 7f675c7

Please sign in to comment.