Skip to main content

big_code_analysis/
function.rs

1// Per-language metric and AST modules deliberately consume the macro-
2// generated tree-sitter token enums via `use crate::*` and `use Foo::*`
3// inside match expressions — explicit imports would list dozens of
4// variants per arm and obscure the per-language token sets that are the
5// point of these files. Allowed at the module level rather than per
6// function so the per-language impl blocks stay readable.
7#![allow(
8    clippy::enum_glob_use,
9    clippy::needless_pass_by_value,
10    clippy::wildcard_imports
11)]
12
13use std::io::Write;
14use std::path::PathBuf;
15
16use serde::Serialize;
17use termcolor::{Color, ColorChoice, StandardStream, StandardStreamLock};
18
19use crate::traits::*;
20
21use crate::checker::Checker;
22use crate::getter::Getter;
23
24use crate::tools::{color, intense_color};
25
26/// Function span data.
27#[derive(Debug, Serialize)]
28pub struct FunctionSpan {
29    /// The function name
30    pub name: String,
31    /// The first line of a function
32    pub start_line: usize,
33    /// The last line of a function
34    pub end_line: usize,
35    /// If `true`, an error is occurred in determining the span
36    /// of a function
37    pub error: bool,
38}
39
40// Hidden from rustdoc because the signature exposes `ParserTrait`,
41// which is `#[doc(hidden)]` per issue #256. The CLI's `Function`
42// callback remains the documented surface.
43#[doc(hidden)]
44/// Detects the span of each function in a code.
45///
46/// Returns a vector containing the [`FunctionSpan`] of each function
47///
48/// [`FunctionSpan`]: struct.FunctionSpan.html
49pub fn function<T: ParserTrait>(parser: &T) -> Vec<FunctionSpan> {
50    let root = parser.get_root();
51    let code = parser.get_code();
52    let mut spans = Vec::new();
53    root.act_on_node(&mut |n| {
54        if T::Checker::is_func(n) {
55            let start_line = n.start_row() + 1;
56            let end_line = n.end_row() + 1;
57            if let Some(name) = T::Getter::get_func_name(n, code) {
58                spans.push(FunctionSpan {
59                    name: name.to_string(),
60                    start_line,
61                    end_line,
62                    error: false,
63                });
64            } else {
65                spans.push(FunctionSpan {
66                    name: String::new(),
67                    start_line,
68                    end_line,
69                    error: true,
70                });
71            }
72        }
73    });
74
75    spans
76}
77
78fn dump_span(
79    span: FunctionSpan,
80    stdout: &mut StandardStreamLock,
81    last: bool,
82) -> std::io::Result<()> {
83    /*if !span.error {
84        return Ok(());
85    }*/
86
87    let pref = if last { "   `- " } else { "   |- " };
88
89    color(stdout, Color::Blue)?;
90    write!(stdout, "{pref}")?;
91
92    if span.error {
93        intense_color(stdout, Color::Red)?;
94        write!(stdout, "error: ")?;
95    } else {
96        intense_color(stdout, Color::Magenta)?;
97        write!(stdout, "{}: ", span.name)?;
98    }
99
100    color(stdout, Color::Green)?;
101    write!(stdout, "from line ")?;
102
103    color(stdout, Color::White)?;
104    write!(stdout, "{}", span.start_line)?;
105
106    color(stdout, Color::Green)?;
107    write!(stdout, " to line ")?;
108
109    color(stdout, Color::White)?;
110    writeln!(stdout, "{}.", span.end_line)
111}
112
113fn dump_spans(spans: Vec<FunctionSpan>, path: PathBuf) -> std::io::Result<()> {
114    if !spans.is_empty() {
115        let stdout = StandardStream::stdout(ColorChoice::Always);
116        let mut stdout = stdout.lock();
117
118        intense_color(&mut stdout, Color::Yellow)?;
119        writeln!(&mut stdout, "In file {}", path.to_str().unwrap_or("..."))?;
120
121        // Consume `spans` by value: `dump_span` takes `FunctionSpan`
122        // by value, so cloning to use `split_last` would allocate
123        // strings unnecessarily. The outer `is_empty` guard ensures
124        // `spans.len() >= 1`, so `last_idx` is well-defined.
125        let last_idx = spans.len() - 1;
126        for (i, span) in spans.into_iter().enumerate() {
127            dump_span(span, &mut stdout, i == last_idx)?;
128        }
129        color(&mut stdout, Color::White)?;
130    }
131    Ok(())
132}
133
134/// Configuration options for detecting the span of
135/// each function in a code.
136#[derive(Debug)]
137pub struct FunctionCfg {
138    /// Path to the file containing the code
139    pub path: PathBuf,
140}
141
142/// Type tag identifying the function-extraction action; carries no data.
143pub struct Function {
144    _guard: (),
145}
146
147impl Callback for Function {
148    type Res = std::io::Result<()>;
149    type Cfg = FunctionCfg;
150
151    fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
152        dump_spans(function(parser), cfg.path)
153    }
154}
155
156#[cfg(test)]
157#[path = "function_tests.rs"]
158mod tests;