assay/install/
manifest.rs1use std::collections::HashMap;
14use std::path::Path;
15
16use mlua::{Lua, LuaOptions, LuaSerdeExt, StdLib, Value};
17use serde::Deserialize;
18use thiserror::Error;
19
20#[derive(Debug, Error)]
21pub enum ManifestError {
22 #[error("manifest {path}: lua error: {source}")]
23 Lua {
24 path: String,
25 #[source]
26 source: mlua::Error,
27 },
28
29 #[error("manifest {path}: top-level value is {got}, expected a table")]
30 NotATable { path: String, got: String },
31
32 #[error("manifest {path}: failed to decode: {source}")]
33 Decode {
34 path: String,
35 #[source]
36 source: mlua::Error,
37 },
38}
39
40#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
42#[serde(deny_unknown_fields)]
43pub struct Manifest {
44 pub assay: Option<String>,
47
48 #[serde(default)]
50 pub extensions: Vec<Extension>,
51
52 #[serde(default)]
54 pub libs: Vec<Lib>,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
60#[serde(deny_unknown_fields)]
61pub struct Extension {
62 pub name: String,
63 pub version: String,
64 pub sha256: HashMap<String, String>,
66 #[serde(default)]
69 pub source: Option<String>,
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
75#[serde(deny_unknown_fields)]
76pub struct Lib {
77 pub name: String,
78 pub version: String,
79 pub sha256: String,
80 #[serde(default)]
81 pub source: Option<String>,
82}
83
84pub fn parse(source: &str, path: impl AsRef<Path>) -> Result<Manifest, ManifestError> {
89 let path_str = path.as_ref().display().to_string();
90
91 let lua = sandboxed_vm().map_err(|e| ManifestError::Lua {
92 path: path_str.clone(),
93 source: e,
94 })?;
95
96 let chunk = lua.load(source).set_name(path_str.clone());
97 let value: Value = chunk.eval().map_err(|e| ManifestError::Lua {
98 path: path_str.clone(),
99 source: e,
100 })?;
101
102 if !matches!(value, Value::Table(_)) {
103 return Err(ManifestError::NotATable {
104 path: path_str,
105 got: value.type_name().to_string(),
106 });
107 }
108
109 lua.from_value::<Manifest>(value)
110 .map_err(|e| ManifestError::Decode {
111 path: path_str,
112 source: e,
113 })
114}
115
116fn sandboxed_vm() -> mlua::Result<Lua> {
117 let libs = StdLib::STRING | StdLib::TABLE | StdLib::MATH;
118 let lua = Lua::new_with(libs, LuaOptions::default())?;
119
120 let globals = lua.globals();
123 for name in ["load", "loadfile", "loadstring", "dofile", "require"] {
124 globals.set(name, mlua::Nil)?;
125 }
126
127 Ok(lua)
128}