Skip to content

Commit

Permalink
feat(builder): Partial MethodBuilder impl
Browse files Browse the repository at this point in the history
Including documentation at least on the method builder part. The
great thing is that fully working examples are now included on
every type !

Now more involved part starts ... namely setting up the individual call
method signatures.
  • Loading branch information
Byron committed Mar 4, 2015
1 parent 8746f5e commit 01db890
Show file tree
Hide file tree
Showing 9 changed files with 931 additions and 75 deletions.
3 changes: 3 additions & 0 deletions gen/youtube3/src/cmn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use std::marker::MarkerTrait;
/// The hub allows to access all resource methods more easily.
pub trait Hub: MarkerTrait {}

/// Identifies types for building methods of a particular type
pub trait MethodBuilder: MarkerTrait {}

/// Identifies types which can be inserted and deleted.
/// Types with this trait are most commonly used by clients of this API.
pub trait Resource: MarkerTrait {}
Expand Down
808 changes: 807 additions & 1 deletion gen/youtube3/src/lib.rs

Large diffs are not rendered by default.

50 changes: 46 additions & 4 deletions src/mako/lib.rs.mako
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<%
from util import (iter_nested_types, new_context, rust_comment, rust_doc_comment,
rust_module_doc_comment, rust_doc_test_norun, canonical_type_name,
rust_test_fn_invisible)
mb_type, singular, rust_test_fn_invisible, put_and)
nested_schemas = list(iter_nested_types(schemas))
c = new_context(resources)
hub_type = canonical_type_name(canonicalName)
%>\
<%namespace name="lib" file="lib/lib.mako"/>\
<%namespace name="mutil" file="lib/util.mako"/>\
Expand All @@ -32,7 +33,7 @@ use std::marker::PhantomData;
use std::borrow::BorrowMut;
use std::cell::RefCell;

pub use cmn::{Hub, Resource, Part, ResponseResult, RequestResult, NestedType};
pub use cmn::{Hub, MethodBuilder, Resource, Part, ResponseResult, RequestResult, NestedType};

// ########
// HUB ###
Expand All @@ -45,7 +46,7 @@ pub use cmn::{Hub, Resource, Part, ResponseResult, RequestResult, NestedType};
/// Instantiate a new hub
///
<%block filter="rust_doc_comment">\
${lib.hub_usage_example()}\
<%lib:hub_usage_example/>\
</%block>
pub struct ${hub_type}<C, NC, A> {
client: RefCell<C>,
Expand All @@ -67,6 +68,12 @@ impl<'a, C, NC, A> ${hub_type}<C, NC, A>
_m: PhantomData,
}
}

% for resource in sorted(c.rta_map.keys()):
pub fn ${resource}(&'a self) -> ${mb_type(resource)}<'a, C, NC, A> {
${mb_type(resource)} { hub: &self }
}
% endfor
}


Expand All @@ -84,4 +91,39 @@ ${schema.new(s, c)}
## We have to find them and process them as normal types
% for s in nested_schemas:
${schema.new(s, c)}
% endfor
% endfor

// ###################
// MethodBuilders ###
// #################

% for resource, methods in c.rta_map.iteritems():
/// A builder providing access to all methods supported on *${singular(resource)}* resources.
/// It is usually not used directly, but through the `${hub_type}` hub.
///
/// # Example
///
/// Instantiate a resource builder
///
<%block filter="rust_doc_test_norun, rust_doc_comment">\
${mutil.test_prelude()}\
<%block filter="rust_test_fn_invisible">\
${lib.test_hub(canonical_type_name(canonicalName))}\
// Usually you wouldn't stick this into a variable, but keep calling `MethodBuilders`
// like ${put_and(sorted('`%s(...)`' % f for f in methods))}
let rb = hub.${resource}();
</%block>
</%block>
pub struct ${mb_type(resource)}<'a, C, NC, A>
where NC: 'a,
C: 'a,
A: 'a, {

hub: &'a ${hub_type}<C, NC, A>
}

impl<'a, C, NC, A> MethodBuilder for ${mb_type(resource)}<'a, C, NC, A> {}
% endfor

