|
1 |
| -use crate::{AmlError, ExpectedAmLabel, ListAmFunctions, Result, FUNC_NAME_CAPTURE}; |
2 |
| -use itertools::Itertools; |
| 1 | +mod queries; |
| 2 | + |
| 3 | +use crate::{ExpectedAmLabel, ListAmFunctions, Result}; |
| 4 | +use queries::{AllFunctionsQuery, AmQuery}; |
3 | 5 | use rayon::prelude::*;
|
4 | 6 | use std::{collections::HashSet, fs::read_to_string, path::Path};
|
5 |
| -use tree_sitter::{Parser, Query}; |
6 |
| -use tree_sitter_go::language; |
7 | 7 | use walkdir::{DirEntry, WalkDir};
|
8 | 8 |
|
9 |
| -const PACK_NAME_CAPTURE: &str = "pack.name"; |
10 |
| - |
| 9 | +/// Implementation of the Go support for listing autometricized functions. |
11 | 10 | #[derive(Clone, Copy, Debug, Default)]
|
12 | 11 | pub struct Impl {}
|
13 | 12 |
|
@@ -51,116 +50,45 @@ impl ListAmFunctions for Impl {
|
51 | 50 |
|
52 | 51 | list.par_extend(source_mod_pairs.par_iter().filter_map(move |path| {
|
53 | 52 | let source = read_to_string(path).ok()?;
|
54 |
| - let names = list_function_names(&source).unwrap_or_default(); |
55 |
| - Some( |
56 |
| - names |
57 |
| - .into_iter() |
58 |
| - .map(move |(module, function)| ExpectedAmLabel { module, function }) |
59 |
| - .collect::<Vec<_>>(), |
60 |
| - ) |
| 53 | + let query = AmQuery::try_new().ok()?; |
| 54 | + let names = query.list_function_names(&source).unwrap_or_default(); |
| 55 | + Some(names) |
61 | 56 | }));
|
62 | 57 |
|
63 | 58 | let mut result = Vec::with_capacity(PREALLOCATED_ELEMS);
|
64 | 59 | result.extend(list.into_iter().flatten());
|
65 | 60 | Ok(result)
|
66 | 61 | }
|
67 | 62 |
|
68 |
| - fn list_all_functions(&mut self, _project_root: &Path) -> Result<Vec<ExpectedAmLabel>> { |
69 |
| - unimplemented!("listing all functions in Golang") |
70 |
| - } |
71 |
| -} |
72 |
| - |
73 |
| -fn new_parser() -> Result<Parser> { |
74 |
| - let mut parser = Parser::new(); |
75 |
| - parser.set_language(language())?; |
76 |
| - Ok(parser) |
77 |
| -} |
| 63 | + fn list_all_functions(&mut self, project_root: &Path) -> Result<Vec<ExpectedAmLabel>> { |
| 64 | + const PREALLOCATED_ELEMS: usize = 100; |
| 65 | + let mut list = HashSet::with_capacity(PREALLOCATED_ELEMS); |
78 | 66 |
|
79 |
| -fn query_builder() -> Result<(Query, u32, u32)> { |
80 |
| - let query = Query::new( |
81 |
| - language(), |
82 |
| - include_str!("../runtime/queries/go/autometrics.scm"), |
83 |
| - )?; |
84 |
| - let idx = query |
85 |
| - .capture_index_for_name(FUNC_NAME_CAPTURE) |
86 |
| - .ok_or_else(|| AmlError::MissingNamedCapture(FUNC_NAME_CAPTURE.into()))?; |
87 |
| - let mod_idx = query |
88 |
| - .capture_index_for_name(PACK_NAME_CAPTURE) |
89 |
| - .ok_or_else(|| AmlError::MissingNamedCapture(PACK_NAME_CAPTURE.into()))?; |
90 |
| - Ok((query, idx, mod_idx)) |
91 |
| -} |
| 67 | + let walker = WalkDir::new(project_root).into_iter(); |
| 68 | + let mut source_mod_pairs = Vec::with_capacity(PREALLOCATED_ELEMS); |
| 69 | + source_mod_pairs.extend(walker.filter_entry(Self::is_valid).filter_map(|entry| { |
| 70 | + let entry = entry.ok()?; |
| 71 | + Some( |
| 72 | + entry |
| 73 | + .path() |
| 74 | + .to_str() |
| 75 | + .map(ToString::to_string) |
| 76 | + .unwrap_or_default(), |
| 77 | + ) |
| 78 | + })); |
92 | 79 |
|
93 |
| -fn list_function_names(source: &str) -> Result<Vec<(String, String)>> { |
94 |
| - let mut parser = new_parser()?; |
95 |
| - let (query, idx, mod_idx) = query_builder()?; |
96 |
| - let parsed_source = parser.parse(source, None).ok_or(AmlError::Parsing)?; |
| 80 | + list.par_extend(source_mod_pairs.par_iter().filter_map(move |path| { |
| 81 | + let source = read_to_string(path).ok()?; |
| 82 | + let query = AllFunctionsQuery::try_new().ok()?; |
| 83 | + let names = query.list_function_names(&source).unwrap_or_default(); |
| 84 | + Some(names) |
| 85 | + })); |
97 | 86 |
|
98 |
| - let mut cursor = tree_sitter::QueryCursor::new(); |
99 |
| - // TODO(maint): the complexity/type tetris needs to go down. |
100 |
| - cursor |
101 |
| - .matches(&query, parsed_source.root_node(), source.as_bytes()) |
102 |
| - .map(|capture| { |
103 |
| - let module = capture |
104 |
| - .nodes_for_capture_index(mod_idx) |
105 |
| - .next() |
106 |
| - .map(|node| node.utf8_text(source.as_bytes()).map(ToString::to_string)) |
107 |
| - .transpose()?; |
108 |
| - let fn_name = capture |
109 |
| - .nodes_for_capture_index(idx) |
110 |
| - .next() |
111 |
| - .map(|node| node.utf8_text(source.as_bytes()).map(ToString::to_string)) |
112 |
| - .transpose()?; |
113 |
| - Ok((module, fn_name)) |
114 |
| - }) |
115 |
| - .filter_map_ok(|(module, fn_name)| Some((module?, fn_name?))) |
116 |
| - .collect::<std::result::Result<Vec<_>, _>>() |
117 |
| - .map_err(|_: anyhow::Error| AmlError::InvalidText) |
| 87 | + let mut result = Vec::with_capacity(PREALLOCATED_ELEMS); |
| 88 | + result.extend(list.into_iter().flatten()); |
| 89 | + Ok(result) |
| 90 | + } |
118 | 91 | }
|
119 | 92 |
|
120 | 93 | #[cfg(test)]
|
121 |
| -mod tests { |
122 |
| - use super::*; |
123 |
| - |
124 |
| - #[test] |
125 |
| - fn detect_simple() { |
126 |
| - let source = r#" |
127 |
| - package lambda |
128 |
| -
|
129 |
| - //autometrics:inst |
130 |
| - func the_one() { |
131 |
| - return nil |
132 |
| - } |
133 |
| - "#; |
134 |
| - |
135 |
| - let list = list_function_names(source).unwrap(); |
136 |
| - |
137 |
| - assert_eq!(list.len(), 1); |
138 |
| - assert_eq!(list[0], ("lambda".to_string(), "the_one".to_string())); |
139 |
| - } |
140 |
| - |
141 |
| - #[test] |
142 |
| - fn detect_legacy() { |
143 |
| - let source = r#" |
144 |
| - package lambda |
145 |
| -
|
146 |
| - func not_the_one() { |
147 |
| - } |
148 |
| -
|
149 |
| - //autometrics:doc |
150 |
| - func sandwiched_function() { |
151 |
| - return nil |
152 |
| - } |
153 |
| -
|
154 |
| - func not_that_one_either() { |
155 |
| - } |
156 |
| - "#; |
157 |
| - |
158 |
| - let list = list_function_names(source).unwrap(); |
159 |
| - |
160 |
| - assert_eq!(list.len(), 1); |
161 |
| - assert_eq!( |
162 |
| - list[0], |
163 |
| - ("lambda".to_string(), "sandwiched_function".to_string()) |
164 |
| - ); |
165 |
| - } |
166 |
| -} |
| 94 | +mod tests; |
0 commit comments