feat(schema): generating valid rust from schemas

It's very nice, even though there is some more work to be done here.
It's just the beginning ... .
This commit is contained in:
Sebastian Thiel
2015-03-02 19:07:15 +01:00
parent 939e631edb
commit a5e675e7a9
3 changed files with 120 additions and 1 deletions

View File

@@ -1,9 +1,27 @@
<% import util %>\
<%namespace name="lib" file="lib/lib.mako"/>\
<%namespace name="mutil" file="lib/util.mako"/>\
<%namespace name="schema" file="lib/schema.mako"/>\
<%block filter="util.rust_module_doc_comment">\
<%lib:docs />\
</%block>
#![allow(non_snake_case)]
extern crate cmn;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "yup-oauth2" as oauth2;
extern crate "yup-oauth2" as oauth2;
use std::default::Default;
use std::collections::HashMap;
## SCHEMAS - normal ones
% for s in schemas.values():
${schema.new(s)}
% endfor
## SCHEMAS - nested
## some schemas are only used once and basically internal types.
## We have to find them and process them as normal types
% for s in util.iter_nested_types(schemas):
${schema.new(s)}
% endfor

25
src/mako/lib/schema.mako Normal file
View File

@@ -0,0 +1,25 @@
<%! import util %>\
## Create new schema with everything.
## 's' contains the schema structure from json to build
<%def name="new(s)">\
<% assert s.type == "object" %>\
<%block filter="util.rust_doc_comment">\
${doc(s)}\
</%block>
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
pub struct ${s.id}\
% if 'properties' in s:
{
% for pn, p in s.properties.iteritems():
${p.get('description', 'no description provided') | util.rust_doc_comment}
pub ${util.mangle_ident(pn)}: ${util.to_rust_type(s.id, pn, p)},
% endfor
}
% else:
;
% endif
</%def>
<%def name="doc(s)">\
${s.get('description', 'There is no detailed description.')}
</%def>

View File

@@ -1,6 +1,18 @@
import re
re_linestart = re.compile('^', flags=re.MULTILINE)
USE_FORMAT = 'use_format_field'
TYPE_MAP = {'boolean' : 'bool',
'integer' : USE_FORMAT,
'number' : USE_FORMAT,
'uint32' : 'u32',
'double' : 'f64',
'int32' : 'i32',
'array' : 'Vec',
'string' : 'String',
'object' : 'HashMap'}
TREF = '$ref'
# rust module doc comment filter
def rust_module_doc_comment(s):
return re_linestart.sub('//! ', s)
@@ -29,3 +41,67 @@ def estr(l):
# build a full library name (non-canonical)
def library_name(name, version):
return name + to_api_version(version)
def nested_type_name(sn, pn):
return sn + pn[:1].upper() + pn[1:]
# Make properties which are reserved keywords usable
def mangle_ident(n):
if n == 'type':
return n + '_'
return n
# map a json type to an rust type
# sn = schema name
# pn = property name
# t = type dict
def to_rust_type(sn, pn, t, allow_optionals=True):
def nested_type(nt):
if nt.get('items', None) is not None:
nt = nt.items
elif nt.get('additionalProperties'):
nt = nt.additionalProperties
else:
assert(is_nested_type(nt))
# It's a nested type - we take it literally like $ref, but generate a name for the type ourselves
# This of course assumes
return nested_type_name(sn, pn)
return to_rust_type(sn, pn, nt, allow_optionals=False)
# unconditionally handle $ref types, which should point to another schema.
if TREF in t:
tn = t[TREF]
if allow_optionals:
return "Option<%s>" % tn
return tn
try:
rust_type = TYPE_MAP[t.type]
if t.type == 'array':
rust_type = "%s<%s>" % (rust_type, nested_type(t))
elif t.type == 'object':
rust_type = "%s<String, %s>" % (rust_type, nested_type(t))
elif rust_type == USE_FORMAT:
rust_type = TYPE_MAP[t.format]
return rust_type
except KeyError as err:
raise AssertionError("%s: Property type '%s' unknown - add new type mapping: %s" % (str(err), t.type, str(t)))
except AttributeError as err:
raise AssertionError("%s: unknown dict layout: %s" % (str(err), t))
def is_nested_type(t):
return 'type' in t and t.type == 'object' and 'additionalProperties' not in t
# return an iterator yielding fake-schemas that identify a nested type
def iter_nested_types(schemas):
for s in schemas.values():
if 'properties' not in s:
continue
for pn, p in s.properties.iteritems():
if is_nested_type(p):
ns = p.copy()
ns.id = nested_type_name(s.id, pn)
yield ns
# end for ach property
# end for aech schma