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        lua.load(rockspec_content).exec()?;
38
39        let globals = lua.globals();
40
41        if globals.contains_key("version")? {
42            return Err(mlua::Error::custom(
43                "field `version` should not be declared in extra.rockspec.",
44            ));
45        }
46        if globals.contains_key("source")? {
47            return Err(mlua::Error::custom(
48                "field `source` should not be declared in extra.rockspec.",
49            ));
50        }
51
52        let rockspec = PartialLuaRockspec {
53            rockspec_format: globals.get("rockspec_format").unwrap_or_default(),
54            package: globals.get("package").unwrap_or_default(),
55            description: parse_lua_tbl_or_default(&lua, "description").unwrap_or_default(),
56            supported_platforms: parse_lua_tbl_or_default(&lua, "supported_platforms")
57                .unwrap_or_default(),
58            dependencies: lua
59                .from_value(globals.get("dependencies").unwrap_or(Value::Nil))
60                .unwrap_or_default(),
61            build_dependencies: lua
62                .from_value(globals.get("build_dependencies").unwrap_or(Value::Nil))
63                .unwrap_or_default(),
64            test_dependencies: lua
65                .from_value(globals.get("test_dependencies").unwrap_or(Value::Nil))
66                .unwrap_or_default(),
67            external_dependencies: lua
68                .from_value(globals.get("external_dependencies").unwrap_or(Value::Nil))
69                .unwrap_or_default(),
70            build: lua
71                .from_value(globals.get("build").unwrap_or(Value::Nil))
72                .unwrap_or_default(),
73            test: lua
74                .from_value(globals.get("test").unwrap_or(Value::Nil))
75                .unwrap_or_default(),
76            deploy: lua
77                .from_value(globals.get("deploy").unwrap_or(Value::Nil))
78                .unwrap_or_default(),
79        };
80
81        Ok(rockspec)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn parse_partial_rockspec() {
91        let partial_rockspec = r#"
92            package = "my-package"
93        "#;
94
95        PartialLuaRockspec::new(partial_rockspec).unwrap();
96
97        // Whether the partial rockspec format can still support entire rockspecs
98        let full_rockspec = r#"
99            rockspec_format = "3.0"
100            package = "my-package"
101
102            description = {
103                summary = "A summary",
104                detailed = "A detailed description",
105                license = "MIT",
106                homepage = "https://example.com",
107                issues_url = "https://example.com/issues",
108                maintainer = "John Doe",
109                labels = {"label1", "label2"},
110            }
111
112            supported_platforms = {"linux", "!windows"}
113
114            dependencies = {
115                "lua 5.1",
116                "foo 1.0",
117                "bar >=2.0",
118            }
119
120            build_dependencies = {
121                "baz 1.0",
122            }
123
124            external_dependencies = {
125                foo = { header = "foo.h" },
126                bar = { library = "libbar.so" },
127            }
128
129            test_dependencies = {
130                "busted 1.0",
131            }
132
133            test = {
134                type = "command",
135                script = "test.lua",
136                flags = {"foo", "bar"},
137            }
138
139            build = {
140                type = "builtin",
141            }
142        "#;
143
144        let rockspec = PartialLuaRockspec::new(full_rockspec).unwrap();
145
146        // No need to verify if the fields were parsed correctly, but worth checking if they were
147        // parsed at all.
148
149        assert!(rockspec.rockspec_format.is_some());
150        assert!(rockspec.package.is_some());
151        assert!(rockspec.description.is_some());
152        assert!(rockspec.supported_platforms.is_some());
153        assert!(rockspec.dependencies.is_some());
154        assert!(rockspec.build_dependencies.is_some());
155        assert!(rockspec.external_dependencies.is_some());
156        assert!(rockspec.test_dependencies.is_some());
157        assert!(rockspec.build.is_some());
158        assert!(rockspec.test.is_some());
159
160        // We don't allow version and source in extra.rockspec
161        let partial_rockspec = r#"
162            version = "2.0.0"
163        "#;
164
165        PartialLuaRockspec::new(partial_rockspec).unwrap_err();
166
167        let partial_rockspec = r#"
168            source = {
169                url = "https://example.com",
170            }
171        "#;
172
173        PartialLuaRockspec::new(partial_rockspec).unwrap_err();
174    }
175}