mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-02-23 15:49:49 +01:00
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:
@@ -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
25
src/mako/lib/schema.mako
Normal 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>
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user