1use clap::Parser as _;
2use commands::Format;
3use sqruff_lib::core::linter::core::Linter;
4use sqruff_lib::{Formatter, core::config::FluffConfig};
5use std::path::Path;
6use std::sync::Arc;
7use stdin::is_std_in_flag_input;
8
9use crate::commands::{Cli, Commands};
10#[cfg(feature = "codegen-docs")]
11use crate::docs::codegen_docs;
12use crate::formatters::OutputStreamFormatter;
13use crate::formatters::github_annotation_native_formatter::GithubAnnotationNativeFormatter;
14use crate::formatters::json::JsonFormatter;
15
16pub mod commands;
17mod commands_fix;
18mod commands_info;
19mod commands_lint;
20mod commands_rules;
21#[cfg(feature = "codegen-docs")]
22mod docs;
23mod formatters;
24mod github_action;
25mod ignore;
26mod logger;
27mod stdin;
28
29#[cfg(feature = "codegen-docs")]
30pub fn run_docs_generation() {
31 #[cfg(feature = "codegen-docs")]
32 return codegen_docs();
33}
34
35pub fn run_with_args<I, T>(args: I) -> i32
36where
37 I: IntoIterator<Item = T>,
38 T: Into<std::ffi::OsString> + Clone,
39{
40 let _ = logger::init();
41 let cli = Cli::parse_from(args);
42 let collect_parse_errors = cli.parsing_errors;
43
44 let config: FluffConfig = if let Some(config) = cli.config.as_ref() {
45 if !Path::new(config).is_file() {
46 eprintln!(
47 "The specified config file '{}' does not exist.",
48 cli.config.as_ref().unwrap()
49 );
50
51 std::process::exit(1);
52 };
53 let read_file = std::fs::read_to_string(config).unwrap();
54 FluffConfig::from_source(&read_file, None)
55 } else {
56 FluffConfig::from_root(None, false, None).unwrap()
57 };
58
59 let current_path = std::env::current_dir().unwrap();
60 let ignore_file = ignore::IgnoreFile::new_from_root(¤t_path).unwrap();
61 let ignore_file = Arc::new(ignore_file);
62 let ignorer = {
63 let ignore_file = Arc::clone(&ignore_file);
64 move |path: &Path| ignore_file.is_ignored(path)
65 };
66
67 match cli.command {
68 Commands::Lint(args) => match is_std_in_flag_input(&args.paths) {
69 Err(e) => {
70 eprintln!("{e}");
71 1
72 }
73 Ok(false) => commands_lint::run_lint(args, config, ignorer, collect_parse_errors),
74 Ok(true) => commands_lint::run_lint_stdin(config, args.format, collect_parse_errors),
75 },
76 Commands::Fix(args) => match is_std_in_flag_input(&args.paths) {
77 Err(e) => {
78 eprintln!("{e}");
79 1
80 }
81 Ok(false) => commands_fix::run_fix(args, config, ignorer, collect_parse_errors),
82 Ok(true) => commands_fix::run_fix_stdin(config, args.format, collect_parse_errors),
83 },
84 Commands::Lsp => {
85 sqruff_lsp::run();
86 0
87 }
88 Commands::Info => {
89 commands_info::info();
90 0
91 }
92 Commands::Rules => {
93 commands_rules::rules_info(config);
94 0
95 }
96 }
97}
98
99pub(crate) fn linter(config: FluffConfig, format: Format, collect_parse_errors: bool) -> Linter {
100 let formatter: Arc<dyn Formatter> = match format {
101 Format::Human => {
102 let output_stream = std::io::stderr().into();
103 let formatter = OutputStreamFormatter::new(
104 output_stream,
105 config.get("nocolor", "core").as_bool().unwrap_or_default(),
106 config.get("verbose", "core").as_int().unwrap_or_default(),
107 );
108 Arc::new(formatter)
109 }
110 Format::GithubAnnotationNative => {
111 let output_stream = std::io::stderr();
112 let formatter = GithubAnnotationNativeFormatter::new(output_stream);
113 Arc::new(formatter)
114 }
115 Format::Json => {
116 let formatter = JsonFormatter::default();
117 Arc::new(formatter)
118 }
119 };
120
121 Linter::new(config, Some(formatter), None, collect_parse_errors)
122}