lux_lib/operations/
run_lua.rs1use bon::Builder;
6
7use crate::config::Config;
8
9use std::{
10 io,
11 path::{Path, PathBuf},
12};
13
14use thiserror::Error;
15use tokio::process::Command;
16
17use crate::{
18 lua_installation::{LuaBinary, LuaBinaryError},
19 path::{Paths, PathsError},
20 tree::Tree,
21 tree::TreeError,
22};
23
24#[derive(Error, Debug)]
25pub enum RunLuaError {
26 #[error("error running lua: {0}")]
27 LuaBinary(#[from] LuaBinaryError),
28 #[error("failed to run {lua_cmd}: {source}")]
29 LuaCommandFailed {
30 lua_cmd: String,
31 #[source]
32 source: io::Error,
33 },
34 #[error("{lua_cmd} exited with non-zero exit code: {}", exit_code.map(|code| code.to_string()).unwrap_or("unknown".into()))]
35 LuaCommandNonZeroExitCode {
36 lua_cmd: String,
37 exit_code: Option<i32>,
38 },
39 #[error(transparent)]
40 Paths(#[from] PathsError),
41
42 #[error(transparent)]
43 Tree(#[from] TreeError),
44}
45
46#[derive(Builder)]
47#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
48pub struct RunLua<'a> {
49 root: &'a Path,
50 tree: &'a Tree,
51 config: &'a Config,
52 lua_cmd: LuaBinary,
53 args: &'a Vec<String>,
54 prepend_test_paths: Option<bool>,
55 prepend_build_paths: Option<bool>,
56 disable_loader: Option<bool>,
57 lua_init: Option<String>,
58 welcome_message: Option<String>,
59}
60
61impl<State> RunLuaBuilder<'_, State>
62where
63 State: run_lua_builder::State + run_lua_builder::IsComplete,
64{
65 pub async fn run_lua(self) -> Result<(), RunLuaError> {
66 let args = self._build();
67 let mut paths = Paths::new(args.tree)?;
68
69 if args.prepend_test_paths.unwrap_or(false) {
70 let test_tree_path = args.tree.test_tree(args.config)?;
71
72 let test_path = Paths::new(&test_tree_path)?;
73
74 paths.prepend(&test_path);
75 }
76
77 if args.prepend_build_paths.unwrap_or(false) {
78 let build_tree_path = args.tree.build_tree(args.config)?;
79
80 let build_path = Paths::new(&build_tree_path)?;
81
82 paths.prepend(&build_path);
83 }
84
85 let lua_cmd: PathBuf = args.lua_cmd.try_into()?;
86
87 let loader_init = if args.disable_loader.unwrap_or(false) {
88 "".to_string()
89 } else if args.tree.version().lux_lib_dir().is_none() {
90 eprintln!(
91 "⚠️ WARNING: lux-lua library not found.
92Cannot use the `lux.loader`.
93To suppress this warning, set the `--no-loader` option.
94 "
95 );
96 "".to_string()
97 } else {
98 paths.init()
99 };
100 let lua_init = format!(
101 r#"print([==[{}]==])
102 {}
103 print([==[
104{}
105]==])
106 "#,
107 args.welcome_message.unwrap_or_default(),
108 args.lua_init.unwrap_or_default(),
109 loader_init
110 );
111
112 let status = match Command::new(&lua_cmd)
113 .current_dir(args.root)
114 .args(args.args)
115 .env("PATH", paths.path_prepended().joined())
116 .env("LUA_PATH", paths.package_path().joined())
117 .env("LUA_CPATH", paths.package_cpath().joined())
118 .env("LUA_INIT", lua_init)
119 .status()
120 .await
121 {
122 Ok(status) => Ok(status),
123 Err(err) => Err(RunLuaError::LuaCommandFailed {
124 lua_cmd: lua_cmd.to_string_lossy().to_string(),
125 source: err,
126 }),
127 }?;
128 if status.success() {
129 Ok(())
130 } else {
131 Err(RunLuaError::LuaCommandNonZeroExitCode {
132 lua_cmd: lua_cmd.to_string_lossy().to_string(),
133 exit_code: status.code(),
134 })
135 }
136 }
137}