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