[ISSUE #1491]⚡️Add some comments for RequestHeaderCodec macor#1492
[ISSUE #1491]⚡️Add some comments for RequestHeaderCodec macor#1492rocketmq-rust-bot merged 1 commit intomainfrom
Conversation
WalkthroughThe changes introduce new functions and modify existing ones in the Changes
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
|
🔊@mxsm 🚀Thanks for your contribution 🎉. CodeRabbit(AI) will review your code first 🔥 |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1492 +/- ##
==========================================
+ Coverage 22.55% 22.58% +0.02%
==========================================
Files 450 450
Lines 58500 58549 +49
==========================================
+ Hits 13196 13222 +26
- Misses 45304 45327 +23 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (4)
rocketmq-macros/src/lib.rs (3)
Line range hint
94-146: Consider simplifying the nested type checking logic.While the implementation is correct, the readability could be improved by:
- Using pattern matching instead of nested if-let chains
- Following Rust comment conventions (using
//for single-line comments)Consider this refactoring:
fn is_struct_type(ty: &Type) -> bool { - // Check if the type is a path (i.e., a named type) if let Type::Path(TypePath { path, .. }) = ty { - // Check if the type is Option<T> - if let Some(segment) = path.segments.first() { - // If it's Option, check its generic argument - if segment.ident == "Option" { - if let syn::PathArguments::AngleBracketed(ref args) = &segment.arguments { - // Extract the first argument (T) inside Option - if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { - // Recursively check the inner type, if it's not basic or String, return - // true - return !is_basic_or_string_type(inner_ty); - } - } - } + // Handle Option<T> case + match path.segments.first() { + Some(segment) if segment.ident == "Option" => { + match &segment.arguments { + syn::PathArguments::AngleBracketed(args) => { + if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { + return !is_basic_or_string_type(inner_ty); + } + } + _ => {} + } + } + _ => {} } - // Check the last segment of the path to determine if it's a basic type or String, - // CheetahString if let Some(_segment) = path.segments.last() { - // If it's a basic type or String/CheetahString, return false - if is_basic_or_string_type(ty) { - return false; - } - - // If it's neither basic nor String/CheetahString, it's likely a struct - return true; + return !is_basic_or_string_type(ty); } } - - // If none of the conditions match, it's not a struct false }
Line range hint
147-191: Consider optimizing type checking and adding support for more primitive types.The current implementation could be enhanced by:
- Using a const array or HashSet for type checking
- Adding support for
isize,usize,charConsider this optimization:
+use std::collections::HashSet; +use once_cell::sync::Lazy; + +static BASIC_TYPES: Lazy<HashSet<&'static str>> = Lazy::new(|| { + vec![ + "String", "CheetahString", + "i8", "i16", "i32", "i64", "isize", + "u8", "u16", "u32", "u64", "usize", + "f32", "f64", + "bool", "char", + ].into_iter().collect() +}); fn is_basic_or_string_type(ty: &syn::Type) -> bool { if let syn::Type::Path(syn::TypePath { path, .. }) = ty { if let Some(segment) = path.segments.last() { - return segment_ident == "String" - || segment_ident == "CheetahString" - || segment_ident == "i8" - || segment_ident == "i16" - || segment_ident == "i32" - || segment_ident == "i64" - || segment_ident == "u8" - || segment_ident == "u16" - || segment_ident == "u32" - || segment_ident == "u64" - || segment_ident == "f32" - || segment_ident == "f64" - || segment_ident == "bool"; + return BASIC_TYPES.contains(segment_ident.to_string().as_str()); } } false }
236-279: Consider adding property-based testing.While the current test coverage is good, consider adding property-based tests using
proptestorquickcheckto:
- Test with randomly generated strings
- Verify invariants like length preservation
Example addition:
#[cfg(test)] mod property_tests { use super::*; use proptest::prelude::*; proptest! { #[test] fn test_snake_to_camel_case_properties(s in "[a-z_]{1,10}") { let camel = snake_to_camel_case(&s); // Verify no underscores in result assert!(!camel.contains('_')); // Verify length invariant assert!(camel.len() <= s.len()); } } }rocketmq-macros/src/request_header_custom.rs (1)
150-245: Consider improving error handling in the FromMap implementation.The current error handling could be more informative by:
- Including the actual value that failed to parse
- Adding context about expected types
Example enhancement:
.map_err(|_| Self::Error::RemotingCommandError( - format!("Parse {} field error", Self::#static_name), + format!("Failed to parse {} field: expected {}, got '{}'", + Self::#static_name, + stringify!(#types), + s + ), ))?
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (2)
rocketmq-macros/src/lib.rs(4 hunks)rocketmq-macros/src/request_header_custom.rs(1 hunks)
🔇 Additional comments (2)
rocketmq-macros/src/lib.rs (2)
Line range hint 62-93: Well-documented and correctly implemented Option type checker!
The function effectively determines if a type is Option<T> and extracts the inner type. The implementation follows Rust idioms and includes comprehensive documentation.
193-234: Well-structured attribute parsing implementation!
The function effectively checks for the serde(flatten) attribute with clear documentation and proper error handling.
| .iter() | ||
| .map(|field| { | ||
| let field_name = field.ident.as_ref().unwrap(); | ||
| let mut required = false; | ||
| let is_struct_type = is_struct_type(&field.ty); | ||
| let has_serde_flatten_attribute = has_serde_flatten_attribute(field); | ||
| for attr in &field.attrs { | ||
| if let Some(ident) = attr.path().get_ident() { | ||
| if ident == "required" { | ||
| required = true; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| //Determining whether it is an Option type or a direct data type | ||
| //This will lead to different ways of processing in the future. | ||
| let has_option = is_option_type(&field.ty); | ||
| let camel_case_name = snake_to_camel_case(&format!("{}", field_name)); | ||
| let static_name = Ident::new( | ||
| &format!("{}", field_name).to_ascii_uppercase(), | ||
| field_name.span(), | ||
| ); | ||
| let type_name = if let Some(value) = &has_option { | ||
| get_type_name(value) | ||
| } else { | ||
| get_type_name(&field.ty) | ||
| }; | ||
| ( | ||
| quote! { | ||
| const #static_name: &'static str = #camel_case_name; | ||
| }, | ||
| ( | ||
| if has_option.is_some() { | ||
| if type_name == "CheetahString" { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| value.clone() | ||
| ); | ||
| } | ||
| } | ||
| } else if type_name == "String" { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(value.clone()) | ||
| ); | ||
| //Determining whether it is an Option type or a direct data type | ||
| //This will lead to different ways of processing in the future. | ||
| let has_option = is_option_type(&field.ty); | ||
| let camel_case_name = snake_to_camel_case(&format!("{}", field_name)); | ||
| let static_name = Ident::new( | ||
| &format!("{}", field_name).to_ascii_uppercase(), | ||
| field_name.span(), | ||
| ); | ||
| let type_name = if let Some(value) = &has_option { | ||
| get_type_name(value) | ||
| } else { | ||
| get_type_name(&field.ty) | ||
| }; | ||
| ( | ||
| //build static field | ||
| quote! { | ||
| const #static_name: &'static str = #camel_case_name; | ||
| }, | ||
| ( | ||
| //build CommandCustomHeader impl | ||
| if has_option.is_some() { | ||
| if type_name == "CheetahString" { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| value.clone() | ||
| ); | ||
| } | ||
| } | ||
| } else if type_name == "String" { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(value.clone()) | ||
| ); | ||
| } | ||
| } | ||
| } else if is_struct_type && has_serde_flatten_attribute { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| if let Some(value) = value.to_map() { | ||
| map.extend(value); | ||
| } | ||
| } | ||
| } | ||
| } else if is_struct_type && has_serde_flatten_attribute { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| if let Some(value) = value.to_map() { | ||
| map.extend(value); | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(value.to_string()) | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } else if type_name == "CheetahString" { | ||
| quote! { | ||
| map.insert ( | ||
| } | ||
| } else { | ||
| quote! { | ||
| if let Some(ref value) = self.#field_name { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| self.#field_name.clone() | ||
| ); | ||
| } | ||
| } else if type_name == "String" { | ||
| quote! { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(self.#field_name.clone()) | ||
| ); | ||
| } | ||
| } else if is_struct_type && has_serde_flatten_attribute { | ||
| quote! { | ||
| if let Some(value) = self.#field_name.to_map() { | ||
| map.extend(value); | ||
| } | ||
| } | ||
| } else { | ||
| quote! { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(self.#field_name.to_string()) | ||
| ); | ||
| } | ||
| } | ||
| , | ||
| // build FromMap impl | ||
| if let Some(value) = has_option { | ||
| if type_name == "CheetahString" || type_name == "String" { | ||
| if required { | ||
| quote! { | ||
| #field_name: Some( | ||
| map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)) | ||
| .cloned() | ||
| .ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))? | ||
| ), | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).cloned(), | ||
| cheetah_string::CheetahString::from_string(value.to_string()) | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } else if type_name == "CheetahString" { | ||
| quote! { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| self.#field_name.clone() | ||
| ); | ||
| } | ||
| } else if type_name == "String" { | ||
| quote! { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(self.#field_name.clone()) | ||
| ); | ||
| } | ||
| } else if is_struct_type && has_serde_flatten_attribute { | ||
| //If it is a struct type and has the serde(flatten) attribute, | ||
| // it will be processed as a struct. | ||
| quote! { | ||
| if let Some(value) = self.#field_name.to_map() { | ||
| map.extend(value); | ||
| } | ||
| } | ||
| } else { | ||
| quote! { | ||
| map.insert ( | ||
| cheetah_string::CheetahString::from_static_str(Self::#static_name), | ||
| cheetah_string::CheetahString::from_string(self.#field_name.to_string()) | ||
| ); | ||
| } | ||
| }, | ||
|
|
||
| // build FromMap impl | ||
| if let Some(value) = has_option { | ||
| if type_name == "CheetahString" || type_name == "String" { | ||
| if required { | ||
| if type_name == "CheetahString" { | ||
| quote! { | ||
| #field_name: Some( | ||
| map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)) | ||
| .cloned() | ||
| .ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))? | ||
| ), | ||
| } | ||
| } else { | ||
| quote! { | ||
| Some( | ||
| map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)) | ||
| .cloned() | ||
| .ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))?.to_string() | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } else if is_struct_type && has_serde_flatten_attribute { | ||
| let type_ = has_option.unwrap(); | ||
| quote! { | ||
| #field_name: Some(<#type_ as crate::protocol::command_custom_header::FromMap>::from(map)?), | ||
| } | ||
| } else if required { | ||
| quote! { | ||
| #field_name: Some( | ||
| map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))? | ||
| .parse::<#value>() | ||
| .map_err(|_| Self::Error::RemotingCommandError(format!("Parse {} field error", Self::#static_name)))? | ||
| ), | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name:map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).and_then(|s| s.parse::<#value>().ok()), | ||
| } else { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).cloned(), | ||
| } | ||
| } | ||
| } else { | ||
| let types = &field.ty; | ||
| if type_name == "CheetahString" || type_name == "String" { | ||
| if required { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)) | ||
| .cloned() | ||
| .ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))?, | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).cloned().unwrap_or_default(), | ||
| } | ||
| } | ||
| }else if is_struct_type && has_serde_flatten_attribute { | ||
| let type_ = &field.ty; | ||
| quote! { | ||
| #field_name: <#type_ as crate::protocol::command_custom_header::FromMap>::from(map)?, | ||
| } | ||
| } else if required { | ||
| quote! { | ||
| #field_name:map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))? | ||
| .parse::<#types>() | ||
| .map_err(|_| Self::Error::RemotingCommandError(format!("Parse {} field error", Self::#static_name)))?, | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name:map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).and_then(|s| s.parse::<#types>().ok()).unwrap_or_default(), | ||
| } | ||
| } else if is_struct_type && has_serde_flatten_attribute { | ||
| let type_ = has_option.unwrap(); | ||
| quote! { | ||
| #field_name: Some(<#type_ as crate::protocol::command_custom_header::FromMap>::from(map)?), | ||
| } | ||
| } else if required { | ||
| quote! { | ||
| #field_name: Some( | ||
| map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))? | ||
| .parse::<#value>() | ||
| .map_err(|_| Self::Error::RemotingCommandError(format!("Parse {} field error", Self::#static_name)))? | ||
| ), | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name:map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).and_then(|s| s.parse::<#value>().ok()), | ||
| } | ||
| } | ||
| } | ||
| ) | ||
| ) | ||
| }) | ||
| .unzip(); | ||
| } | ||
| } else { | ||
| let types = &field.ty; | ||
| if type_name == "CheetahString" || type_name == "String" { | ||
| if required { | ||
| if type_name == "CheetahString" { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)) | ||
| .cloned() | ||
| .ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))?, | ||
| } | ||
| }else { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)) | ||
| .cloned() | ||
| .ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))?.to_string(), | ||
| } | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name: map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).cloned().unwrap_or_default(), | ||
| } | ||
| } | ||
| }else if is_struct_type && has_serde_flatten_attribute { | ||
| let type_ = &field.ty; | ||
| quote! { | ||
| #field_name: <#type_ as crate::protocol::command_custom_header::FromMap>::from(map)?, | ||
| } | ||
| } else if required { | ||
| quote! { | ||
| #field_name:map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).ok_or(Self::Error::RemotingCommandError( | ||
| format!("Missing {} field", Self::#static_name), | ||
| ))? | ||
| .parse::<#types>() | ||
| .map_err(|_| Self::Error::RemotingCommandError(format!("Parse {} field error", Self::#static_name)))?, | ||
| } | ||
| } else { | ||
| quote! { | ||
| #field_name:map.get(&cheetah_string::CheetahString::from_static_str(Self::#static_name)).and_then(|s| s.parse::<#types>().ok()).unwrap_or_default(), | ||
| } | ||
| } | ||
| } | ||
| ) | ||
| ) | ||
| }) | ||
| .unzip(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider breaking down the complex mapping logic into smaller functions.
The current implementation, while functional, could benefit from better organization:
- Extract type-specific quote generation into separate functions
- Add more comments explaining the code generation strategy
Consider this restructuring:
fn generate_option_type_mapping(
field_name: &Ident,
static_name: &Ident,
type_name: &str,
is_struct_type: bool,
has_serde_flatten: bool,
) -> TokenStream2 {
match (type_name, is_struct_type, has_serde_flatten) {
("CheetahString", _, _) => quote! {
if let Some(ref value) = self.#field_name {
map.insert(
cheetah_string::CheetahString::from_static_str(Self::#static_name),
value.clone()
);
}
},
// ... other cases
}
}
fn generate_regular_type_mapping(/* similar parameters */) -> TokenStream2 {
// ... similar pattern matching
}
Which Issue(s) This PR Fixes(Closes)
Fixes #1491
Brief Description
How Did You Test This Change?
Summary by CodeRabbit
New Features
is_option_typeandis_struct_type.snake_to_camel_casefunction.Bug Fixes
request_header_codec_innerfunction without altering core functionality.Documentation