1use std::path::{Path, PathBuf};
2
3use clap::Args;
4use emmylua_check::OutputDestination;
5use itertools::Itertools;
6use lux_lib::{config::Config, progress::MultiProgress, workspace::Workspace};
7use miette::{miette, Result};
8
9use crate::{
10 args::OutputFormat,
11 utils::path::{classify_path, PathTarget},
12 workspace::{sync_dependencies_if_locked, sync_test_dependencies_if_locked},
13};
14
15#[derive(Args)]
16pub struct Check {
17 #[arg(long)]
19 path: Option<PathBuf>,
20 #[arg(short, long, value_delimiter = ',')]
24 ignore: Option<Vec<String>>,
25
26 #[arg(long, default_value = "text", value_enum, ignore_case = true)]
28 output_format: OutputFormat,
29
30 #[arg(long, default_value = "stdout")]
33 output: OutputDestination,
34
35 #[arg(long)]
37 warnings_as_errors: bool,
38}
39
40impl From<OutputFormat> for emmylua_check::OutputFormat {
41 fn from(value: OutputFormat) -> Self {
42 match value {
43 OutputFormat::Json => emmylua_check::OutputFormat::Json,
44 OutputFormat::Text => emmylua_check::OutputFormat::Text,
45 }
46 }
47}
48
49pub async fn check(args: Check, config: Config) -> Result<()> {
50 let target = match args.path.as_deref() {
51 None => PathTarget::Workspace(Box::new(Workspace::current_or_err()?)),
52 Some(path) => classify_path(path)?,
53 };
54
55 let (workspace_dirs, rc_files) = match target {
56 PathTarget::Workspace(workspace) => {
57 let progress = MultiProgress::new_arc(&config);
58 sync_dependencies_if_locked(&workspace, progress.clone(), &config).await?;
59 sync_test_dependencies_if_locked(&workspace, progress, &config).await?;
60
61 let dirs = workspace
62 .members()
63 .iter()
64 .map(|project| project.root())
65 .flat_map(|project_root| {
66 vec![
67 project_root.join("src"),
68 project_root.join("lua"),
69 ]
76 })
77 .filter(|dir| dir.is_dir())
78 .collect_vec();
79
80 let luarc_path = workspace.luarc_path(&config);
81
82 let rc = if luarc_path.is_file() {
83 Some(vec![luarc_path])
84 } else {
85 None
86 };
87
88 (dirs, rc)
89 }
90 PathTarget::Directory(dir) => {
91 let rc = resolve_rc_files(&dir, &config)?;
92 (vec![dir], rc)
93 }
94 PathTarget::File(file) => {
95 let root = file
96 .parent()
97 .unwrap_or_else(|| Path::new("."))
98 .to_path_buf();
99
100 let rc = resolve_rc_files(&root, &config)?;
101 (vec![root], rc)
102 }
103 };
104
105 if workspace_dirs.is_empty() {
106 println!("Nothing to check!");
107 return Ok(());
108 }
109
110 let emmylua_check_args = emmylua_check::CmdArgs {
111 config: rc_files,
112 workspace: workspace_dirs,
113 ignore: args.ignore,
114 output_format: args.output_format.into(),
115 output: args.output,
116 warnings_as_errors: args.warnings_as_errors,
117 verbose: config.verbose(),
118 };
119
120 emmylua_check::run_check(emmylua_check_args)
121 .await
122 .map_err(|err| miette!("{err}"))?;
123 Ok(())
124}
125
126fn resolve_rc_files(root: &Path, config: &Config) -> Result<Option<Vec<PathBuf>>> {
127 let rc_path = match Workspace::from(root)? {
128 Some(workspace) => workspace.luarc_path(config),
129 None => root.join(".luarc.json"),
130 };
131
132 if rc_path.is_file() {
133 Ok(Some(vec![rc_path]))
134 } else {
135 Ok(None)
136 }
137}