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