Skip to main content

similarity_core/
rust_structure_adapter.rs

1use crate::language_parser::GenericTypeDef;
2use crate::structure_comparator::{
3    ComparisonOptions, SourceLocation, Structure, StructureComparator, StructureComparisonResult,
4    StructureIdentifier, StructureKind, StructureMember, StructureMetadata,
5};
6
7/// Rustの型定義を一般構造に変換
8impl From<GenericTypeDef> for Structure {
9    fn from(type_def: GenericTypeDef) -> Self {
10        let kind = match type_def.kind.as_str() {
11            "struct" => StructureKind::RustStruct,
12            "enum" => StructureKind::RustEnum,
13            _ => StructureKind::Generic(type_def.kind.clone()),
14        };
15
16        let members = type_def
17            .fields
18            .into_iter()
19            .map(|field| {
20                // For enums, fields are variants
21                // For structs, fields are actual fields
22                let (name, value_type) = if type_def.kind == "enum" {
23                    // Enum variant - the field is the variant name
24                    (field.clone(), "variant".to_string())
25                } else {
26                    // Struct field - we need to parse the type
27                    // For now, we'll use a placeholder since GenericTypeDef doesn't store types
28                    (field.clone(), "unknown".to_string())
29                };
30
31                StructureMember { name, value_type, modifiers: vec![], nested: None }
32            })
33            .collect();
34
35        Structure {
36            identifier: StructureIdentifier {
37                name: type_def.name.clone(),
38                kind,
39                namespace: None, // Could be module path
40            },
41            members,
42            metadata: StructureMetadata {
43                location: SourceLocation {
44                    file_path: String::new(), // Would need to pass this separately
45                    start_line: type_def.start_line as usize,
46                    end_line: type_def.end_line as usize,
47                },
48                generics: Vec::new(), // Could extract from type parameters
49                extends: Vec::new(),  // Could extract traits
50                visibility: None,     // Could extract pub/pub(crate)/etc
51            },
52        }
53    }
54}
55
56/// Rust構造体の詳細定義(より詳細な情報を含む)
57#[derive(Debug, Clone)]
58pub struct RustStructDef {
59    pub name: String,
60    pub fields: Vec<RustFieldDef>,
61    pub generics: Vec<String>,
62    pub derives: Vec<String>,
63    pub attributes: Vec<String>, // Other attributes like #[serde(...)], #[cfg(...)]
64    pub visibility: Option<String>,
65    pub is_tuple_struct: bool,
66    pub start_line: usize,
67    pub end_line: usize,
68    pub file_path: String,
69}
70
71#[derive(Debug, Clone)]
72pub struct RustFieldDef {
73    pub name: String,
74    pub field_type: String,
75    pub visibility: Option<String>,
76}
77
78/// Rust enum の詳細定義
79#[derive(Debug, Clone)]
80pub struct RustEnumDef {
81    pub name: String,
82    pub variants: Vec<RustVariantDef>,
83    pub generics: Vec<String>,
84    pub derives: Vec<String>,
85    pub attributes: Vec<String>, // Other attributes like #[serde(...)], #[cfg(...)]
86    pub visibility: Option<String>,
87    pub start_line: usize,
88    pub end_line: usize,
89    pub file_path: String,
90}
91
92#[derive(Debug, Clone)]
93pub struct RustVariantDef {
94    pub name: String,
95    pub variant_type: RustVariantType,
96}
97
98#[derive(Debug, Clone)]
99pub enum RustVariantType {
100    Unit,
101    Tuple(Vec<String>),
102    Struct(Vec<RustFieldDef>),
103}
104
105/// Rust構造体を一般構造に変換
106impl From<RustStructDef> for Structure {
107    fn from(struct_def: RustStructDef) -> Self {
108        let mut members: Vec<StructureMember> = struct_def
109            .fields
110            .into_iter()
111            .map(|field| StructureMember {
112                name: field.name,
113                value_type: field.field_type,
114                modifiers: field.visibility.map(|v| vec![v]).unwrap_or_default(),
115                nested: None,
116            })
117            .collect();
118
119        // Add derives as special members for comparison
120        if !struct_def.derives.is_empty() {
121            members.push(StructureMember {
122                name: "@derives".to_string(),
123                value_type: struct_def.derives.join(", "),
124                modifiers: vec!["attribute".to_string()],
125                nested: None,
126            });
127        }
128
129        // Add other attributes as special members
130        if !struct_def.attributes.is_empty() {
131            members.push(StructureMember {
132                name: "@attributes".to_string(),
133                value_type: struct_def.attributes.join(", "),
134                modifiers: vec!["attribute".to_string()],
135                nested: None,
136            });
137        }
138
139        Structure {
140            identifier: StructureIdentifier {
141                name: struct_def.name.clone(),
142                kind: StructureKind::RustStruct,
143                namespace: Some(struct_def.file_path.clone()),
144            },
145            members,
146            metadata: StructureMetadata {
147                location: SourceLocation {
148                    file_path: struct_def.file_path,
149                    start_line: struct_def.start_line,
150                    end_line: struct_def.end_line,
151                },
152                generics: struct_def.generics,
153                extends: vec![], // Don't put derives here, they're in members now
154                visibility: struct_def.visibility,
155            },
156        }
157    }
158}
159
160/// Rust enumを一般構造に変換
161impl From<RustEnumDef> for Structure {
162    fn from(enum_def: RustEnumDef) -> Self {
163        let mut members: Vec<StructureMember> = enum_def
164            .variants
165            .into_iter()
166            .map(|variant| {
167                let value_type = match variant.variant_type {
168                    RustVariantType::Unit => "unit".to_string(),
169                    RustVariantType::Tuple(ref types) => format!("({})", types.join(", ")),
170                    RustVariantType::Struct(ref fields) => {
171                        let field_strs: Vec<String> = fields
172                            .iter()
173                            .map(|f| format!("{}: {}", f.name, f.field_type))
174                            .collect();
175                        format!("{{ {} }}", field_strs.join(", "))
176                    }
177                };
178
179                StructureMember {
180                    name: variant.name,
181                    value_type,
182                    modifiers: vec!["variant".to_string()],
183                    nested: None,
184                }
185            })
186            .collect();
187
188        // Add derives as special members for comparison
189        if !enum_def.derives.is_empty() {
190            members.push(StructureMember {
191                name: "@derives".to_string(),
192                value_type: enum_def.derives.join(", "),
193                modifiers: vec!["attribute".to_string()],
194                nested: None,
195            });
196        }
197
198        // Add other attributes as special members
199        if !enum_def.attributes.is_empty() {
200            members.push(StructureMember {
201                name: "@attributes".to_string(),
202                value_type: enum_def.attributes.join(", "),
203                modifiers: vec!["attribute".to_string()],
204                nested: None,
205            });
206        }
207
208        Structure {
209            identifier: StructureIdentifier {
210                name: enum_def.name.clone(),
211                kind: StructureKind::RustEnum,
212                namespace: Some(enum_def.file_path.clone()),
213            },
214            members,
215            metadata: StructureMetadata {
216                location: SourceLocation {
217                    file_path: enum_def.file_path,
218                    start_line: enum_def.start_line,
219                    end_line: enum_def.end_line,
220                },
221                generics: enum_def.generics,
222                extends: vec![], // Don't put derives here, they're in members now
223                visibility: enum_def.visibility,
224            },
225        }
226    }
227}
228
229/// Rust用の比較エンジン
230pub struct RustStructureComparator {
231    pub comparator: StructureComparator,
232}
233
234impl Default for RustStructureComparator {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240impl RustStructureComparator {
241    pub fn new() -> Self {
242        let options = ComparisonOptions {
243            name_weight: 0.3,
244            structure_weight: 0.7,
245            threshold: 0.7,
246            ..Default::default()
247        };
248
249        Self { comparator: StructureComparator::new(options) }
250    }
251
252    pub fn with_options(options: ComparisonOptions) -> Self {
253        Self { comparator: StructureComparator::new(options) }
254    }
255
256    /// 構造体を比較
257    pub fn compare_structs(
258        &mut self,
259        struct1: &RustStructDef,
260        struct2: &RustStructDef,
261    ) -> StructureComparisonResult {
262        let s1 = Structure::from(struct1.clone());
263        let s2 = Structure::from(struct2.clone());
264        self.comparator.compare(&s1, &s2)
265    }
266
267    /// Enumを比較
268    pub fn compare_enums(
269        &mut self,
270        enum1: &RustEnumDef,
271        enum2: &RustEnumDef,
272    ) -> StructureComparisonResult {
273        let s1 = Structure::from(enum1.clone());
274        let s2 = Structure::from(enum2.clone());
275        self.comparator.compare(&s1, &s2)
276    }
277
278    /// 汎用型定義を比較
279    pub fn compare_generic_types(
280        &mut self,
281        type1: &GenericTypeDef,
282        type2: &GenericTypeDef,
283    ) -> StructureComparisonResult {
284        let s1 = Structure::from(type1.clone());
285        let s2 = Structure::from(type2.clone());
286        self.comparator.compare(&s1, &s2)
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn test_struct_to_structure_conversion() {
296        let rust_struct = RustStructDef {
297            name: "User".to_string(),
298            fields: vec![
299                RustFieldDef {
300                    name: "id".to_string(),
301                    field_type: "u64".to_string(),
302                    visibility: Some("pub".to_string()),
303                },
304                RustFieldDef {
305                    name: "name".to_string(),
306                    field_type: "String".to_string(),
307                    visibility: Some("pub".to_string()),
308                },
309            ],
310            generics: vec![],
311            derives: vec!["Debug".to_string(), "Clone".to_string()],
312            attributes: vec![],
313            visibility: Some("pub".to_string()),
314            is_tuple_struct: false,
315            start_line: 1,
316            end_line: 5,
317            file_path: "user.rs".to_string(),
318        };
319
320        let structure = Structure::from(rust_struct);
321
322        assert_eq!(structure.identifier.name, "User");
323        assert_eq!(structure.identifier.kind, StructureKind::RustStruct);
324        assert_eq!(structure.members.len(), 3); // 2 fields + 1 @derives
325        assert_eq!(structure.members[0].name, "id");
326        assert_eq!(structure.members[0].value_type, "u64");
327        // Check derives member
328        assert_eq!(structure.members[2].name, "@derives");
329        assert_eq!(structure.members[2].value_type, "Debug, Clone");
330    }
331
332    #[test]
333    fn test_enum_to_structure_conversion() {
334        let rust_enum = RustEnumDef {
335            name: "Result".to_string(),
336            variants: vec![
337                RustVariantDef {
338                    name: "Ok".to_string(),
339                    variant_type: RustVariantType::Tuple(vec!["T".to_string()]),
340                },
341                RustVariantDef {
342                    name: "Err".to_string(),
343                    variant_type: RustVariantType::Tuple(vec!["E".to_string()]),
344                },
345            ],
346            generics: vec!["T".to_string(), "E".to_string()],
347            derives: vec!["Debug".to_string()],
348            attributes: vec![],
349            visibility: Some("pub".to_string()),
350            start_line: 1,
351            end_line: 4,
352            file_path: "result.rs".to_string(),
353        };
354
355        let structure = Structure::from(rust_enum);
356
357        assert_eq!(structure.identifier.name, "Result");
358        assert_eq!(structure.identifier.kind, StructureKind::RustEnum);
359        assert_eq!(structure.members.len(), 3); // 2 variants + 1 @derives
360        assert_eq!(structure.members[0].name, "Ok");
361        assert_eq!(structure.members[0].value_type, "(T)");
362        // Check derives member
363        assert_eq!(structure.members[2].name, "@derives");
364        assert_eq!(structure.members[2].value_type, "Debug");
365    }
366
367    #[test]
368    fn test_rust_comparator() {
369        let mut comparator = RustStructureComparator::new();
370
371        let struct1 = RustStructDef {
372            name: "User".to_string(),
373            fields: vec![
374                RustFieldDef {
375                    name: "id".to_string(),
376                    field_type: "u64".to_string(),
377                    visibility: Some("pub".to_string()),
378                },
379                RustFieldDef {
380                    name: "name".to_string(),
381                    field_type: "String".to_string(),
382                    visibility: Some("pub".to_string()),
383                },
384            ],
385            generics: vec![],
386            derives: vec![],
387            attributes: vec![],
388            visibility: Some("pub".to_string()),
389            is_tuple_struct: false,
390            start_line: 1,
391            end_line: 5,
392            file_path: "user.rs".to_string(),
393        };
394
395        let struct2 = RustStructDef {
396            name: "Person".to_string(),
397            fields: vec![
398                RustFieldDef {
399                    name: "id".to_string(),
400                    field_type: "u64".to_string(),
401                    visibility: Some("pub".to_string()),
402                },
403                RustFieldDef {
404                    name: "name".to_string(),
405                    field_type: "String".to_string(),
406                    visibility: Some("pub".to_string()),
407                },
408            ],
409            generics: vec![],
410            derives: vec![],
411            attributes: vec![],
412            visibility: Some("pub".to_string()),
413            is_tuple_struct: false,
414            start_line: 10,
415            end_line: 15,
416            file_path: "person.rs".to_string(),
417        };
418
419        let result = comparator.compare_structs(&struct1, &struct2);
420
421        // Same structure, different names
422        assert!(result.member_similarity > 0.9);
423        assert!(result.identifier_similarity < 0.5);
424        assert!(result.overall_similarity > 0.6);
425    }
426
427    #[test]
428    fn test_struct_comparison_with_derives() {
429        let mut comparator = RustStructureComparator::new();
430
431        let struct1 = RustStructDef {
432            name: "User".to_string(),
433            fields: vec![RustFieldDef {
434                name: "id".to_string(),
435                field_type: "u64".to_string(),
436                visibility: Some("pub".to_string()),
437            }],
438            generics: vec![],
439            derives: vec!["Debug".to_string(), "Clone".to_string(), "Serialize".to_string()],
440            attributes: vec![],
441            visibility: Some("pub".to_string()),
442            is_tuple_struct: false,
443            start_line: 1,
444            end_line: 5,
445            file_path: "user.rs".to_string(),
446        };
447
448        // Same fields, different derives
449        let struct2 = RustStructDef {
450            name: "User".to_string(),
451            fields: vec![RustFieldDef {
452                name: "id".to_string(),
453                field_type: "u64".to_string(),
454                visibility: Some("pub".to_string()),
455            }],
456            generics: vec![],
457            derives: vec!["Debug".to_string(), "PartialEq".to_string()],
458            attributes: vec![],
459            visibility: Some("pub".to_string()),
460            is_tuple_struct: false,
461            start_line: 10,
462            end_line: 14,
463            file_path: "user.rs".to_string(),
464        };
465
466        let result1 = comparator.compare_structs(&struct1, &struct2);
467
468        // Same fields, same derives
469        let struct3 = RustStructDef {
470            name: "User".to_string(),
471            fields: vec![RustFieldDef {
472                name: "id".to_string(),
473                field_type: "u64".to_string(),
474                visibility: Some("pub".to_string()),
475            }],
476            generics: vec![],
477            derives: vec!["Debug".to_string(), "Clone".to_string(), "Serialize".to_string()],
478            attributes: vec![],
479            visibility: Some("pub".to_string()),
480            is_tuple_struct: false,
481            start_line: 20,
482            end_line: 24,
483            file_path: "user.rs".to_string(),
484        };
485
486        let result2 = comparator.compare_structs(&struct1, &struct3);
487
488        // Structs with same derives should have equal or higher similarity
489        // Both have the same fields, but different @derives members
490        assert!(result2.overall_similarity >= result1.overall_similarity);
491
492        // Check @derives member comparison
493        // For struct1 and struct3 (same derives), @derives should match perfectly (1.0)
494        let derives_match_result2 =
495            result2.member_matches.iter().find(|m| m.member1 == "@derives").map(|m| m.similarity);
496        assert_eq!(derives_match_result2, Some(1.0));
497
498        // For struct1 and struct2 (different derives), @derives should have lower similarity
499        let derives_match_result1 =
500            result1.member_matches.iter().find(|m| m.member1 == "@derives").map(|m| m.similarity);
501        // The derives are different but may have partial match (both have "Debug")
502        assert!(derives_match_result1.unwrap_or(0.0) < 1.0);
503
504        // Both should have same number of member matches (id + @derives)
505        assert_eq!(result2.member_matches.len(), 2);
506        assert_eq!(result1.member_matches.len(), 2);
507    }
508}