feat(types): prevent duplicate schema types

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
This commit is contained in:
Sebastian Thiel
2015-03-19 19:09:40 +01:00
parent 508d14eafb
commit 3a15430339
5 changed files with 44 additions and 33 deletions

View File

@@ -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;
@@ -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};
// ##############

View File

@@ -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"/>\
@@ -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

View File

@@ -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:
@@ -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
@@ -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())} {
@@ -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;
@@ -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 %>
@@ -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
@@ -382,7 +384,7 @@ 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);
@@ -390,7 +392,7 @@ match result {
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'
@@ -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()));
@@ -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() {
@@ -584,7 +586,7 @@ else {
Some(value) => params.push(("key", value)),
None => {
${delegate_finish}
return cmn::Result::MissingAPIKey
return Result::MissingAPIKey
}
}
% else:
@@ -658,7 +660,7 @@ else {
}}
if token.is_none() {
${delegate_finish}
return cmn::Result::MissingToken
return Result::MissingToken
}
let auth_header = Authorization(token.unwrap().access_token);
% endif
@@ -666,7 +668,7 @@ else {
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);
@@ -721,7 +723,7 @@ else {
}
}
${delegate_finish}
return cmn::Result::HttpError(err)
return Result::HttpError(err)
}
Ok(mut res) => {
if !res.status.is_success() {
@@ -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
@@ -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)
}
}
}

View File

@@ -1,7 +1,7 @@
<%!
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')
%>\
@@ -9,7 +9,7 @@
###################################################################################################################
###################################################################################################################
<%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():
@@ -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)}\
@@ -50,17 +52,17 @@ ${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:
@@ -68,11 +70,11 @@ impl Default for ${s.id} {
% 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.

View File

@@ -35,6 +35,7 @@ RESERVED_WORDS = set(('abstract', 'alignof', 'as', 'become', 'box', 'break', 'co
'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(),
@@ -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
@@ -305,7 +306,7 @@ def to_rust_type(schemas, sn, pn, t, allow_optionals=True):
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:
@@ -314,7 +315,7 @@ def to_rust_type(schemas, sn, pn, t, allow_optionals=True):
# 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]
@@ -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))