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