@@ -182,6 +182,86 @@ fn is_primitive_type(ty: &Type) -> bool {
182182 }
183183}
184184
185+ /// Extract rename_all attribute from struct attributes
186+ fn extract_rename_all ( attrs : & [ syn:: Attribute ] ) -> Option < String > {
187+ for attr in attrs {
188+ if attr. path ( ) . is_ident ( "serde" ) {
189+ // Parse the attribute tokens manually
190+ // Format: #[serde(rename_all = "camelCase")]
191+ let tokens = attr. meta . require_list ( ) . ok ( ) ?;
192+ let token_str = tokens. tokens . to_string ( ) ;
193+
194+ // Look for rename_all = "..." pattern
195+ if let Some ( start) = token_str. find ( "rename_all" ) {
196+ let remaining = & token_str[ start + "rename_all" . len ( ) ..] ;
197+ if let Some ( equals_pos) = remaining. find ( '=' ) {
198+ let value_part = & remaining[ equals_pos + 1 ..] . trim ( ) ;
199+ // Extract string value (remove quotes)
200+ if value_part. starts_with ( '"' ) && value_part. ends_with ( '"' ) {
201+ let value = & value_part[ 1 ..value_part. len ( ) - 1 ] ;
202+ return Some ( value. to_string ( ) ) ;
203+ }
204+ }
205+ }
206+ }
207+ }
208+ None
209+ }
210+
211+ /// Convert field name according to rename_all rule
212+ fn rename_field ( field_name : & str , rename_all : Option < & str > ) -> String {
213+ match rename_all {
214+ Some ( "camelCase" ) => {
215+ // Convert snake_case to camelCase
216+ let mut result = String :: new ( ) ;
217+ let mut capitalize_next = false ;
218+ for ch in field_name. chars ( ) {
219+ if ch == '_' {
220+ capitalize_next = true ;
221+ } else if capitalize_next {
222+ result. push ( ch. to_uppercase ( ) . next ( ) . unwrap_or ( ch) ) ;
223+ capitalize_next = false ;
224+ } else {
225+ result. push ( ch) ;
226+ }
227+ }
228+ result
229+ }
230+ Some ( "snake_case" ) => {
231+ // Convert camelCase to snake_case
232+ let mut result = String :: new ( ) ;
233+ for ( i, ch) in field_name. chars ( ) . enumerate ( ) {
234+ if ch. is_uppercase ( ) && i > 0 {
235+ result. push ( '_' ) ;
236+ }
237+ result. push ( ch. to_lowercase ( ) . next ( ) . unwrap_or ( ch) ) ;
238+ }
239+ result
240+ }
241+ Some ( "kebab-case" ) => {
242+ // Convert snake_case to kebab-case
243+ field_name. replace ( '_' , "-" )
244+ }
245+ Some ( "PascalCase" ) => {
246+ // Convert snake_case to PascalCase
247+ let mut result = String :: new ( ) ;
248+ let mut capitalize_next = true ;
249+ for ch in field_name. chars ( ) {
250+ if ch == '_' {
251+ capitalize_next = true ;
252+ } else if capitalize_next {
253+ result. push ( ch. to_uppercase ( ) . next ( ) . unwrap_or ( ch) ) ;
254+ capitalize_next = false ;
255+ } else {
256+ result. push ( ch) ;
257+ }
258+ }
259+ result
260+ }
261+ _ => field_name. to_string ( ) ,
262+ }
263+ }
264+
185265/// Parse struct definition to OpenAPI Schema
186266pub fn parse_struct_to_schema (
187267 struct_item : & syn:: ItemStruct ,
@@ -190,15 +270,21 @@ pub fn parse_struct_to_schema(
190270 let mut properties = BTreeMap :: new ( ) ;
191271 let mut required = Vec :: new ( ) ;
192272
273+ // Extract rename_all attribute from struct
274+ let rename_all = extract_rename_all ( & struct_item. attrs ) ;
275+
193276 match & struct_item. fields {
194277 Fields :: Named ( fields_named) => {
195278 for field in & fields_named. named {
196- let field_name = field
279+ let rust_field_name = field
197280 . ident
198281 . as_ref ( )
199282 . map ( |i| i. to_string ( ) )
200283 . unwrap_or_else ( || "unknown" . to_string ( ) ) ;
201284
285+ // Apply rename_all transformation if present
286+ let field_name = rename_field ( & rust_field_name, rename_all. as_deref ( ) ) ;
287+
202288 let field_type = & field. ty ;
203289 let schema_ref = parse_type_to_schema_ref ( field_type, known_schemas) ;
204290
0 commit comments