lucet_module_data/
bindings.rs1use failure::{format_err, Error};
2use serde_json::{self, Map, Value};
3use std::collections::{hash_map::Entry, HashMap};
4use std::fs;
5use std::path::Path;
6
7#[derive(Debug, Clone)]
8pub struct Bindings {
9 bindings: HashMap<String, HashMap<String, String>>,
10}
11
12impl Bindings {
13 pub fn new(bindings: HashMap<String, HashMap<String, String>>) -> Bindings {
14 Self { bindings: bindings }
15 }
16
17 pub fn env(env: HashMap<String, String>) -> Bindings {
18 let mut bindings = HashMap::new();
19 bindings.insert("env".to_owned(), env);
20 Self::new(bindings)
21 }
22
23 pub fn empty() -> Bindings {
24 Self::new(HashMap::new())
25 }
26
27 pub fn from_json(v: &Value) -> Result<Bindings, Error> {
28 match v.as_object() {
29 Some(modules) => Self::parse_modules_json_obj(modules),
30 None => Err(format_err!("top level json expected to be object"))?,
31 }
32 }
33
34 pub fn from_str(s: &str) -> Result<Bindings, Error> {
35 let top: Value = serde_json::from_str(s)?;
36 Ok(Self::from_json(&top)?)
37 }
38
39 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Bindings, Error> {
40 let contents = fs::read_to_string(path.as_ref())?;
41 Ok(Self::from_str(&contents)?)
42 }
43
44 pub fn extend(&mut self, other: &Bindings) -> Result<(), Error> {
45 for (modname, othermodbindings) in other.bindings.iter() {
46 match self.bindings.entry(modname.clone()) {
47 Entry::Occupied(mut e) => {
48 let existing = e.get_mut();
49 for (bindname, binding) in othermodbindings {
50 match existing.entry(bindname.clone()) {
51 Entry::Vacant(e) => {
52 e.insert(binding.clone());
53 }
54 Entry::Occupied(e) => {
55 if binding != e.get() {
56 Err(format_err!(
57 "cannot re-bind {} from {} to {}",
58 e.key(),
59 binding,
60 e.get()
61 ))?;
62 }
63 }
64 }
65 }
66 }
67 Entry::Vacant(e) => {
68 e.insert(othermodbindings.clone());
69 }
70 }
71 }
72 Ok(())
73 }
74
75 pub fn translate(&self, module: &str, symbol: &str) -> Result<String, Error> {
76 match self.bindings.get(module) {
77 Some(m) => match m.get(symbol) {
78 Some(s) => Ok(s.clone()),
79 None => Err(format_err!("Unknown symbol `{}::{}`", module, symbol)),
80 },
81 None => Err(format_err!(
82 "Unknown module for symbol `{}::{}`",
83 module,
84 symbol
85 )),
86 }
87 }
88
89 fn parse_modules_json_obj(m: &Map<String, Value>) -> Result<Self, Error> {
90 let mut res = HashMap::new();
91 for (modulename, values) in m {
92 match values.as_object() {
93 Some(methods) => {
94 let methodmap = Self::parse_methods_json_obj(methods)?;
95 res.insert(modulename.to_owned(), methodmap);
96 }
97 None => Err(format_err!(""))?,
98 }
99 }
100 Ok(Self::new(res))
101 }
102
103 fn parse_methods_json_obj(m: &Map<String, Value>) -> Result<HashMap<String, String>, Error> {
104 let mut res = HashMap::new();
105 for (method, i) in m {
106 match i.as_str() {
107 Some(importbinding) => {
108 res.insert(method.to_owned(), importbinding.to_owned());
109 }
110 None => Err(format_err!(""))?,
111 }
112 }
113 Ok(res)
114 }
115
116 pub fn to_string(&self) -> Result<String, Error> {
117 let s = serde_json::to_string(&self.to_json())?;
118 Ok(s)
119 }
120
121 pub fn to_json(&self) -> Value {
122 Value::from(self.serialize_modules_json_obj())
123 }
124
125 fn serialize_modules_json_obj(&self) -> Map<String, Value> {
126 let mut m = Map::new();
127 for (modulename, values) in self.bindings.iter() {
128 m.insert(
129 modulename.to_owned(),
130 Value::from(Self::serialize_methods_json_obj(values)),
131 );
132 }
133 m
134 }
135
136 fn serialize_methods_json_obj(methods: &HashMap<String, String>) -> Map<String, Value> {
137 let mut m = Map::new();
138 for (methodname, symbol) in methods.iter() {
139 m.insert(methodname.to_owned(), Value::from(symbol.to_owned()));
140 }
141 m
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 fn test_file(f: &str) -> PathBuf {
148 PathBuf::from(format!("tests/bindings/{}", f))
149 }
150
151 use super::Bindings;
152 use std::collections::HashMap;
153 use std::path::PathBuf;
154
155 #[test]
156 fn explicit() {
157 let mut explicit_map = HashMap::new();
158 explicit_map.insert(String::from("hello"), String::from("goodbye"));
159 let map = Bindings::env(explicit_map);
160
161 let result = map.translate("env", "hello").unwrap();
162 assert!(result == "goodbye");
163
164 let result = map.translate("env", "nonexistent");
165 if let Ok(_) = result {
166 assert!(
167 false,
168 "explicit import map returned value for non-existent symbol"
169 )
170 }
171 }
172
173 #[test]
174 fn explicit_from_nonexistent_file() {
175 let fail_map = Bindings::from_file(&test_file("nonexistent_bindings.json"));
176 assert!(
177 fail_map.is_err(),
178 "ImportMap::explicit_from_file did not fail on a non-existent file"
179 );
180 }
181
182 #[test]
183 fn explicit_from_garbage_file() {
184 let fail_map = Bindings::from_file(&test_file("garbage.json"));
185 assert!(
186 fail_map.is_err(),
187 "ImportMap::explicit_from_file did not fail on a garbage file"
188 );
189 }
190
191 #[test]
192 fn explicit_from_file() {
193 let map = Bindings::from_file(&test_file("bindings_test.json"))
194 .expect("load valid bindings from file");
195 let result = map.translate("env", "hello").expect("hello has a binding");
196 assert!(result == "json is cool");
197
198 assert!(
199 map.translate("env", "nonexistent").is_err(),
200 "bindings from file returned value for non-existent symbol"
201 );
202 }
203}