diff --git a/crates/infra_utils/src/lib.rs b/crates/infra_utils/src/lib.rs index 40a4b1bf8a..129c5be816 100644 --- a/crates/infra_utils/src/lib.rs +++ b/crates/infra_utils/src/lib.rs @@ -2,3 +2,4 @@ pub mod command; pub mod path; pub mod run_until; pub mod tracing; +pub mod type_name; diff --git a/crates/infra_utils/src/type_name.rs b/crates/infra_utils/src/type_name.rs new file mode 100644 index 0000000000..ffe817f865 --- /dev/null +++ b/crates/infra_utils/src/type_name.rs @@ -0,0 +1,83 @@ +use std::any::type_name; + +#[cfg(test)] +#[path = "type_name_test.rs"] +mod type_name_test; + +pub fn short_type_name() -> String { + let full_name = type_name::(); + truncate_type(full_name) +} + +/// Truncates a fully qualified Rust type name by removing module paths, leaving only the type +/// name and its generic parameters. +/// +/// # Description +/// This function processes a Rust type string containing module paths, type names, and generic +/// parameters, such as: +/// ```text +/// starknet_sequencer_infra::component_client::local_component_client::LocalComponentClient +/// ``` +/// It removes the module paths (`::module_name`) and keeps only the type name and its +/// generic parameters: +/// ```text +/// LocalComponentClient +/// ``` +/// +/// # Algorithm +/// - Iterates over the input string using a character iterator with indices. +/// - When encountering two consecutive colons (`::`), skips the preceding module path. +/// - When encountering delimiters (`<`, `,`, `>`), slices the substring from the current segment +/// start to the current index, appends it to the result, and resets the segment start. +/// - At the end, appends the remaining segment to the result. +/// +/// # Examples +/// ```rust,ignore +/// let input = "a::b::c::Type"; +/// let output = truncate_type(input); +/// assert_eq!(output, "Type"); +/// ``` +/// +/// # Panics +/// This function does not panic as it only operates on valid UTF-8 strings. +/// +/// # Complexity +/// The function runs in O(n) time, where `n` is the length of the input string. +/// +/// # Limitations +/// - The function assumes well-formed Rust type strings. Incorrectly formatted input may yield +/// unexpected results. +/// +/// # Returns +/// A new `String` with module paths removed and generic parameters preserved. +fn truncate_type(input: &str) -> String { + let mut result = String::new(); + let mut segment_start = 0; + let mut iter = input.char_indices().peekable(); + + while let Some((index, c)) = iter.next() { + if c == ':' { + if let Some((_, next_char)) = iter.peek() { + if *next_char == ':' { + // Skip the next ':' + iter.next(); + segment_start = index + 2; // Move `segment_start` after the second ':' + } + } + } else if c == '<' || c == ',' || c == '>' { + // Add the slice from `segment_start` to the current index to the result + if segment_start < index { + result.push_str(input[segment_start..index].trim()); + result.push(c); + } + segment_start = index + 1; // Move `segment_start` after the current delimiter + } + } + + // Add the final slice from `segment_start` to the end + if segment_start < input.len() { + result.push_str(input[segment_start..].trim()); + } + + result +} diff --git a/crates/infra_utils/src/type_name_test.rs b/crates/infra_utils/src/type_name_test.rs new file mode 100644 index 0000000000..c10379f61e --- /dev/null +++ b/crates/infra_utils/src/type_name_test.rs @@ -0,0 +1,23 @@ +use pretty_assertions::assert_eq; + +use crate::type_name::short_type_name; + +struct TestStruct {} + +struct GenericTestStruct { + _placeholder: T, +} + +mod submodule { + pub struct SubmoduleStruct {} +} + +#[test] +fn resolve_project_relative_path_success() { + assert_eq!(short_type_name::().as_str(), "TestStruct"); + assert_eq!(short_type_name::>().as_str(), "GenericTestStruct"); + assert_eq!( + short_type_name::>().as_str(), + "GenericTestStruct" + ); +}