10 changes: 2 additions & 8 deletions src/mako/lib/lib.mako
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,13 @@
# fr == fattest resource, the fatter, the more important, right ?
fr = None
fr = sorted(schemas.values(), key=lambda s: (len(c.sta_map.get(s.id, [])), len(s.get('properties', []))), reverse=True)[0]
# resouce -> [activity, ...]
amap = dict()
for an in c.fqan_map:
resource, activity = activity_split(an)
amap.setdefault(resource, list()).append(activity)
%>\
# Features

Handle the following *Resources* with ease ...

% for r in sorted(amap.keys()):
* ${split_camelcase_s(r)} (${put_and(md_italic(sorted(amap[r])))})
% for r in sorted(c.rta_map.keys()):
* ${split_camelcase_s(r)} (${put_and(md_italic(sorted(c.rta_map[r])))})
% endfor

# Structure of this Library
Expand Down
7 changes: 4 additions & 3 deletions src/mako/lib/schema.mako
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<%! from util import (schema_markers, rust_doc_comment, mangle_ident, to_rust_type, put_and, IO_TYPES, activity_split) %>\
<%! from util import (schema_markers, rust_doc_comment, mangle_ident, to_rust_type, put_and,
IO_TYPES, activity_split, enclose_in)
%>\
## Create new schema with everything.
## 's' contains the schema structure from json to build
<%def name="new(s, c)">\
Expand Down Expand Up @@ -34,8 +36,7 @@ ${s.get('description', 'There is no detailed description.')}
# Activities
This type is used in activities, which are methods you may call on this type or where this type is involved in.
The list links the activity name, along with information about where it is used (one of ${put_and(list('*%s*' % t
for t in IO_TYPES))}).
The list links the activity name, along with information about where it is used (one of ${put_and(enclose_in('*', IO_TYPES))}).
${''.join("* %s (%s)\n" % (activity_split(a)[1], iot and '|'.join(iot) or 'none')
for a, iot in c.sta_map[s.id].iteritems())}
Expand Down
99 changes: 58 additions & 41 deletions src/mako/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,17 @@ def put_and(l):
return l[0]
return ', '.join(l[:-1]) + ' and ' + l[-1]

# ['foo', ...] with e == '*' -> ['*foo*', ...]
def enclose_in(e, l):
return ['%s%s%s' % (e, s, e) for s in l]

def md_italic(l):
return ['*%s*' % s for s in l]
return enclose_in('*', l)

def singular(s):
if s[-1] == 's':
return s[:-1]
return s

def split_camelcase_s(s):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1 \2', s)
Expand Down Expand Up @@ -226,44 +235,6 @@ def schema_markers(s, c):
# -------------------------
## @name Activity Utilities
# @{

# Returns (A, B) where
# A: { SchemaTypeName -> { fqan -> ['request'|'response', ...]}
# B: { fqan -> activity_method_data }
# fqan = fully qualified activity name
def build_activity_mappings(activities):
res = dict()
fqan = dict()
for an, a in activities.iteritems():
if 'methods' not in a:
continue
for mn, m in a.methods.iteritems():
assert m.id not in fqan
fqan[m.id] = m
for in_out_type_name in IO_TYPES:
t = m.get(in_out_type_name, None)
if t is None:
continue
tn = to_rust_type(None, None, t, allow_optionals=False)
info = res.setdefault(tn, dict())
io_info = info.setdefault(m.id, [])
io_info.append(in_out_type_name)
# end for each io type

# handle delete/getrating/(possibly others)
# delete: has no response or request
# getrating: response is a 'SomethingResult', which is still related to activities name
# the latter is used to deduce the resource name
an, _ = activity_split(m.id)
tn = activity_name_to_type_name(an)
info = res.setdefault(tn, dict())
if m.id not in info:
info.setdefault(m.id, [])
# end handle other cases
# end for each method
# end for each activity
return res, fqan

# return (name, method)
def activity_split(fqan):
t = fqan.split('.')
Expand All @@ -281,12 +252,54 @@ def iter_acitivities(c):
## -- End Activity Utilities -- @}


Context = collections.namedtuple('Context', ['sta_map', 'fqan_map'])
Context = collections.namedtuple('Context', ['sta_map', 'fqan_map', 'rta_map'])

