Skip to main content

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