Skip to content

Commit 582aca3

Browse files
committed
feat(setters): properties and setters for mbuilder
This includes descriptions, of course, and generally seems to look quite neat. For now, we brutally consume all input to own it, but in future we might be able to put in Borrow to support them all.
1 parent 942cbe1 commit 582aca3

7 files changed

Lines changed: 3318 additions & 519 deletions

File tree

etc/api/shared.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ api:
1515
base_path: "etc/api"
1616
terms:
1717
# how to actually do something with the API
18-
action: do
18+
action: doit
1919
templates:
2020
# all output directories are relative to the one set for the respective API
2121
- source: README.md

gen/youtube3/README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,24 @@ The API is structured into the following primary items:
4545
Generally speaking, you can invoke *Activities* like this:
4646

4747
```Rust,ignore
48-
let r = hub.resource().activity(...).do()
48+
let r = hub.resource().activity(...).doit()
4949
```
5050

5151
Or specifically ...
5252

5353
```ignore
54-
let r = hub.videos().rate(...).do()
55-
let r = hub.videos().getRating(...).do()
56-
let r = hub.videos().list(...).do()
57-
let r = hub.videos().insert(...).do()
58-
let r = hub.videos().update(...).do()
59-
let r = hub.videos().delete(...).do()
54+
let r = hub.videos().rate(...).doit()
55+
let r = hub.videos().getRating(...).doit()
56+
let r = hub.videos().list(...).doit()
57+
let r = hub.videos().insert(...).doit()
58+
let r = hub.videos().update(...).doit()
59+
let r = hub.videos().delete(...).doit()
6060
```
6161

6262
The `resource()` and `activity(...)` calls create [builders][builder-pattern]. The second one dealing with `Activities`
6363
supports various methods to configure the impending operation (not shown here). It is made such that all required arguments have to be
6464
specified right away (i.e. `(...)`), whereas all optional ones can be [build up][builder-pattern] as desired.
65-
The `do()` method performs the actual communication with the server and returns the respective result.
65+
The `doit()` method performs the actual communication with the server and returns the respective result.
6666

6767
# Usage (*TODO*)
6868

gen/youtube3/src/lib.rs

Lines changed: 3168 additions & 496 deletions
Large diffs are not rendered by default.

src/mako/lib.rs.mako

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<%
22
from util import (iter_nested_types, new_context, rust_comment, rust_doc_comment,
3-
rust_module_doc_comment, rb_type, hub_type)
3+
rust_module_doc_comment, rb_type, hub_type, mangle_ident)
44
nested_schemas = list(iter_nested_types(schemas))
55
66
c = new_context(resources)
@@ -21,7 +21,6 @@
2121
${lib.docs(c)}
2222
</%block>
2323
#![feature(core)]
24-
#![allow(non_snake_case)]
2524

2625
extern crate hyper;
2726
extern crate "rustc-serialize" as rustc_serialize;
@@ -71,7 +70,7 @@ impl<'a, C, NC, A> ${hub_type}<C, NC, A>
7170
}
7271

