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 let read_file = std::fs::read_to_string(config).unwrap();
59 FluffConfig::from_source(&read_file, None)
60 } else {
61 FluffConfig::from_root(None, false, None).unwrap()
62 };
63
64 if let Some(dialect) = cli.dialect {
65 let dialect_kind = DialectKind::try_from(dialect.as_str());
66 match dialect_kind {
67 Ok(dialect_kind) => {
68 config.override_dialect(dialect_kind).unwrap_or_else(|e| {
69 eprintln!("{}", e);
70 std::process::exit(1);
71 });
72 }
73 Err(e) => {
74 eprintln!("{}", e);
75 std::process::exit(1);
76 }
77 }
78 }
79
80 let current_path = std::env::current_dir().unwrap();
81 let ignore_file = ignore::IgnoreFile::new_from_root(¤t_path).unwrap();
82 let ignore_file = Arc::new(ignore_file);
83 let ignorer = {
84 let ignore_file = Arc::clone(&ignore_file);
85 move |path: &Path| ignore_file.is_ignored(path)
86 };
87
88 match cli.command {
89 Commands::Lint(args) => match is_std_in_flag_input(&args.paths) {
90 Err(e) => {
91 eprintln!("{e}");
92 1
93 }
94 Ok(false) => commands_lint::run_lint(args, config, ignorer, collect_parse_errors),
95 Ok(true) => commands_lint::run_lint_stdin(config, args.format, collect_parse_errors),
96 },
97 Commands::Fix(args) => match is_std_in_flag_input(&args.paths) {
98 Err(e) => {
99 eprintln!("{e}");
100 1
101 }
102 Ok(false) => commands_fix::run_fix(args, config, ignorer, collect_parse_errors),
103 Ok(true) => commands_fix::run_fix_stdin(config, args.format, collect_parse_errors),
104 },
105 Commands::Lsp => {
106 sqruff_lsp::run();
107 0
108 }
109 Commands::Info => {
110 commands_info::info();
111 0
112 }
113 Commands::Rules => {
114 commands_rules::rules_info(config);
115 0
116 }
117 Commands::Dialects => {
118 commands_dialects::dialects();
119 0
120 }
121 Commands::Templaters => {
122 commands_templaters::templaters();
123 0
124 }
125 #[cfg(feature = "parser")]
126 Commands::Parse(args) => commands_parse::run_parse(args, config),
127 }
128}
129
130pub(crate) fn linter(config: FluffConfig, format: Format, collect_parse_errors: bool) -> Linter {
131 let formatter: Arc<dyn Formatter> = match format {
132 Format::Human => {
133 let output_stream = std::io::stderr().into();
134 let formatter = OutputStreamFormatter::new(
135 output_stream,
136 config.get("nocolor", "core").as_bool().unwrap_or_default(),
137 config.get("verbose", "core").as_int().unwrap_or_default(),
138 );
139 Arc::new(formatter)
140 }
141 Format::GithubAnnotationNative => {
142 let output_stream = std::io::stderr();
143 let formatter = GithubAnnotationNativeFormatter::new(output_stream);
144 Arc::new(formatter)
145 }
146 Format::Json => {
147 let formatter = JsonFormatter::default();
148 Arc::new(formatter)
149 }
150 };
151
152 Linter::new(config, Some(formatter), None, collect_parse_errors)
153}