lux_lib/lua_rockspec/
dependency.rs1use 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#[derive(Debug, PartialEq, Clone, Deserialize, Default)]
13#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
14pub struct ExternalDependencySpec {
15 pub header: Option<PathBuf>,
17 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}