Skip to main content

lux_lib/lua_rockspec/
dependency.rs

1use std::{collections::HashMap, convert::Infallible, path::PathBuf};
2
3use path_slash::PathBufExt;
4use serde::Deserialize;
5
6use super::{
7    DisplayAsLuaKV, DisplayLuaKV, DisplayLuaValue, PartialOverride, PerPlatform,
8    PlatformOverridable,
9};
10
11/// Can be defined in a [platform-agnostic](https://github.com/luarocks/luarocks/wiki/platform-agnostic-external-dependencies) manner
12#[derive(Debug, PartialEq, Clone, Deserialize, Default)]
13#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
14pub struct ExternalDependencySpec {
15    /// A header file, e.g. "foo.h"
16    pub header: Option<PathBuf>,
17    /// A library file, e.g. "libfoo.so"
18    pub library: Option<PathBuf>,
19}
20
21impl PartialOverride for ExternalDependencySpec {
22    type Err = Infallible;
23
24    fn apply_overrides(&self, override_val: &Self) -> Result<Self, Self::Err> {
25        Ok(Self {
26            header: override_val.header.clone().or(self.header.clone()),
27            library: override_val.library.clone().or(self.header.clone()),
28        })
29    }
30}
31
32impl PartialOverride for HashMap<String, ExternalDependencySpec> {
33    type Err = Infallible;
34
35    fn apply_overrides(&self, override_map: &Self) -> Result<Self, Self::Err> {
36        let mut result = Self::new();
37        for (key, value) in self {
38            result.insert(
39                key.clone(),
40                override_map
41                    .get(key)
42                    .map(|override_val| value.apply_overrides(override_val).expect("infallible"))
43                    .unwrap_or(value.clone()),
44            );
45        }
46        for (key, value) in override_map {
47            if !result.contains_key(key) {
48                result.insert(key.clone(), value.clone());
49            }
50        }
51        Ok(result)
52    }
53}
54
55impl PlatformOverridable for HashMap<String, ExternalDependencySpec> {
56    type Err = Infallible;
57
58    fn on_nil<T>() -> Result<super::PerPlatform<T>, <Self as PlatformOverridable>::Err>
59    where
60        T: PlatformOverridable,
61        T: Default,
62    {
63        Ok(PerPlatform::default())
64    }
65}
66
67pub(crate) struct ExternalDependencies<'a>(pub(crate) &'a HashMap<String, ExternalDependencySpec>);
68
69impl DisplayAsLuaKV for ExternalDependencies<'_> {
70    fn display_lua(&self) -> DisplayLuaKV {
71        DisplayLuaKV {
72            key: "external_dependencies".to_string(),
73            value: DisplayLuaValue::Table(
74                self.0
75                    .iter()
76                    .map(|(key, value)| {
77                        let mut value_entries = Vec::new();
78                        if let Some(path) = &value.header {
79                            value_entries.push(DisplayLuaKV {
80                                key: "header".to_string(),
81                                value: DisplayLuaValue::String(path.to_slash_lossy().to_string()),
82                            });
83                        }
84                        if let Some(path) = &value.library {
85                            value_entries.push(DisplayLuaKV {
86                                key: "library".to_string(),
87                                value: DisplayLuaValue::String(path.to_slash_lossy().to_string()),
88                            });
89                        }
90                        DisplayLuaKV {
91                            key: key.clone(),
92                            value: DisplayLuaValue::Table(value_entries),
93                        }
94                    })
95                    .collect(),
96            ),
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use ottavino::{Closure, Executor, Fuel, Lua, Value};
104    use ottavino_util::serde::from_value;
105
106    use super::*;
107
108    fn eval_lua<T: serde::de::DeserializeOwned>(code: &str) -> Result<T, ottavino::ExternError> {
109        Lua::core().try_enter(|ctx| {
110            let closure = Closure::load(ctx, None, code.as_bytes())?;
111            let executor = Executor::start(ctx, closure.into(), ());
112            executor.step(ctx, &mut Fuel::with(i32::MAX))?;
113            from_value(executor.take_result::<Value<'_>>(ctx)??).map_err(ottavino::Error::from)
114        })
115    }
116
117    #[test]
118    fn test_external_dependency_spec_from_lua() {
119        let lua_code = r#"
120            return {
121                foo = { header = "foo.h", library = "libfoo.so" },
122                bar = { header = "bar.h" },
123                baz = { library = "libbaz.so" },
124            }
125        "#;
126        let deps: HashMap<String, ExternalDependencySpec> = eval_lua(lua_code).unwrap();
127        assert_eq!(deps.len(), 3);
128        assert_eq!(
129            deps["foo"].header.as_ref().unwrap().to_slash_lossy(),
130            "foo.h"
131        );
132        assert_eq!(
133            deps["foo"].library.as_ref().unwrap().to_slash_lossy(),
134            "libfoo.so"
135        );
136
137        assert_eq!(
138            deps["bar"].header.as_ref().unwrap().to_slash_lossy(),
139            "bar.h"
140        );
141        assert!(deps["bar"].library.is_none());
142
143        assert!(deps["baz"].header.is_none());
144        assert_eq!(
145            deps["baz"].library.as_ref().unwrap().to_slash_lossy(),
146            "libbaz.so"
147        );
148    }
149}