# return a newly build context from the given data
def new_context(resources):
# Returns (A, B) where
# A: { SchemaTypeName -> { fqan -> ['request'|'response', ...]}
# B: { fqan -> activity_method_data }
# fqan = fully qualified activity name
def build_activity_mappings(activities):
res = dict()
fqan = dict()
for an, a in activities.iteritems():
if 'methods' not in a:
continue
for mn, m in a.methods.iteritems():
assert m.id not in fqan
fqan[m.id] = m
for in_out_type_name in IO_TYPES:
t = m.get(in_out_type_name, None)
if t is None:
continue
tn = to_rust_type(None, None, t, allow_optionals=False)
info = res.setdefault(tn, dict())
io_info = info.setdefault(m.id, [])
io_info.append(in_out_type_name)
# end for each io type

# handle delete/getrating/(possibly others)
# delete: has no response or request
# getrating: response is a 'SomethingResult', which is still related to activities name
# the latter is used to deduce the resource name
an, _ = activity_split(m.id)
tn = activity_name_to_type_name(an)
info = res.setdefault(tn, dict())
if m.id not in info:
info.setdefault(m.id, [])
# end handle other cases
# end for each method
# end for each activity
return res, fqan
# end utility

sta_map, fqan_map = build_activity_mappings(resources)
return Context(sta_map, fqan_map)
rta_map = dict()
for an in fqan_map:
resource, activity = activity_split(an)
rta_map.setdefault(resource, list()).append(activity)
return Context(sta_map, fqan_map, rta_map)

# Expects v to be 'v\d+', throws otherwise
def to_api_version(v):
Expand All @@ -296,3 +309,7 @@ def to_api_version(v):
# build a full library name (non-canonical)
def library_name(name, version):
return name + to_api_version(version)

# return type name of a resource builder, from a resource name
def mb_type(r):
return "%sMethodBuilder" % canonical_type_name(r)
3 changes: 3 additions & 0 deletions src/rust/cmn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use std::marker::MarkerTrait;
/// The hub allows to access all resource methods more easily.
pub trait Hub: MarkerTrait {}

/// Identifies types for building methods of a particular type
pub trait MethodBuilder: MarkerTrait {}

/// Identifies types which can be inserted and deleted.
/// Types with this trait are most commonly used by clients of this API.
pub trait Resource: MarkerTrait {}
Expand Down
2 changes: 1 addition & 1 deletion src/rust/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<'a, C, NC, A> YouTube<C, NC, A>
}

pub fn videos(&'a self) -> videos::Service<'a, C, NC, A> {
videos::Service::new(&self.client, &self.auth)
videos::Service::new(&self)
}
}

Expand Down
24 changes: 7 additions & 17 deletions src/rust/dev/videos/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use rustc_serialize;
use hyper;
use oauth2;

use super::super::YouTube;

/// Reresents all aspects of a youtube video resource. May only be partially
/// available
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
Expand Down Expand Up @@ -75,32 +77,23 @@ pub struct Service<'a, C, NC, A>
C: 'a,
A: 'a, {

client: &'a RefCell<C>,
auth: &'a RefCell<A>,

_m: PhantomData<NC>
hub: &'a YouTube<C, NC, A>
}

impl<'a, C, NC, A> Service<'a, C, NC, A>
where NC: hyper::net::NetworkConnector,
C: BorrowMut<hyper::Client<NC>> + 'a,
A: oauth2::GetToken + 'a {

pub fn new(client: &'a RefCell<C>, authenticator: &'a RefCell<A>) -> Service<'a, C, NC, A> {
Service {
client: client,
auth: authenticator,
_m: PhantomData,
}
pub fn new(hub: &'a YouTube<C, NC, A>) -> Service<'a, C, NC, A> {
Service { hub: hub }
}

pub fn insert(&self, parts: &str, video: &Video) -> VideosInsertBuilder<'a, C, NC, A> {
VideosInsertBuilder {
client: self.client,
auth: self.auth,
hub: self.hub,
video: video.clone(),
parts: parts.to_string(),
_m: PhantomData,
}
}
}
Expand All @@ -110,12 +103,9 @@ pub struct VideosInsertBuilder<'a, C, NC, A>
C: 'a,
A: 'a {

client: &'a RefCell<C>,
auth: &'a RefCell<A>,
hub: &'a YouTube<C, NC, A>,
video: Video,
parts: String,

_m: PhantomData<NC>
}


Expand Down

0 comments on commit 01db890

Please sign in to comment.