feat(builders): request type handling part 1

Now we will generate proper resoure methods builder calls to instaniate
the more or less valid method builders.

However, it doesn't compile yet, and the 'to_parts()' method on
resources is still missing.
This commit is contained in:
Sebastian Thiel
2015-03-05 08:27:07 +01:00
parent 452b658c27
commit 48d40d45c5
7 changed files with 731 additions and 370 deletions

View File

@@ -21,7 +21,7 @@ pub trait Resource: MarkerTrait {}
pub trait ResponseResult: MarkerTrait {}
/// Identifies types which are used in API requests.
pub trait RequestResult: MarkerTrait {}
pub trait RequestValue: MarkerTrait {}
/// Identifies types which are only used as part of other types, which
/// usually are carrying the `Resource` trait.

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,8 @@
from util import (put_and, rust_test_fn_invisible, rust_doc_test_norun, rust_doc_comment,
rb_type, mb_type, singular, hub_type, to_fqan, indent_all_but_first_by,
method_params, activity_rust_type, mangle_ident, activity_input_type, get_word,
split_camelcase_s, property, is_pod_property, TREF)
split_camelcase_s, property, is_pod_property, TREF, method_io, IO_REQUEST,
RESOURCE_MARKER, schema_to_required_property, rust_copy_value_s, is_required_property)
%>\
<%namespace name="util" file="util.mako"/>\
<%namespace name="lib" file="lib.mako"/>\
@@ -16,6 +17,11 @@
m = c.fqan_map[to_fqan(name, resource, method)]
# an identifier for a property. We prefix them to prevent clashes with the setters
ThisType = mb_type(resource, method) + "<'a, C, NC, A>"
request_resource = method_io(schemas, c, m, IO_REQUEST, RESOURCE_MARKER)
params = method_params(m)
if request_resource:
params.insert(0, schema_to_required_property(request_resource, 'request'))
%>\
% if 'description' in m:
${m.description | rust_doc_comment}
@@ -51,8 +57,13 @@ pub struct ${ThisType}
hub: &'a ${hub_type_name}<C, NC, A>,
## PROPERTIES ###############
% for p in method_params(m):
${property(p.name)}: ${activity_rust_type(p)},
% for p in params:
${property(p.name)}:\
% if is_required_property(p):
${activity_rust_type(p, allow_optionals=False)},
% else:
${activity_rust_type(p)},
% endif
% endfor
}
@@ -68,7 +79,7 @@ impl<'a, C, NC, A> ${ThisType} {
}
## SETTERS ###############
% for p in method_params(m):
% for p in params:
${self._setter(resource, method, m, p, ThisType, c)}\
% endfor
}
@@ -90,11 +101,9 @@ ${self._setter(resource, method, m, p, ThisType, c)}\
return m.request.get(TREF, 'first') == m.response.get(TREF, 'second')
value_name = 'new_value'
new_value_copied = value_name + '.clone()'
if InType == '&str':
new_value_copied = value_name + '.to_string()'
elif is_pod_property(p):
new_value_copied = value_name
new_value_copied = rust_copy_value_s(value_name, InType, p)
if not is_required_property(p):
new_value_copied = 'Some(%s)' % new_value_copied
%>\
/// Sets the *${split_camelcase_s(p.name)}* ${get_word(p, 'location')}property to the given value.
///
@@ -104,7 +113,7 @@ ${self._setter(resource, method, m, p, ThisType, c)}\
/// This may not always be desirable, as you can obtain (newly generated) parts you cannot pass in,
/// like statistics that are generated server side. Therefore you should use this method to specify
/// the parts you provide in addition to the ones you want in the response.
% elif p.get('required', False):
% elif is_required_property(p):
/// Even though the property as already been set when instantiating this call,
/// we provide this method for API completeness.
% endif
@@ -113,7 +122,7 @@ ${self._setter(resource, method, m, p, ThisType, c)}\
${p.description | rust_doc_comment, indent_all_but_first_by(1)}
% endif
pub fn ${mangle_ident(p.name)}(&mut self, ${value_name}: ${InType}) -> &mut ${ThisType} {
self.${property(p.name)} = Some(${new_value_copied});
self.${property(p.name)} = ${new_value_copied};
return self;
}
</%def>

View File

