1use directories::ProjectDirs;
2use external_deps::ExternalDependencySearchConfig;
3use itertools::Itertools;
4use mlua::{ExternalError, ExternalResult, FromLua, IntoLua, UserData};
5use serde::{Deserialize, Serialize, Serializer};
6use std::env::current_exe;
7use std::path::Path;
8use std::{
9 collections::HashMap, env, fmt::Display, io, path::PathBuf, str::FromStr, time::Duration,
10};
11use thiserror::Error;
12use tree::RockLayoutConfig;
13use url::Url;
14
15use crate::tree::{Tree, TreeError};
16use crate::variables::GetVariableError;
17use crate::{
18 build::utils,
19 package::{PackageVersion, PackageVersionReq},
20 variables::HasVariables,
21};
22
23pub mod external_deps;
24pub mod tree;
25
26const DEV_PATH: &str = "dev/";
27
28#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
29pub enum LuaVersion {
30 #[serde(rename = "5.1")]
31 Lua51,
32 #[serde(rename = "5.2")]
33 Lua52,
34 #[serde(rename = "5.3")]
35 Lua53,
36 #[serde(rename = "5.4")]
37 Lua54,
38 #[serde(rename = "5.5")]
39 Lua55,
40 #[serde(rename = "jit")]
41 LuaJIT,
42 #[serde(rename = "jit5.2")]
43 LuaJIT52,
44 }
47
48impl FromLua for LuaVersion {
49 fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
50 let version_str: String = FromLua::from_lua(value, lua)?;
51 LuaVersion::from_str(&version_str).into_lua_err()
52 }
53}
54
55impl IntoLua for LuaVersion {
56 fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
57 self.to_string().into_lua(lua)
58 }
59}
60
61#[derive(Debug, Error)]
62pub enum LuaVersionError {
63 #[error("unsupported Lua version: {0}")]
64 UnsupportedLuaVersion(PackageVersion),
65}
66
67impl LuaVersion {
68 pub fn as_version(&self) -> PackageVersion {
69 unsafe {
70 match self {
71 LuaVersion::Lua51 => "5.1.0".parse().unwrap_unchecked(),
72 LuaVersion::Lua52 => "5.2.0".parse().unwrap_unchecked(),
73 LuaVersion::Lua53 => "5.3.0".parse().unwrap_unchecked(),
74 LuaVersion::Lua54 => "5.4.0".parse().unwrap_unchecked(),
75 LuaVersion::Lua55 => "5.5.0".parse().unwrap_unchecked(),
76 LuaVersion::LuaJIT => "5.1.0".parse().unwrap_unchecked(),
77 LuaVersion::LuaJIT52 => "5.2.0".parse().unwrap_unchecked(),
78 }
79 }
80 }
81 pub fn version_compatibility_str(&self) -> String {
82 match self {
83 LuaVersion::Lua51 | LuaVersion::LuaJIT => "5.1".into(),
84 LuaVersion::Lua52 | LuaVersion::LuaJIT52 => "5.2".into(),
85 LuaVersion::Lua53 => "5.3".into(),
86 LuaVersion::Lua54 => "5.4".into(),
87 LuaVersion::Lua55 => "5.5".into(),
88 }
89 }
90 pub fn as_version_req(&self) -> PackageVersionReq {
91 unsafe {
92 format!("~> {}", self.version_compatibility_str())
93 .parse()
94 .unwrap_unchecked()
95 }
96 }
97
98 pub fn from_version(version: PackageVersion) -> Result<LuaVersion, LuaVersionError> {
100 let luajit_version_req: PackageVersionReq = unsafe { "~> 2".parse().unwrap_unchecked() };
102 if luajit_version_req.matches(&version) {
103 Ok(LuaVersion::LuaJIT)
104 } else if LuaVersion::Lua51.as_version_req().matches(&version) {
105 Ok(LuaVersion::Lua51)
106 } else if LuaVersion::Lua52.as_version_req().matches(&version) {
107 Ok(LuaVersion::Lua52)
108 } else if LuaVersion::Lua53.as_version_req().matches(&version) {
109 Ok(LuaVersion::Lua53)
110 } else if LuaVersion::Lua54.as_version_req().matches(&version) {
111 Ok(LuaVersion::Lua54)
112 } else if LuaVersion::Lua55.as_version_req().matches(&version) {
113 Ok(LuaVersion::Lua55)
114 } else {
115 Err(LuaVersionError::UnsupportedLuaVersion(version))
116 }
117 }
118
119 pub(crate) fn is_luajit(&self) -> bool {
120 matches!(self, Self::LuaJIT | Self::LuaJIT52)
121 }
122
123 pub fn lux_lib_dir(&self) -> Option<PathBuf> {
125 option_env!("LUX_LIB_DIR")
126 .map(PathBuf::from)
127 .map(|path| path.join(self.to_string()))
128 .or_else(|| {
129 let lib_name = format!("lux-lua{self}");
130 pkg_config::Config::new()
131 .print_system_libs(false)
132 .cargo_metadata(false)
133 .env_metadata(false)
134 .probe(&lib_name)
135 .ok()
136 .and_then(|library| library.link_paths.first().cloned())
137 })
138 .or_else(|| lux_lib_resource_dir().map(|path| path.join(self.to_string())))
139 }
140}
141
142fn lux_lib_resource_dir() -> Option<PathBuf> {
144 if cfg!(target_env = "msvc") {
145 current_exe()
147 .ok()
148 .and_then(|exe_path| exe_path.parent().map(Path::to_path_buf))
149 .and_then(|exe_dir| {
150 let lib_dir = exe_dir.join("lux-lua");
151 if lib_dir.is_dir() {
152 Some(lib_dir)
153 } else {
154 None
155 }
156 })
157 } else if cfg!(target_os = "macos") {
158 current_exe()
160 .ok()
161 .and_then(|exe_path| exe_path.parent().map(Path::to_path_buf))
162 .and_then(|macos_dir| macos_dir.parent().map(Path::to_path_buf))
163 .and_then(|contents_dir| {
164 let lib_dir = contents_dir.join("Resources").join("lux-lua");
165 if lib_dir.is_dir() {
166 Some(lib_dir)
167 } else {
168 None
169 }
170 })
171 } else {
172 let lib_dir = PathBuf::from("/usr/share/lux-lua");
174 if lib_dir.is_dir() {
175 Some(lib_dir)
176 } else {
177 None
178 }
179 }
180}
181
182#[derive(Error, Debug)]
183#[error(
184 r#"lua version not set.
185Please provide a version through `lx --lua-version <ver> <cmd>`
186Valid versions are: '5.1', '5.2', '5.3', '5.4', '5.5', 'jit' and 'jit52'.
187"#
188)]
189pub struct LuaVersionUnset;
190
191impl LuaVersion {
192 pub fn from(config: &Config) -> Result<&Self, LuaVersionUnset> {
193 config.lua_version.as_ref().ok_or(LuaVersionUnset)
194 }
195}
196
197impl FromStr for LuaVersion {
198 type Err = String;
199
200 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
201 match s {
202 "5.1" | "51" => Ok(LuaVersion::Lua51),
203 "5.2" | "52" => Ok(LuaVersion::Lua52),
204 "5.3" | "53" => Ok(LuaVersion::Lua53),
205 "5.4" | "54" => Ok(LuaVersion::Lua54),
206 "5.5" | "55" => Ok(LuaVersion::Lua55),
207 "jit" | "luajit" => Ok(LuaVersion::LuaJIT),
208 "jit52" | "luajit52" => Ok(LuaVersion::LuaJIT52),
209 _ => Err(r#"unrecognized Lua version.
210 Supported versions: '5.1', '5.2', '5.3', '5.4', '5.5', 'jit', 'jit52'.
211 "#
212 .into()),
213 }
214 }
215}
216
217impl Display for LuaVersion {
218 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219 f.write_str(match self {
220 LuaVersion::Lua51 => "5.1",
221 LuaVersion::Lua52 => "5.2",
222 LuaVersion::Lua53 => "5.3",
223 LuaVersion::Lua54 => "5.4",
224 LuaVersion::Lua55 => "5.5",
225 LuaVersion::LuaJIT => "jit",
226 LuaVersion::LuaJIT52 => "jit52",
227 })
228 }
229}
230
231#[derive(Error, Debug)]
232#[error("could not find a valid home directory")]
233pub struct NoValidHomeDirectory;
234
235#[derive(Debug, Clone, FromLua)]
236pub struct Config {
237 enable_development_packages: bool,
238 server: Url,
239 extra_servers: Vec<Url>,
240 only_sources: Option<String>,
241 namespace: Option<String>,
242 lua_dir: Option<PathBuf>,
243 lua_version: Option<LuaVersion>,
244 user_tree: PathBuf,
245 verbose: bool,
246 no_progress: bool,
248 timeout: Duration,
249 max_jobs: usize,
250 variables: HashMap<String, String>,
251 external_deps: ExternalDependencySearchConfig,
252 entrypoint_layout: RockLayoutConfig,
255
256 cache_dir: PathBuf,
257 data_dir: PathBuf,
258 vendor_dir: Option<PathBuf>,
259
260 generate_luarc: bool,
261}
262
263impl Config {
264 pub fn get_project_dirs() -> Result<ProjectDirs, NoValidHomeDirectory> {
265 directories::ProjectDirs::from("org", "lumenlabs", "lux").ok_or(NoValidHomeDirectory)
266 }
267
268 pub fn get_default_cache_path() -> Result<PathBuf, NoValidHomeDirectory> {
269 let project_dirs = Config::get_project_dirs()?;
270 Ok(project_dirs.cache_dir().to_path_buf())
271 }
272
273 pub fn get_default_data_path() -> Result<PathBuf, NoValidHomeDirectory> {
274 let project_dirs = Config::get_project_dirs()?;
275 Ok(project_dirs.data_local_dir().to_path_buf())
276 }
277
278 pub fn with_lua_version(self, lua_version: LuaVersion) -> Self {
279 Self {
280 lua_version: Some(lua_version),
281 ..self
282 }
283 }
284
285 pub fn with_tree(self, tree: PathBuf) -> Self {
286 Self {
287 user_tree: tree,
288 ..self
289 }
290 }
291
292 pub fn server(&self) -> &Url {
293 &self.server
294 }
295
296 pub fn extra_servers(&self) -> &Vec<Url> {
297 self.extra_servers.as_ref()
298 }
299
300 pub fn enabled_dev_servers(&self) -> Result<Vec<Url>, ConfigError> {
301 let mut enabled_dev_servers = Vec::new();
302 if self.enable_development_packages {
303 enabled_dev_servers.push(self.server().join(DEV_PATH)?);
304 for server in self.extra_servers() {
305 enabled_dev_servers.push(server.join(DEV_PATH)?);
306 }
307 }
308 Ok(enabled_dev_servers)
309 }
310
311 pub fn only_sources(&self) -> Option<&String> {
312 self.only_sources.as_ref()
313 }
314
315 pub fn namespace(&self) -> Option<&String> {
316 self.namespace.as_ref()
317 }
318
319 pub fn lua_dir(&self) -> Option<&PathBuf> {
320 self.lua_dir.as_ref()
321 }
322
323 #[cfg(test)]
324 pub(crate) fn lua_version(&self) -> Option<&LuaVersion> {
325 self.lua_version.as_ref()
326 }
327
328 pub fn user_tree(&self, version: LuaVersion) -> Result<Tree, TreeError> {
331 Tree::new(self.user_tree.clone(), version, self)
332 }
333
334 pub fn verbose(&self) -> bool {
335 self.verbose
336 }
337
338 pub fn no_progress(&self) -> bool {
339 self.no_progress
340 }
341
342 pub fn timeout(&self) -> &Duration {
343 &self.timeout
344 }
345
346 pub fn max_jobs(&self) -> usize {
347 self.max_jobs
348 }
349
350 pub fn make_cmd(&self) -> String {
351 match self.variables.get("MAKE") {
352 Some(make) => make.clone(),
353 None => "make".into(),
354 }
355 }
356
357 pub fn cmake_cmd(&self) -> String {
358 match self.variables.get("CMAKE") {
359 Some(cmake) => cmake.clone(),
360 None => "cmake".into(),
361 }
362 }
363
364 pub fn variables(&self) -> &HashMap<String, String> {
365 &self.variables
366 }
367
368 pub fn external_deps(&self) -> &ExternalDependencySearchConfig {
369 &self.external_deps
370 }
371
372 pub fn entrypoint_layout(&self) -> &RockLayoutConfig {
373 &self.entrypoint_layout
374 }
375
376 pub fn cache_dir(&self) -> &PathBuf {
377 &self.cache_dir
378 }
379
380 pub fn data_dir(&self) -> &PathBuf {
381 &self.data_dir
382 }
383
384 pub fn vendor_dir(&self) -> Option<&PathBuf> {
385 self.vendor_dir.as_ref()
386 }
387
388 pub fn generate_luarc(&self) -> bool {
389 self.generate_luarc
390 }
391}
392
393impl HasVariables for Config {
394 fn get_variable(&self, input: &str) -> Result<Option<String>, GetVariableError> {
395 Ok(self.variables.get(input).cloned())
396 }
397}
398
399#[derive(Error, Debug)]
400pub enum ConfigError {
401 #[error(transparent)]
402 Io(#[from] io::Error),
403 #[error(transparent)]
404 NoValidHomeDirectory(#[from] NoValidHomeDirectory),
405 #[error("error deserializing lux config: {0}")]
406 Deserialize(#[from] toml::de::Error),
407 #[error("error parsing URL: {0}")]
408 UrlParseError(#[from] url::ParseError),
409 #[error("error initializing compiler toolchain: {0}")]
410 CompilerToolchain(#[from] cc::Error),
411}
412
413#[derive(Clone, Default, Deserialize, Serialize)]
414pub struct ConfigBuilder {
415 #[serde(
416 default,
417 deserialize_with = "deserialize_url",
418 serialize_with = "serialize_url"
419 )]
420 server: Option<Url>,
421 #[serde(
422 default,
423 deserialize_with = "deserialize_url_vec",
424 serialize_with = "serialize_url_vec"
425 )]
426 extra_servers: Option<Vec<Url>>,
427 only_sources: Option<String>,
428 namespace: Option<String>,
429 lua_version: Option<LuaVersion>,
430 user_tree: Option<PathBuf>,
431 lua_dir: Option<PathBuf>,
432 cache_dir: Option<PathBuf>,
433 data_dir: Option<PathBuf>,
434 vendor_dir: Option<PathBuf>,
435 enable_development_packages: Option<bool>,
436 verbose: Option<bool>,
437 no_progress: Option<bool>,
438 timeout: Option<Duration>,
439 max_jobs: Option<usize>,
440 variables: Option<HashMap<String, String>>,
441 #[serde(default)]
442 external_deps: ExternalDependencySearchConfig,
443 #[serde(default)]
446 entrypoint_layout: RockLayoutConfig,
447 generate_luarc: Option<bool>,
448}
449
450impl ConfigBuilder {
452 pub fn new() -> Result<Self, ConfigError> {
455 let config_file = Self::config_file()?;
456 if config_file.is_file() {
457 Ok(toml::from_str(&std::fs::read_to_string(&config_file)?)?)
458 } else {
459 Ok(Self::default())
460 }
461 }
462
463 pub fn config_file() -> Result<PathBuf, NoValidHomeDirectory> {
465 let project_dirs = directories::ProjectDirs::from("org", "lumenlabs", "lux")
466 .ok_or(NoValidHomeDirectory)?;
467 Ok(project_dirs.config_dir().join("config.toml").to_path_buf())
468 }
469
470 pub fn dev(self, dev: Option<bool>) -> Self {
471 Self {
472 enable_development_packages: dev.or(self.enable_development_packages),
473 ..self
474 }
475 }
476
477 pub fn server(self, server: Option<Url>) -> Self {
478 Self {
479 server: server.or(self.server),
480 ..self
481 }
482 }
483
484 pub fn extra_servers(self, extra_servers: Option<Vec<Url>>) -> Self {
485 Self {
486 extra_servers: extra_servers.or(self.extra_servers),
487 ..self
488 }
489 }
490
491 pub fn only_sources(self, sources: Option<String>) -> Self {
492 Self {
493 only_sources: sources.or(self.only_sources),
494 ..self
495 }
496 }
497
498 pub fn namespace(self, namespace: Option<String>) -> Self {
499 Self {
500 namespace: namespace.or(self.namespace),
501 ..self
502 }
503 }
504
505 pub fn lua_dir(self, lua_dir: Option<PathBuf>) -> Self {
506 Self {
507 lua_dir: lua_dir.or(self.lua_dir),
508 ..self
509 }
510 }
511
512 pub fn lua_version(self, lua_version: Option<LuaVersion>) -> Self {
513 Self {
514 lua_version: lua_version.or(self.lua_version),
515 ..self
516 }
517 }
518
519 pub fn user_tree(self, tree: Option<PathBuf>) -> Self {
520 Self {
521 user_tree: tree.or(self.user_tree),
522 ..self
523 }
524 }
525
526 pub fn variables(self, variables: Option<HashMap<String, String>>) -> Self {
527 Self {
528 variables: variables.or(self.variables),
529 ..self
530 }
531 }
532
533 pub fn verbose(self, verbose: Option<bool>) -> Self {
534 Self {
535 verbose: verbose.or(self.verbose),
536 ..self
537 }
538 }
539
540 pub fn no_progress(self, no_progress: Option<bool>) -> Self {
541 Self {
542 no_progress: no_progress.or(self.no_progress),
543 ..self
544 }
545 }
546
547 pub fn timeout(self, timeout: Option<Duration>) -> Self {
548 Self {
549 timeout: timeout.or(self.timeout),
550 ..self
551 }
552 }
553
554 pub fn max_jobs(self, max_jobs: Option<usize>) -> Self {
555 Self {
556 max_jobs: max_jobs.or(self.max_jobs),
557 ..self
558 }
559 }
560
561 pub fn cache_dir(self, cache_dir: Option<PathBuf>) -> Self {
562 Self {
563 cache_dir: cache_dir.or(self.cache_dir),
564 ..self
565 }
566 }
567
568 pub fn data_dir(self, data_dir: Option<PathBuf>) -> Self {
569 Self {
570 data_dir: data_dir.or(self.data_dir),
571 ..self
572 }
573 }
574
575 pub fn vendor_dir(self, vendor_dir: Option<PathBuf>) -> Self {
576 Self {
577 vendor_dir: vendor_dir.or(self.vendor_dir),
578 ..self
579 }
580 }
581
582 pub fn entrypoint_layout(self, rock_layout: RockLayoutConfig) -> Self {
583 Self {
584 entrypoint_layout: rock_layout,
585 ..self
586 }
587 }
588
589 pub fn generate_luarc(self, generate: Option<bool>) -> Self {
590 Self {
591 generate_luarc: generate.or(self.generate_luarc),
592 ..self
593 }
594 }
595
596 pub fn build(self) -> Result<Config, ConfigError> {
597 let data_dir = self.data_dir.unwrap_or(Config::get_default_data_path()?);
598 let cache_dir = self.cache_dir.unwrap_or(Config::get_default_cache_path()?);
599 let user_tree = self.user_tree.unwrap_or(data_dir.join("tree"));
600
601 let lua_version = self
602 .lua_version
603 .or(crate::lua_installation::detect_installed_lua_version());
604
605 Ok(Config {
606 enable_development_packages: self.enable_development_packages.unwrap_or(false),
607 server: self.server.unwrap_or_else(|| unsafe {
608 Url::parse("https://luarocks.org/").unwrap_unchecked()
609 }),
610 extra_servers: self.extra_servers.unwrap_or_default(),
611 only_sources: self.only_sources,
612 namespace: self.namespace,
613 lua_dir: self.lua_dir,
614 lua_version,
615 user_tree,
616 verbose: self.verbose.unwrap_or(false),
617 no_progress: self.no_progress.unwrap_or(false),
618 timeout: self.timeout.unwrap_or_else(|| Duration::from_secs(30)),
619 max_jobs: match self.max_jobs.unwrap_or(usize::MAX) {
620 0 => usize::MAX,
621 max_jobs => max_jobs,
622 },
623 variables: default_variables()
624 .chain(self.variables.unwrap_or_default())
625 .collect(),
626 external_deps: self.external_deps,
627 entrypoint_layout: self.entrypoint_layout,
628 cache_dir,
629 data_dir,
630 vendor_dir: self.vendor_dir,
631 generate_luarc: self.generate_luarc.unwrap_or(true),
632 })
633 }
634}
635
636impl From<Config> for ConfigBuilder {
638 fn from(value: Config) -> Self {
639 ConfigBuilder {
640 enable_development_packages: Some(value.enable_development_packages),
641 server: Some(value.server),
642 extra_servers: Some(value.extra_servers),
643 only_sources: value.only_sources,
644 namespace: value.namespace,
645 lua_dir: value.lua_dir,
646 lua_version: value.lua_version,
647 user_tree: Some(value.user_tree),
648 verbose: Some(value.verbose),
649 no_progress: Some(value.no_progress),
650 timeout: Some(value.timeout),
651 max_jobs: if value.max_jobs == usize::MAX {
652 None
653 } else {
654 Some(value.max_jobs)
655 },
656 variables: Some(value.variables),
657 cache_dir: Some(value.cache_dir),
658 data_dir: Some(value.data_dir),
659 vendor_dir: value.vendor_dir,
660 external_deps: value.external_deps,
661 entrypoint_layout: value.entrypoint_layout,
662 generate_luarc: Some(value.generate_luarc),
663 }
664 }
665}
666
667fn default_variables() -> impl Iterator<Item = (String, String)> {
668 let cflags = env::var("CFLAGS").unwrap_or(utils::default_cflags().into());
669 let ldflags = env::var("LDFLAGS").unwrap_or("".into());
670 vec![
671 ("MAKE".into(), "make".into()),
672 ("CMAKE".into(), "cmake".into()),
673 ("LIB_EXTENSION".into(), utils::c_dylib_extension().into()),
674 ("OBJ_EXTENSION".into(), utils::c_obj_extension().into()),
675 ("CFLAGS".into(), cflags),
676 ("LDFLAGS".into(), ldflags),
677 ("LIBFLAG".into(), utils::default_libflag().into()),
678 ]
679 .into_iter()
680}
681
682fn deserialize_url<'de, D>(deserializer: D) -> Result<Option<Url>, D::Error>
683where
684 D: serde::Deserializer<'de>,
685{
686 let s = Option::<String>::deserialize(deserializer)?;
687 s.map(|s| Url::parse(&s).map_err(serde::de::Error::custom))
688 .transpose()
689}
690
691fn serialize_url<S>(url: &Option<Url>, serializer: S) -> Result<S::Ok, S::Error>
692where
693 S: Serializer,
694{
695 match url {
696 Some(url) => serializer.serialize_some(url.as_str()),
697 None => serializer.serialize_none(),
698 }
699}
700
701fn deserialize_url_vec<'de, D>(deserializer: D) -> Result<Option<Vec<Url>>, D::Error>
702where
703 D: serde::Deserializer<'de>,
704{
705 let s = Option::<Vec<String>>::deserialize(deserializer)?;
706 s.map(|v| {
707 v.into_iter()
708 .map(|s| Url::parse(&s).map_err(serde::de::Error::custom))
709 .try_collect()
710 })
711 .transpose()
712}
713
714fn serialize_url_vec<S>(urls: &Option<Vec<Url>>, serializer: S) -> Result<S::Ok, S::Error>
715where
716 S: Serializer,
717{
718 match urls {
719 Some(urls) => {
720 let url_strings: Vec<String> = urls.iter().map(|url| url.to_string()).collect();
721 serializer.serialize_some(&url_strings)
722 }
723 None => serializer.serialize_none(),
724 }
725}
726
727struct LuaUrl(Url);
728
729impl FromLua for LuaUrl {
730 fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
731 let url_str: String = FromLua::from_lua(value, lua)?;
732
733 Url::parse(&url_str).map(LuaUrl).into_lua_err()
734 }
735}
736
737impl UserData for Config {
738 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
739 methods.add_function("default", |_, _: ()| {
740 ConfigBuilder::default()
741 .build()
742 .map_err(|err| err.into_lua_err())
743 });
744
745 methods.add_function("builder", |_, ()| ConfigBuilder::new().into_lua_err());
746
747 methods.add_method("server", |_, this, ()| Ok(this.server().to_string()));
748 methods.add_method("extra_servers", |_, this, ()| {
749 Ok(this
750 .extra_servers()
751 .iter()
752 .map(|url| url.to_string())
753 .collect_vec())
754 });
755 methods.add_method("only_sources", |_, this, ()| {
756 Ok(this.only_sources().cloned())
757 });
758 methods.add_method("namespace", |_, this, ()| Ok(this.namespace().cloned()));
759 methods.add_method("lua_dir", |_, this, ()| Ok(this.lua_dir().cloned()));
760 methods.add_method("user_tree", |_, this, lua_version: LuaVersion| {
761 this.user_tree(lua_version).into_lua_err()
762 });
763 methods.add_method("verbose", |_, this, ()| Ok(this.verbose()));
764 methods.add_method("no_progress", |_, this, ()| Ok(this.no_progress()));
765 methods.add_method("timeout", |_, this, ()| Ok(this.timeout().as_secs()));
766 methods.add_method("cache_dir", |_, this, ()| Ok(this.cache_dir().clone()));
767 methods.add_method("data_dir", |_, this, ()| Ok(this.data_dir().clone()));
768 methods.add_method("entrypoint_layout", |_, this, ()| {
769 Ok(this.entrypoint_layout().clone())
770 });
771 methods.add_method("variables", |_, this, ()| Ok(this.variables().clone()));
772 methods.add_method("make_cmd", |_, this, ()| Ok(this.make_cmd()));
777 methods.add_method("cmake_cmd", |_, this, ()| Ok(this.cmake_cmd()));
778 methods.add_method("enabled_dev_servers", |_, this, ()| {
779 Ok(this
780 .enabled_dev_servers()
781 .into_lua_err()?
782 .into_iter()
783 .map(|url| url.to_string())
784 .collect_vec())
785 });
786 }
787}
788
789impl UserData for ConfigBuilder {
790 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
791 methods.add_method("dev", |_, this, dev: Option<bool>| {
792 Ok(this.clone().dev(dev))
793 });
794 methods.add_method("server", |_, this, server: Option<LuaUrl>| {
795 Ok(this.clone().server(server.map(|url| url.0)))
796 });
797 methods.add_method("extra_servers", |_, this, servers: Option<Vec<LuaUrl>>| {
798 Ok(this
799 .clone()
800 .extra_servers(servers.map(|urls| urls.into_iter().map(|url| url.0).collect())))
801 });
802 methods.add_method("only_sources", |_, this, sources: Option<String>| {
803 Ok(this.clone().only_sources(sources))
804 });
805 methods.add_method("namespace", |_, this, namespace: Option<String>| {
806 Ok(this.clone().namespace(namespace))
807 });
808 methods.add_method("lua_dir", |_, this, lua_dir: Option<PathBuf>| {
809 Ok(this.clone().lua_dir(lua_dir))
810 });
811 methods.add_method("lua_version", |_, this, lua_version: Option<LuaVersion>| {
812 Ok(this.clone().lua_version(lua_version))
813 });
814 methods.add_method("user_tree", |_, this, tree: Option<PathBuf>| {
815 Ok(this.clone().user_tree(tree))
816 });
817 methods.add_method("verbose", |_, this, verbose: Option<bool>| {
818 Ok(this.clone().verbose(verbose))
819 });
820 methods.add_method("no_progress", |_, this, no_progress: Option<bool>| {
821 Ok(this.clone().no_progress(no_progress))
822 });
823 methods.add_method("timeout", |_, this, timeout: Option<u64>| {
824 Ok(this.clone().timeout(timeout.map(Duration::from_secs)))
825 });
826 methods.add_method("cache_dir", |_, this, cache_dir: Option<PathBuf>| {
827 Ok(this.clone().cache_dir(cache_dir))
828 });
829 methods.add_method("data_dir", |_, this, data_dir: Option<PathBuf>| {
830 Ok(this.clone().data_dir(data_dir))
831 });
832 methods.add_method(
833 "entrypoint_layout",
834 |_, this, entrypoint_layout: Option<RockLayoutConfig>| {
835 Ok(this
836 .clone()
837 .entrypoint_layout(entrypoint_layout.unwrap_or_default()))
838 },
839 );
840 methods.add_method("generate_luarc", |_, this, generate: Option<bool>| {
841 Ok(this.clone().generate_luarc(generate))
842 });
843 methods.add_method("build", |_, this, ()| this.clone().build().into_lua_err());
844 }
845}