autocorrect-rb 2.5.6.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,151 @@
1
+ use magnus::{define_class, function, method, Error, Module, Object};
2
+
3
+ #[derive(Debug, Clone)]
4
+ pub struct LineResult {
5
+ line: usize,
6
+ col: usize,
7
+ new: String,
8
+ old: String,
9
+ severity: usize,
10
+ }
11
+
12
+ impl LineResult {
13
+ pub fn line(&self) -> usize {
14
+ self.line
15
+ }
16
+
17
+ pub fn col(&self) -> usize {
18
+ self.col
19
+ }
20
+
21
+ pub fn get_new(&self) -> String {
22
+ self.new.clone()
23
+ }
24
+
25
+ pub fn old(&self) -> String {
26
+ self.old.clone()
27
+ }
28
+
29
+ pub fn severity(&self) -> usize {
30
+ self.severity
31
+ }
32
+
33
+ pub fn inspect(&self) -> String {
34
+ format!("{:?}", self)
35
+ }
36
+
37
+ pub fn to_hash(&self) -> Result<magnus::RHash, Error> {
38
+ let hash = magnus::RHash::new();
39
+ hash.aset("line", self.line())?;
40
+ hash.aset("col", self.col())?;
41
+ hash.aset("new", self.get_new())?;
42
+ hash.aset("old", self.old())?;
43
+ hash.aset("severity", self.severity())?;
44
+ Ok(hash)
45
+ }
46
+ }
47
+
48
+ #[derive(Debug, Clone)]
49
+ pub struct LintResult {
50
+ pub filepath: String,
51
+ pub lines: Vec<LineResult>,
52
+ pub error: String,
53
+ }
54
+
55
+ impl LintResult {
56
+ pub fn filepath(&self) -> String {
57
+ self.filepath.clone()
58
+ }
59
+
60
+ pub fn lines(&self) -> Vec<LineResult> {
61
+ self.lines.clone()
62
+ }
63
+
64
+ pub fn error(&self) -> String {
65
+ self.error.clone()
66
+ }
67
+
68
+ pub fn inspect(&self) -> String {
69
+ format!("{:?}", self)
70
+ }
71
+
72
+ pub fn to_hash(&self) -> Result<magnus::RHash, Error> {
73
+ let hash = magnus::RHash::new();
74
+ hash.aset("filepath", self.filepath())?;
75
+ hash.aset(
76
+ "lines",
77
+ self.lines()
78
+ .iter()
79
+ .map(|l| l.to_hash().unwrap())
80
+ .collect::<Vec<magnus::RHash>>(),
81
+ )?;
82
+ hash.aset("error", self.error())?;
83
+ Ok(hash)
84
+ }
85
+ }
86
+
87
+ #[magnus::wrap(class = "AutoCorrect::Ignorer")]
88
+ pub struct Ignorer {
89
+ core: autocorrect::ignorer::Ignorer,
90
+ }
91
+
92
+ impl Ignorer {
93
+ pub fn new(work_dir: String) -> Self {
94
+ Ignorer {
95
+ core: autocorrect::ignorer::Ignorer::new(&work_dir),
96
+ }
97
+ }
98
+
99
+ fn is_ignored(&self, path: String) -> bool {
100
+ self.core.is_ignored(&path)
101
+ }
102
+ }
103
+
104
+ pub fn format(input: String) -> String {
105
+ autocorrect::format(&input)
106
+ }
107
+
108
+ pub fn format_for(input: String, filename_or_ext: String) -> String {
109
+ autocorrect::format_for(&input, &filename_or_ext).out
110
+ }
111
+
112
+ pub fn lint_for(input: String, filename_or_ext: String) -> magnus::RHash {
113
+ let result = autocorrect::lint_for(&input, &filename_or_ext);
114
+
115
+ LintResult {
116
+ filepath: filename_or_ext,
117
+ lines: result
118
+ .lines
119
+ .iter()
120
+ .map(|l| LineResult {
121
+ line: l.line,
122
+ col: l.col,
123
+ new: l.new.clone(),
124
+ old: l.old.clone(),
125
+ severity: l.severity as usize,
126
+ })
127
+ .collect::<_>(),
128
+ error: result.error,
129
+ }
130
+ .to_hash()
131
+ .unwrap()
132
+ }
133
+
134
+ pub fn load_config(config_str: String) {
135
+ autocorrect::config::load(&config_str).unwrap();
136
+ }
137
+
138
+ #[magnus::init(name = "autocorrect")]
139
+ fn init() -> Result<(), Error> {
140
+ let class = define_class("AutoCorrect", Default::default())?;
141
+ class.define_singleton_method("format", function!(format, 1))?;
142
+ class.define_singleton_method("format_for", function!(format_for, 2))?;
143
+ class.define_singleton_method("lint_for", function!(lint_for, 2))?;
144
+ class.define_singleton_method("load_config", function!(load_config, 1))?;
145
+
146
+ let ignorer_class = class.define_class("Ignorer", Default::default())?;
147
+ ignorer_class.define_singleton_method("new", function!(Ignorer::new, 1))?;
148
+ ignorer_class.define_method("ignored?", method!(Ignorer::is_ignored, 1))?;
149
+
150
+ Ok(())
151
+ }
@@ -0,0 +1,319 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ extern crate glob;
4
+
5
+ use std::cell::RefCell;
6
+ use std::collections::HashMap;
7
+ use std::env;
8
+ use std::path::{Path, PathBuf};
9
+ use std::process::Command;
10
+
11
+ use glob::{MatchOptions, Pattern};
12
+
13
+ //================================================
14
+ // Commands
15
+ //================================================
16
+
17
+ thread_local! {
18
+ /// The errors encountered by the build script while executing commands.
19
+ static COMMAND_ERRORS: RefCell<HashMap<String, Vec<String>>> = RefCell::default();
20
+ }
21
+
22
+ /// Adds an error encountered by the build script while executing a command.
23
+ fn add_command_error(name: &str, path: &str, arguments: &[&str], message: String) {
24
+ COMMAND_ERRORS.with(|e| {
25
+ e.borrow_mut()
26
+ .entry(name.into())
27
+ .or_insert_with(Vec::new)
28
+ .push(format!(
29
+ "couldn't execute `{} {}` (path={}) ({})",
30
+ name,
31
+ arguments.join(" "),
32
+ path,
33
+ message,
34
+ ))
35
+ });
36
+ }
37
+
38
+ /// A struct that prints the errors encountered by the build script while
39
+ /// executing commands when dropped (unless explictly discarded).
40
+ ///
41
+ /// This is handy because we only want to print these errors when the build
42
+ /// script fails to link to an instance of `libclang`. For example, if
43
+ /// `llvm-config` couldn't be executed but an instance of `libclang` was found
44
+ /// anyway we don't want to pollute the build output with irrelevant errors.
45
+ #[derive(Default)]
46
+ pub struct CommandErrorPrinter {
47
+ discard: bool,
48
+ }
49
+
50
+ impl CommandErrorPrinter {
51
+ pub fn discard(mut self) {
52
+ self.discard = true;
53
+ }
54
+ }
55
+
56
+ impl Drop for CommandErrorPrinter {
57
+ fn drop(&mut self) {
58
+ if self.discard {
59
+ return;
60
+ }
61
+
62
+ let errors = COMMAND_ERRORS.with(|e| e.borrow().clone());
63
+
64
+ if let Some(errors) = errors.get("llvm-config") {
65
+ println!(
66
+ "cargo:warning=could not execute `llvm-config` one or more \
67
+ times, if the LLVM_CONFIG_PATH environment variable is set to \
68
+ a full path to valid `llvm-config` executable it will be used \
69
+ to try to find an instance of `libclang` on your system: {}",
70
+ errors
71
+ .iter()
72
+ .map(|e| format!("\"{}\"", e))
73
+ .collect::<Vec<_>>()
74
+ .join("\n "),
75
+ )
76
+ }
77
+
78
+ if let Some(errors) = errors.get("xcode-select") {
79
+ println!(
80
+ "cargo:warning=could not execute `xcode-select` one or more \
81
+ times, if a valid instance of this executable is on your PATH \
82
+ it will be used to try to find an instance of `libclang` on \
83
+ your system: {}",
84
+ errors
85
+ .iter()
86
+ .map(|e| format!("\"{}\"", e))
87
+ .collect::<Vec<_>>()
88
+ .join("\n "),
89
+ )
90
+ }
91
+ }
92
+ }
93
+
94
+ /// Executes a command and returns the `stdout` output if the command was
95
+ /// successfully executed (errors are added to `COMMAND_ERRORS`).
96
+ fn run_command(name: &str, path: &str, arguments: &[&str]) -> Option<String> {
97
+ let output = match Command::new(path).args(arguments).output() {
98
+ Ok(output) => output,
99
+ Err(error) => {
100
+ let message = format!("error: {}", error);
101
+ add_command_error(name, path, arguments, message);
102
+ return None;
103
+ }
104
+ };
105
+
106
+ if output.status.success() {
107
+ Some(String::from_utf8_lossy(&output.stdout).into_owned())
108
+ } else {
109
+ let message = format!("exit code: {}", output.status);
110
+ add_command_error(name, path, arguments, message);
111
+ None
112
+ }
113
+ }
114
+
115
+ /// Executes the `llvm-config` command and returns the `stdout` output if the
116
+ /// command was successfully executed (errors are added to `COMMAND_ERRORS`).
117
+ pub fn run_llvm_config(arguments: &[&str]) -> Option<String> {
118
+ let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into());
119
+ run_command("llvm-config", &path, arguments)
120
+ }
121
+
122
+ /// Executes the `xcode-select` command and returns the `stdout` output if the
123
+ /// command was successfully executed (errors are added to `COMMAND_ERRORS`).
124
+ pub fn run_xcode_select(arguments: &[&str]) -> Option<String> {
125
+ run_command("xcode-select", "xcode-select", arguments)
126
+ }
127
+
128
+ //================================================
129
+ // Search Directories
130
+ //================================================
131
+
132
+ /// `libclang` directory patterns for Haiku.
133
+ const DIRECTORIES_HAIKU: &[&str] = &[
134
+ "/boot/system/lib",
135
+ "/boot/system/develop/lib",
136
+ "/boot/system/non-packaged/lib",
137
+ "/boot/system/non-packaged/develop/lib",
138
+ "/boot/home/config/non-packaged/lib",
139
+ "/boot/home/config/non-packaged/develop/lib",
140
+ ];
141
+
142
+ /// `libclang` directory patterns for Linux (and FreeBSD).
143
+ const DIRECTORIES_LINUX: &[&str] = &[
144
+ "/usr/lib*",
145
+ "/usr/lib*/*",
146
+ "/usr/lib*/*/*",
147
+ "/usr/local/lib*",
148
+ "/usr/local/lib*/*",
149
+ "/usr/local/lib*/*/*",
150
+ "/usr/local/llvm*/lib*",
151
+ ];
152
+
153
+ /// `libclang` directory patterns for macOS.
154
+ const DIRECTORIES_MACOS: &[&str] = &[
155
+ "/usr/local/opt/llvm*/lib",
156
+ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
157
+ "/Library/Developer/CommandLineTools/usr/lib",
158
+ "/usr/local/opt/llvm*/lib/llvm*/lib",
159
+ ];
160
+
161
+ /// `libclang` directory patterns for Windows.
162
+ const DIRECTORIES_WINDOWS: &[&str] = &[
163
+ "C:\\LLVM\\lib",
164
+ "C:\\Program Files*\\LLVM\\lib",
165
+ "C:\\MSYS*\\MinGW*\\lib",
166
+ // LLVM + Clang can be installed as a component of Visual Studio.
167
+ // https://github.com/KyleMayes/clang-sys/issues/121
168
+ "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin",
169
+ // LLVM + Clang can be installed using Scoop (https://scoop.sh).
170
+ // Other Windows package managers install LLVM + Clang to previously listed
171
+ // system-wide directories.
172
+ "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin",
173
+ ];
174
+
175
+ /// `libclang` directory patterns for illumos
176
+ const DIRECTORIES_ILLUMOS: &[&str] = &[
177
+ "/opt/ooce/clang-*/lib",
178
+ "/opt/ooce/llvm-*/lib",
179
+ ];
180
+
181
+ //================================================
182
+ // Searching
183
+ //================================================
184
+
185
+ /// Finds the files in a directory that match one or more filename glob patterns
186
+ /// and returns the paths to and filenames of those files.
187
+ fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
188
+ // Escape the specified directory in case it contains characters that have
189
+ // special meaning in glob patterns (e.g., `[` or `]`).
190
+ let directory = Pattern::escape(directory.to_str().unwrap());
191
+ let directory = Path::new(&directory);
192
+
193
+ // Join the escaped directory to the filename glob patterns to obtain
194
+ // complete glob patterns for the files being searched for.
195
+ let paths = filenames
196
+ .iter()
197
+ .map(|f| directory.join(f).to_str().unwrap().to_owned());
198
+
199
+ // Prevent wildcards from matching path separators to ensure that the search
200
+ // is limited to the specified directory.
201
+ let mut options = MatchOptions::new();
202
+ options.require_literal_separator = true;
203
+
204
+ paths
205
+ .map(|p| glob::glob_with(&p, options))
206
+ .filter_map(Result::ok)
207
+ .flatten()
208
+ .filter_map(|p| {
209
+ let path = p.ok()?;
210
+ let filename = path.file_name()?.to_str().unwrap();
211
+
212
+ // The `libclang_shared` library has been renamed to `libclang-cpp`
213
+ // in Clang 10. This can cause instances of this library (e.g.,
214
+ // `libclang-cpp.so.10`) to be matched by patterns looking for
215
+ // instances of `libclang`.
216
+ if filename.contains("-cpp.") {
217
+ return None;
218
+ }
219
+
220
+ Some((directory.to_owned(), filename.into()))
221
+ })
222
+ .collect::<Vec<_>>()
223
+ }
224
+
225
+ /// Finds the files in a directory (and any relevant sibling directories) that
226
+ /// match one or more filename glob patterns and returns the paths to and
227
+ /// filenames of those files.
228
+ fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
229
+ let mut results = search_directory(directory, filenames);
230
+
231
+ // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory
232
+ // while `libclang.lib` is usually found in the LLVM `lib` directory. To
233
+ // keep things consistent with other platforms, only LLVM `lib` directories
234
+ // are included in the backup search directory globs so we need to search
235
+ // the LLVM `bin` directory here.
236
+ if cfg!(target_os = "windows") && directory.ends_with("lib") {
237
+ let sibling = directory.parent().unwrap().join("bin");
238
+ results.extend(search_directory(&sibling, filenames).into_iter());
239
+ }
240
+
241
+ results
242
+ }
243
+
244
+ /// Finds the `libclang` static or dynamic libraries matching one or more
245
+ /// filename glob patterns and returns the paths to and filenames of those files.
246
+ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<(PathBuf, String)> {
247
+ // Search only the path indicated by the relevant environment variable
248
+ // (e.g., `LIBCLANG_PATH`) if it is set.
249
+ if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) {
250
+ // Check if the path is a matching file.
251
+ if let Some(parent) = path.parent() {
252
+ let filename = path.file_name().unwrap().to_str().unwrap();
253
+ let libraries = search_directories(parent, filenames);
254
+ if libraries.iter().any(|(_, f)| f == filename) {
255
+ return vec![(parent.into(), filename.into())];
256
+ }
257
+ }
258
+
259
+ // Check if the path is directory containing a matching file.
260
+ return search_directories(&path, filenames);
261
+ }
262
+
263
+ let mut found = vec![];
264
+
265
+ // Search the `bin` and `lib` directories in the directory returned by
266
+ // `llvm-config --prefix`.
267
+ if let Some(output) = run_llvm_config(&["--prefix"]) {
268
+ let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
269
+ found.extend(search_directories(&directory.join("bin"), filenames));
270
+ found.extend(search_directories(&directory.join("lib"), filenames));
271
+ found.extend(search_directories(&directory.join("lib64"), filenames));
272
+ }
273
+
274
+ // Search the toolchain directory in the directory returned by
275
+ // `xcode-select --print-path`.
276
+ if cfg!(target_os = "macos") {
277
+ if let Some(output) = run_xcode_select(&["--print-path"]) {
278
+ let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
279
+ let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib");
280
+ found.extend(search_directories(&directory, filenames));
281
+ }
282
+ }
283
+
284
+ // Search the directories in the `LD_LIBRARY_PATH` environment variable.
285
+ if let Ok(path) = env::var("LD_LIBRARY_PATH") {
286
+ for directory in env::split_paths(&path) {
287
+ found.extend(search_directories(&directory, filenames));
288
+ }
289
+ }
290
+
291
+ // Determine the `libclang` directory patterns.
292
+ let directories = if cfg!(target_os = "haiku") {
293
+ DIRECTORIES_HAIKU
294
+ } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
295
+ DIRECTORIES_LINUX
296
+ } else if cfg!(target_os = "macos") {
297
+ DIRECTORIES_MACOS
298
+ } else if cfg!(target_os = "windows") {
299
+ DIRECTORIES_WINDOWS
300
+ } else if cfg!(target_os = "illumos") {
301
+ DIRECTORIES_ILLUMOS
302
+ } else {
303
+ &[]
304
+ };
305
+
306
+ // Search the directories provided by the `libclang` directory patterns.
307
+ let mut options = MatchOptions::new();
308
+ options.case_sensitive = false;
309
+ options.require_literal_separator = true;
310
+ for directory in directories.iter().rev() {
311
+ if let Ok(directories) = glob::glob_with(directory, options) {
312
+ for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) {
313
+ found.extend(search_directories(&directory, filenames));
314
+ }
315
+ }
316
+ }
317
+
318
+ found
319
+ }