rusty_ast/
json_visitor.rs

1use quote::ToTokens;
2use serde::Serialize;
3use syn::{Expr, File, Item, Lit, Pat, Stmt, visit::Visit};
4
5/// A serializable representation of a Rust AST for JSON output
6///
7/// # Fields
8/// * `items`: Vec<ItemJson> - the items in the AST
9#[derive(Serialize, Debug, Default)]
10pub struct AstJson {
11    #[serde(skip_serializing_if = "Vec::is_empty")]
12    pub items: Vec<ItemJson>,
13}
14
15/// # Fields
16/// * `name`: String - the name of the item
17/// * `parameters`: Vec<ParameterJson> - the parameters of the item
18/// * `return_type`: Option<String> - the return type of the item
19/// * `body`: Vec<StmtJson> - the body of the item
20#[derive(Serialize, Debug)]
21#[serde(tag = "type")]
22pub enum ItemJson {
23    Function {
24        name: String,
25        parameters: Vec<ParameterJson>,
26        return_type: Option<String>,
27        body: Vec<StmtJson>,
28    },
29    Struct {
30        name: String,
31        fields: Vec<FieldJson>,
32    },
33    Enum {
34        name: String,
35        variants: Vec<VariantJson>,
36    },
37    Other {
38        description: String,
39    },
40}
41
42/// # Fields
43/// * `name`: String - the name of the parameter
44/// * `type_info`: String - the type of the parameter
45#[derive(Serialize, Debug)]
46pub struct ParameterJson {
47    pub name: String,
48    pub type_info: String,
49}
50
51/// # Fields
52/// * `name`: Option<String> - the name of the field
53/// * `type_info`: String - the type of the field
54#[derive(Serialize, Debug)]
55pub struct FieldJson {
56    pub name: Option<String>,
57    pub type_info: String,
58}
59
60/// # Fields
61/// * `name`: String - the name of the variant
62#[derive(Serialize, Debug)]
63pub struct VariantJson {
64    pub name: String,
65}
66
67/// # Fields
68/// * `name`: String - the name of the statement
69/// * `initializer`: Option<Box<ExprJson>> - the initializer of the statement
70#[derive(Serialize, Debug)]
71#[serde(tag = "type")]
72pub enum StmtJson {
73    VariableDeclaration {
74        name: String,
75        initializer: Option<Box<ExprJson>>,
76    },
77    Expression {
78        expr: Box<ExprJson>,
79    },
80    Other {
81        description: String,
82    },
83}
84
85/// # Fields
86/// * `value`: String - the value of the literal
87#[derive(Serialize, Debug)]
88#[serde(tag = "type")]
89pub enum ExprJson {
90    IntLiteral {
91        value: String,
92    },
93    FloatLiteral {
94        value: String,
95    },
96    StringLiteral {
97        value: String,
98    },
99    BoolLiteral {
100        value: bool,
101    },
102    Binary {
103        operator: String,
104        left: Box<ExprJson>,
105        right: Box<ExprJson>,
106    },
107    FunctionCall {
108        function: Box<ExprJson>,
109        arguments: Vec<ExprJson>,
110    },
111    Identifier {
112        name: String,
113    },
114    If {
115        condition: Box<ExprJson>,
116        then_branch: Vec<StmtJson>,
117        else_branch: Option<Box<ExprJson>>,
118    },
119    Loop {
120        body: Vec<StmtJson>,
121    },
122    While {
123        condition: Box<ExprJson>,
124        body: Vec<StmtJson>,
125    },
126    Return {
127        value: Option<Box<ExprJson>>,
128    },
129    Other {
130        description: String,
131    },
132}
133
134/// A visitor that builds a JSON representation of a Rust AST
135///
136/// # Fields
137/// * `ast`: AstJson - the AST to be converted to JSON
138pub struct JsonVisitor {
139    pub ast: AstJson,
140}
141
142/// # Methods
143/// * `new()`: creates a new JsonVisitor
144/// * `to_json()`: converts the AST to a JSON string
145/// * `process_file()`: processes a file and adds its items to the AST
146/// * `process_item()`: processes an item and adds it to the AST
147impl Default for JsonVisitor {
148    fn default() -> Self {
149        Self::new()
150    }
151}
152
153impl JsonVisitor {
154    /// new
155    ///
156    /// # Arguments
157    /// * `()`
158    ///
159    /// # Returns
160    /// * `JsonVisitor` - a new JsonVisitor
161    pub fn new() -> Self {
162        JsonVisitor {
163            ast: AstJson::default(),
164        }
165    }
166
167    /// to_json
168    ///
169    /// # Arguments
170    /// * `self`: &Self - the JsonVisitor
171    ///
172    /// # Returns
173    /// * `String` - the JSON string
174    pub fn to_json(&self) -> String {
175        match serde_json::to_string_pretty(&self.ast) {
176            Ok(json) => json,
177            Err(_) => String::from("{}"),
178        }
179    }
180
181    /// process_file
182    ///
183    /// # Arguments
184    /// * `self`: &mut Self - the JsonVisitor
185    /// * `file`: &File - the file to process
186    ///
187    /// # Returns
188    /// * `()`
189    pub fn process_file(&mut self, file: &File) {
190        for item in &file.items {
191            self.process_item(item);
192        }
193    }
194
195    /// process_item
196    ///
197    /// # Arguments
198    /// * `self`: &mut Self - the JsonVisitor
199    /// * `item`: &Item - the item to process
200    ///
201    /// # Returns
202    /// * `()`
203    fn process_item(&mut self, item: &Item) {
204        match item {
205            Item::Fn(item_fn) => {
206                let mut parameters = Vec::new();
207                for param in &item_fn.sig.inputs {
208                    match param {
209                        syn::FnArg::Typed(pat_type) => {
210                            if let Pat::Ident(pat_ident) = &*pat_type.pat {
211                                parameters.push(ParameterJson {
212                                    name: pat_ident.ident.to_string(),
213                                    type_info: format!("{}", (*pat_type.ty).to_token_stream()),
214                                });
215                            }
216                        }
217                        syn::FnArg::Receiver(receiver) => {
218                            parameters.push(ParameterJson {
219                                name: "self".to_string(),
220                                type_info: format!("{}", receiver.to_token_stream()),
221                            });
222                        }
223                    }
224                }
225
226                let return_type = match &item_fn.sig.output {
227                    syn::ReturnType::Default => None,
228                    syn::ReturnType::Type(_, return_type) => {
229                        Some(format!("{}", return_type.to_token_stream()))
230                    }
231                };
232
233                let mut statements = Vec::new();
234                for stmt in &item_fn.block.stmts {
235                    statements.push(self.visit_stmt_json(stmt));
236                }
237
238                self.ast.items.push(ItemJson::Function {
239                    name: item_fn.sig.ident.to_string(),
240                    parameters,
241                    return_type,
242                    body: statements,
243                });
244            }
245            Item::Struct(item_struct) => {
246                let mut fields = Vec::new();
247                for field in &item_struct.fields {
248                    fields.push(FieldJson {
249                        name: field.ident.as_ref().map(|ident| ident.to_string()),
250                        type_info: format!("{}", field.ty.to_token_stream()),
251                    });
252                }
253
254                self.ast.items.push(ItemJson::Struct {
255                    name: item_struct.ident.to_string(),
256                    fields,
257                });
258            }
259            Item::Enum(item_enum) => {
260                let mut variants = Vec::new();
261                for variant in &item_enum.variants {
262                    variants.push(VariantJson {
263                        name: variant.ident.to_string(),
264                    });
265                }
266
267                self.ast.items.push(ItemJson::Enum {
268                    name: item_enum.ident.to_string(),
269                    variants,
270                });
271            }
272            _ => {
273                self.ast.items.push(ItemJson::Other {
274                    description: format!("{}", item.to_token_stream()),
275                });
276            }
277        }
278    }
279}
280
281/// # Implementations
282/// * `Visit<'ast>` - the Visit trait
283impl<'ast> Visit<'ast> for JsonVisitor {
284    /// visit_file
285    ///
286    /// # Arguments
287    /// * `self`: &mut Self - the JsonVisitor
288    /// * `file`: &File - the file to process
289    ///
290    /// # Returns
291    /// * `()`
292    fn visit_file(&mut self, file: &'ast File) {
293        self.process_file(file);
294    }
295}
296
297/// # Implementations
298/// * `Visit<'ast>` - the Visit trait
299impl JsonVisitor {
300    /// visit_stmt_json
301    ///
302    /// # Arguments
303    /// * `self`: &mut Self - the JsonVisitor
304    /// * `stmt`: &Stmt - the statement to process
305    ///
306    /// # Returns
307    fn visit_stmt_json(&mut self, stmt: &Stmt) -> StmtJson {
308        match stmt {
309            Stmt::Local(local) => {
310                let name = if let Pat::Ident(pat_ident) = &local.pat {
311                    pat_ident.ident.to_string()
312                } else {
313                    "unknown".to_string()
314                };
315
316                let initializer = local.init.as_ref().map(|init| Box::new(self.visit_expr_json(&init.expr)));
317
318                StmtJson::VariableDeclaration { name, initializer }
319            }
320            Stmt::Expr(expr, _) => StmtJson::Expression {
321                expr: Box::new(self.visit_expr_json(expr)),
322            },
323            _ => StmtJson::Other {
324                description: format!("{}", stmt.to_token_stream()),
325            },
326        }
327    }
328
329    /// visit_expr_json
330    ///
331    /// # Arguments
332    /// * `self`: &mut Self - the JsonVisitor
333    /// * `expr`: &Expr - the expression to process
334    ///
335    /// # Returns
336    /// * `ExprJson` - the JSON representation of the expression
337    fn visit_expr_json(&mut self, expr: &Expr) -> ExprJson {
338        match expr {
339            Expr::Lit(expr_lit) => match &expr_lit.lit {
340                Lit::Int(lit_int) => ExprJson::IntLiteral {
341                    value: lit_int.base10_digits().to_string(),
342                },
343                Lit::Float(lit_float) => ExprJson::FloatLiteral {
344                    value: lit_float.base10_digits().to_string(),
345                },
346                Lit::Str(lit_str) => ExprJson::StringLiteral {
347                    value: lit_str.value(),
348                },
349                Lit::Bool(lit_bool) => ExprJson::BoolLiteral {
350                    value: lit_bool.value,
351                },
352                _ => ExprJson::Other {
353                    description: format!("{}", expr_lit.to_token_stream()),
354                },
355            },
356            Expr::Binary(expr_bin) => {
357                let op = match expr_bin.op {
358                    syn::BinOp::Add(_) => "+",
359                    syn::BinOp::Sub(_) => "-",
360                    syn::BinOp::Mul(_) => "*",
361                    syn::BinOp::Div(_) => "/",
362                    syn::BinOp::Eq(_) => "==",
363                    syn::BinOp::Lt(_) => "<",
364                    syn::BinOp::Le(_) => "<=",
365                    syn::BinOp::Ne(_) => "!=",
366                    syn::BinOp::Ge(_) => ">=",
367                    syn::BinOp::Gt(_) => ">",
368                    _ => "other_operator",
369                };
370
371                ExprJson::Binary {
372                    operator: op.to_string(),
373                    left: Box::new(self.visit_expr_json(&expr_bin.left)),
374                    right: Box::new(self.visit_expr_json(&expr_bin.right)),
375                }
376            }
377            Expr::Call(expr_call) => ExprJson::FunctionCall {
378                function: Box::new(self.visit_expr_json(&expr_call.func)),
379                arguments: expr_call
380                    .args
381                    .iter()
382                    .map(|arg| self.visit_expr_json(arg))
383                    .collect(),
384            },
385            Expr::Path(expr_path) => ExprJson::Identifier {
386                name: format!("{}", expr_path.to_token_stream()),
387            },
388            Expr::If(expr_if) => {
389                let mut then_stmts = Vec::new();
390                for stmt in &expr_if.then_branch.stmts {
391                    then_stmts.push(self.visit_stmt_json(stmt));
392                }
393
394                let else_branch = if let Some((_, else_expr)) = &expr_if.else_branch {
395                    Some(Box::new(self.visit_expr_json(else_expr)))
396                } else {
397                    None
398                };
399
400                ExprJson::If {
401                    condition: Box::new(self.visit_expr_json(&expr_if.cond)),
402                    then_branch: then_stmts,
403                    else_branch,
404                }
405            }
406            Expr::Loop(expr_loop) => {
407                let mut stmts = Vec::new();
408                for stmt in &expr_loop.body.stmts {
409                    stmts.push(self.visit_stmt_json(stmt));
410                }
411
412                ExprJson::Loop { body: stmts }
413            }
414            Expr::While(expr_while) => {
415                let mut stmts = Vec::new();
416                for stmt in &expr_while.body.stmts {
417                    stmts.push(self.visit_stmt_json(stmt));
418                }
419
420                ExprJson::While {
421                    condition: Box::new(self.visit_expr_json(&expr_while.cond)),
422                    body: stmts,
423                }
424            }
425            Expr::Return(expr_return) => ExprJson::Return {
426                value: expr_return
427                    .expr
428                    .as_ref()
429                    .map(|e| Box::new(self.visit_expr_json(e))),
430            },
431            _ => ExprJson::Other {
432                description: format!("{}", expr.to_token_stream()),
433            },
434        }
435    }
436}
437
438#[cfg(test)]
439mod tests {
440    use super::*;
441    use crate::parse_rust_source;
442    use serde_json::Value;
443
444    #[test]
445    fn test_json_serialization_function() {
446        let source = r#"
447            fn add(a: i32, b: i32) -> i32 {
448                a + b
449            }
450        "#;
451
452        let file = parse_rust_source(source).unwrap();
453        let mut visitor = JsonVisitor::new();
454        visitor.process_file(&file);
455
456        let json = visitor.to_json();
457
458        // JSONをパースして構造を確認
459        let parsed: Value = serde_json::from_str(&json).expect("JSONのパースに失敗");
460
461        // 構造を確認 - itemsは配列
462        assert!(parsed["items"].is_array());
463
464        // 最初の要素を取得
465        let first_item = &parsed["items"][0];
466
467        // 型と名前を確認
468        assert_eq!(first_item["type"], "Function");
469        assert_eq!(first_item["name"], "add");
470
471        // パラメータを確認
472        assert!(first_item["parameters"].is_array());
473        assert_eq!(first_item["parameters"][0]["name"], "a");
474        assert_eq!(first_item["parameters"][1]["name"], "b");
475
476        // 戻り値を確認
477        assert_eq!(first_item["return_type"], "i32");
478    }
479
480    #[test]
481    fn test_json_serialization_struct() {
482        let source = r#"
483            struct Point {
484                x: f64,
485                y: f64,
486            }
487        "#;
488
489        let file = parse_rust_source(source).unwrap();
490        let mut visitor = JsonVisitor::new();
491        visitor.process_file(&file);
492
493        let json = visitor.to_json();
494
495        // JSONをパースして構造を確認
496        let parsed: Value = serde_json::from_str(&json).expect("JSONのパースに失敗");
497
498        // 構造を確認
499        assert!(parsed["items"].is_array());
500
501        // 最初の要素を取得
502        let first_item = &parsed["items"][0];
503
504        // 型と名前を確認
505        assert_eq!(first_item["type"], "Struct");
506        assert_eq!(first_item["name"], "Point");
507
508        // フィールドを確認
509        assert!(first_item["fields"].is_array());
510        assert_eq!(first_item["fields"][0]["name"], "x");
511        assert_eq!(first_item["fields"][1]["name"], "y");
512    }
513
514    #[test]
515    fn test_json_serialization_enum() {
516        let source = r#"
517            enum Direction {
518                North,
519                East,
520                South,
521                West,
522            }
523        "#;
524
525        let file = parse_rust_source(source).unwrap();
526        let mut visitor = JsonVisitor::new();
527        visitor.process_file(&file);
528
529        let json = visitor.to_json();
530
531        // JSONをパースして構造を確認
532        let parsed: Value = serde_json::from_str(&json).expect("JSONのパースに失敗");
533
534        // 構造を確認
535        assert!(parsed["items"].is_array());
536
537        // 最初の要素を取得
538        let first_item = &parsed["items"][0];
539
540        // 型と名前を確認
541        assert_eq!(first_item["type"], "Enum");
542        assert_eq!(first_item["name"], "Direction");
543
544        // バリアントを確認
545        assert!(first_item["variants"].is_array());
546        assert_eq!(first_item["variants"][0]["name"], "North");
547        assert_eq!(first_item["variants"][1]["name"], "East");
548        assert_eq!(first_item["variants"][2]["name"], "South");
549        assert_eq!(first_item["variants"][3]["name"], "West");
550    }
551
552    #[test]
553    fn test_json_serialization_complex() {
554        let source = r#"
555            fn complex_expr() {
556                let result = (10 + 20) * 30 / (5 - 2);
557                if result > 100 {
558                    println!("Large result: {}", result);
559                } else {
560                    println!("Small result: {}", result);
561                }
562            }
563        "#;
564
565        let file = parse_rust_source(source).unwrap();
566        let mut visitor = JsonVisitor::new();
567        visitor.process_file(&file);
568
569        let json = visitor.to_json();
570
571        // JSONをパースして構造を確認
572        let parsed: Value = serde_json::from_str(&json).expect("JSONのパースに失敗");
573
574        // 構造を確認
575        assert!(parsed["items"].is_array());
576
577        // 最初の要素を取得
578        let first_item = &parsed["items"][0];
579
580        // 型と名前を確認
581        assert_eq!(first_item["type"], "Function");
582        assert_eq!(first_item["name"], "complex_expr");
583
584        // 関数本体の文を確認
585        assert!(first_item["body"].is_array());
586        assert_eq!(first_item["body"][0]["type"], "VariableDeclaration");
587        assert_eq!(first_item["body"][0]["name"], "result");
588
589        // if文を確認
590        assert_eq!(first_item["body"][1]["type"], "Expression");
591        assert_eq!(first_item["body"][1]["expr"]["type"], "If");
592    }
593
594    // 追加のデバッグテスト
595    #[test]
596    fn test_debug_json_output() {
597        let source = r#"
598            fn test_func() {
599                println!("Hello");
600            }
601        "#;
602
603        let file = parse_rust_source(source).unwrap();
604        let mut visitor = JsonVisitor::new();
605        visitor.process_file(&file);
606
607        // JSON化
608        let json = visitor.to_json();
609
610        // 検証 - 項目が空でないこと
611        assert!(!visitor.ast.items.is_empty(), "AST項目が空です!");
612
613        // JSONをパースして構造を確認
614        let parsed: Value = serde_json::from_str(&json).expect("JSONのパースに失敗");
615
616        // 構造を確認
617        assert!(parsed["items"].is_array());
618
619        // 最初の要素を取得
620        let first_item = &parsed["items"][0];
621
622        // 型と名前を確認
623        assert_eq!(first_item["type"], "Function");
624        assert_eq!(first_item["name"], "test_func");
625    }
626
627    // 基本的なシリアライズのテスト
628    #[test]
629    fn test_basic_serialization() {
630        // 手動でAstJson構造を作成
631        let mut ast = AstJson::default();
632
633        // 関数アイテムを追加
634        ast.items.push(ItemJson::Function {
635            name: "manual_func".to_string(),
636            parameters: vec![],
637            return_type: Some("i32".to_string()),
638            body: vec![],
639        });
640
641        // JSONシリアライズ
642        let json = serde_json::to_string_pretty(&ast).unwrap();
643
644        // JSONをパースして構造を確認
645        let parsed: Value = serde_json::from_str(&json).expect("JSONのパースに失敗");
646
647        // 構造を確認
648        assert!(parsed["items"].is_array());
649
650        // 最初の要素を取得
651        let first_item = &parsed["items"][0];
652
653        // 型と名前を確認
654        assert_eq!(first_item["type"], "Function");
655        assert_eq!(first_item["name"], "manual_func");
656    }
657}