lux_lib/lua_version/
mod.rs1use std::{
2 fmt::Display,
3 path::{Path, PathBuf},
4 str::FromStr,
5};
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10use crate::{
11 config::Config,
12 package::{PackageVersion, PackageVersionReq},
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
16pub enum LuaVersion {
17 #[serde(rename = "5.1")]
18 Lua51,
19 #[serde(rename = "5.2")]
20 Lua52,
21 #[serde(rename = "5.3")]
22 Lua53,
23 #[serde(rename = "5.4")]
24 Lua54,
25 #[serde(rename = "5.5")]
26 Lua55,
27 #[serde(rename = "jit")]
28 LuaJIT,
29 #[serde(rename = "jit5.2")]
30 LuaJIT52,
31 }
34
35#[derive(Debug, Error)]
36pub enum LuaVersionError {
37 #[error("unsupported Lua version: {0}")]
38 UnsupportedLuaVersion(PackageVersion),
39}
40
41impl LuaVersion {
42 pub fn as_version(&self) -> PackageVersion {
43 unsafe {
44 match self {
45 LuaVersion::Lua51 => "5.1.0".parse().unwrap_unchecked(),
46 LuaVersion::Lua52 => "5.2.0".parse().unwrap_unchecked(),
47 LuaVersion::Lua53 => "5.3.0".parse().unwrap_unchecked(),
48 LuaVersion::Lua54 => "5.4.0".parse().unwrap_unchecked(),
49 LuaVersion::Lua55 => "5.5.0".parse().unwrap_unchecked(),
50 LuaVersion::LuaJIT => "5.1.0".parse().unwrap_unchecked(),
51 LuaVersion::LuaJIT52 => "5.2.0".parse().unwrap_unchecked(),
52 }
53 }
54 }
55 pub fn version_compatibility_str(&self) -> String {
56 match self {
57 LuaVersion::Lua51 | LuaVersion::LuaJIT => "5.1".into(),
58 LuaVersion::Lua52 | LuaVersion::LuaJIT52 => "5.2".into(),
59 LuaVersion::Lua53 => "5.3".into(),
60 LuaVersion::Lua54 => "5.4".into(),
61 LuaVersion::Lua55 => "5.5".into(),
62 }
63 }
64 pub fn as_version_req(&self) -> PackageVersionReq {
65 unsafe {
66 format!("~> {}", self.version_compatibility_str())
67 .parse()
68 .unwrap_unchecked()
69 }
70 }
71
72 pub fn from_version(version: PackageVersion) -> Result<LuaVersion, LuaVersionError> {
74 let luajit_version_req: PackageVersionReq = unsafe { "~> 2".parse().unwrap_unchecked() };
76 if luajit_version_req.matches(&version) {
77 Ok(LuaVersion::LuaJIT)
78 } else if LuaVersion::Lua51.as_version_req().matches(&version) {
79 Ok(LuaVersion::Lua51)
80 } else if LuaVersion::Lua52.as_version_req().matches(&version) {
81 Ok(LuaVersion::Lua52)
82 } else if LuaVersion::Lua53.as_version_req().matches(&version) {
83 Ok(LuaVersion::Lua53)
84 } else if LuaVersion::Lua54.as_version_req().matches(&version) {
85 Ok(LuaVersion::Lua54)
86 } else if LuaVersion::Lua55.as_version_req().matches(&version) {
87 Ok(LuaVersion::Lua55)
88 } else {
89 Err(LuaVersionError::UnsupportedLuaVersion(version))
90 }
91 }
92
93 pub(crate) fn is_luajit(&self) -> bool {
94 matches!(self, Self::LuaJIT | Self::LuaJIT52)
95 }
96
97 pub fn lux_lib_dir(&self) -> Option<PathBuf> {
99 option_env!("LUX_LIB_DIR")
100 .map(PathBuf::from)
101 .map(|path| path.join(self.to_string()))
102 .or_else(|| {
103 let lib_name = format!("lux-lua{self}");
104 pkg_config::Config::new()
105 .print_system_libs(false)
106 .cargo_metadata(false)
107 .env_metadata(false)
108 .probe(&lib_name)
109 .ok()
110 .and_then(|library| library.link_paths.first().cloned())
111 })
112 .or_else(|| lux_lib_resource_dir().map(|path| path.join(self.to_string())))
113 }
114}
115
116#[derive(Error, Debug)]
117#[error(
118 r#"lua version not set.
119Please provide a version through `lx --lua-version <ver> <cmd>`
120Valid versions are: '5.1', '5.2', '5.3', '5.4', '5.5', 'jit' and 'jit52'.
121"#
122)]
123pub struct LuaVersionUnset;
124
125impl LuaVersion {
126 pub fn from(config: &Config) -> Result<&Self, LuaVersionUnset> {
127 config.lua_version().ok_or(LuaVersionUnset)
128 }
129}
130
131impl FromStr for LuaVersion {
132 type Err = String;
133
134 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
135 match s {
136 "5.1" | "51" => Ok(LuaVersion::Lua51),
137 "5.2" | "52" => Ok(LuaVersion::Lua52),
138 "5.3" | "53" => Ok(LuaVersion::Lua53),
139 "5.4" | "54" => Ok(LuaVersion::Lua54),
140 "5.5" | "55" => Ok(LuaVersion::Lua55),
141 "jit" | "luajit" => Ok(LuaVersion::LuaJIT),
142 "jit52" | "luajit52" => Ok(LuaVersion::LuaJIT52),
143 _ => Err(r#"unrecognized Lua version.
144 Supported versions: '5.1', '5.2', '5.3', '5.4', '5.5', 'jit', 'jit52'.
145 "#
146 .into()),
147 }
148 }
149}
150
151impl Display for LuaVersion {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 f.write_str(match self {
154 LuaVersion::Lua51 => "5.1",
155 LuaVersion::Lua52 => "5.2",
156 LuaVersion::Lua53 => "5.3",
157 LuaVersion::Lua54 => "5.4",
158 LuaVersion::Lua55 => "5.5",
159 LuaVersion::LuaJIT => "jit",
160 LuaVersion::LuaJIT52 => "jit52",
161 })
162 }
163}
164
165fn lux_lib_resource_dir() -> Option<PathBuf> {
167 if cfg!(target_env = "msvc") {
168 std::env::current_exe()
170 .ok()
171 .and_then(|exe_path| exe_path.parent().map(Path::to_path_buf))
172 .and_then(|exe_dir| {
173 let lib_dir = exe_dir.join("lux-lua");
174 if lib_dir.is_dir() {
175 Some(lib_dir)
176 } else {
177 None
178 }
179 })
180 } else if cfg!(target_os = "macos") {
181 std::env::current_exe()
183 .ok()
184 .and_then(|exe_path| exe_path.parent().map(Path::to_path_buf))
185 .and_then(|macos_dir| macos_dir.parent().map(Path::to_path_buf))
186 .and_then(|contents_dir| {
187 let lib_dir = contents_dir.join("Resources").join("lux-lua");
188 if lib_dir.is_dir() {
189 Some(lib_dir)
190 } else {
191 None
192 }
193 })
194 } else {
195 let lib_dir = PathBuf::from("/usr/share/lux-lua");
197 if lib_dir.is_dir() {
198 Some(lib_dir)
199 } else {
200 None
201 }
202 }
203}