mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-01-19 09:50:46 +01:00
Merge branch 'schema'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.timestamp
|
||||
.pyenv
|
||||
*.pyc
|
||||
**target/
|
||||
|
||||
13
Makefile
13
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: json-to-xml clean help api-deps rebuild-apis license
|
||||
.PHONY: json-to-xml clean help api-deps regen-apis license
|
||||
.SUFFIXES:
|
||||
|
||||
include Makefile.helpers
|
||||
@@ -11,12 +11,13 @@ MAKO_RENDER := etc/bin/mako-render
|
||||
TPL := $(PYTHON) $(MAKO_RENDER)
|
||||
|
||||
MAKO_SRC = src/mako
|
||||
RUST_SRC = src/rust
|
||||
API_DEPS_TPL = $(MAKO_SRC)/deps.mako
|
||||
API_DEPS = .api.deps
|
||||
API_SHARED_INFO = etc/api/shared.yaml
|
||||
API_JSON_FILES = $(shell find etc -type f -name '*-api.json')
|
||||
MAKO_LIB_DIR = $(MAKO_SRC)/lib
|
||||
MAKO_LIB_FILES = $(shell find $(MAKO_LIB_DIR) -type f -name '*.mako' -or -name '*.py')
|
||||
MAKO_LIB_FILES = $(shell find $(MAKO_LIB_DIR) -type f -name '*.*')
|
||||
|
||||
help:
|
||||
$(info using template engine: '$(TPL)')
|
||||
@@ -24,7 +25,7 @@ help:
|
||||
$(info Targets)
|
||||
$(info help - print this help)
|
||||
$(info api-deps - generate a file to tell make what API file dependencies will be)
|
||||
$(info rebuild-apis - clear out all generated apis, and regenerate them)
|
||||
$(info regen-apis - clear out all generated apis, and regenerate them)
|
||||
$(info help-api - show all api targets to build individually)
|
||||
$(info license - regenerate the main license file)
|
||||
|
||||
@@ -35,18 +36,18 @@ $(PYTHON):
|
||||
$(MAKO_RENDER): $(PYTHON)
|
||||
|
||||
$(API_DEPS): $(API_SHARED_INFO) $(API_DEPS_TPL) $(MAKO_LIB_FILES) $(MAKO_RENDER)
|
||||
PYTHONPATH=$(MAKO_LIB_DIR) $(TPL) --template-dir '.' -io $(API_DEPS_TPL) --data-files $(API_SHARED_INFO) > $@
|
||||
PYTHONPATH=$(MAKO_LIB_DIR) $(TPL) --template-dir '.' -io $(API_DEPS_TPL)=$@ --data-files $(API_SHARED_INFO)
|
||||
|
||||
api-deps: $(API_DEPS)
|
||||
|
||||
include $(API_DEPS)
|
||||
|
||||
LICENSE.md: $(MAKO_SRC)/LICENSE.md.mako $(API_SHARED_INFO)
|
||||
$(TPL) -io $<=$@ --data-files $(API_SHARED_INFO)
|
||||
PYTHONPATH=$(MAKO_LIB_DIR) $(TPL) -io $<=$@ --data-files $(API_SHARED_INFO)
|
||||
|
||||
license: LICENSE.md
|
||||
|
||||
rebuild-apis: clean-apis apis license
|
||||
regen-apis: clean-apis apis license
|
||||
|
||||
clean: clean-apis
|
||||
-rm -Rf $(VENV_DIR)
|
||||
|
||||
@@ -13,6 +13,9 @@ api:
|
||||
- name: youtube
|
||||
version: v3
|
||||
base_path: "etc/api"
|
||||
terms:
|
||||
# how to actually do something with the API
|
||||
action: do
|
||||
templates:
|
||||
# all output directories are relative to the one set for the respective API
|
||||
- source: README.md
|
||||
|
||||
30
gen/youtube3/LICENSE.md
Normal file
30
gen/youtube3/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
84
gen/youtube3/README.md
Normal file
84
gen/youtube3/README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/README.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The `youtube3` library allows access to all features of *YouTube*.
|
||||
|
||||
# Features
|
||||
|
||||
Handle the following *Resources* with ease ...
|
||||
|
||||
* activities (*insert* and *list*)
|
||||
* channel banners (*insert*)
|
||||
* channel sections (*delete*, *insert*, *list* and *update*)
|
||||
* channels (*list* and *update*)
|
||||
* guide categories (*list*)
|
||||
* i18n languages (*list*)
|
||||
* i18n regions (*list*)
|
||||
* live broadcasts (*bind*, *control*, *delete*, *insert*, *list*, *transition* and *update*)
|
||||
* live streams (*delete*, *insert*, *list* and *update*)
|
||||
* playlist items (*delete*, *insert*, *list* and *update*)
|
||||
* playlists (*delete*, *insert*, *list* and *update*)
|
||||
* search (*list*)
|
||||
* subscriptions (*delete*, *insert* and *list*)
|
||||
* thumbnails (*set*)
|
||||
* video categories (*list*)
|
||||
* videos (*delete*, *getRating*, *insert*, *list*, *rate* and *update*)
|
||||
* watermarks (*set* and *unset*)
|
||||
|
||||
# Structure of this Library
|
||||
|
||||
The API is structured into the following primary items:
|
||||
|
||||
* **Hub**
|
||||
* a central object to maintain state and allow accessing all *Activities*
|
||||
* **Resources**
|
||||
* primary types that you can apply *Activities* to
|
||||
* a collection of properties and *Parts*
|
||||
* **Parts**
|
||||
* a collection of properties
|
||||
* never directly used in *Activities*
|
||||
* **Activities**
|
||||
* operations to apply to *Resources*
|
||||
|
||||
Generally speaking, you can invoke *Activities* like this:
|
||||
|
||||
```Rust,ignore
|
||||
let r = hub.resource().activity(...).do()
|
||||
```
|
||||
|
||||
Or specifically ...
|
||||
|
||||
```ignore
|
||||
let r = hub.videos().rate(...).do()
|
||||
let r = hub.videos().getRating(...).do()
|
||||
let r = hub.videos().list(...).do()
|
||||
let r = hub.videos().insert(...).do()
|
||||
let r = hub.videos().update(...).do()
|
||||
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
|
||||
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.
|
||||
|
||||
# Usage (*TODO*)
|
||||
|
||||
## Instantiating the Hub
|
||||
|
||||
## About error handling
|
||||
|
||||
## About costumization
|
||||
|
||||
[builder-pattern]: http://en.wikipedia.org/wiki/Builder_pattern
|
||||
[google-go-api]: https://github.com/google/google-api-go-client
|
||||
|
||||
|
||||
# License
|
||||
The **youtube3** library was generated by Sebastian Thiel, and is placed
|
||||
under the *MIT* license.
|
||||
You can read the full text at the repository's [license file][repo-license].
|
||||
|
||||
[repo-license]: https://github.com/Byron/google-apis-rsLICENSE.md
|
||||
26
gen/youtube3/cargo.toml
Normal file
26
gen/youtube3/cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "youtube3"
|
||||
version = "0.0.1"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail>"]
|
||||
description = "A complete library to interact with YouTube (protocol v3)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/gen/youtube3/.timestamp"
|
||||
homepage = "https://developers.google.com/youtube/v3"
|
||||
documentation = "http://byron.github.io/google-apis-rs"
|
||||
license = "MIT"
|
||||
keywords = ["youtube", "google", "protocol", "web", "api"]
|
||||
|
||||
[dependencies]
|
||||
# Just to get hyper to work !
|
||||
openssl = "= 0.4.3"
|
||||
# Just to get hyper to work !
|
||||
cookie = "= 0.1.13"
|
||||
hyper = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-oauth2 = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
yup-hyper-mock = "*"
|
||||
21
gen/youtube3/src/cmn.rs
Normal file
21
gen/youtube3/src/cmn.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// COPY OF 'src/rust/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use std::marker::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 {}
|
||||
|
||||
/// Identifies types which are used in API responses.
|
||||
pub trait ResponseResult: MarkerTrait {}
|
||||
|
||||
/// Identifies types which are used in API requests.
|
||||
pub trait RequestResult: MarkerTrait {}
|
||||
|
||||
/// Identifies types which are only used as part of other types, which
|
||||
/// usually are carrying the `Resource` trait.
|
||||
pub trait Part: MarkerTrait {}
|
||||
|
||||
/// Identifies types which are only used by other types internally.
|
||||
/// They have no special meaning, this trait just marks them for completeness.
|
||||
pub trait NestedType: MarkerTrait {}
|
||||
2948
gen/youtube3/src/lib.rs
Normal file
2948
gen/youtube3/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,9 @@
|
||||
## -*- coding: utf-8 -*-
|
||||
<%! import util %>\
|
||||
<%namespace name="mutil" file="lib/util.mako"/>\
|
||||
<%block filter="util.markdown_comment">\
|
||||
<%mutil:gen_info source="${self.uri}" />\
|
||||
</%block>
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<%
|
||||
from util import (markdown_comment, new_context)
|
||||
c = new_context(resources)
|
||||
%>\
|
||||
<%namespace name="lib" file="lib/lib.mako"/>\
|
||||
<%namespace name="util" file="lib/util.mako"/>\
|
||||
<%block filter="markdown_comment">\
|
||||
<%util:gen_info source="${self.uri}" />\
|
||||
</%block>
|
||||
The `${util.library_name()}` library allows access to all features of *${canonicalName}*.
|
||||
|
||||
<%lib:docs />
|
||||
${lib.docs(c)}
|
||||
<%lib:license />
|
||||
@@ -1,8 +1,8 @@
|
||||
<%! import util %>\
|
||||
<%namespace name="mutil" file="lib/util.mako"/>\
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically by '${self.uri}'
|
||||
# DO NOT EDIT !
|
||||
<%block filter="util.hash_comment">\
|
||||
<%mutil:gen_info source="${self.uri}" />\
|
||||
</%block>
|
||||
[package]
|
||||
|
||||
name = "${mutil.library_name()}"
|
||||
@@ -24,8 +24,5 @@ hyper = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-oauth2 = "*"
|
||||
|
||||
[dependencies.cmn]
|
||||
path = "${directories.common}/.."
|
||||
|
||||
[dev-dependencies]
|
||||
yup-hyper-mock = "*"
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<%
|
||||
import util
|
||||
gen_root = directories.output + '/' + a.name + util.to_api_version(a.version)
|
||||
gen_root_stamp = gen_root + '/.timestamp'
|
||||
api_name = util.library_name(a.name, a.version)
|
||||
api_common = gen_root + '/src/cmn.rs'
|
||||
api_clean = api_name + '-clean'
|
||||
# source, destination of individual output files
|
||||
sds = [(directories.mako_src + '/' + i.source + '.mako', gen_root + '/' + i.get('output_dir', '') + '/' + i.source)
|
||||
@@ -16,10 +18,16 @@
|
||||
api_json_inputs = api_json + " $(API_SHARED_INFO)"
|
||||
api_info.append((api_name, api_clean, gen_root))
|
||||
%>\
|
||||
${gen_root}: ${' '.join(i[0] for i in sds)} ${api_json_inputs} $(MAKO_LIB_FILES) $(MAKO_RENDER)
|
||||
PYTHONPATH=$(MAKO_LIB_DIR) $(TPL) --template-dir '.' --var OUTPUT_DIR=$@ -io ${' '.join("%s=%s" % (s, d) for s, d in sds)} --data-files ${api_json_inputs}
|
||||
${api_common}: $(RUST_SRC)/cmn.rs $(lastword $(MAKEFILE_LIST))
|
||||
@ echo "// COPY OF '$<'" > $@
|
||||
@ echo "// DO NOT EDIT" >> $@
|
||||
@cat $< >> $@
|
||||
|
||||
${api_name}: ${gen_root}
|
||||
${gen_root_stamp}: ${' '.join(i[0] for i in sds)} ${api_json_inputs} $(MAKO_LIB_FILES) $(MAKO_RENDER)
|
||||
PYTHONPATH=$(MAKO_LIB_DIR) $(TPL) --template-dir '.' --var OUTPUT_DIR=$@ -io ${' '.join("%s=%s" % (s, d) for s, d in sds)} --data-files ${api_json_inputs}
|
||||
@touch $@
|
||||
|
||||
${api_name}: ${gen_root_stamp} ${api_common}
|
||||
|
||||
${api_clean}:
|
||||
-rm -Rf ${gen_root}
|
||||
|
||||
@@ -1,9 +1,43 @@
|
||||
<% import util %>\
|
||||
<%
|
||||
from util import (iter_nested_types, new_context, rust_comment, rust_module_doc_comment, )
|
||||
nested_schemas = list(iter_nested_types(schemas))
|
||||
|
||||
c = new_context(resources)
|
||||
%>\
|
||||
<%namespace name="lib" file="lib/lib.mako"/>\
|
||||
<%namespace name="mutil" file="lib/util.mako"/>\
|
||||
<%block filter="util.rust_module_doc_comment">\
|
||||
<%lib:docs />\
|
||||
<%namespace name="schema" file="lib/schema.mako"/>\
|
||||
<%block filter="rust_comment">\
|
||||
<%mutil:gen_info source="${self.uri}" />\
|
||||
</%block>
|
||||
extern crate cmn;
|
||||
|
||||
<%block filter="rust_module_doc_comment">\
|
||||
${lib.docs(c)}
|
||||
</%block>
|
||||
#![feature(core)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
extern crate "rustc-serialize" as rustc_serialize;
|
||||
extern crate "yup-oauth2" as oauth2;
|
||||
extern crate "yup-oauth2" as oauth2;
|
||||
|
||||
mod cmn;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use cmn::{Resource, Part, ResponseResult, RequestResult, NestedType};
|
||||
|
||||
// ############
|
||||
// SCHEMAS ###
|
||||
// ##########
|
||||
% for s in schemas.values():
|
||||
${schema.new(s, c)}
|
||||
% endfor
|
||||
|
||||
// ###################
|
||||
// NESTED SCHEMAS ###
|
||||
// #################
|
||||
## some schemas are only used once and basically internal types.
|
||||
## We have to find them and process them as normal types
|
||||
% for s in nested_schemas:
|
||||
${schema.new(s, c)}
|
||||
% endfor
|
||||
@@ -1,14 +1,79 @@
|
||||
<%! import util %>\
|
||||
<%namespace name="util" file="lib/util.mako"/>\
|
||||
<%! from util import (activity_split, put_and, md_italic, split_camelcase_s) %>\
|
||||
<%namespace name="util" file="util.mako"/>\
|
||||
|
||||
<%def name="docs(c)">\
|
||||
<%
|
||||
# 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])))})
|
||||
% endfor
|
||||
|
||||
# Structure of this Library
|
||||
|
||||
The API is structured into the following primary items:
|
||||
|
||||
* **Hub**
|
||||
* a central object to maintain state and allow accessing all *Activities*
|
||||
* **Resources**
|
||||
* primary types that you can apply *Activities* to
|
||||
* a collection of properties and *Parts*
|
||||
* **Parts**
|
||||
* a collection of properties
|
||||
* never directly used in *Activities*
|
||||
* **Activities**
|
||||
* operations to apply to *Resources*
|
||||
|
||||
Generally speaking, you can invoke *Activities* like this:
|
||||
|
||||
```Rust,ignore
|
||||
let r = hub.resource().activity(...).${api.terms.action}()
|
||||
```
|
||||
|
||||
Or specifically ...
|
||||
|
||||
```ignore
|
||||
% for an, a in c.sta_map[fr.id].iteritems():
|
||||
<%
|
||||
resource, activity = activity_split(an)
|
||||
%>\
|
||||
let r = hub.${resource}().${activity}(...).${api.terms.action}()
|
||||
% endfor
|
||||
```
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
# Usage (*TODO*)
|
||||
|
||||
${'##'} Instantiating the Hub
|
||||
|
||||
${'##'} About error handling
|
||||
|
||||
${'##'} About costumization
|
||||
|
||||
[builder-pattern]: http://en.wikipedia.org/wiki/Builder_pattern
|
||||
[google-go-api]: https://github.com/google/google-api-go-client
|
||||
|
||||
<%def name="docs()">\
|
||||
TODO: Library level fully fledged documentation, incuding **summary** and **usage**.
|
||||
And another line, for testing
|
||||
</%def>
|
||||
|
||||
<%def name="license()">\
|
||||
# License
|
||||
The **${util.library_name(name, version)}** library was generated by ${util.put_and(copyright.authors)}, and is placed
|
||||
The **${util.library_name()}** library was generated by ${put_and(copyright.authors)}, and is placed
|
||||
under the *${copyright.license_abbrev}* license.
|
||||
You can read the full text at the repository's [license file][repo-license].
|
||||
|
||||
|
||||
46
src/mako/lib/schema.mako
Normal file
46
src/mako/lib/schema.mako
Normal file
@@ -0,0 +1,46 @@
|
||||
<%! from util import (schema_markers, rust_doc_comment, mangle_ident, to_rust_type, put_and, IO_TYPES, activity_split) %>\
|
||||
## Create new schema with everything.
|
||||
## 's' contains the schema structure from json to build
|
||||
<%def name="new(s, c)">\
|
||||
<%
|
||||
assert s.type == "object"
|
||||
markers = schema_markers(s, c)
|
||||
%>\
|
||||
<%block filter="rust_doc_comment">\
|
||||
${doc(s, c)}\
|
||||
</%block>
|
||||
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
|
||||
pub struct ${s.id}\
|
||||
% if 'properties' in s:
|
||||
{
|
||||
% for pn, p in s.properties.iteritems():
|
||||
${p.get('description', 'no description provided') | rust_doc_comment}
|
||||
pub ${mangle_ident(pn)}: ${to_rust_type(s.id, pn, p)},
|
||||
% endfor
|
||||
}
|
||||
% else:
|
||||
;
|
||||
% endif
|
||||
|
||||
% for marker_trait in markers:
|
||||
impl ${marker_trait} for ${s.id} {}
|
||||
% endfor
|
||||
</%def>
|
||||
|
||||
<%def name="doc(s, c)">\
|
||||
${s.get('description', 'There is no detailed description.')}
|
||||
% if s.id in c.sta_map:
|
||||
|
||||
# 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))}).
|
||||
|
||||
${''.join("* %s (%s)\n" % (activity_split(a)[1], iot and '|'.join(iot) or 'none')
|
||||
for a, iot in c.sta_map[s.id].iteritems())}
|
||||
% else:
|
||||
|
||||
This type is not used in any activity, and only used as *part* of another schema.
|
||||
% endif
|
||||
</%def>
|
||||
@@ -1,5 +1,12 @@
|
||||
<%! import util %>\
|
||||
|
||||
## source should be ${self.uri}
|
||||
## you need to escape the output, using a filter for example
|
||||
<%def name="gen_info(source)">\
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from '${source}'
|
||||
DO NOT EDIT !\
|
||||
</%def>
|
||||
|
||||
## This will only work within a substitution, not within python code
|
||||
<%def name="to_api_version(v)">\
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
import re
|
||||
import collections
|
||||
|
||||
re_linestart = re.compile('^', flags=re.MULTILINE)
|
||||
|
||||
USE_FORMAT = 'use_format_field'
|
||||
TYPE_MAP = {'boolean' : 'bool',
|
||||
'integer' : USE_FORMAT,
|
||||
'number' : USE_FORMAT,
|
||||
'uint32' : 'u32',
|
||||
'double' : 'f64',
|
||||
'int32' : 'i32',
|
||||
'array' : 'Vec',
|
||||
'string' : 'String',
|
||||
'object' : 'HashMap'}
|
||||
TREF = '$ref'
|
||||
IO_RESPONSE = 'response'
|
||||
IO_REQUEST = 'request'
|
||||
IO_TYPES = (IO_REQUEST, IO_RESPONSE)
|
||||
INS_METHOD = 'insert'
|
||||
DEL_METHOD = 'delete'
|
||||
|
||||
NESTED_TYPE_MARKER = 'is_nested'
|
||||
|
||||
# ==============================================================================
|
||||
## @name Filters
|
||||
# ------------------------------------------------------------------------------
|
||||
## @{
|
||||
|
||||
# rust module doc comment filter
|
||||
def rust_module_doc_comment(s):
|
||||
return re_linestart.sub('//! ', s)
|
||||
@@ -9,10 +35,31 @@ def rust_module_doc_comment(s):
|
||||
def rust_doc_comment(s):
|
||||
return re_linestart.sub('/// ', s)
|
||||
|
||||
# Expects v to be 'v\d+', throws otherwise
|
||||
def to_api_version(v):
|
||||
assert len(v) >= 2 and v[0] == 'v'
|
||||
return v[1:]
|
||||
# rust comment filter
|
||||
def rust_comment(s):
|
||||
return re_linestart.sub('// ', s)
|
||||
|
||||
# hash-based comment filter
|
||||
def hash_comment(s):
|
||||
return re_linestart.sub('# ', s)
|
||||
|
||||
# markdown comments
|
||||
def markdown_comment(s):
|
||||
nl = ''
|
||||
if not s.endswith('\n'):
|
||||
nl = '\n'
|
||||
return "<!---\n%s%s-->" % (s, nl)
|
||||
|
||||
# escape each string in l with "s" and return the new list
|
||||
def estr(l):
|
||||
return ['"%s"' % i for i in l]
|
||||
|
||||
## -- End Filters -- @}
|
||||
|
||||
# ==============================================================================
|
||||
## @name Natural Language Utilities
|
||||
# ------------------------------------------------------------------------------
|
||||
## @{
|
||||
|
||||
# l must be a list, if it is more than one, 'and' will before last item
|
||||
# l will also be coma-separtated
|
||||
@@ -22,9 +69,190 @@ def put_and(l):
|
||||
return l[0]
|
||||
return ', '.join(l[:-1]) + ' and ' + l[-1]
|
||||
|
||||
# escape each string in l with "s" and return the new list
|
||||
def estr(l):
|
||||
return ['"%s"' % i for i in l]
|
||||
def md_italic(l):
|
||||
return ['*%s*' % s for s in l]
|
||||
|
||||
def split_camelcase_s(s):
|
||||
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1 \2', s)
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1 \2', s1).lower()
|
||||
|
||||
## -- End Natural Language Utilities -- @}
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
## @name Rust TypeSystem
|
||||
# ------------------------------------------------------------------------------
|
||||
## @{
|
||||
|
||||
|
||||
|
||||
def nested_type_name(sn, pn):
|
||||
return sn + pn.capitalize()
|
||||
|
||||
# Make properties which are reserved keywords usable
|
||||
def mangle_ident(n):
|
||||
if n == 'type':
|
||||
return n + '_'
|
||||
return n
|
||||
|
||||
# map a json type to an rust type
|
||||
# sn = schema name
|
||||
# pn = property name
|
||||
# t = type dict
|
||||
def to_rust_type(sn, pn, t, allow_optionals=True):
|
||||
def nested_type(nt):
|
||||
if nt.get('items', None) is not None:
|
||||
nt = nt.items
|
||||
elif nt.get('additionalProperties'):
|
||||
nt = nt.additionalProperties
|
||||
else:
|
||||
assert(is_nested_type_property(nt))
|
||||
# It's a nested type - we take it literally like $ref, but generate a name for the type ourselves
|
||||
# This of course assumes
|
||||
return nested_type_name(sn, pn)
|
||||
return to_rust_type(sn, pn, nt, allow_optionals=False)
|
||||
|
||||
# unconditionally handle $ref types, which should point to another schema.
|
||||
if TREF in t:
|
||||
tn = t[TREF]
|
||||
if allow_optionals:
|
||||
return "Option<%s>" % tn
|
||||
return tn
|
||||
try:
|
||||
is_pod = True
|
||||
rust_type = TYPE_MAP[t.type]
|
||||
if t.type == 'array':
|
||||
rust_type = "%s<%s>" % (rust_type, nested_type(t))
|
||||
is_pod = False
|
||||
elif t.type == 'object':
|
||||
rust_type = "%s<String, %s>" % (rust_type, nested_type(t))
|
||||
is_pod = False
|
||||
elif t.type == 'string' and 'Count' in pn:
|
||||
rust_type = 'i64'
|
||||
elif rust_type == USE_FORMAT:
|
||||
rust_type = TYPE_MAP[t.format]
|
||||
if is_pod and allow_optionals:
|
||||
return "Option<%s>" % rust_type
|
||||
return rust_type
|
||||
except KeyError as err:
|
||||
raise AssertionError("%s: Property type '%s' unknown - add new type mapping: %s" % (str(err), t.type, str(t)))
|
||||
except AttributeError as err:
|
||||
raise AssertionError("%s: unknown dict layout: %s" % (str(err), t))
|
||||
|
||||
# return True if this property is actually a nested type
|
||||
def is_nested_type_property(t):
|
||||
return 'type' in t and t.type == 'object' and 'properties' in t
|
||||
|
||||
# Return True if the schema is nested
|
||||
def is_nested_type(s):
|
||||
return NESTED_TYPE_MARKER in s
|
||||
|
||||
# return an iterator yielding fake-schemas that identify a nested type
|
||||
def iter_nested_types(schemas):
|
||||
for s in schemas.values():
|
||||
if 'properties' not in s:
|
||||
continue
|
||||
for pn, p in s.properties.iteritems():
|
||||
if is_nested_type_property(p):
|
||||
ns = p.copy()
|
||||
ns.id = nested_type_name(s.id, pn)
|
||||
ns[NESTED_TYPE_MARKER] = True
|
||||
yield ns
|
||||
# end for ach property
|
||||
# end for aech schma
|
||||
|
||||
# Return sorted type names of all markers applicable to the given schema
|
||||
def schema_markers(s, c):
|
||||
res = set()
|
||||
|
||||
activities = c.sta_map.get(s.id, dict())
|
||||
if len(activities) == 0:
|
||||
res.add('Part')
|
||||
else:
|
||||
# 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')
|
||||
if IO_RESPONSE in iot:
|
||||
res.add('ResponseResult')
|
||||
if IO_REQUEST in iot:
|
||||
res.add('RequestResult')
|
||||
# end for each activity
|
||||
# end handle activites
|
||||
|
||||
if is_nested_type(s):
|
||||
res.add('NestedType')
|
||||
|
||||
return sorted(res)
|
||||
|
||||
## -- End Rust TypeSystem -- @}
|
||||
|
||||
|
||||
# -------------------------
|
||||
## @name Activity Utilities
|
||||
# @{
|
||||
|
||||
# Returns (A, B) where
|
||||
# A: { SchemaTypeName -> { fqan -> ['request'|'response', ...]}
|
||||
# B: { fqan -> activity_method }
|
||||
# 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('.')
|
||||
assert len(t) == 3
|
||||
return t[1:]
|
||||
|
||||
# videos -> Video
|
||||
def activity_name_to_type_name(an):
|
||||
return an.capitalize()[:-1]
|
||||
|
||||
## -- End Activity Utilities -- @}
|
||||
|
||||
|
||||
Context = collections.namedtuple('Context', ['sta_map', 'fqan_map'])
|
||||
|
||||
# return a newly build context from the given data
|
||||
def new_context(resources):
|
||||
sta_map, fqan_map = build_activity_mappings(resources)
|
||||
return Context(sta_map, fqan_map)
|
||||
|
||||
# Expects v to be 'v\d+', throws otherwise
|
||||
def to_api_version(v):
|
||||
assert len(v) >= 2 and v[0] == 'v'
|
||||
return v[1:]
|
||||
|
||||
# build a full library name (non-canonical)
|
||||
def library_name(name, version):
|
||||
|
||||
19
src/rust/cmn.rs
Normal file
19
src/rust/cmn.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use std::marker::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 {}
|
||||
|
||||
/// Identifies types which are used in API responses.
|
||||
pub trait ResponseResult: MarkerTrait {}
|
||||
|
||||
/// Identifies types which are used in API requests.
|
||||
pub trait RequestResult: MarkerTrait {}
|
||||
|
||||
/// Identifies types which are only used as part of other types, which
|
||||
/// usually are carrying the `Resource` trait.
|
||||
pub trait Part: MarkerTrait {}
|
||||
|
||||
/// Identifies types which are only used by other types internally.
|
||||
/// They have no special meaning, this trait just marks them for completeness.
|
||||
pub trait NestedType: MarkerTrait {}
|
||||
@@ -1,8 +1,12 @@
|
||||
#![feature(core)]
|
||||
//! library with code shared by all generated implementations
|
||||
extern crate hyper;
|
||||
extern crate "rustc-serialize" as rustc_serialize;
|
||||
extern crate "yup-oauth2" as oauth2;
|
||||
|
||||
// just pull it in the check if it compiles
|
||||
mod cmn;
|
||||
|
||||
/// This module is for testing only, its code is used in mako templates
|
||||
#[cfg(test)]
|
||||
mod dev;
|
||||
Reference in New Issue
Block a user