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