lux_cli/
check.rs

1use std::collections::HashSet;
2
3use clap::Args;
4use eyre::Result;
5use itertools::Itertools;
6use lux_lib::{
7    config::Config,
8    operations::{Exec, Install, PackageInstallSpec},
9    progress::MultiProgress,
10    project::Project,
11    tree,
12};
13use path_slash::PathBufExt;
14use walkdir::WalkDir;
15
16#[derive(Args)]
17pub struct Check {
18    /// Arguments to pass to the luacheck command.{n}
19    /// If you pass arguments to luacheck, Lux will not pass any default arguments.
20    check_args: Option<Vec<String>>,
21    /// By default, Lux will add top-level ignored files and directories{n}
22    /// (like those in .gitignore) to luacheck's exclude files.{n}
23    /// This flag disables that behaviour.{n}
24    #[arg(long)]
25    no_ignore: bool,
26}
27
28pub async fn check(check: Check, config: Config) -> Result<()> {
29    let project = Project::current_or_err()?;
30
31    let luacheck =
32        PackageInstallSpec::new("luacheck".parse()?, tree::EntryType::Entrypoint).build();
33
34    Install::new(&config)
35        .package(luacheck)
36        .project(&project)?
37        .progress(MultiProgress::new_arc())
38        .install()
39        .await?;
40
41    let check_args: Vec<String> = match check.check_args {
42        Some(args) => args,
43        None if check.no_ignore => Vec::new(),
44        None => {
45            let top_level_project_files = ignore::WalkBuilder::new(project.root())
46                .max_depth(Some(1))
47                .build()
48                .filter_map(Result::ok)
49                .filter_map(|entry| {
50                    let file = entry.into_path();
51                    if file.is_dir() || file.extension().is_some_and(|ext| ext == "lua") {
52                        Some(file)
53                    } else {
54                        None
55                    }
56                })
57                .collect::<HashSet<_>>();
58
59            let top_level_files = WalkDir::new(project.root())
60                .max_depth(1)
61                .into_iter()
62                .filter_map(Result::ok)
63                .filter_map(|entry| {
64                    let file = entry.into_path();
65                    if file.is_dir() || file.extension().is_some_and(|ext| ext == "lua") {
66                        Some(file)
67                    } else {
68                        None
69                    }
70                })
71                .collect::<HashSet<_>>();
72
73            let ignored_files = top_level_files
74                .difference(&top_level_project_files)
75                .map(|file| file.to_slash_lossy().to_string());
76
77            std::iter::once("--exclude-files".into())
78                .chain(ignored_files)
79                .collect_vec()
80        }
81    };
82
83    Exec::new("luacheck", Some(&project), &config)
84        .arg(project.root().to_slash_lossy())
85        .args(check_args)
86        .exec()
87        .await?;
88
89    Ok(())
90}