reproto_derive/
lib.rs

1extern crate inflector;
2extern crate linked_hash_map;
3extern crate reproto_ast as ast;
4extern crate reproto_core as core;
5extern crate serde;
6extern crate serde_json;
7extern crate serde_yaml;
8
9mod format;
10mod json;
11mod sir;
12mod utils;
13mod yaml;
14
15pub use self::format::Format;
16pub use self::json::Json;
17pub use self::yaml::Yaml;
18use ast::{Attribute, AttributeItem, Decl, Field, InterfaceBody, Item, Name, SubType, TupleBody,
19          Type, TypeBody, TypeMember, Value};
20use core::errors::Result;
21use core::{Loc, RpPackage, Source, Span, DEFAULT_TAG};
22use inflector::cases::pascalcase::to_pascal_case;
23use inflector::cases::snakecase::to_snake_case;
24use linked_hash_map::LinkedHashMap;
25use sir::{FieldSir, Sir, SubTypeSir};
26use std::borrow::Cow;
27use std::cmp;
28use std::collections::{HashMap, HashSet};
29use std::fmt;
30use std::hash;
31use std::ops;
32use std::rc::Rc;
33
34#[derive(Debug)]
35pub struct Derive {
36    root_name: String,
37    format: Box<format::Format>,
38    package_prefix: Option<RpPackage>,
39}
40
41#[derive(Debug, Clone)]
42struct Context {
43    path: Vec<String>,
44    package_prefix: Option<Rc<RpPackage>>,
45}
46
47impl Context {
48    /// Extract the 'local name' (last component).
49    fn ident(&self) -> Result<&str> {
50        if let Some(ident) = self.path.iter().last() {
51            Ok(ident.as_str())
52        } else {
53            Err(format!("No last component in name").into())
54        }
55    }
56
57    /// Join this context with another path component.
58    fn join(&self, name: String) -> Context {
59        let mut path = self.path.iter().cloned().collect::<Vec<_>>();
60        path.push(name);
61
62        Context {
63            path: path,
64            package_prefix: self.package_prefix.clone(),
65        }
66    }
67
68    /// Constructs an ``NAme`.
69    fn name(&self) -> Name<'static> {
70        Name::Absolute {
71            prefix: None,
72            parts: self.path
73                .clone()
74                .into_iter()
75                .map(|p| Loc::new(Cow::from(p), Span::empty()))
76                .collect(),
77        }
78    }
79}
80
81impl Derive {
82    pub fn new(
83        root_name: String,
84        format: Box<format::Format>,
85        package_prefix: Option<RpPackage>,
86    ) -> Derive {
87        Derive {
88            root_name: root_name,
89            format: format,
90            package_prefix: package_prefix,
91        }
92    }
93}
94
95type TypesCache<'input> = HashMap<Sir, Name<'input>>;
96
97/// An opaque data structure, well all instances are equal but can contain different data.
98#[derive(Debug, Clone)]
99pub struct Opaque<T> {
100    content: T,
101}
102
103impl<T> Opaque<T> {
104    pub fn new(content: T) -> Self {
105        Self { content: content }
106    }
107}
108
109impl<T> cmp::PartialEq for Opaque<T> {
110    fn eq(&self, _: &Self) -> bool {
111        true
112    }
113}
114
115impl<T> cmp::Eq for Opaque<T> {}
116
117impl<T> hash::Hash for Opaque<T> {
118    fn hash<H: hash::Hasher>(&self, _state: &mut H) {}
119}
120
121impl<T> ops::Deref for Opaque<T> {
122    type Target = T;
123
124    fn deref(&self) -> &Self::Target {
125        &self.content
126    }
127}
128
129impl<T> ops::DerefMut for Opaque<T> {
130    fn deref_mut(&mut self) -> &mut Self::Target {
131        &mut self.content
132    }
133}
134
135struct FieldInit<'a, 'input: 'a> {
136    span: &'a Span,
137    ctx: Context,
138    types: &'a mut TypesCache<'input>,
139}
140
141impl<'a, 'input: 'a> FieldInit<'a, 'input> {
142    fn new(
143        span: &'a Span,
144        ctx: Context,
145        types: &'a mut TypesCache<'input>,
146    ) -> FieldInit<'a, 'input> {
147        FieldInit { span, ctx, types }
148    }
149
150    fn init(
151        self,
152        original_name: String,
153        sir: &FieldSir,
154        members: &mut Vec<TypeMember<'input>>,
155    ) -> Result<Item<'input, Field<'input>>> {
156        let mut comment = Vec::new();
157
158        let name = to_snake_case(&original_name);
159
160        let ty = match sir.field {
161            Sir::Boolean => Type::Boolean,
162            Sir::Float => Type::Float,
163            Sir::Double => Type::Double,
164            Sir::I64(ref examples) => {
165                format_comment(&mut comment, examples)?;
166                Type::Signed { size: 64 }
167            }
168            Sir::U64(ref examples) => {
169                format_comment(&mut comment, examples)?;
170                Type::Unsigned { size: 64 }
171            }
172            Sir::String(ref examples) => {
173                format_comment(&mut comment, examples)?;
174                Type::String
175            }
176            Sir::DateTime(ref examples) => {
177                format_comment(&mut comment, examples)?;
178                Type::DateTime
179            }
180            Sir::Any => Type::Any,
181            Sir::Array(ref inner) => {
182                let field = FieldSir {
183                    optional: false,
184                    field: (**inner).clone(),
185                };
186
187                let f = FieldInit::new(&self.span, self.ctx.clone(), self.types).init(
188                    name.clone(),
189                    &field,
190                    members,
191                )?;
192
193                Type::Array {
194                    inner: Box::new(f.item.ty.clone()),
195                }
196            }
197            ref sir => {
198                let ctx = self.ctx.join(to_pascal_case(&name));
199
200                let name = if let Some(name) = self.types.get(sir).cloned() {
201                    name
202                } else {
203                    let name = ctx.name();
204
205                    self.types.insert(sir.clone(), name.clone());
206
207                    let decl = DeclDeriver {
208                        span: &self.span,
209                        ctx: ctx.clone(),
210                        types: self.types,
211                    }.derive(sir)?;
212
213                    members.push(TypeMember::InnerDecl(decl));
214
215                    name
216                };
217
218                Type::Name {
219                    name: Loc::new(name, Span::empty()),
220                }
221            }
222        };
223
224        let field_as = if name != original_name {
225            Some(original_name)
226        } else {
227            None
228        };
229
230        let field = Field {
231            required: !sir.optional,
232            name: name.clone().into(),
233            ty: Loc::new(ty.into(), self.span.clone()),
234            field_as: field_as,
235            endl: true,
236        };
237
238        // field referencing inner declaration
239        return Ok(Item {
240            comment: comment,
241            attributes: Vec::new(),
242            item: Loc::new(field, self.span.clone()),
243        });
244
245        /// Format comments and attach examples.
246        fn format_comment<T>(out: &mut Vec<Cow<'static, str>>, examples: &[T]) -> Result<()>
247        where
248            T: serde::Serialize + fmt::Debug,
249        {
250            out.push(format!("## Examples").into());
251            out.push("".to_string().into());
252
253            out.push(format!("```json").into());
254
255            let mut seen = HashSet::new();
256
257            for example in examples.iter() {
258                let string = serde_json::to_string_pretty(example)
259                    .map_err(|e| format!("Failed to convert to JSON: {}: {:?}", e, example))?;
260
261                if !seen.contains(&string) {
262                    seen.insert(string.clone());
263                    out.push(string.into());
264                }
265            }
266
267            out.push(format!("```").into());
268
269            Ok(())
270        }
271    }
272}
273
274struct DeclDeriver<'a, 'input: 'a> {
275    span: &'a Span,
276    ctx: Context,
277    types: &'a mut TypesCache<'input>,
278}
279
280impl<'a, 'input: 'a> DeclDeriver<'a, 'input> {
281    /// Derive a declaration from the given JSON.
282    fn derive<'s>(self, sir: &'s Sir) -> Result<Decl<'input>> {
283        let decl = match *sir {
284            Sir::Tuple(ref array) => {
285                let tuple = TupleRefiner {
286                    span: &self.span,
287                    ctx: self.ctx,
288                    types: self.types,
289                }.derive(array)?;
290
291                Decl::Tuple(tuple)
292            }
293            Sir::Object(ref object) => {
294                let type_ = TypeRefiner {
295                    span: &self.span,
296                    ctx: self.ctx,
297                    types: self.types,
298                }.derive(object)?;
299
300                Decl::Type(type_)
301            }
302            Sir::Interface(ref type_field, ref sub_types) => {
303                let interface = InterfaceRefiner {
304                    span: &self.span,
305                    ctx: self.ctx,
306                    types: self.types,
307                }.derive(type_field, sub_types)?;
308
309                Decl::Interface(interface)
310            }
311            // For arrays, only generate the inner type.
312            Sir::Array(ref inner) => self.derive(inner)?,
313            ref value => return Err(format!("Unexpected JSON value: {:?}", value).into()),
314        };
315
316        Ok(decl)
317    }
318}
319
320struct TypeRefiner<'a, 'input: 'a> {
321    span: &'a Span,
322    ctx: Context,
323    types: &'a mut TypesCache<'input>,
324}
325
326impl<'a, 'input: 'a> TypeRefiner<'a, 'input> {
327    /// Derive an struct body from the given input array.
328    fn derive(
329        &mut self,
330        object: &LinkedHashMap<String, FieldSir>,
331    ) -> Result<Item<'input, TypeBody<'input>>> {
332        let mut body = TypeBody {
333            name: Loc::new(self.ctx.ident()?.to_string().into(), Span::empty()),
334            members: Vec::new(),
335        };
336
337        self.init(&mut body, object)?;
338
339        Ok(Item {
340            comment: Vec::new(),
341            attributes: Vec::new(),
342            item: Loc::new(body, self.span.clone()),
343        })
344    }
345
346    fn init(
347        &mut self,
348        base: &mut TypeBody<'input>,
349        object: &LinkedHashMap<String, FieldSir>,
350    ) -> Result<()> {
351        for (name, added) in object.iter() {
352            let field = FieldInit::new(&self.span, self.ctx.clone(), self.types).init(
353                name.to_string(),
354                added,
355                &mut base.members,
356            )?;
357
358            base.members.push(TypeMember::Field(field));
359        }
360
361        Ok(())
362    }
363}
364
365struct SubTypeRefiner<'a, 'input: 'a> {
366    span: &'a Span,
367    ctx: Context,
368    types: &'a mut TypesCache<'input>,
369}
370
371impl<'a, 'input: 'a> SubTypeRefiner<'a, 'input> {
372    /// Derive an struct body from the given input array.
373    fn derive(&mut self, sub_type: &SubTypeSir) -> Result<Item<'input, SubType<'input>>> {
374        let mut body = SubType {
375            name: Loc::new(self.ctx.ident()?.to_string().into(), self.span.clone()),
376            members: vec![],
377            alias: None,
378        };
379
380        self.init(&mut body, sub_type)?;
381
382        Ok(Item {
383            comment: Vec::new(),
384            attributes: Vec::new(),
385            item: Loc::new(body, self.span.clone()),
386        })
387    }
388
389    fn init(&mut self, base: &mut SubType<'input>, sub_type: &SubTypeSir) -> Result<()> {
390        if sub_type.name.as_str() != base.name.as_ref() {
391            base.alias = Some(Loc::new(
392                Value::String(sub_type.name.to_string()),
393                self.span.clone(),
394            ));
395        }
396
397        for (field_name, field_value) in &sub_type.structure {
398            let field = FieldInit::new(&self.span, self.ctx.clone(), self.types).init(
399                field_name.to_string(),
400                field_value,
401                &mut base.members,
402            )?;
403
404            base.members.push(TypeMember::Field(field));
405        }
406
407        Ok(())
408    }
409}
410
411struct InterfaceRefiner<'a, 'input: 'a> {
412    span: &'a Span,
413    ctx: Context,
414    types: &'a mut TypesCache<'input>,
415}
416
417impl<'a, 'input: 'a> InterfaceRefiner<'a, 'input> {
418    /// Derive an struct body from the given input array.
419    fn derive(
420        &mut self,
421        tag: &str,
422        sub_types: &[SubTypeSir],
423    ) -> Result<Item<'input, InterfaceBody<'input>>> {
424        let mut attributes = Vec::new();
425
426        if tag != DEFAULT_TAG {
427            let name = Loc::new("type_info".into(), self.span.clone());
428            let mut values = Vec::new();
429
430            values.push(AttributeItem::NameValue {
431                name: Loc::new("type".into(), self.span.clone()),
432                value: Loc::new(Value::String("type".to_string()), self.span.clone()),
433            });
434
435            let a = Attribute::List(name, values);
436
437            attributes.push(Loc::new(a, self.span.clone()));
438        };
439
440        let mut body = InterfaceBody {
441            name: Loc::new(self.ctx.ident()?.to_string().into(), Span::empty()),
442            members: Vec::new(),
443            sub_types: Vec::new(),
444        };
445
446        self.init(&mut body, sub_types)?;
447
448        Ok(Item {
449            comment: Vec::new(),
450            attributes: attributes,
451            item: Loc::new(body, self.span.clone()),
452        })
453    }
454
455    fn init(&mut self, base: &mut InterfaceBody<'input>, sub_types: &[SubTypeSir]) -> Result<()> {
456        for st in sub_types {
457            let ident = to_pascal_case(&st.name);
458            let ctx = self.ctx.join(ident.clone());
459
460            let sub_type = SubTypeRefiner {
461                span: self.span,
462                ctx: ctx,
463                types: self.types,
464            }.derive(st)?;
465
466            base.sub_types.push(sub_type);
467        }
468
469        Ok(())
470    }
471}
472
473struct TupleRefiner<'a, 'input: 'a> {
474    span: &'a Span,
475    ctx: Context,
476    types: &'a mut TypesCache<'input>,
477}
478
479impl<'a, 'input: 'a> TupleRefiner<'a, 'input> {
480    /// Derive an tuple body from the given input array.
481    fn derive(&mut self, array: &[FieldSir]) -> Result<Item<'input, TupleBody<'input>>> {
482        let mut body = TupleBody {
483            name: Loc::new(self.ctx.ident()?.to_string().into(), Span::empty()),
484            members: Vec::new(),
485        };
486
487        self.init(&mut body, array)?;
488
489        Ok(Item {
490            comment: Vec::new(),
491            attributes: Vec::new(),
492            item: Loc::new(body, self.span.clone()),
493        })
494    }
495
496    fn init(&mut self, base: &mut TupleBody<'input>, array: &[FieldSir]) -> Result<()> {
497        for (index, added) in array.iter().enumerate() {
498            let field = FieldInit::new(&self.span, self.ctx.clone(), self.types).init(
499                format!("field_{}", index),
500                added,
501                &mut base.members,
502            )?;
503
504            base.members.push(TypeMember::Field(field));
505        }
506
507        Ok(())
508    }
509}
510
511/// Derive a declaration from the given input.
512pub fn derive<'input>(derive: Derive, object: &'input Source) -> Result<Decl<'input>> {
513    let Derive {
514        root_name,
515        format,
516        package_prefix,
517    } = derive;
518
519    let sir = format.decode(object)?;
520
521    let span = Span::empty();
522
523    let mut types = HashMap::new();
524
525    let ctx = Context {
526        path: vec![root_name],
527        package_prefix: package_prefix.as_ref().map(|p| Rc::new(p.clone())),
528    };
529
530    let decl = DeclDeriver {
531        span: &span,
532        ctx: ctx,
533        types: &mut types,
534    }.derive(&sir)?;
535
536    Ok(decl)
537}
538
539#[cfg(test)]
540mod tests {
541    use super::{derive, Derive, Json};
542    use ast::Decl;
543    use core::Source;
544
545    fn input<T>(input: &str, test: T)
546    where
547        T: Fn(Decl) -> (),
548    {
549        let source = Source::bytes("test", input.as_bytes().iter().cloned().collect());
550
551        let derive_config = Derive {
552            root_name: "Generator".to_string(),
553            format: Box::new(Json),
554            package_prefix: None,
555        };
556
557        test(derive(derive_config, &source).expect("bad derive"))
558    }
559
560    #[test]
561    fn simple_declaration() {
562        input(r#"{"id": 42, "name": "Oscar"}"#, |decl| {
563            let ty = match decl {
564                Decl::Type(ty) => ty,
565                other => panic!("expected type, got: {:?}", other),
566            };
567
568            assert_eq!(2, ty.members.len());
569        });
570    }
571
572    #[test]
573    fn test_interface() {
574        input(
575            r#"[
576    {"kind": "dragon", "name": "Stephen", "age": 4812, "fire": "blue"},
577    {"kind": "knight", "name": "Olivia", "armor": "Unobtanium"}
578]"#,
579            |decl| {
580                let intf = match decl {
581                    Decl::Interface(intf) => intf,
582                    other => panic!("expected interface, got: {:?}", other),
583                };
584
585                assert_eq!(2, intf.sub_types.len());
586            },
587        );
588    }
589}