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 welcome_message: Option<String>,
57}
58
59impl<State> RunLuaBuilder<'_, State>
60where
61 State: run_lua_builder::State + run_lua_builder::IsComplete,
62{
63 pub async fn run_lua(self) -> Result<(), RunLuaError> {
64 let args = self._build();
65 let mut paths = Paths::new(args.tree)?;
66
67 if args.prepend_test_paths.unwrap_or(false) {
68 let test_tree_path = args.tree.test_tree(args.config)?;
69
70 let test_path = Paths::new(&test_tree_path)?;
71
72 paths.prepend(&test_path);
73 }
74
75 if args.prepend_build_paths.unwrap_or(false) {
76 let build_tree_path = args.tree.build_tree(args.config)?;
77
78 let build_path = Paths::new(&build_tree_path)?;
79
80 paths.prepend(&build_path);
81 }
82
83 let lua_cmd: PathBuf = args.lua_cmd.try_into()?;
84
85 let loader_init = if args.tree.version().lux_lib_dir().is_none() {
86 eprintln!(
87 "⚠️ WARNING: lux-lua library not found.
88Cannot use the `lux.loader`.
89 "
90 );
91 "".to_string()
92 } else {
93 paths.init()
94 };
95 let lua_init = format!(
96 r#"print([==[{}]==])
97 exit = os.exit
98 print([==[
99{}
100To exit type 'exit()' or <C-d>.
101]==])
102 "#,
103 args.welcome_message.unwrap_or_default(),
104 loader_init
105 );
106
107 let status = match Command::new(&lua_cmd)
108 .current_dir(args.root)
109 .args(args.args)
110 .env("PATH", paths.path_prepended().joined())
111 .env("LUA_PATH", paths.package_path().joined())
112 .env("LUA_CPATH", paths.package_cpath().joined())
113 .env("LUA_INIT", lua_init)
114 .status()
115 .await
116 {
117 Ok(status) => Ok(status),
118 Err(err) => Err(RunLuaError::LuaCommandFailed {
119 lua_cmd: lua_cmd.to_string_lossy().to_string(),
120 source: err,
121 }),
122 }?;
123 if status.success() {
124 Ok(())
125 } else {
126 Err(RunLuaError::LuaCommandNonZeroExitCode {
127 lua_cmd: lua_cmd.to_string_lossy().to_string(),
128 exit_code: status.code(),
129 })
130 }
131 }
132}