1mod builtin;
2mod cmake;
3mod make;
4mod rust_mlua;
5mod tree_sitter;
6
7pub use builtin::{BuiltinBuildSpec, LuaModule, ModulePaths, ModuleSpec};
8pub use cmake::*;
9pub use make::*;
10pub use rust_mlua::*;
11pub use tree_sitter::*;
12
13use builtin::{
14 ModulePathsMissingSources, ModuleSpecAmbiguousPlatformOverride, ModuleSpecInternal,
15 ParseLuaModuleError,
16};
17
18use itertools::Itertools;
19
20use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, UserData, Value};
21use std::{
22 collections::HashMap, env::consts::DLL_EXTENSION, fmt::Display, path::PathBuf, str::FromStr,
23};
24use thiserror::Error;
25
26use serde::{de, de::IntoDeserializer, Deserialize, Deserializer};
27
28use super::{
29 mlua_json_value_to_map, mlua_json_value_to_vec, DisplayAsLuaKV, DisplayAsLuaValue,
30 DisplayLuaKV, DisplayLuaValue, LuaTableKey, PartialOverride, PerPlatform, PlatformIdentifier,
31};
32
33#[derive(Clone, Debug, PartialEq)]
38pub struct BuildSpec {
39 pub build_backend: Option<BuildBackendSpec>,
41 pub install: InstallSpec,
45 pub copy_directories: Vec<PathBuf>,
47 pub patches: HashMap<PathBuf, String>,
51}
52
53impl Default for BuildSpec {
54 fn default() -> Self {
55 Self {
56 build_backend: Some(BuildBackendSpec::default()),
57 install: InstallSpec::default(),
58 copy_directories: Vec::default(),
59 patches: HashMap::default(),
60 }
61 }
62}
63
64impl UserData for BuildSpec {
65 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
66 methods.add_method("build_backend", |_, this, _: ()| {
67 Ok(this.build_backend.clone())
68 });
69 methods.add_method("install", |_, this, _: ()| Ok(this.install.clone()));
70 methods.add_method("copy_directories", |_, this, _: ()| {
71 Ok(this.copy_directories.clone())
72 });
73 methods.add_method("patches", |_, this, _: ()| Ok(this.patches.clone()));
74 }
75}
76
77#[derive(Error, Debug)]
78pub enum BuildSpecInternalError {
79 #[error("'builtin' modules should not have list elements")]
80 ModulesHaveListElements,
81 #[error("no 'modules' specified for the 'rust-mlua' build backend")]
82 NoModulesSpecified,
83 #[error("no 'lang' specified for 'treesitter-parser' build backend")]
84 NoTreesitterParserLanguageSpecified,
85 #[error("invalid 'rust-mlua' modules format")]
86 InvalidRustMLuaFormat,
87 #[error(transparent)]
88 ModulePathsMissingSources(#[from] ModulePathsMissingSources),
89 #[error(transparent)]
90 ParseLuaModuleError(#[from] ParseLuaModuleError),
91}
92
93impl BuildSpec {
94 pub(crate) fn from_internal_spec(
95 internal: BuildSpecInternal,
96 ) -> Result<Self, BuildSpecInternalError> {
97 let build_backend = match internal.build_type.unwrap_or_default() {
98 BuildType::Builtin => Some(BuildBackendSpec::Builtin(BuiltinBuildSpec {
99 modules: internal
100 .builtin_spec
101 .unwrap_or_default()
102 .into_iter()
103 .map(|(key, module_spec_internal)| {
104 let key_str = match key {
105 LuaTableKey::IntKey(_) => {
106 Err(BuildSpecInternalError::ModulesHaveListElements)
107 }
108 LuaTableKey::StringKey(str) => Ok(LuaModule::from_str(str.as_str())?),
109 }?;
110 match ModuleSpec::from_internal(module_spec_internal) {
111 Ok(module_spec) => Ok((key_str, module_spec)),
112 Err(err) => Err(err.into()),
113 }
114 })
115 .collect::<Result<HashMap<LuaModule, ModuleSpec>, BuildSpecInternalError>>()?,
116 })),
117 BuildType::Make => {
118 let default = MakeBuildSpec::default();
119 Some(BuildBackendSpec::Make(MakeBuildSpec {
120 makefile: internal.makefile.unwrap_or(default.makefile),
121 build_target: internal.make_build_target,
122 build_pass: internal.build_pass.unwrap_or(default.build_pass),
123 install_target: internal
124 .make_install_target
125 .unwrap_or(default.install_target),
126 install_pass: internal.install_pass.unwrap_or(default.install_pass),
127 build_variables: internal.make_build_variables.unwrap_or_default(),
128 install_variables: internal.make_install_variables.unwrap_or_default(),
129 variables: internal.variables.unwrap_or_default(),
130 }))
131 }
132 BuildType::CMake => {
133 let default = CMakeBuildSpec::default();
134 Some(BuildBackendSpec::CMake(CMakeBuildSpec {
135 cmake_lists_content: internal.cmake_lists_content,
136 build_pass: internal.build_pass.unwrap_or(default.build_pass),
137 install_pass: internal.install_pass.unwrap_or(default.install_pass),
138 variables: internal.variables.unwrap_or_default(),
139 }))
140 }
141 BuildType::Command => Some(BuildBackendSpec::Command(CommandBuildSpec {
142 build_command: internal.build_command,
143 install_command: internal.install_command,
144 })),
145 BuildType::None => None,
146 BuildType::LuaRock(s) => Some(BuildBackendSpec::LuaRock(s)),
147 BuildType::RustMlua => Some(BuildBackendSpec::RustMlua(RustMluaBuildSpec {
148 modules: internal
149 .builtin_spec
150 .ok_or(BuildSpecInternalError::NoModulesSpecified)?
151 .into_iter()
152 .map(|(key, value)| match (key, value) {
153 (LuaTableKey::IntKey(_), ModuleSpecInternal::SourcePath(module)) => {
154 let mut rust_lib: PathBuf = format!("lib{}", module.display()).into();
155 rust_lib.set_extension(DLL_EXTENSION);
156 Ok((module.to_string_lossy().to_string(), rust_lib))
157 }
158 (
159 LuaTableKey::StringKey(module_name),
160 ModuleSpecInternal::SourcePath(module),
161 ) => {
162 let mut rust_lib: PathBuf = format!("lib{}", module.display()).into();
163 rust_lib.set_extension(DLL_EXTENSION);
164 Ok((module_name, rust_lib))
165 }
166 _ => Err(BuildSpecInternalError::InvalidRustMLuaFormat),
167 })
168 .try_collect()?,
169 target_path: internal.target_path.unwrap_or("target".into()),
170 default_features: internal.default_features.unwrap_or(true),
171 include: internal
172 .include
173 .unwrap_or_default()
174 .into_iter()
175 .map(|(key, dest)| match key {
176 LuaTableKey::IntKey(_) => (dest.clone(), dest),
177 LuaTableKey::StringKey(src) => (src.into(), dest),
178 })
179 .collect(),
180 features: internal.features.unwrap_or_default(),
181 })),
182 BuildType::TreesitterParser => Some(BuildBackendSpec::TreesitterParser(
183 TreesitterParserBuildSpec {
184 lang: internal
185 .lang
186 .ok_or(BuildSpecInternalError::NoTreesitterParserLanguageSpecified)?,
187 parser: internal.parser.unwrap_or(false),
188 generate: internal.generate.unwrap_or(false),
189 location: internal.location,
190 queries: internal.queries.unwrap_or_default(),
191 },
192 )),
193 BuildType::Source => Some(BuildBackendSpec::Source),
194 };
195 Ok(Self {
196 build_backend,
197 install: internal.install.unwrap_or_default(),
198 copy_directories: internal.copy_directories.unwrap_or_default(),
199 patches: internal.patches.unwrap_or_default(),
200 })
201 }
202}
203
204impl<'de> Deserialize<'de> for BuildSpec {
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: Deserializer<'de>,
208 {
209 let internal = BuildSpecInternal::deserialize(deserializer)?;
210 BuildSpec::from_internal_spec(internal).map_err(de::Error::custom)
211 }
212}
213
214impl FromLua for PerPlatform<BuildSpec> {
215 fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
216 let internal = PerPlatform::from_lua(value, lua)?;
217 let mut per_platform = HashMap::new();
218 for (platform, internal_override) in internal.per_platform {
219 let override_spec = BuildSpec::from_internal_spec(internal_override)
220 .map_err(|err| mlua::Error::DeserializeError(err.to_string()))?;
221 per_platform.insert(platform, override_spec);
222 }
223 let result = PerPlatform {
224 default: BuildSpec::from_internal_spec(internal.default)
225 .map_err(|err| mlua::Error::DeserializeError(err.to_string()))?,
226 per_platform,
227 };
228 Ok(result)
229 }
230}
231
232impl Default for BuildBackendSpec {
233 fn default() -> Self {
234 Self::Builtin(BuiltinBuildSpec::default())
235 }
236}
237
238#[derive(Debug, PartialEq, Clone)]
245pub enum BuildBackendSpec {
246 Builtin(BuiltinBuildSpec),
247 Make(MakeBuildSpec),
248 CMake(CMakeBuildSpec),
249 Command(CommandBuildSpec),
250 LuaRock(String),
251 RustMlua(RustMluaBuildSpec),
252 TreesitterParser(TreesitterParserBuildSpec),
253 Source,
259}
260
261impl IntoLua for BuildBackendSpec {
262 fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
263 match self {
264 BuildBackendSpec::Builtin(spec) => spec.into_lua(lua),
265 BuildBackendSpec::Make(spec) => spec.into_lua(lua),
266 BuildBackendSpec::CMake(spec) => spec.into_lua(lua),
267 BuildBackendSpec::Command(spec) => spec.into_lua(lua),
268 BuildBackendSpec::LuaRock(s) => s.into_lua(lua),
269 BuildBackendSpec::RustMlua(spec) => spec.into_lua(lua),
270 BuildBackendSpec::TreesitterParser(spec) => spec.into_lua(lua),
271 BuildBackendSpec::Source => "source".into_lua(lua),
272 }
273 }
274}
275
276#[derive(Debug, PartialEq, Clone)]
277pub struct CommandBuildSpec {
278 pub build_command: Option<String>,
279 pub install_command: Option<String>,
280}
281
282impl UserData for CommandBuildSpec {
283 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
284 methods.add_method("build_command", |_, this, _: ()| {
285 Ok(this.build_command.clone())
286 });
287 methods.add_method("install_command", |_, this, _: ()| {
288 Ok(this.install_command.clone())
289 });
290 }
291}
292
293#[derive(Clone, Debug)]
294pub(crate) enum InstallBinaries {
295 Array(Vec<PathBuf>),
296 Table(HashMap<String, PathBuf>),
297}
298
299impl<'de> Deserialize<'de> for InstallBinaries {
300 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
301 where
302 D: Deserializer<'de>,
303 {
304 let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
305 if value.is_array() {
306 let array = mlua_json_value_to_vec(value).map_err(de::Error::custom)?;
307 Ok(InstallBinaries::Array(array))
308 } else {
309 let table: HashMap<String, PathBuf> =
310 mlua_json_value_to_map(value).map_err(de::Error::custom)?;
311 Ok(InstallBinaries::Table(table))
312 }
313 }
314}
315
316impl InstallBinaries {
317 pub(crate) fn coerce(self) -> HashMap<String, PathBuf> {
318 match self {
319 InstallBinaries::Array(array) => array
320 .into_iter()
321 .map(|path| {
322 (
323 path.file_stem().unwrap().to_str().unwrap().to_string(),
324 path,
325 )
326 })
327 .collect(),
328 InstallBinaries::Table(table) => table,
329 }
330 }
331}
332
333#[derive(Debug, PartialEq, Default, Deserialize, Clone)]
342pub struct InstallSpec {
343 #[serde(default)]
345 pub lua: HashMap<LuaModule, PathBuf>,
346 #[serde(default)]
348 pub lib: HashMap<LuaModule, PathBuf>,
349 #[serde(default)]
351 pub conf: HashMap<String, PathBuf>,
352 #[serde(default, deserialize_with = "deserialize_binaries")]
356 pub bin: HashMap<String, PathBuf>,
357}
358
359impl UserData for InstallSpec {
360 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
361 methods.add_method("lua", |_, this, _: ()| Ok(this.lua.clone()));
362 methods.add_method("lib", |_, this, _: ()| Ok(this.lib.clone()));
363 methods.add_method("conf", |_, this, _: ()| Ok(this.conf.clone()));
364 methods.add_method("bin", |_, this, _: ()| Ok(this.bin.clone()));
365 }
366}
367
368fn deserialize_binaries<'de, D>(deserializer: D) -> Result<HashMap<String, PathBuf>, D::Error>
369where
370 D: Deserializer<'de>,
371{
372 let binaries = InstallBinaries::deserialize(deserializer)?;
373 Ok(binaries.coerce())
374}
375
376fn deserialize_copy_directories<'de, D>(deserializer: D) -> Result<Option<Vec<PathBuf>>, D::Error>
377where
378 D: Deserializer<'de>,
379{
380 let value: Option<serde_json::Value> = Option::deserialize(deserializer)?;
381 let copy_directories: Option<Vec<String>> = match value {
382 Some(json_value) => Some(mlua_json_value_to_vec(json_value).map_err(de::Error::custom)?),
383 None => None,
384 };
385 let special_directories: Vec<String> = vec!["lua".into(), "lib".into(), "rock_manifest".into()];
386 match special_directories
387 .into_iter()
388 .find(|dir| copy_directories.clone().unwrap_or_default().contains(dir))
389 {
390 Some(d) => Err(format!(
393 "directory '{}' in copy_directories clashes with the .rock format", d
395 )),
396 _ => Ok(copy_directories.map(|vec| vec.into_iter().map(PathBuf::from).collect())),
397 }
398 .map_err(de::Error::custom)
399}
400
401impl DisplayAsLuaKV for InstallSpec {
402 fn display_lua(&self) -> DisplayLuaKV {
403 let mut result = Vec::new();
404
405 self.lua
406 .iter()
407 .chain(self.lib.iter())
408 .for_each(|(key, value)| {
409 result.push(DisplayLuaKV {
410 key: key.to_string(),
411 value: DisplayLuaValue::String(value.to_string_lossy().to_string()),
412 });
413 });
414
415 self.conf
416 .iter()
417 .chain(self.bin.iter())
418 .for_each(|(key, value)| {
419 result.push(DisplayLuaKV {
420 key: key.clone(),
421 value: DisplayLuaValue::String(value.to_string_lossy().to_string()),
422 });
423 });
424
425 DisplayLuaKV {
426 key: "install".to_string(),
427 value: DisplayLuaValue::Table(result),
428 }
429 }
430}
431
432#[derive(Debug, PartialEq, Deserialize, Default, Clone)]
433pub(crate) struct BuildSpecInternal {
434 #[serde(rename = "type", default)]
435 pub(crate) build_type: Option<BuildType>,
436 #[serde(rename = "modules", default)]
437 pub(crate) builtin_spec: Option<HashMap<LuaTableKey, ModuleSpecInternal>>,
438 #[serde(default)]
439 pub(crate) makefile: Option<PathBuf>,
440 #[serde(rename = "build_target", default)]
441 pub(crate) make_build_target: Option<String>,
442 #[serde(default)]
443 pub(crate) build_pass: Option<bool>,
444 #[serde(rename = "install_target", default)]
445 pub(crate) make_install_target: Option<String>,
446 #[serde(default)]
447 pub(crate) install_pass: Option<bool>,
448 #[serde(rename = "build_variables", default)]
449 pub(crate) make_build_variables: Option<HashMap<String, String>>,
450 #[serde(rename = "install_variables", default)]
451 pub(crate) make_install_variables: Option<HashMap<String, String>>,
452 #[serde(default)]
453 pub(crate) variables: Option<HashMap<String, String>>,
454 #[serde(rename = "cmake", default)]
455 pub(crate) cmake_lists_content: Option<String>,
456 #[serde(default)]
457 pub(crate) build_command: Option<String>,
458 #[serde(default)]
459 pub(crate) install_command: Option<String>,
460 #[serde(default)]
461 pub(crate) install: Option<InstallSpec>,
462 #[serde(default, deserialize_with = "deserialize_copy_directories")]
463 pub(crate) copy_directories: Option<Vec<PathBuf>>,
464 #[serde(default)]
465 pub(crate) patches: Option<HashMap<PathBuf, String>>,
466 #[serde(default)]
468 pub(crate) target_path: Option<PathBuf>,
469 #[serde(default)]
470 pub(crate) default_features: Option<bool>,
471 #[serde(default)]
472 pub(crate) include: Option<HashMap<LuaTableKey, PathBuf>>,
473 #[serde(default)]
474 pub(crate) features: Option<Vec<String>>,
475 #[serde(default)]
477 pub(crate) lang: Option<String>,
478 #[serde(default)]
479 pub(crate) parser: Option<bool>,
480 #[serde(default)]
481 pub(crate) generate: Option<bool>,
482 #[serde(default)]
483 pub(crate) location: Option<PathBuf>,
484 #[serde(default)]
485 pub(crate) queries: Option<HashMap<PathBuf, String>>,
486}
487
488impl FromLua for PerPlatform<BuildSpecInternal> {
489 fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
490 match &value {
491 list @ Value::Table(tbl) => {
492 let mut per_platform = match tbl.get("platforms")? {
493 Value::Table(overrides) => Ok(lua.from_value(Value::Table(overrides))?),
494 Value::Nil => Ok(HashMap::default()),
495 val => Err(mlua::Error::DeserializeError(format!(
496 "Expected rockspec 'build' to be table or nil, but got {}",
497 val.type_name()
498 ))),
499 }?;
500 let _ = tbl.raw_remove("platforms");
501 let default = lua.from_value(list.clone())?;
502 override_platform_specs(&mut per_platform, &default)
503 .map_err(|err| mlua::Error::DeserializeError(err.to_string()))?;
504 Ok(PerPlatform {
505 default,
506 per_platform,
507 })
508 }
509 Value::Nil => Ok(PerPlatform::default()),
510 val => Err(mlua::Error::DeserializeError(format!(
511 "Expected rockspec 'build' to be a table or nil, but got {}",
512 val.type_name()
513 ))),
514 }
515 }
516}
517
518fn override_platform_specs(
521 per_platform: &mut HashMap<PlatformIdentifier, BuildSpecInternal>,
522 base: &BuildSpecInternal,
523) -> Result<(), ModuleSpecAmbiguousPlatformOverride> {
524 let per_platform_raw = per_platform.clone();
525 for (platform, build_spec) in per_platform.clone() {
526 per_platform.insert(platform, override_build_spec_internal(base, &build_spec)?);
528 }
529 for (platform, build_spec) in per_platform_raw {
530 for extended_platform in &platform.get_extended_platforms() {
531 let extended_spec = per_platform
532 .get(extended_platform)
533 .unwrap_or(&base.to_owned())
534 .to_owned();
535 per_platform.insert(
536 extended_platform.to_owned(),
537 override_build_spec_internal(&extended_spec, &build_spec)?,
538 );
539 }
540 }
541 Ok(())
542}
543
544fn override_build_spec_internal(
545 base: &BuildSpecInternal,
546 override_spec: &BuildSpecInternal,
547) -> Result<BuildSpecInternal, ModuleSpecAmbiguousPlatformOverride> {
548 Ok(BuildSpecInternal {
549 build_type: override_opt(&override_spec.build_type, &base.build_type),
550 builtin_spec: match (
551 override_spec.builtin_spec.clone(),
552 base.builtin_spec.clone(),
553 ) {
554 (Some(override_val), Some(base_spec_map)) => {
555 Some(base_spec_map.into_iter().chain(override_val).try_fold(
556 HashMap::default(),
557 |mut acc: HashMap<LuaTableKey, ModuleSpecInternal>,
558 (k, module_spec_override)|
559 -> Result<
560 HashMap<LuaTableKey, ModuleSpecInternal>,
561 ModuleSpecAmbiguousPlatformOverride,
562 > {
563 let overridden = match acc.get(&k) {
564 None => module_spec_override,
565 Some(base_module_spec) => {
566 base_module_spec.apply_overrides(&module_spec_override)?
567 }
568 };
569 acc.insert(k, overridden);
570 Ok(acc)
571 },
572 )?)
573 }
574 (override_val @ Some(_), _) => override_val,
575 (_, base_val @ Some(_)) => base_val,
576 _ => None,
577 },
578 makefile: override_opt(&override_spec.makefile, &base.makefile),
579 make_build_target: override_opt(&override_spec.make_build_target, &base.make_build_target),
580 build_pass: override_opt(&override_spec.build_pass, &base.build_pass),
581 make_install_target: override_opt(
582 &override_spec.make_install_target,
583 &base.make_install_target,
584 ),
585 install_pass: override_opt(&override_spec.install_pass, &base.install_pass),
586 make_build_variables: merge_map_opts(
587 &override_spec.make_build_variables,
588 &base.make_build_variables,
589 ),
590 make_install_variables: merge_map_opts(
591 &override_spec.make_install_variables,
592 &base.make_build_variables,
593 ),
594 variables: merge_map_opts(&override_spec.variables, &base.variables),
595 cmake_lists_content: override_opt(
596 &override_spec.cmake_lists_content,
597 &base.cmake_lists_content,
598 ),
599 build_command: override_opt(&override_spec.build_command, &base.build_command),
600 install_command: override_opt(&override_spec.install_command, &base.install_command),
601 install: override_opt(&override_spec.install, &base.install),
602 copy_directories: match (
603 override_spec.copy_directories.clone(),
604 base.copy_directories.clone(),
605 ) {
606 (Some(override_vec), Some(base_vec)) => {
607 let merged: Vec<PathBuf> =
608 base_vec.into_iter().chain(override_vec).unique().collect();
609 Some(merged)
610 }
611 (None, base_vec @ Some(_)) => base_vec,
612 (override_vec @ Some(_), None) => override_vec,
613 _ => None,
614 },
615 patches: override_opt(&override_spec.patches, &base.patches),
616 target_path: override_opt(&override_spec.target_path, &base.target_path),
617 default_features: override_opt(&override_spec.default_features, &base.default_features),
618 features: override_opt(&override_spec.features, &base.features),
619 include: merge_map_opts(&override_spec.include, &base.include),
620 lang: override_opt(&override_spec.lang, &base.lang),
621 parser: override_opt(&override_spec.parser, &base.parser),
622 generate: override_opt(&override_spec.generate, &base.generate),
623 location: override_opt(&override_spec.location, &base.location),
624 queries: merge_map_opts(&override_spec.queries, &base.queries),
625 })
626}
627
628fn override_opt<T: Clone>(override_opt: &Option<T>, base: &Option<T>) -> Option<T> {
629 match override_opt.clone() {
630 override_val @ Some(_) => override_val,
631 None => base.clone(),
632 }
633}
634
635fn merge_map_opts<K, V>(
636 override_map: &Option<HashMap<K, V>>,
637 base_map: &Option<HashMap<K, V>>,
638) -> Option<HashMap<K, V>>
639where
640 K: Clone,
641 K: Eq,
642 K: std::hash::Hash,
643 V: Clone,
644{
645 match (override_map.clone(), base_map.clone()) {
646 (Some(override_map), Some(base_map)) => {
647 Some(base_map.into_iter().chain(override_map).collect())
648 }
649 (_, base_map @ Some(_)) => base_map,
650 (override_map @ Some(_), _) => override_map,
651 _ => None,
652 }
653}
654
655impl DisplayAsLuaKV for BuildSpecInternal {
656 fn display_lua(&self) -> DisplayLuaKV {
657 let mut result = Vec::new();
658
659 if let Some(build_type) = &self.build_type {
660 result.push(DisplayLuaKV {
661 key: "type".to_string(),
662 value: DisplayLuaValue::String(build_type.to_string()),
663 });
664 }
665 if let Some(builtin_spec) = &self.builtin_spec {
666 result.push(DisplayLuaKV {
667 key: "modules".to_string(),
668 value: DisplayLuaValue::Table(
669 builtin_spec
670 .iter()
671 .map(|(key, value)| DisplayLuaKV {
672 key: match key {
673 LuaTableKey::StringKey(s) => s.clone(),
674 LuaTableKey::IntKey(_) => unreachable!("integer key in modules"),
675 },
676 value: value.display_lua_value(),
677 })
678 .collect(),
679 ),
680 });
681 }
682 if let Some(makefile) = &self.makefile {
683 result.push(DisplayLuaKV {
684 key: "makefile".to_string(),
685 value: DisplayLuaValue::String(makefile.to_string_lossy().to_string()),
686 });
687 }
688 if let Some(make_build_target) = &self.make_build_target {
689 result.push(DisplayLuaKV {
690 key: "build_target".to_string(),
691 value: DisplayLuaValue::String(make_build_target.clone()),
692 });
693 }
694 if let Some(build_pass) = &self.build_pass {
695 result.push(DisplayLuaKV {
696 key: "build_pass".to_string(),
697 value: DisplayLuaValue::Boolean(*build_pass),
698 });
699 }
700 if let Some(make_install_target) = &self.make_install_target {
701 result.push(DisplayLuaKV {
702 key: "install_target".to_string(),
703 value: DisplayLuaValue::String(make_install_target.clone()),
704 });
705 }
706 if let Some(install_pass) = &self.install_pass {
707 result.push(DisplayLuaKV {
708 key: "install_pass".to_string(),
709 value: DisplayLuaValue::Boolean(*install_pass),
710 });
711 }
712 if let Some(make_build_variables) = &self.make_build_variables {
713 result.push(DisplayLuaKV {
714 key: "build_variables".to_string(),
715 value: DisplayLuaValue::Table(
716 make_build_variables
717 .iter()
718 .map(|(key, value)| DisplayLuaKV {
719 key: key.clone(),
720 value: DisplayLuaValue::String(value.clone()),
721 })
722 .collect(),
723 ),
724 });
725 }
726 if let Some(make_install_variables) = &self.make_install_variables {
727 result.push(DisplayLuaKV {
728 key: "install_variables".to_string(),
729 value: DisplayLuaValue::Table(
730 make_install_variables
731 .iter()
732 .map(|(key, value)| DisplayLuaKV {
733 key: key.clone(),
734 value: DisplayLuaValue::String(value.clone()),
735 })
736 .collect(),
737 ),
738 });
739 }
740 if let Some(variables) = &self.variables {
741 result.push(DisplayLuaKV {
742 key: "variables".to_string(),
743 value: DisplayLuaValue::Table(
744 variables
745 .iter()
746 .map(|(key, value)| DisplayLuaKV {
747 key: key.clone(),
748 value: DisplayLuaValue::String(value.clone()),
749 })
750 .collect(),
751 ),
752 });
753 }
754 if let Some(cmake_lists_content) = &self.cmake_lists_content {
755 result.push(DisplayLuaKV {
756 key: "cmake".to_string(),
757 value: DisplayLuaValue::String(cmake_lists_content.clone()),
758 });
759 }
760 if let Some(build_command) = &self.build_command {
761 result.push(DisplayLuaKV {
762 key: "build_command".to_string(),
763 value: DisplayLuaValue::String(build_command.clone()),
764 });
765 }
766 if let Some(install_command) = &self.install_command {
767 result.push(DisplayLuaKV {
768 key: "install_command".to_string(),
769 value: DisplayLuaValue::String(install_command.clone()),
770 });
771 }
772 if let Some(install) = &self.install {
773 result.push(install.display_lua());
774 }
775 if let Some(copy_directories) = &self.copy_directories {
776 result.push(DisplayLuaKV {
777 key: "copy_directories".to_string(),
778 value: DisplayLuaValue::List(
779 copy_directories
780 .iter()
781 .map(|path_buf| {
782 DisplayLuaValue::String(path_buf.to_string_lossy().to_string())
783 })
784 .collect(),
785 ),
786 });
787 }
788 if let Some(patches) = &self.patches {
789 result.push(DisplayLuaKV {
790 key: "patches".to_string(),
791 value: DisplayLuaValue::Table(
792 patches
793 .iter()
794 .map(|(key, value)| DisplayLuaKV {
795 key: key.to_string_lossy().to_string(),
796 value: DisplayLuaValue::String(value.clone()),
797 })
798 .collect(),
799 ),
800 });
801 }
802 if let Some(target_path) = &self.target_path {
803 result.push(DisplayLuaKV {
804 key: "target_path".to_string(),
805 value: DisplayLuaValue::String(target_path.to_string_lossy().to_string()),
806 });
807 }
808 if let Some(default_features) = &self.default_features {
809 result.push(DisplayLuaKV {
810 key: "default_features".to_string(),
811 value: DisplayLuaValue::Boolean(*default_features),
812 });
813 }
814 if let Some(include) = &self.include {
815 result.push(DisplayLuaKV {
816 key: "include".to_string(),
817 value: DisplayLuaValue::Table(
818 include
819 .iter()
820 .map(|(key, value)| DisplayLuaKV {
821 key: match key {
822 LuaTableKey::StringKey(s) => s.clone(),
823 LuaTableKey::IntKey(_) => unreachable!("integer key in include"),
824 },
825 value: DisplayLuaValue::String(value.to_string_lossy().to_string()),
826 })
827 .collect(),
828 ),
829 });
830 }
831 if let Some(features) = &self.features {
832 result.push(DisplayLuaKV {
833 key: "features".to_string(),
834 value: DisplayLuaValue::List(
835 features
836 .iter()
837 .map(|feature| DisplayLuaValue::String(feature.clone()))
838 .collect(),
839 ),
840 });
841 }
842 if let Some(lang) = &self.lang {
843 result.push(DisplayLuaKV {
844 key: "lang".to_string(),
845 value: DisplayLuaValue::String(lang.to_string()),
846 });
847 }
848 if let Some(parser) = &self.parser {
849 result.push(DisplayLuaKV {
850 key: "parser".to_string(),
851 value: DisplayLuaValue::Boolean(*parser),
852 });
853 }
854 if let Some(generate) = &self.generate {
855 result.push(DisplayLuaKV {
856 key: "generate".to_string(),
857 value: DisplayLuaValue::Boolean(*generate),
858 });
859 }
860 if let Some(location) = &self.location {
861 result.push(DisplayLuaKV {
862 key: "location".to_string(),
863 value: DisplayLuaValue::String(location.to_string_lossy().to_string()),
864 });
865 }
866 if let Some(queries) = &self.queries {
867 result.push(DisplayLuaKV {
868 key: "queries".to_string(),
869 value: DisplayLuaValue::Table(
870 queries
871 .iter()
872 .map(|(key, value)| DisplayLuaKV {
873 key: key.to_string_lossy().to_string(),
874 value: DisplayLuaValue::String(value.to_string()),
875 })
876 .collect(),
877 ),
878 });
879 }
880
881 DisplayLuaKV {
882 key: "build".to_string(),
883 value: DisplayLuaValue::Table(result),
884 }
885 }
886}
887
888#[derive(Debug, PartialEq, Deserialize, Clone)]
890#[serde(rename_all = "lowercase", remote = "BuildType")]
891pub(crate) enum BuildType {
892 Builtin,
894 Make,
896 CMake,
898 Command,
900 None,
902 LuaRock(String),
904 #[serde(rename = "rust-mlua")]
905 RustMlua,
906 #[serde(rename = "treesitter-parser")]
907 TreesitterParser,
908 Source,
909}
910
911impl<'de> Deserialize<'de> for BuildType {
914 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
915 where
916 D: Deserializer<'de>,
917 {
918 let s = String::deserialize(deserializer)?;
919 if s == "builtin" || s == "module" {
920 Ok(Self::Builtin)
921 } else {
922 match Self::deserialize(s.clone().into_deserializer()) {
923 Err(_) => Ok(Self::LuaRock(s)),
924 ok => ok,
925 }
926 }
927 }
928}
929
930impl Display for BuildType {
931 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
932 match self {
933 BuildType::Builtin => write!(f, "builtin"),
934 BuildType::Make => write!(f, "make"),
935 BuildType::CMake => write!(f, "cmake"),
936 BuildType::Command => write!(f, "command"),
937 BuildType::None => write!(f, "none"),
938 BuildType::LuaRock(s) => write!(f, "{}", s),
939 BuildType::RustMlua => write!(f, "rust-mlua"),
940 BuildType::TreesitterParser => write!(f, "treesitter-parser"),
941 BuildType::Source => write!(f, "source"),
942 }
943 }
944}
945
946impl Default for BuildType {
947 fn default() -> Self {
948 Self::Builtin
949 }
950}
951
952#[cfg(test)]
953mod tests {
954
955 use super::*;
956
957 #[tokio::test]
958 pub async fn deserialize_build_type() {
959 let build_type: BuildType = serde_json::from_str("\"builtin\"").unwrap();
960 assert_eq!(build_type, BuildType::Builtin);
961 let build_type: BuildType = serde_json::from_str("\"module\"").unwrap();
962 assert_eq!(build_type, BuildType::Builtin);
963 let build_type: BuildType = serde_json::from_str("\"make\"").unwrap();
964 assert_eq!(build_type, BuildType::Make);
965 let build_type: BuildType = serde_json::from_str("\"custom_build_backend\"").unwrap();
966 assert_eq!(
967 build_type,
968 BuildType::LuaRock("custom_build_backend".into())
969 );
970 let build_type: BuildType = serde_json::from_str("\"rust-mlua\"").unwrap();
971 assert_eq!(build_type, BuildType::RustMlua);
972 }
973}