From 615a12465415cfa155271ce2fb94be9faa7405db Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 4 Mar 2015 11:05:41 +0100 Subject: [PATCH] feat(hub): generate hub implementation and docs This includes docs for the library usage. It's totally great to be able to paste example code right were it belongs, and also put the same elsewhere to compose more complex docs. --- gen/youtube3/README.md | 29 ++++++++++++- gen/youtube3/src/cmn.rs | 5 +++ gen/youtube3/src/lib.rs | 96 +++++++++++++++++++++++++++++++++++++++-- src/mako/README.md.mako | 2 +- src/mako/lib.rs.mako | 50 +++++++++++++++++++-- src/mako/lib/lib.mako | 53 +++++++++++++++++++++-- src/mako/lib/util.mako | 9 ++++ src/mako/lib/util.py | 34 ++++++++++++--- src/rust/cmn.rs | 5 +++ 9 files changed, 264 insertions(+), 19 deletions(-) diff --git a/gen/youtube3/README.md b/gen/youtube3/README.md index 9ff8020c6c..eeed44c776 100644 --- a/gen/youtube3/README.md +++ b/gen/youtube3/README.md @@ -60,7 +60,7 @@ let r = hub.videos().delete(...).do() ``` The `resource()` and `activity(...)` calls create [builders][builder-pattern]. The second one dealing with `Activities` -supports various methods to configure the impending operation. It is made such that all required arguments have to be +supports various methods to configure the impending operation (not shown here). It is made such that all required arguments have to be specified right away (i.e. `(...)`), whereas all optional ones can be [build up][builder-pattern] as desired. The `do()` method performs the actual communication with the server and returns the respective result. @@ -68,9 +68,34 @@ The `do()` method performs the actual communication with the server and returns ## Instantiating the Hub +```Rust +extern crate hyper; +extern crate "yup-oauth2" as oauth2; +extern crate "rustc-serialize" as rustc_serialize; +extern crate youtube3; + +use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage}; +use std::default::Default; + +use youtube3::YouTube; + +// Get an ApplicationSecret instance by some means. It contains the `client_id` and `client_secret`, +// among other things. +let secret: ApplicationSecret = Default::default(); +// Instantiate the authenticator. It will choose a suitable authentication flow for you, +// unless you replace `None` with the desired Flow +// Provide your own `AuthenticatorDelegate` to adjust the way it operates and get feedback about what's going on +// You probably want to bring in your own `TokenStorage` to persist tokens and retrieve them from storage. +let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate, + hyper::Client::new(), + ::default(), None); +let mut hub = YouTube::new(hyper::Client::new(), auth); +``` + +**TODO** Example calls - there should soon be a generator able to do that with proper inputs ## About error handling -## About costumization +## About Customization/Callbacks [builder-pattern]: http://en.wikipedia.org/wiki/Builder_pattern [google-go-api]: https://github.com/google/google-api-go-client diff --git a/gen/youtube3/src/cmn.rs b/gen/youtube3/src/cmn.rs index ccbad6cc41..d55dbfa1df 100644 --- a/gen/youtube3/src/cmn.rs +++ b/gen/youtube3/src/cmn.rs @@ -2,6 +2,11 @@ // DO NOT EDIT use std::marker::MarkerTrait; +/// Identifies the Hub. There is only one per library, this trait is supposed +/// to make intended use more explicit. +/// The hub allows to access all resource methods more easily. +pub trait Hub: 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 {} diff --git a/gen/youtube3/src/lib.rs b/gen/youtube3/src/lib.rs index b7f40d6b0c..685297a4b2 100644 --- a/gen/youtube3/src/lib.rs +++ b/gen/youtube3/src/lib.rs @@ -57,7 +57,7 @@ //! ``` //! //! The `resource()` and `activity(...)` calls create [builders][builder-pattern]. The second one dealing with `Activities` -//! supports various methods to configure the impending operation. It is made such that all required arguments have to be +//! supports various methods to configure the impending operation (not shown here). It is made such that all required arguments have to be //! specified right away (i.e. `(...)`), whereas all optional ones can be [build up][builder-pattern] as desired. //! The `do()` method performs the actual communication with the server and returns the respective result. //! @@ -65,9 +65,36 @@ //! //! ## Instantiating the Hub //! +//! ```test_harness,no_run +//! extern crate hyper; +//! extern crate "yup-oauth2" as oauth2; +//! extern crate "rustc-serialize" as rustc_serialize; +//! extern crate youtube3; +//! +//! # #[test] fn egal() { +//! use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage}; +//! use std::default::Default; +//! +//! use youtube3::YouTube; +//! +//! // Get an ApplicationSecret instance by some means. It contains the `client_id` and `client_secret`, +//! // among other things. +//! let secret: ApplicationSecret = Default::default(); +//! // Instantiate the authenticator. It will choose a suitable authentication flow for you, +//! // unless you replace `None` with the desired Flow +//! // Provide your own `AuthenticatorDelegate` to adjust the way it operates and get feedback about what's going on +//! // You probably want to bring in your own `TokenStorage` to persist tokens and retrieve them from storage. +//! let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate, +//! hyper::Client::new(), +//! ::default(), None); +//! let mut hub = YouTube::new(hyper::Client::new(), auth); +//! # } +//! ``` +//! +//! **TODO** Example calls - there should soon be a generator able to do that with proper inputs //! ## About error handling //! -//! ## About costumization +//! ## About Customization/Callbacks //! //! [builder-pattern]: http://en.wikipedia.org/wiki/Builder_pattern //! [google-go-api]: https://github.com/google/google-api-go-client @@ -77,14 +104,77 @@ #![feature(core)] #![allow(non_snake_case)] +extern crate hyper; extern crate "rustc-serialize" as rustc_serialize; extern crate "yup-oauth2" as oauth2; mod cmn; use std::collections::HashMap; +use std::marker::PhantomData; +use std::borrow::BorrowMut; +use std::cell::RefCell; + +pub use cmn::{Hub, Resource, Part, ResponseResult, RequestResult, NestedType}; + +// ######## +// HUB ### +// ###### + +/// Central instance to access all YouTube related resource activities +/// +/// # Examples +/// +/// Instantiate a new hub +/// +/// ```test_harness,no_run +/// extern crate hyper; +/// extern crate "yup-oauth2" as oauth2; +/// extern crate "rustc-serialize" as rustc_serialize; +/// extern crate youtube3; +/// +/// # #[test] fn egal() { +/// use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage}; +/// use std::default::Default; +/// +/// use youtube3::YouTube; +/// +/// // Get an ApplicationSecret instance by some means. It contains the `client_id` and `client_secret`, +/// // among other things. +/// let secret: ApplicationSecret = Default::default(); +/// // Instantiate the authenticator. It will choose a suitable authentication flow for you, +/// // unless you replace `None` with the desired Flow +/// // Provide your own `AuthenticatorDelegate` to adjust the way it operates and get feedback about what's going on +/// // You probably want to bring in your own `TokenStorage` to persist tokens and retrieve them from storage. +/// let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate, +/// hyper::Client::new(), +/// ::default(), None); +/// let mut hub = YouTube::new(hyper::Client::new(), auth); +/// # } +/// ``` +/// +pub struct YouTube { + client: RefCell, + auth: RefCell, + _m: PhantomData +} + +impl<'a, C, NC, A> Hub for YouTube {} + +impl<'a, C, NC, A> YouTube + where NC: hyper::net::NetworkConnector, + C: BorrowMut> + 'a, + A: oauth2::GetToken { + + pub fn new(client: C, authenticator: A) -> YouTube { + YouTube { + client: RefCell::new(client), + auth: RefCell::new(authenticator), + _m: PhantomData, + } + } +} -pub use cmn::{Resource, Part, ResponseResult, RequestResult, NestedType}; // ############ // SCHEMAS ### diff --git a/src/mako/README.md.mako b/src/mako/README.md.mako index 73f3b72b09..75e131829f 100644 --- a/src/mako/README.md.mako +++ b/src/mako/README.md.mako @@ -9,5 +9,5 @@ The `${util.library_name()}` library allows access to all features of *${canonicalName}*. -${lib.docs(c)} +${lib.docs(c, rust_doc=False)} <%lib:license /> \ No newline at end of file diff --git a/src/mako/lib.rs.mako b/src/mako/lib.rs.mako index 819b832452..69ca9fd4f0 100644 --- a/src/mako/lib.rs.mako +++ b/src/mako/lib.rs.mako @@ -1,8 +1,12 @@ <% - from util import (iter_nested_types, new_context, rust_comment, rust_module_doc_comment, ) + 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) nested_schemas = list(iter_nested_types(schemas)) - c = new_context(resources) + c = new_context(resources) + + hub_type = canonical_type_name(canonicalName) %>\ <%namespace name="lib" file="lib/lib.mako"/>\ <%namespace name="mutil" file="lib/util.mako"/>\ @@ -17,14 +21,54 @@ ${lib.docs(c)} #![feature(core)] #![allow(non_snake_case)] +extern crate hyper; extern crate "rustc-serialize" as rustc_serialize; extern crate "yup-oauth2" as oauth2; mod cmn; use std::collections::HashMap; +use std::marker::PhantomData; +use std::borrow::BorrowMut; +use std::cell::RefCell; + +pub use cmn::{Hub, Resource, Part, ResponseResult, RequestResult, NestedType}; + +// ######## +// HUB ### +// ###### + +/// Central instance to access all ${hub_type} related resource activities +/// +/// # Examples +/// +/// Instantiate a new hub +/// +<%block filter="rust_doc_comment">\ +${lib.hub_usage_example()}\ + +pub struct ${hub_type} { + client: RefCell, + auth: RefCell, + _m: PhantomData +} + +impl<'a, C, NC, A> Hub for ${hub_type} {} + +impl<'a, C, NC, A> ${hub_type} + where NC: hyper::net::NetworkConnector, + C: BorrowMut> + 'a, + A: oauth2::GetToken { + + pub fn new(client: C, authenticator: A) -> ${hub_type} { + ${hub_type} { + client: RefCell::new(client), + auth: RefCell::new(authenticator), + _m: PhantomData, + } + } +} -pub use cmn::{Resource, Part, ResponseResult, RequestResult, NestedType}; // ############ // SCHEMAS ### diff --git a/src/mako/lib/lib.mako b/src/mako/lib/lib.mako index 65319e8918..b2d6eec209 100644 --- a/src/mako/lib/lib.mako +++ b/src/mako/lib/lib.mako @@ -1,7 +1,10 @@ -<%! from util import (activity_split, put_and, md_italic, split_camelcase_s) %>\ +<%! from util import (activity_split, put_and, md_italic, split_camelcase_s, canonical_type_name, + rust_test_fn_invisible, rust_doc_test_norun, rust_doc_comment, markdown_rust_block) %>\ <%namespace name="util" file="util.mako"/>\ -<%def name="docs(c)">\ +## If rust-doc is True, examples will be made to work for rust doc tests. Otherwise they are set +## for github markdown. +<%def name="docs(c, rust_doc=True)">\ <% # fr == fattest resource, the fatter, the more important, right ? fr = None @@ -54,7 +57,7 @@ let r = hub.${resource}().${activity}(...).${api.terms.action}() ``` The `resource()` and `activity(...)` calls create [builders][builder-pattern]. The second one dealing with `Activities` -supports various methods to configure the impending operation. It is made such that all required arguments have to be +supports various methods to configure the impending operation (not shown here). It is made such that all required arguments have to be specified right away (i.e. `(...)`), whereas all optional ones can be [build up][builder-pattern] as desired. The `${api.terms.action}()` method performs the actual communication with the server and returns the respective result. @@ -62,15 +65,57 @@ The `${api.terms.action}()` method performs the actual communication with the se ${'##'} Instantiating the Hub +${self.hub_usage_example(rust_doc)}\ + +**TODO** Example calls - there should soon be a generator able to do that with proper inputs ${'##'} About error handling -${'##'} About costumization +${'##'} About Customization/Callbacks [builder-pattern]: http://en.wikipedia.org/wiki/Builder_pattern [google-go-api]: https://github.com/google/google-api-go-client +## Sets up a hub ready for use. You must wrap it into a test function for it to work +## Needs test_prelude. +<%def name="test_hub(hub_type)">\ +use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage}; +use std::default::Default; + +use ${util.library_name()}::${hub_type}; + +// Get an ApplicationSecret instance by some means. It contains the `client_id` and `client_secret`, +// among other things. +let secret: ApplicationSecret = Default::default(); +// Instantiate the authenticator. It will choose a suitable authentication flow for you, +// unless you replace `None` with the desired Flow +// Provide your own `AuthenticatorDelegate` to adjust the way it operates and get feedback about what's going on +// You probably want to bring in your own `TokenStorage` to persist tokens and retrieve them from storage. +let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate, + hyper::Client::new(), + ::default(), None); +let mut hub = ${hub_type}::new(hyper::Client::new(), auth);\ + + +## You will still have to set the filter for your comment type - either nothing, or rust_doc_comment ! +<%def name="hub_usage_example(rust_doc=True)">\ +<% + test_filter = rust_test_fn_invisible + main_filter = rust_doc_test_norun + if not rust_doc: + test_filter = lambda s: s + main_filter = markdown_rust_block +%>\ +<%block filter="main_filter">\ +${util.test_prelude()}\ + +<%block filter="test_filter">\ +${self.test_hub(canonical_type_name(canonicalName))}\ + + + + <%def name="license()">\ # License The **${util.library_name()}** library was generated by ${put_and(copyright.authors)}, and is placed diff --git a/src/mako/lib/util.mako b/src/mako/lib/util.mako index 8101652bdf..bc4e2bcda9 100644 --- a/src/mako/lib/util.mako +++ b/src/mako/lib/util.mako @@ -21,4 +21,13 @@ ${cargo.repo_base_url}/${OUTPUT_DIR}\ <%def name="library_name()">\ ${util.library_name(name, version)}\ + + +## All crates and standard `use` declaration, required for all examples +## Must be outside of a test function +<%def name="test_prelude()">\ +extern crate hyper; +extern crate "yup-oauth2" as oauth2; +extern crate "rustc-serialize" as rustc_serialize; +extern crate ${self.library_name()}; \ No newline at end of file diff --git a/src/mako/lib/util.py b/src/mako/lib/util.py index 9119948ce2..57295457c5 100644 --- a/src/mako/lib/util.py +++ b/src/mako/lib/util.py @@ -43,12 +43,27 @@ def rust_comment(s): def hash_comment(s): return re_linestart.sub('# ', s) +# return s, with trailing newline +def trailing_newline(s): + if not s.endswith('\n'): + return s + '\n' + return s + +# a rust test that doesn't run though +def rust_doc_test_norun(s): + return "```test_harness,no_run\n%s```" % trailing_newline(s) + +# a rust code block in (github) markdown +def markdown_rust_block(s): + return "```Rust\n%s```" % trailing_newline(s) + +# wraps s into an invisible doc test function. +def rust_test_fn_invisible(s): + return "# #[test] fn egal() {\n%s# }" % trailing_newline(s) + # markdown comments def markdown_comment(s): - nl = '' - if not s.endswith('\n'): - nl = '\n' - return "" % (s, nl) + return "" % trailing_newline(s) # escape each string in l with "s" and return the new list def estr(l): @@ -84,7 +99,10 @@ def split_camelcase_s(s): # ------------------------------------------------------------------------------ ## @{ - +# Return transformed string that could make a good type name +def canonical_type_name(s): + # can't use s.capitalize() as it will lower-case the remainder of the string + return s[:1].upper() + s[1:] def nested_type_name(sn, pn): return sn + pn.capitalize() @@ -194,7 +212,7 @@ def schema_markers(s, c): # Returns (A, B) where # A: { SchemaTypeName -> { fqan -> ['request'|'response', ...]} -# B: { fqan -> activity_method } +# B: { fqan -> activity_method_data } # fqan = fully qualified activity name def build_activity_mappings(activities): res = dict() @@ -239,6 +257,10 @@ def activity_split(fqan): def activity_name_to_type_name(an): return an.capitalize()[:-1] +# yields (resource, activity, activity_data) +def iter_acitivities(c): + return ((activity_split(an) + [a]) for an, a in c.fqan_map.iteritems()) + ## -- End Activity Utilities -- @} diff --git a/src/rust/cmn.rs b/src/rust/cmn.rs index 8dde454d26..928c549f0b 100644 --- a/src/rust/cmn.rs +++ b/src/rust/cmn.rs @@ -1,5 +1,10 @@ use std::marker::MarkerTrait; +/// Identifies the Hub. There is only one per library, this trait is supposed +/// to make intended use more explicit. +/// The hub allows to access all resource methods more easily. +pub trait Hub: 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 {}