Skip to main content

rust_code_analysis_code_split/output/
dump.rs

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