@@ -1,7 +1,9 @@
<%!
from util import (put_and, rust_test_fn_invisible, rust_doc_test_norun, rust_doc_comment,
rb_type, singular, hub_type, mangle_ident, mb_type, method_params, property,
to_fqan, indent_all_but_first_by)
to_fqan, indent_all_but_first_by, RESOURCE_MARKER, schema_markers,
activity_input_type, TREF, method_io, IO_REQUEST, schema_to_required_property,
rust_copy_value_s, is_required_property)
%>\
<%namespace name="util" file="util.mako"/>\
<%namespace name="lib" file="lib.mako"/>\
@@ -49,6 +51,33 @@ impl<'a, C, NC, A> ${ThisType} {
<%
m = c.fqan_map[to_fqan(name, resource, a)]
RType = mb_type(resource, a)
# skip part if we have a request resource. Only resources can have parts
# that we can easily deduce
request_resource = method_io(schemas, c, m, IO_REQUEST, RESOURCE_MARKER)
params = method_params(m)
REQUEST_RESOURCE_PROPERTY_NAME = 'request'
if request_resource:
# resource into property
resprop = schema_to_required_property(request_resource, REQUEST_RESOURCE_PROPERTY_NAME)
params.insert(0, resprop)
part_prop = None
optional_props = list()
required_props = list()
for p in params:
if is_required_property(p):
if request_resource and p.name == 'part':
part_prop = p
else:
required_props.append(p)
else:
optional_props.append(p)
# end for each property
method_args = ''
if required_props:
method_args = ', ' + ', '.join('%s: %s' % (mangle_ident(p.name), activity_input_type(p)) for p in required_props)
%>\
% if 'description' in m:
@@ -56,10 +85,17 @@ impl<'a, C, NC, A> ${ThisType} {
///
${m.description | rust_doc_comment, indent_all_but_first_by(1)}
% endif
pub fn ${mangle_ident(a)}(&self) -> ${RType}<'a, C, NC, A> {
pub fn ${mangle_ident(a)}(&self${method_args}) -> ${RType}<'a, C, NC, A> {
${RType} {
hub: self.hub,
% for p in method_params(m):
% for p in required_props:
${property(p.name)}: ${rust_copy_value_s(mangle_ident(p.name), activity_input_type(p), p)},
% endfor
## auto-generate parts from request resources
% if part_prop and request_resource:
${property(part_prop.name)}: ${mangle_ident(REQUEST_RESOURCE_PROPERTY_NAME)}.to_parts(),
% endif
% for p in optional_props:
${property(p.name)}: Default::default(),
% endfor
}

View File

@@ -27,6 +27,10 @@ pub struct ${s.id}\
% for marker_trait in markers:
impl ${marker_trait} for ${s.id} {}
% endfor
% if RESOURCE_MARKER in markers:
% endif
</%def>
<%def name="doc(s, c)">\

View File

@@ -24,6 +24,10 @@ DEL_METHOD = 'delete'
NESTED_TYPE_MARKER = 'is_nested'
SPACES_PER_TAB = 4
REQUEST_PRIORITY = 100
REQUEST_MARKER = 'RequestValue'
RESOURCE_MARKER = 'Resource'
# ==============================================================================
## @name Filters
# ------------------------------------------------------------------------------
@@ -225,7 +229,7 @@ def activity_input_type(p):
return '&%s' % n
def is_pod_property(p):
return 'format' in p or p.type == 'boolean'
return 'format' in p or p.get('type','') == 'boolean'
# return an iterator yielding fake-schemas that identify a nested type
def iter_nested_types(schemas):
@@ -252,11 +256,11 @@ def schema_markers(s, c):
# it should have at least one activity that matches it's type to qualify for the Resource trait
for fqan, iot in activities.iteritems():
if activity_name_to_type_name(activity_split(fqan)[0]).lower() == s.id.lower():
res.add('Resource')
res.add(RESOURCE_MARKER)
if IO_RESPONSE in iot:
res.add('ResponseResult')
if IO_REQUEST in iot:
res.add('RequestResult')
res.add(REQUEST_MARKER)
# end for each activity
# end handle activites
@@ -319,6 +323,32 @@ def method_params(m, required=None, location=None):
# end for each parameter
return sorted(res, key=lambda p: (p.priority, p.name), reverse=True)
# return the given method's request or response schema (dict), or None.
# optionally return only schemas with the given marker trait
def method_io(schemas, c, m, type, marker=None):
s = schemas.get(m.get('request', dict()).get(TREF))
if s is None:
return s
if s and marker and marker not in schema_markers(s, c):
return None
return s
# return string like 'n.clone()', but depending on the type name of tn (e.g. &str -> n.to_string())
def rust_copy_value_s(n, tn, p):
nc = n + '.clone()'
if tn == '&str':
nc = n + '.to_string()'
elif is_pod_property(p):
nc = n
return nc
# convert a schema into a property (for use with rust type generation).
# n = name of the property
def schema_to_required_property(s, n):
return type(s)({'name': n, TREF: s.id, 'priority': REQUEST_PRIORITY})
def is_required_property(p):
return p.get('required', False) or p.get('priority', 0) > 0
## -- End Activity Utilities -- @}

View File

@@ -19,7 +19,7 @@ pub trait Resource: MarkerTrait {}
pub trait ResponseResult: MarkerTrait {}
/// Identifies types which are used in API requests.
pub trait RequestResult: MarkerTrait {}
pub trait RequestValue: MarkerTrait {}
/// Identifies types which are only used as part of other types, which
/// usually are carrying the `Resource` trait.