Skip to main content

lux_lib/lua_rockspec/
partial.rs

1use std::collections::HashMap;
2
3use mlua::{Lua, LuaSerdeExt, UserData, Value};
4use serde::de::Error;
5
6use crate::{
7    lua_rockspec::RockspecFormat, package::PackageName, rockspec::lua_dependency::LuaDependencySpec,
8};
9
10use super::{
11    parse_lua_tbl_or_default, BuildSpecInternal, DeploySpec, ExternalDependencySpec,
12    PlatformSupport, RockDescription, TestSpecInternal,
13};
14
15#[derive(Debug)]
16pub struct PartialLuaRockspec {
17    pub(crate) rockspec_format: Option<RockspecFormat>,
18    pub(crate) package: Option<PackageName>,
19    pub(crate) build: Option<BuildSpecInternal>,
20    pub(crate) deploy: Option<DeploySpec>,
21    pub(crate) description: Option<RockDescription>,
22    pub(crate) supported_platforms: Option<PlatformSupport>,
23    pub(crate) dependencies: Option<Vec<LuaDependencySpec>>,
24    pub(crate) build_dependencies: Option<Vec<LuaDependencySpec>>,
25    pub(crate) external_dependencies: Option<HashMap<String, ExternalDependencySpec>>,
26    pub(crate) test_dependencies: Option<Vec<LuaDependencySpec>>,
27    pub(crate) test: Option<TestSpecInternal>,
28}
29
30impl UserData for PartialLuaRockspec {}
31
32pub type PartialRockspecError = mlua::Error;
33
34impl PartialLuaRockspec {
35    pub fn new(rockspec_content: &str) -> Result<Self, PartialRockspecError> {
36        let lua = Lua::new();
37
38        #[cfg(feature = "luau")]
39        lua.sandbox(true)?;
40
41        lua.load(rockspec_content).exec()?;
42
43        let globals = lua.globals();
44
45        if globals.contains_key("version")? {
46            return Err(mlua::Error::custom(
47                "field `version` should not be declared in extra.rockspec.",
48            ));
49        }
50        if globals.contains_key("source")? {
51            return Err(mlua::Error::custom(
52                "field `source` should not be declared in extra.rockspec.",
53            ));
54        }
55
56        let rockspec = PartialLuaRockspec {
57            rockspec_format: globals.get("rockspec_format").unwrap_or_default(),
58            package: globals.get("package").unwrap_or_default(),
59            description: parse_lua_tbl_or_default(&lua, "description").unwrap_or_default(),
60            supported_platforms: parse_lua_tbl_or_default(&lua, "supported_platforms")
61                .unwrap_or_default(),
62            dependencies: lua
63                .from_value(globals.get("dependencies").unwrap_or(Value::Nil))
64                .unwrap_or_default(),
65            build_dependencies: lua
66                .from_value(globals.get("build_dependencies").unwrap_or(Value::Nil))
67                .unwrap_or_default(),
68            test_dependencies: lua
69                .from_value(globals.get("test_dependencies").unwrap_or(Value::Nil))
70                .unwrap_or_default(),
71            external_dependencies: lua
72                .from_value(globals.get("external_dependencies").unwrap_or(Value::Nil))
73                .unwrap_or_default(),
74            build: lua
75                .from_value(globals.get("build").unwrap_or(Value::Nil))
76                .unwrap_or_default(),
77            test: lua
78                .from_value(globals.get("test").unwrap_or(Value::Nil))
79                .unwrap_or_default(),
80            deploy: lua
81                .from_value(globals.get("deploy").unwrap_or(Value::Nil))
82                .unwrap_or_default(),
83        };
84
85        Ok(rockspec)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn parse_partial_rockspec() {
95        let partial_rockspec = r#"
96            package = "my-package"
97        "#;
98
99        PartialLuaRockspec::new(partial_rockspec).unwrap();
100
101        // Whether the partial rockspec format can still support entire rockspecs
102        let full_rockspec = r#"
103            rockspec_format = "3.0"
104            package = "my-package"
105
106            description = {
107                summary = "A summary",
108                detailed = "A detailed description",
109                license = "MIT",
110                homepage = "https://example.com",
111                issues_url = "https://example.com/issues",
112                maintainer = "John Doe",
113                labels = {"label1", "label2"},
114            }
115
116            supported_platforms = {"linux", "!windows"}
117
118            dependencies = {
119                "lua 5.1",
120                "foo 1.0",
121                "bar >=2.0",
122            }
123
124            build_dependencies = {
125                "baz 1.0",
126            }
127
128            external_dependencies = {
129                foo = { header = "foo.h" },
130                bar = { library = "libbar.so" },
131            }
132
133            test_dependencies = {
134                "busted 1.0",
135            }
136
137            test = {
138                type = "command",
139                script = "test.lua",
140                flags = {"foo", "bar"},
141            }
142
143            build = {
144                type = "builtin",
145            }
146        "#;
147
148        let rockspec = PartialLuaRockspec::new(full_rockspec).unwrap();
149
150        // No need to verify if the fields were parsed correctly, but worth checking if they were
151        // parsed at all.
152
153        assert!(rockspec.rockspec_format.is_some());
154        assert!(rockspec.package.is_some());
155        assert!(rockspec.description.is_some());
156        assert!(rockspec.supported_platforms.is_some());
157        assert!(rockspec.dependencies.is_some());
158        assert!(rockspec.build_dependencies.is_some());
159        assert!(rockspec.external_dependencies.is_some());
160        assert!(rockspec.test_dependencies.is_some());
161        assert!(rockspec.build.is_some());
162        assert!(rockspec.test.is_some());
163
164        // We don't allow version and source in extra.rockspec
165        let partial_rockspec = r#"
166            version = "2.0.0"
167        "#;
168
169        PartialLuaRockspec::new(partial_rockspec).unwrap_err();
170
171        let partial_rockspec = r#"
172            source = {
173                url = "https://example.com",
174            }
175        "#;
176
177        PartialLuaRockspec::new(partial_rockspec).unwrap_err();
178    }
179}