Skip to main content

lux_cli/
lint.rs

1use std::path::PathBuf;
2
3use clap::Args;
4use eyre::Result;
5use itertools::Itertools;
6use lux_lib::{config::Config, operations::Exec, workspace::Workspace};
7use path_slash::PathExt;
8
9use crate::utils::path::{classify_path, PathTarget};
10use crate::workspace::top_level_ignored_files;
11
12#[derive(Args)]
13pub struct Lint {
14    /// Path to a workspace, directory, or Lua file to lint. Defaults to the current workspace.
15    #[arg(long)]
16    path: Option<PathBuf>,
17    /// Arguments to pass to the luacheck command.{n}
18    /// If you pass arguments to luacheck, Lux will not pass any default arguments.
19    args: Option<Vec<String>>,
20    /// By default, Lux will add top-level ignored files and directories{n}
21    /// (like those in .gitignore) to luacheck's exclude files.{n}
22    /// This flag disables that behaviour.{n}
23    #[arg(long)]
24    no_ignore: bool,
25}
26
27pub async fn lint(lint_args: Lint, config: Config) -> Result<()> {
28    let target = match lint_args.path.as_deref() {
29        Some(path) => classify_path(path)?,
30        None => match Workspace::current()? {
31            Some(workspace) => PathTarget::Workspace(Box::new(workspace)),
32            None => PathTarget::Directory(std::env::current_dir()?),
33        },
34    };
35
36    let (target_path, workspace) = match target {
37        PathTarget::Workspace(ws) => (ws.root().to_slash_lossy().to_string(), Some(*ws)),
38        PathTarget::Directory(dir) => {
39            let ws = Workspace::from(&dir)?;
40            (dir.to_slash_lossy().to_string(), ws)
41        }
42        PathTarget::File(file) => {
43            let ws = match file.parent() {
44                Some(parent) => Workspace::from(parent)?,
45                None => None,
46            };
47            (file.to_slash_lossy().to_string(), ws)
48        }
49    };
50
51    let check_args: Vec<String> = match lint_args.args {
52        Some(args) => args,
53        None if lint_args.no_ignore => Vec::new(),
54        None => {
55            let ignored_files = workspace.iter().flat_map(|workspace| {
56                top_level_ignored_files(workspace)
57                    .into_iter()
58                    .map(|file| file.to_slash_lossy().to_string())
59            });
60            std::iter::once("--exclude-files".into())
61                .chain(ignored_files)
62                .collect_vec()
63        }
64    };
65
66    Exec::new("luacheck", None, &config)
67        .arg(target_path)
68        .args(check_args)
69        .disable_loader(true)
70        .exec()
71        .await?;
72
73    Ok(())
74}