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: Option<PathBuf>,
16 args: Option<Vec<String>>,
19 #[arg(long)]
23 no_ignore: bool,
24}
25
26pub async fn lint(lint_args: Lint, config: Config) -> Result<()> {
27 let target = match lint_args.path.as_deref() {
28 Some(path) => classify_path(path)?,
29 None => match Workspace::current()? {
30 Some(workspace) => PathTarget::Workspace(Box::new(workspace)),
31 None => PathTarget::Directory(std::env::current_dir()?),
32 },
33 };
34
35 let (target_path, workspace) = match target {
36 PathTarget::Workspace(ws) => (ws.root().to_slash_lossy().to_string(), Some(*ws)),
37 PathTarget::Directory(dir) => {
38 let ws = Workspace::from(&dir)?;
39 (dir.to_slash_lossy().to_string(), ws)
40 }
41 PathTarget::File(file) => {
42 let ws = match file.parent() {
43 Some(parent) => Workspace::from(parent)?,
44 None => None,
45 };
46 (file.to_slash_lossy().to_string(), ws)
47 }
48 };
49
50 let check_args: Vec<String> = match lint_args.args {
51 Some(args) => args,
52 None if lint_args.no_ignore => Vec::new(),
53 None => {
54 let ignored_files = workspace.iter().flat_map(|workspace| {
55 top_level_ignored_files(workspace)
56 .into_iter()
57 .map(|file| file.to_slash_lossy().to_string())
58 });
59 std::iter::once("--exclude-files".into())
60 .chain(ignored_files)
61 .collect_vec()
62 }
63 };
64
65 Exec::new("luacheck", None, &config)
66 .arg(target_path)
67 .args(check_args)
68 .disable_loader(true)
69 .exec()
70 .await?;
71
72 Ok(())
73}