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