Skip to content

Commit

Permalink
feat(types): prevent duplicate schema types
Browse files Browse the repository at this point in the history
These could clash with types we import from Cmn. When that happens,
just a single list must be adjusted for a fix, see
`unique_type_name`

Fixes #26
  • Loading branch information
Byron committed Mar 19, 2015
1 parent 508d14e commit 3a15430
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/mako/lib.rs.mako
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extern crate "yup-oauth2" as oauth2;
extern crate mime;
extern crate url;

pub mod cmn;
mod cmn;

use std::collections::HashMap;
use std::cell::RefCell;
Expand All @@ -49,7 +49,7 @@ use std::io;
use std::fs;
use std::old_io::timer::sleep;

use cmn::{Hub, ReadSeek, Part, ResponseResult, RequestValue, NestedType, Delegate, DefaultDelegate};
pub use cmn::{MultiPartReader, MethodInfo, Result, MethodBuilder, Hub, ReadSeek, Part, ResponseResult, RequestValue, NestedType, Delegate, DefaultDelegate};


// ##############
Expand Down
4 changes: 2 additions & 2 deletions src/mako/lib/lib.mako
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
find_fattest_resource, build_all_params, pass_through, parts_from_params,
REQUEST_MARKER_TRAIT, RESPONSE_MARKER_TRAIT, supports_scopes, to_api_version,
to_fqan, METHODS_RESOURCE, ADD_PARAM_MEDIA_EXAMPLE, PROTOCOL_TYPE_INFO, enclose_in,
upload_action_fn) %>\
upload_action_fn, unique_type_name) %>\
<%namespace name="util" file="util.mako"/>\
<%namespace name="mbuild" file="mbuild.mako"/>\
Expand Down Expand Up @@ -78,7 +78,7 @@ It seems there is nothing you can do here ... .
sn = singular(canonical_type_name(r))
if sn in schemas:
md_resource = link(md_resource, 'struct.%s.html' % singular(canonical_type_name(r)))
md_resource = link(md_resource, 'struct.%s.html' % unique_type_name(singular(canonical_type_name(r))))
%>\
* ${md_resource} (${put_and(md_methods)})
% endfor ## each resource activity
Expand Down
36 changes: 19 additions & 17 deletions src/mako/lib/mbuild.mako
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
METHOD_BUILDER_MARKERT_TRAIT, pass_through, markdown_rust_block, parts_from_params,
DELEGATE_PROPERTY_NAME, struct_type_bounds_s, supports_scopes, scope_url_to_variant,
re_find_replacements, ADD_PARAM_FN, ADD_PARAM_MEDIA_EXAMPLE, upload_action_fn, METHODS_RESOURCE,
method_name_to_variant)
method_name_to_variant, unique_type_name)
def get_parts(part_prop):
if not part_prop:
Expand Down Expand Up @@ -67,7 +67,7 @@ ${m.description | rust_doc_comment}
/// `${ADD_PARAM_MEDIA_EXAMPLE}`.
% if response_schema:
/// Please note that due to missing multi-part support on the server side, you will only receive the media,
/// but not the `${response_schema.id}` structure that you would usually get. The latter will be a default value.
/// but not the `${unique_type_name(response_schema.id)}` structure that you would usually get. The latter will be a default value.
% endif
///
% endif ## supports media download
Expand Down Expand Up @@ -125,7 +125,7 @@ pub struct ${ThisType}
% endif
}
impl${mb_tparams} cmn::${METHOD_BUILDER_MARKERT_TRAIT} for ${ThisType} {}
impl${mb_tparams} ${METHOD_BUILDER_MARKERT_TRAIT} for ${ThisType} {}
impl${mb_tparams} ${ThisType} where ${', '.join(mb_type_bounds())} {
Expand Down Expand Up @@ -297,14 +297,17 @@ ${self._setter_fn(resource, method, m, p, part_prop, ThisType, c)}\
hide_filter = show_all and pass_through or hide_rust_doc_test
test_block_filter = rust_doc and rust_doc_test_norun or markdown_rust_block
test_fn_filter = rust_doc and rust_test_fn_invisible or pass_through
if request_value:
request_value_type = unique_type_name(request_value.id)
%>\
<%block filter="test_block_filter">\
${capture(util.test_prelude) | hide_filter}\
% if request_value:
use ${util.library_name()}::${request_value.id};
use ${util.library_name()}::${request_value_type};
% endif
% if handle_result:
use ${util.library_name()}::cmn::Result;
use ${util.library_name()}::Result;
% endif
% if media_params:
use std::fs;
Expand All @@ -315,7 +318,7 @@ ${capture(lib.test_hub, hub_type_name, comments=show_all) | hide_filter}
// As the method needs a request, you would usually fill it with the desired information
// into the respective structure. Some of the parts shown here might not be applicable !
// ${random_value_warning}
let mut ${rb_name}: ${request_value.id} = Default::default();
let mut ${rb_name}: ${request_value_type} = Default::default();
% for spn, sp in request_value.get('properties', dict()).iteritems():
% if parts is not None and spn not in parts:
<% continue %>
Expand All @@ -331,7 +334,6 @@ let mut ${rb_name}: ${request_value.id} = Default::default();
else:
assignment = 'Some(%s);' % assignment
%>\
## ${to_rust_type(request_value.id, spn, sp, allow_optionals=False)}
${rb_name}.${mangle_ident(spn)} = ${assignment}
% endfor
Expand Down Expand Up @@ -382,15 +384,15 @@ match result {
where = ''
qualifier = 'pub '
add_args = ''
rtype = 'cmn::Result<hyper::client::Response>'
rtype = 'Result<hyper::client::Response>'
response_schema = method_response(c, m)
supports_download = m.get('supportsMediaDownload', False);
reserved_params = []
if response_schema:
if not supports_download:
reserved_params = ['alt']
rtype = 'cmn::Result<(hyper::client::Response, %s)>' % (response_schema.id)
rtype = 'Result<(hyper::client::Response, %s)>' % (unique_type_name(response_schema.id))
mtype_param = 'RS'
mtype_where = 'ReadSeek'
Expand Down Expand Up @@ -482,7 +484,7 @@ match result {
use std::io::{Read, Seek};
use hyper::header::{ContentType, ContentLength, Authorization, UserAgent};
if let Some(ref mut d) = ${delegate} {
d.begin(cmn::MethodInfo { id: "${m.id}",
d.begin(MethodInfo { id: "${m.id}",
http_method: ${method_name_to_variant(m.httpMethod)} });
}
let mut params: Vec<(&str, String)> = Vec::with_capacity((${len(params) + len(reserved_params)} + ${paddfields}.len()));
Expand Down Expand Up @@ -522,7 +524,7 @@ match result {
for &field in [${', '.join(enclose_in('"', reserved_params + [p.name for p in field_params]))}].iter() {
if ${paddfields}.contains_key(field) {
${delegate_finish}
return cmn::Result::FieldClash(field);
return Result::FieldClash(field);
}
}
for (name, value) in ${paddfields}.iter() {
Expand Down Expand Up @@ -584,7 +586,7 @@ else {
Some(value) => params.push(("key", value)),
None => {
${delegate_finish}
return cmn::Result::MissingAPIKey
return Result::MissingAPIKey
}
}
% else:
Expand Down Expand Up @@ -658,15 +660,15 @@ else {
}}
if token.is_none() {
${delegate_finish}
return cmn::Result::MissingToken
return Result::MissingToken
}
let auth_header = Authorization(token.unwrap().access_token);
% endif
% if request_value:
request_value_reader.seek(io::SeekFrom::Start(0)).unwrap();
% endif
% if request_value and simple_media_param:
let mut mp_reader: cmn::MultiPartReader = Default::default();
let mut mp_reader: MultiPartReader = Default::default();
let (mut body_reader, content_type) = match ${simple_media_param.type.arg_name}.as_mut() {
Some(&mut (ref mut reader, ref mime)) => {
mp_reader.reserve_exact(2);
Expand Down Expand Up @@ -721,7 +723,7 @@ else {
}
}
${delegate_finish}
return cmn::Result::HttpError(err)
return Result::HttpError(err)
}
Ok(mut res) => {
if !res.status.is_success() {
Expand All @@ -735,7 +737,7 @@ else {
}
}
${delegate_finish}
return cmn::Result::Failure(res)
return Result::Failure(res)
}
% if response_schema:
## If 'alt' is not json, we cannot attempt to decode the response
Expand All @@ -756,7 +758,7 @@ if enable_resource_parsing \
let result_value = res;
% endif
${delegate_finish}
return cmn::Result::Success(result_value)
return Result::Success(result_value)
}
}
}
Expand Down
20 changes: 11 additions & 9 deletions src/mako/lib/schema.mako
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<%!
from util import (schema_markers, rust_doc_comment, mangle_ident, to_rust_type, put_and,
IO_TYPES, activity_split, enclose_in, REQUEST_MARKER_TRAIT, mb_type, indent_all_but_first_by,
NESTED_TYPE_SUFFIX, RESPONSE_MARKER_TRAIT, split_camelcase_s, METHODS_RESOURCE)
NESTED_TYPE_SUFFIX, RESPONSE_MARKER_TRAIT, split_camelcase_s, METHODS_RESOURCE, unique_type_name)
default_traits = ('RustcEncodable', 'Clone', 'Default')
%>\
## Build a schema which must be an object
###################################################################################################################
###################################################################################################################
<%def name="_new_object(s, properties, c)">\
<% struct = 'pub struct ' + s.id %>\
<% struct = 'pub struct ' + unique_type_name(s.id) %>\
% if properties:
${struct} {
% for pn, p in properties.iteritems():
Expand Down Expand Up @@ -41,6 +41,8 @@ ${struct};
## waiting for Default: https://github.com/rust-lang/rustc-serialize/issues/71
if s.type == 'any':
traits.remove('Default')
s_type = unique_type_name(s.id)
%>\
<%block filter="rust_doc_comment">\
${doc(s, c)}\
Expand All @@ -50,29 +52,29 @@ ${doc(s, c)}\
${_new_object(s, s.get('properties'), c)}\
% elif s.type == 'array':
% if s.items.get('type') != 'object':
pub struct ${s.id}(${to_rust_type(schemas, s.id, NESTED_TYPE_SUFFIX, s)});
pub struct ${s_type}(${to_rust_type(schemas, s.id, NESTED_TYPE_SUFFIX, s)});
% else:
${_new_object(s, s.items.get('properties'), c)}\
% endif ## array item != 'object'
% elif s.type == 'any':
## waiting for Default: https://github.com/rust-lang/rustc-serialize/issues/71
pub struct ${s.id}(rustc_serialize::json::Json);
pub struct ${s_type}(rustc_serialize::json::Json);
impl Default for ${s.id} {
fn default() -> ${s.id} {
${s.id}(rustc_serialize::json::Json::Null)
impl Default for ${s_type} {
fn default() -> ${s_type} {
${s_type}(rustc_serialize::json::Json::Null)
}
}
% else:
<% assert False, "Object not handled: %s" % str(s) %>\
% endif ## type == ?
% for marker_trait in markers:
impl ${marker_trait} for ${s.id} {}
impl ${marker_trait} for ${s_type} {}
% endfor
% if REQUEST_MARKER_TRAIT in markers and 'properties' in s:
impl ${s.id} {
impl ${s_type} {
/// Return a comma separated list of members that are currently set, i.e. for which `self.member.is_some()`.
/// The produced string is suitable for use as a parts list that indicates the parts you are sending, and/or
/// the parts you want to see in the server response.
Expand Down
13 changes: 10 additions & 3 deletions src/mako/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
'return', 'sizeof', 'static', 'self', 'struct', 'super', 'true', 'trait', 'type', 'typeof',
'unsafe', 'unsized', 'use', 'virtual', 'where', 'while', 'yield'))

RESERVED_TYPES = set(("Result", ))

_words = [w.strip(',') for w in "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.".split(' ')]
RUST_TYPE_RND_MAP = {'bool': lambda: str(bool(randint(0, 1))).lower(),
Expand Down Expand Up @@ -283,7 +284,7 @@ def _assure_unique_type_name(schemas, tn):
if tn in schemas:
tn += 'Nested'
assert tn not in schemas
return tn
return unique_type_name(tn)

# map a json type to an rust type
# sn = schema name
Expand All @@ -305,7 +306,7 @@ def nested_type(nt):
def wrap_type(tn):
if allow_optionals:
tn = "Option<%s>" % tn
return tn
return unique_type_name(tn)

# unconditionally handle $ref types, which should point to another schema.
if TREF in t:
Expand All @@ -314,7 +315,7 @@ def wrap_type(tn):
# usually is on on the first call, and off when recursion is involved.
tn = t[TREF]
if allow_optionals and tn == sn:
tn = 'Box<%s>' % tn
tn = 'Box<%s>' % unique_type_name(tn)
return wrap_type(tn)
try:
rust_type = TYPE_MAP[t.type]
Expand Down Expand Up @@ -799,6 +800,12 @@ def mb_type_params_s(m):
def mb_additional_type_params(m):
return []

# check type_name against a list of reserved types, and return a possibly rename type_name to prevent a clash
def unique_type_name(type_name):
if type_name in RESERVED_TYPES:
type_name += 'Type'
return type_name

# return type name for a method on the given resource
def mb_type(r, m):
return "%s%sMethodBuilder" % (singular(canonical_type_name(r)), dot_sep_to_canonical_type_name(m))
Expand Down

0 comments on commit 3a15430

Please sign in to comment.