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