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