openfga_dsl_parser/json/
mod.rs

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        // loop through types, adding to vecs
24        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}