sqruff_cli_lib/
lib.rs

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(&current_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}