Skip to content

Commit

Permalink
feat(forge): Add semconv_const filter.
Browse files Browse the repository at this point in the history
  • Loading branch information
lquerel committed Jun 6, 2024
1 parent 24965a1 commit 806070e
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/weaver_forge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,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.
- `semconv_const(case)`: Follows the semantic convention for constants. Underscores are removed and the string is converted to the case provided.
- `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
28 changes: 27 additions & 1 deletion crates/weaver_forge/src/extensions/case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! Case converter filters used by the template engine.

use crate::config::{CaseConvention, TargetConfig};
use crate::error::Error;
use minijinja::Environment;

/// Add case converter filters to the environment.
Expand Down Expand Up @@ -41,7 +42,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 @@ -57,6 +59,30 @@ pub fn case_converter(case_convention: CaseConvention) -> fn(&str) -> String {
}
}

/// Converts a "string" case convention to a function that converts a string to the specified case
/// convention.
///
/// # Returns
///
/// Returns an error if the case convention is not recognized or a function that converts a string
/// to the specified case convention.
pub fn str_to_case_converter(case_convention: &str) -> Result<fn(&str) -> String, Error> {
match case_convention {
"lower_case" => Ok(lower_case),
"upper_case" => Ok(upper_case),
"title_case" => Ok(title_case),
"camel_case" => Ok(camel_case),
"pascal_case" => Ok(pascal_case),
"snake_case" => Ok(snake_case),
"screaming_snake_case" => Ok(screaming_snake_case),
"kebab_case" => Ok(kebab_case),
"screaming_kebab_case" => Ok(screaming_kebab_case),
_ => Err(Error::InvalidCaseConvention {
case: case_convention.to_owned(),
}),
}
}

/// Converts input string to lower case
fn lower_case(input: &str) -> String {
CaseConvention::LowerCase.convert(input)
Expand Down
68 changes: 68 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,7 @@ use minijinja::{ErrorKind, Value};
use serde::de::Error;

use crate::config::CaseConvention;
use crate::extensions::case::str_to_case_converter;

const TEMPLATE_PREFIX: &str = "template[";
const TEMPLATE_SUFFIX: &str = "]";
Expand All @@ -24,6 +25,7 @@ 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("semconv_const", semconv_const);

env.add_test("stable", is_stable);
env.add_test("experimental", is_experimental);
Expand Down Expand Up @@ -141,6 +143,17 @@ 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.
///
/// A [`minijinja::Error`] if the case is not supported.
pub(crate) fn semconv_const(input: &str, case: &str) -> Result<String, minijinja::Error> {
let converter = str_to_case_converter(case)
.map_err(|e| minijinja::Error::new(ErrorKind::InvalidOperation, format!("{}", e)))?;
// Remove all _ and convert to the desired case
let converted_input = converter(&input.replace('_', ""));
Ok(converted_input)
}

/// Compares two attributes by their requirement_level, then name.
fn compare_requirement_level(
lhs: &Value,
Expand Down Expand Up @@ -1180,4 +1193,59 @@ 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' | semconv_const('screaming_snake_case') }}",
&ctx,
)
.unwrap(),
"MESSAGING_CLIENTID"
);

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

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

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

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

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

0 comments on commit 806070e

Please sign in to comment.