7372
% for resource in sorted(c.rta_map.keys()):
74-
pub fn ${resource}(&'a self) -> ${rb_type(resource)}<'a, C, NC, A> {
73+
pub fn ${mangle_ident(resource)}(&'a self) -> ${rb_type(resource)}<'a, C, NC, A> {
7574
${rb_type(resource)} { hub: &self }
7675
}
7776
% endfor

src/mako/lib/mbuild.mako

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<%!
22
from util import (put_and, rust_test_fn_invisible, rust_doc_test_norun, rust_doc_comment,
3-
rb_type, mb_type, singular, hub_type)
3+
rb_type, mb_type, singular, hub_type, to_fqan, indent_all_but_first_by,
4+
method_params, activity_rust_type, mangle_ident, activity_input_type, get_word,
5+
split_camelcase_s)
46
%>\
57
<%namespace name="util" file="util.mako"/>\
68
<%namespace name="lib" file="lib.mako"/>\
@@ -9,7 +11,17 @@
911
###############################################################################################
1012
###############################################################################################
1113
<%def name="new(resource, method, c)">\
12-
<% hub_type_name = hub_type(canonicalName) %>\
14+
<%
15+
hub_type_name = hub_type(canonicalName)
16+
m = c.fqan_map[to_fqan(name, resource, method)]
17+
# an identifier for a property. We prefix them to prevent clashes with the setters
18+
property = lambda x: '_' + mangle_ident(x)
19+
ThisType = mb_type(resource, method) + "<'a, C, NC, A>"
20+
%>\
21+
% if 'description' in m:
22+
${m.description | rust_doc_comment}
23+
///
24+
% endif
1325
/// A builder for the *${method}* method supported by a *${singular(resource)}* resource.
1426
/// It is not used directly, but through a `${rb_type(resource)}`.
1527
///
@@ -33,13 +45,52 @@ ${lib.test_hub(hub_type_name, comments=False)}\
3345
// mb.do()
3446
</%block>
3547
</%block>
36-
pub struct ${mb_type(resource, method)}<'a, C, NC, A>
48+
pub struct ${ThisType}
3749
where NC: 'a,
3850
C: 'a,
3951
A: 'a, {
4052
41-
hub: &'a ${hub_type_name}<C, NC, A>
53+
hub: &'a ${hub_type_name}<C, NC, A>,
54+
% for p in method_params(m):
55+
${property(p.name)}: ${activity_rust_type(p)},
56+
% endfor
4257
}
4358
44-
impl<'a, C, NC, A> MethodBuilder for ${mb_type(resource, method)}<'a, C, NC, A> {}
59+
impl<'a, C, NC, A> MethodBuilder for ${ThisType} {}
60+
61+
impl<'a, C, NC, A> ${ThisType} {
62+
63+
% if 'description' in m:
64+
${m.description | rust_doc_comment, indent_all_but_first_by(1)}
65+
///
66+
% endif
67+
/// TODO: Build actual call
68+
pub fn ${api.terms.action}(self) {
69+
70+
}
71+
72+
% for p in method_params(m):
73+
<%
74+
InType = activity_input_type(p)
75+
%>\
76+
/// Sets the *${split_camelcase_s(p.name)}* ${get_word(p, 'location')}property to the given value.
77+
% if p.get('required', False):
78+
/// Even though the property as already been set when instantiating this call,
79+
/// we provide this method for API completeness.
80+
%endif
81+
///
82+
% if 'description' in p:
83+
${p.description | rust_doc_comment, indent_all_but_first_by(1)}
84+
% endif
85+
pub fn ${mangle_ident(p.name)}(&mut self, new_value: ${InType}) -> &mut ${ThisType} {
86+
% if InType == '&str':
87+
self.${property(p.name)} = Some(new_value.to_string());
88+
% else:
89+
self.${property(p.name)} = Some(new_value.clone());
90+
% endif
91+
return self;
92+
}
93+
94+
% endfor
95+
}
4596
</%def>

src/mako/lib/rbuild.mako

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<%!
22
from util import (put_and, rust_test_fn_invisible, rust_doc_test_norun, rust_doc_comment,
3-
rb_type, singular, hub_type)
3+
rb_type, singular, hub_type, mangle_ident)
44
%>\
55
<%namespace name="util" file="util.mako"/>\
66
<%namespace name="lib" file="lib.mako"/>\
@@ -9,7 +9,10 @@
99
###############################################################################################
1010
###############################################################################################
1111
<%def name="new(resource, c)">\
12-
<% hub_type_name = hub_type(canonicalName) %>\
12+
<%
13+
hub_type_name = hub_type(canonicalName)
14+
ThisType = rb_type(resource) + "<'a, C, NC, A>"
15+
%>\
1316
/// A builder providing access to all methods supported on *${singular(resource)}* resources.
1417
/// It is not used directly, but through the `${hub_type_name}` hub.
1518
///
@@ -26,16 +29,16 @@ ${lib.test_hub(hub_type_name, comments=False)}\
2629
// Usually you wouldn't bind this to a variable, but keep calling *MethodBuilders*
2730
// like ${put_and(sorted('`%s(...)`' % f for f in c.rta_map[resource]))}
2831
// to build up your call.
29-
let rb = hub.${resource}();
32+
let rb = hub.${mangle_ident(resource)}();
3033
</%block>
3134
</%block>
32-
pub struct ${rb_type(resource)}<'a, C, NC, A>
35+
pub struct ${ThisType}
3336
where NC: 'a,
3437
C: 'a,
3538
A: 'a, {
3639
3740
hub: &'a ${hub_type_name}<C, NC, A>
3841
}
3942
40-
impl<'a, C, NC, A> ResourceMethodsBuilder for ${rb_type(resource)}<'a, C, NC, A> {}
43+
impl<'a, C, NC, A> ResourceMethodsBuilder for ${ThisType} {}
4144
</%def>

src/mako/lib/util.py

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
DEL_METHOD = 'delete'
2323

2424
NESTED_TYPE_MARKER = 'is_nested'
25+
SPACES_PER_TAB = 4
2526

