rust_code_analysis/output/
dump.rs

1use std::io::Write;
2use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor};
3
4use crate::node::Node;
5
6use crate::traits::*;
7
8/// Dumps the `AST` of a code.
9///
10/// Returns a [`Result`] value, when an error occurs.
11///
12/// # Examples
13///
14/// ```
15/// use std::path::PathBuf;
16///
17/// use rust_code_analysis::{dump_node, CppParser, ParserTrait};
18///
19/// let source_code = "int a = 42;";
20///
21/// // The path to a dummy file used to contain the source code
22/// let path = PathBuf::from("foo.c");
23/// let source_as_vec = source_code.as_bytes().to_vec();
24///
25/// // The parser of the code, in this case a CPP parser
26/// let parser = CppParser::new(source_as_vec.clone(), &path, None);
27///
28/// // The root of the AST
29/// let root = parser.get_root();
30///
31/// // Dump the AST from the first line of code in a file to the last one
32/// dump_node(&source_as_vec, &root, -1, None, None).unwrap();
33/// ```
34///
35/// [`Result`]: #variant.Result
36pub fn dump_node(
37    code: &[u8],
38    node: &Node,
39    depth: i32,
40    line_start: Option<usize>,
41    line_end: Option<usize>,
42) -> std::io::Result<()> {
43    let stdout = StandardStream::stdout(ColorChoice::Always);
44    let mut stdout = stdout.lock();
45    let ret = dump_tree_helper(
46        code,
47        node,
48        "",
49        true,
50        &mut stdout,
51        depth,
52        &line_start,
53        &line_end,
54    );
55
56    color!(stdout, White);
57
58    ret
59}
60
61#[allow(clippy::too_many_arguments)]
62fn dump_tree_helper(
63    code: &[u8],
64    node: &Node,
65    prefix: &str,
66    last: bool,
67    stdout: &mut StandardStreamLock,
68    depth: i32,
69    line_start: &Option<usize>,
70    line_end: &Option<usize>,
71) -> std::io::Result<()> {
72    if depth == 0 {
73        return Ok(());
74    }
75
76    let (pref_child, pref) = if node.object().parent().is_none() {
77        ("", "")
78    } else if last {
79        ("   ", "╰─ ")
80    } else {
81        ("│  ", "├─ ")
82    };
83
84    let node_row = node.object().start_position().row + 1;
85    let mut display = true;
86    if let Some(line_start) = line_start {
87        display = node_row >= *line_start
88    }
89    if let Some(line_end) = line_end {
90        display = display && node_row <= *line_end
91    }
92
93    if display {
94        color!(stdout, Blue);
95        write!(stdout, "{}{}", prefix, pref)?;
96
97        color!(stdout, Yellow, true);
98        write!(
99            stdout,
100            "{{{}:{}}} ",
101            node.object().kind(),
102            node.object().kind_id()
103        )?;
104
105        color!(stdout, White);
106        write!(stdout, "from ")?;
107
108        color!(stdout, Green);
109        let pos = node.object().start_position();
110        write!(stdout, "({}, {}) ", pos.row + 1, pos.column + 1)?;
111
112        color!(stdout, White);
113        write!(stdout, "to ")?;
114
115        color!(stdout, Green);
116        let pos = node.object().end_position();
117        write!(stdout, "({}, {}) ", pos.row + 1, pos.column + 1)?;
118
119        if node.object().start_position().row == node.object().end_position().row {
120            color!(stdout, White);
121            write!(stdout, ": ")?;
122
123            color!(stdout, Red, true);
124            let code = &code[node.object().start_byte()..node.object().end_byte()];
125            if let Ok(code) = String::from_utf8(code.to_vec()) {
126                write!(stdout, "{} ", code)?;
127            } else {
128                stdout.write_all(code).unwrap();
129            }
130        }
131
132        writeln!(stdout)?;
133    }
134
135    let count = node.object().child_count();
136    if count != 0 {
137        let prefix = format!("{}{}", prefix, pref_child);
138        let mut i = count;
139        let mut cursor = node.object().walk();
140        cursor.goto_first_child();
141
142        loop {
143            i -= 1;
144            dump_tree_helper(
145                code,
146                &Node::new(cursor.node()),
147                &prefix,
148                i == 0,
149                stdout,
150                depth - 1,
151                line_start,
152                line_end,
153            )?;
154            if !cursor.goto_next_sibling() {
155                break;
156            }
157        }
158    }
159
160    Ok(())
161}
162
163/// Configuration options for dumping the `AST` of a code.
164pub struct DumpCfg {
165    /// The first line of code to dump
166    ///
167    /// If `None`, the code is dumped from the first line of code
168    /// in a file
169    pub line_start: Option<usize>,
170    /// The last line of code to dump
171    ///
172    /// If `None`, the code is dumped until the last line of code
173    /// in a file
174    pub line_end: Option<usize>,
175}
176
177pub struct Dump {
178    _guard: (),
179}
180
181impl Callback for Dump {
182    type Res = std::io::Result<()>;
183    type Cfg = DumpCfg;
184
185    fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
186        dump_node(
187            parser.get_code(),
188            &parser.get_root(),
189            -1,
190            cfg.line_start,
191            cfg.line_end,
192        )
193    }
194}