1use std::path::{Path, PathBuf};
2
3use clap::Args;
4use emmylua_check::OutputDestination;
5use itertools::Itertools;
6use lux_lib::{config::Config, workspace::Workspace};
7
8use miette::{miette, Result};
9
10use crate::{
11 args::OutputFormat,
12 utils::path::{classify_path, PathTarget},
13 workspace::{sync_dependencies_if_locked, sync_test_dependencies_if_locked},
14};
15
16#[derive(Args)]
17pub struct Check {
18 #[arg(long)]
20 path: Option<PathBuf>,
21 #[arg(short, long, value_delimiter = ',')]
25 ignore: Option<Vec<String>>,
26
27 #[arg(long, default_value = "text", value_enum, ignore_case = true)]
29 output_format: OutputFormat,
30
31 #[arg(long, default_value = "stdout")]
34 output: OutputDestination,
35
36 #[arg(long)]
38 warnings_as_errors: bool,
39}
40
41impl From<OutputFormat> for emmylua_check::OutputFormat {
42 fn from(value: OutputFormat) -> Self {
43 match value {
44 OutputFormat::Json => emmylua_check::OutputFormat::Json,
45 OutputFormat::Text => emmylua_check::OutputFormat::Text,
46 }
47 }
48}
49
50pub async fn check(args: Check, config: Config) -> Result<()> {
51 let target = match args.path.as_deref() {
52 None => PathTarget::Workspace(Box::new(Workspace::current_or_err()?)),
53 Some(path) => classify_path(path)?,
54 };
55
56 let (workspace_dirs, rc_files) = match target {
57 PathTarget::Workspace(workspace) => {
58 sync_dependencies_if_locked(&workspace, &config).await?;
59 sync_test_dependencies_if_locked(&workspace, &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}