2627
# ==============================================================================
2728
## @name Filters
@@ -51,9 +52,23 @@ def unindent(s):
5152
# tabs: 1 tabs is 4 spaces
5253
def unindent_first_by(tabs):
5354
def unindent_inner(s):
54-
return re.sub("^ {1,%i}" % (tabs*4), '', s)
55+
return re_linestart.sub(' ' * tabs * SPACES_PER_TAB, s)
5556
return unindent_inner
5657

58+
# tabs: 1 tabs is 4 spaces
59+
def indent_all_but_first_by(tabs):
60+
def indent_inner(s):
61+
try:
62+
i = s.index('\n')
63+
except ValueError:
64+
f = s
65+
p = ''
66+
else:
67+
f = s[:i+1]
68+
p = s[i+1:]
69+
return f + re_linestart.sub(' ' * (tabs * SPACES_PER_TAB), p)
70+
return indent_inner
71+
5772
# add 4 spaces to the beginning of a line.
5873
# useful if you have defs embedded in an unindent block - they need to counteract.
5974
# It's a bit itchy, but logical
@@ -119,6 +134,10 @@ def split_camelcase_s(s):
119134
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1 \2', s)
120135
return re.sub('([a-z0-9])([A-Z])', r'\1 \2', s1).lower()
121136

137+
def camel_to_under(s):
138+
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', s)
139+
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
140+
122141
## -- End Natural Language Utilities -- @}
123142

124143

@@ -137,6 +156,7 @@ def nested_type_name(sn, pn):
137156

138157
# Make properties which are reserved keywords usable
139158
def mangle_ident(n):
159+
n = camel_to_under(n)
140160
if n == 'type':
141161
return n + '_'
142162
return n
@@ -193,6 +213,14 @@ def is_nested_type_property(t):
193213
def is_nested_type(s):
194214
return NESTED_TYPE_MARKER in s
195215

216+
# convert a rust-type to something that would be taken as input of a function
217+
# even though our storage type is different
218+
def activity_input_type(p):
219+
n = activity_rust_type(p, allow_optionals=False)
220+
if n == 'String':
221+
n = 'str'
222+
return '&%s' % n
223+
196224
# return an iterator yielding fake-schemas that identify a nested type
197225
def iter_nested_types(schemas):
198226
for s in schemas.values():
@@ -243,6 +271,14 @@ def activity_split(fqan):
243271
assert len(t) == 3
244272
return t[1:]
245273

274+
# Shorthand to get a type from parameters of activities
275+
def activity_rust_type(p, allow_optionals=True):
276+
return to_rust_type(None, p.name, p, allow_optionals=allow_optionals)
277+
278+
# the inverse of activity-split, but needs to know the 'name' of the API
279+
def to_fqan(name, resource, method):
280+
return '%s.%s.%s' % (name, resource, method)
281+
246282
# videos -> Video
247283
def activity_name_to_type_name(an):
248284
return an.capitalize()[:-1]
@@ -251,6 +287,33 @@ def activity_name_to_type_name(an):
251287
def iter_acitivities(c):
252288
return ((activity_split(an) + [a]) for an, a in c.fqan_map.iteritems())
253289

290+
# return a list of parameter structures of all params of the given method dict
291+
# apply a prune filter to restrict the set of returned parameters.
292+
# The order will always be: partOrder + alpha
293+
def method_params(m, required=None, location=None):
294+
res = list()
295+
po = m.get('parameterOrder', [])
296+
for pn, p in m.parameters.iteritems():
297+
if required is not None and p.get('required', False) != required:
298+
continue
299+
if location is not None and p.get('location', '') != location:
300+
continue
301+
np = p.copy()
302+
np['name'] = pn
303+
try:
304+
# po = ['part', 'foo']
305+
# part_prio = 2 - 0 = 2
306+
# foo_prio = 2 - 1 = 1
307+
# default = 0
308+
prio = len(po) - po.index(pn)
309+
except ValueError:
310+
prio = 0
311+
np['priority'] = prio
312+
res.append(np)
313+
# end for each parameter
314+
return sorted(res, key=lambda p: (p.priority, p.name), reverse=True)
315+
316+
254317
## -- End Activity Utilities -- @}
255318

256319

@@ -322,3 +385,14 @@ def mb_type(r, m):
322385

323386
def hub_type(canonicalName):
324387
return canonical_type_name(canonicalName)
388+
389+
# return e + d[n] + e + ' ' or ''
390+
def get_word(d, n, e = ''):
391+
if n in d:
392+
v = e + d[n] + e
393+
if not v.endswith(' '):
394+
v += ' '
395+
return v
396+
else:
397+
return ''
398+

0 commit comments

Comments
 (0)