1use crate::ast::*;
2use serde_json::{json, Map, Value};
3
4pub struct JsonTransformer<'d> {
5 doc: &'d Document,
6}
7
8impl<'d> JsonTransformer<'d> {
9 pub fn new(doc: &'d Document) -> Self {
10 Self { doc }
11 }
12
13 pub fn serialize(self) -> String {
14 let map = self.to_json_map();
15 json!(map).to_string()
16 }
17
18 fn to_json_map(self) -> Map<String, Value> {
19 let mut root = Map::new();
20
21 let mut types: Vec<Map<String, Value>> = Vec::new();
22
23 for ty in &self.doc.types {
25 let type_obj = serialize_type_obj(ty);
26 types.push(type_obj);
27 }
28
29 root.insert("type_definitions".into(), types.into());
30 root
31 }
32}
33
34fn serialize_type_obj(ty: &Type) -> Map<String, Value> {
35 let mut type_obj = Map::new();
36 type_obj.insert("type".into(), ty.kind.clone().into());
37
38 let relations_obj = serialize_relations_obj(&ty.relations);
39 type_obj.insert("relations".into(), relations_obj.into());
40 type_obj
41}
42
43fn serialize_relations_obj(relations: &[Relation]) -> Map<String, Value> {
44 let mut rel_obj = Map::new();
45 for rel in relations {
46 if rel.aliases.len() <= 1 {
47 let mut rel_content = Map::new();
48 for alias in &rel.aliases {
49 let (key, obj) = serialize_alias_obj(&alias);
50 rel_content.insert(key, obj);
51 }
52 rel_obj.insert(rel.kind.clone().into(), json!(rel_content));
53 } else {
54 let mut children = Vec::new();
55 for alias in &rel.aliases {
56 let (key, obj) = serialize_alias_obj(&alias);
57 let out = json!({ key: obj });
58 children.push(out);
59 }
60 let obj = json!({
61 "union": {
62 "child": children
63 }
64 });
65 rel_obj.insert(rel.kind.clone().into(), obj);
66 }
67 }
68 rel_obj
69}
70
71fn serialize_alias_obj(alias: &Alias) -> (String, Value) {
72 match &alias.kind {
73 AliasKind::This => ("this".into(), json!({})),
74 AliasKind::Negative(not) => (
75 "difference".into(),
76 json!({
77 "base": {
78 "this": {}
79 },
80 "subtract": {
81 "computedUserset": {
82 "object": "",
83 "relation": not
84 }
85 }
86 }),
87 ),
88 AliasKind::Named(name) => match &alias.parent {
89 Some(parent) => (
90 "tupleToUserset".into(),
91 json!({
92 "tupleset": {
93 "object": "",
94 "relation": parent
95 },
96 "computedUserset": {
97 "object": "",
98 "relation": name
99 }
100 }),
101 ),
102 None => (
103 "computedUserset".into(),
104 json!({
105 "object": "",
106 "relation": name
107 }),
108 ),
109 },
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn basic_single_type() {
119 let i = Document {
120 types: vec![Type {
121 kind: String::from("foo"),
122 relations: Vec::new(),
123 }],
124 };
125 let exp = json!({
126 "type_definitions": [
127 {
128 "type": "foo",
129 "relations": {}
130 }
131 ]
132 });
133 let res = JsonTransformer::new(&i).to_json_map();
134 assert_eq!(json!(res), exp);
135 }
136
137 #[test]
138 fn basic_self_relation() {
139 let i = vec![Relation {
140 kind: "foo".into(),
141 aliases: vec![Alias {
142 kind: AliasKind::This,
143 parent: None,
144 }],
145 }];
146 let exp = json!({
147 "foo": {
148 "this": {}
149 }
150 });
151 let res = serialize_relations_obj(&i);
152 assert_eq!(exp, json!(res));
153 }
154
155 #[test]
156 fn basic_single_alias_relation() {
157 let i = vec![Relation {
158 kind: "foo".into(),
159 aliases: vec![Alias {
160 kind: AliasKind::Named("bar".into()),
161 parent: None,
162 }],
163 }];
164 let exp = json!({
165 "foo": {
166 "computedUserset": {
167 "object": "",
168 "relation": "bar"
169 }
170 }
171 });
172 let res = serialize_relations_obj(&i);
173 assert_eq!(exp, json!(res));
174 }
175
176 #[test]
177 fn self_plus_single_alias_relation() {
178 let i = vec![Relation {
179 kind: "foo".into(),
180 aliases: vec![
181 Alias {
182 kind: AliasKind::This,
183 parent: None,
184 },
185 Alias {
186 kind: AliasKind::Named("bar".into()),
187 parent: None,
188 },
189 ],
190 }];
191 let exp = json!({
192 "foo": {
193 "union": {
194 "child": [
195 {
196 "this": {}
197 },
198 {
199 "computedUserset": {
200 "object": "",
201 "relation": "bar"
202 }
203 }
204 ]
205 }
206 }
207 });
208 let res = serialize_relations_obj(&i);
209 assert_eq!(exp, json!(res));
210 }
211
212 #[test]
213 fn alias_relation_with_parent() {
214 let i = vec![Relation {
215 kind: "foo".into(),
216 aliases: vec![Alias {
217 kind: AliasKind::Named("bar".into()),
218 parent: Some("parent".into()),
219 }],
220 }];
221 let exp = json!({
222 "foo": {
223 "tupleToUserset": {
224 "tupleset": {
225 "object": "",
226 "relation": "parent",
227 },
228 "computedUserset": {
229 "object": "",
230 "relation": "bar",
231 }
232 }
233 }
234 });
235 let res = serialize_relations_obj(&i);
236 assert_eq!(exp, json!(res));
237 }
238
239 #[test]
240 fn big_one() {
241 let i = Document {
242 types: vec![
243 Type {
244 kind: "domain".into(),
245 relations: vec![Relation {
246 kind: "member".into(),
247 aliases: vec![Alias {
248 kind: AliasKind::This,
249 parent: None,
250 }],
251 }],
252 },
253 Type {
254 kind: "folder".into(),
255 relations: vec![
256 Relation {
257 kind: "can_share".into(),
258 aliases: vec![Alias {
259 kind: AliasKind::Named("writer".into()),
260 parent: None,
261 }],
262 },
263 Relation {
264 kind: "owner".into(),
265 aliases: vec![
266 Alias {
267 kind: AliasKind::This,
268 parent: None,
269 },
270 Alias {
271 kind: AliasKind::Named("owner".into()),
272 parent: Some("parent_folder".into()),
273 },
274 ],
275 },
276 ],
277 },
278 ],
279 };
280
281 let exp = json!({
282 "type_definitions": [{
283 "type": "domain",
284 "relations": {
285 "member": {
286 "this": {}
287 }
288 }
289 },
290 {
291 "type": "folder",
292 "relations": {
293 "can_share": {
294 "computedUserset": {
295 "object": "",
296 "relation": "writer"
297 }
298 },
299 "owner": {
300 "union": {
301 "child": [{
302 "this": {}
303 },
304 {
305 "tupleToUserset": {
306 "tupleset": {
307 "object": "",
308 "relation": "parent_folder"
309 },
310 "computedUserset": {
311 "object": "",
312 "relation": "owner"
313 }
314 }
315 }
316 ]
317 }
318 }
319 }
320 }
321 ]
322 });
323
324 let res = JsonTransformer::new(&i).to_json_map();
325 assert_eq!(exp, json!(res));
326 }
327}