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 check_args: Option<Vec<String>>,
21 #[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}