feat(doit): initial part writing

We are a state-machine, and handle parts of it correctly.
However, we don't yet write the boundary at all, and could improve
our use of match.
This commit is contained in:
Sebastian Thiel
2015-03-18 17:53:39 +01:00
parent fc589cb965
commit 71c827b306
9 changed files with 65 additions and 337 deletions

View File

@@ -21,6 +21,3 @@ mime = "*"
url = "*"
rustc-serialize = "*"
yup-oauth2 = "*"
[dev-dependencies]
yup-hyper-mock = "*"

View File

@@ -619,9 +619,9 @@ else {
let mut body_reader: &mut io::Read = match ${simple_media_param.type.arg_name}.as_mut() {
Some(&mut (ref mut reader, size, ref mime)) => {
let rsize = request_value_reader.seek(io::SeekFrom::End(0)).unwrap();
request_value_reader.seek(io::SeekFrom::Start(0)).ok();
mp_reader = mp_reader.add_part(&mut request_value_reader, rsize, &json_mime_type)
.add_part(reader, size, mime);
request_value_reader.seek(io::SeekFrom::Start(0)).unwrap();
mp_reader.add_part(&mut request_value_reader, rsize, &json_mime_type)
.add_part(reader, size, mime);
content_type = ContentType(mp_reader.mime_type());
&mut mp_reader
}
@@ -675,7 +675,7 @@ else {
if !res.status.is_success() {
if ${delegate}.is_some() {
let mut json_err = String::new();
res.read_to_string(&mut json_err).ok();
res.read_to_string(&mut json_err).unwrap();
let error_info: cmn::JsonServerError = json::decode(&json_err).unwrap();
if let oauth2::Retry::After(d) = ${delegate_call}.http_failure(&res, error_info) {
sleep(d);
@@ -686,7 +686,7 @@ else {
}
% if response_schema:
let mut json_response = String::new();
res.read_to_string(&mut json_response).ok();
res.read_to_string(&mut json_response).unwrap();
let result_value = (res, json::decode(&json_response).unwrap());
% else:
let result_value = res;

View File

@@ -1,10 +1,11 @@
use std::marker::MarkerTrait;
use std::io::{self, Read, Seek, Cursor};
use std::io::{self, Read, Seek, Cursor, Write, SeekFrom};
use mime::{Mime, TopLevel, SubLevel, Attr, Value};
use oauth2;
use hyper;
use hyper::header::{ContentType, ContentLength, Headers};
use hyper::http::LINE_ENDING;
/// Identifies the Hub. There is only one per library, this trait is supposed
/// to make intended use more explicit.
@@ -145,7 +146,7 @@ impl<'a> MultiPartReader<'a> {
/// # Panics
///
/// If this method is called after the first `read` call, it will panic
pub fn add_part(mut self, reader: &'a mut Read, size: u64, mime_type: &Mime) -> MultiPartReader<'a> {
pub fn add_part(&mut self, reader: &'a mut Read, size: u64, mime_type: &Mime) -> &mut MultiPartReader<'a> {
let mut headers = Headers::new();
headers.set(ContentType(mime_type.clone()));
headers.set(ContentLength(size));
@@ -166,6 +167,36 @@ impl<'a> MultiPartReader<'a> {
impl<'a> Read for MultiPartReader<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::from_os_error(0))
if self.raw_parts.len() == 0 && self.current_part.is_none() {
return Ok(0)
} else if self.raw_parts.len() > 0 && self.current_part.is_none() {
let (headers, reader) = self.raw_parts.remove(0);
let mut c = Cursor::new(Vec::<u8>::new());
write!(&mut c, "{}{}", headers, LINE_ENDING).unwrap();
c.seek(SeekFrom::Start(0)).unwrap();
self.current_part = Some((c, reader));
}
// read headers as long as possible
let (hb, rr) = {
let &mut (ref mut c, ref mut reader) = self.current_part.as_mut().unwrap();
let b = c.read(buf).unwrap_or(0);
(b, reader.read(&mut buf[b..]))
};
match rr {
Ok(bytes_read) => {
if bytes_read == 0 {
// We are depleted - this can trigger the next part to come in
self.current_part = None;
}
Ok(hb + bytes_read)
}
Err(err) => {
// fail permanently
self.current_part = None;
self.raw_parts.clear();
Err(err)
}
}
}
}

View File

@@ -1,28 +0,0 @@
/// Identifies the an OAuth2 authorization scope.
/// A scope is needed when requesting an
/// [authorization token](https://developers.google.com/youtube/v3/guides/authentication).
pub enum Scope {
/// Manage your YouTube account
Account,
/// View your YouTube account
AccountReadOnly,
/// Manage your YouTube videos, which includes uploads and meta-data changes
Video,
/// View and manage your assets and associated content on YouTube
Partner,
/// View private information of your YouTube channel relevant during the
/// audit process with a YouTube partner.
Audit,
}
impl Str for Scope {
fn as_slice(&self) -> &str {
match *self {
Scope::Account => "https://www.googleapis.com/auth/youtube",
Scope::AccountReadOnly => "https://www.googleapis.com/auth/youtube.readonly",
Scope::Video => "https://www.googleapis.com/auth/youtube.upload",
Scope::Partner => "https://www.googleapis.com/auth/youtubepartner",
Scope::Audit => "https://www.googleapis.com/auth/youtubepartner-channel-audit",
}
}
}

View File

@@ -1,168 +0,0 @@
#![feature(core)]
//!
//! # Usage
//!
//! ```test_harness
//! extern crate "youtube3-dev" as youtube3;
//! extern crate hyper;
//!
//! # #[test]
//! # fn test() {
//! # // TODO - generate !
//! # }
//! ```
use std::marker::PhantomData;
use std::cell::RefCell;
use std::borrow::BorrowMut;
use std::default::Default;
use hyper;
use oauth2;
mod common;
pub mod videos;
/// Central instance to access all youtube related services
pub struct YouTube<C, NC, A> {
client: RefCell<C>,
auth: RefCell<A>,
_m: PhantomData<NC>
}
impl<'a, C, NC, A> YouTube<C, NC, A>
where NC: hyper::net::NetworkConnector,
C: BorrowMut<hyper::Client<NC>> + 'a,
A: oauth2::GetToken {
pub fn new(client: C, authenticator: A) -> YouTube<C, NC, A> {
YouTube {
client: RefCell::new(client),
auth: RefCell::new(authenticator),
_m: PhantomData,
}
}
pub fn videos(&'a self) -> videos::Service<'a, C, NC, A> {
videos::Service::new(&self)
}
pub fn channel_sections(&'a self) -> ChannelSectionMethodsBuilder<'a, C, NC, A> {
ChannelSectionMethodsBuilder { hub: &self }
}
}
pub fn new<C, NC, A>(client: C, authenticator: A) -> YouTube<C, NC, A>
where NC: hyper::net::NetworkConnector,
C: BorrowMut<hyper::Client<NC>>,
A: oauth2::GetToken {
YouTube::new(client, authenticator)
}
#[cfg(test)]
mod tests {
use super::*;
use hyper;
use oauth2;
use std::default::Default;
#[test]
fn instantiate() {
let secret = <oauth2::ApplicationSecret as Default>::default();
let auth = oauth2::Authenticator::new(
&secret,
oauth2::DefaultAuthenticatorDelegate,
hyper::Client::new(),
<oauth2::MemoryStorage as Default>::default(),
None);
let yt = YouTube::new(hyper::Client::new(), auth);
let v = yt.videos().insert("snippet", &Default::default());
}
#[test] fn helper_test() {
use std::default::Default;
use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage};
let secret: ApplicationSecret = Default::default();
let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate,
hyper::Client::new(),
<MemoryStorage as Default>::default(), None);
let mut hub = YouTube::new(hyper::Client::new(), auth);
let result = hub.channel_sections().insert()
.delegate(&mut DefaultDelegate)
.doit();
}
}
pub struct ChannelSectionMethodsBuilder<'a, C, NC, A>
where NC: 'a,
C: 'a,
A: 'a, {
hub: &'a YouTube<C, NC, A>,
}
impl<'a, C, NC, A> ChannelSectionMethodsBuilder<'a, C, NC, A> {
/// Create a builder to help you perform the following task:
///
/// Adds a channelSection for the authenticated user's channel.
pub fn insert(&self) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
ChannelSectionInsertMethodBuilder {
hub: self.hub,
_delegate: Default::default(),
_part: None,
}
}
}
pub struct ChannelSectionInsertMethodBuilder<'a, C, NC, A>
where NC: 'a,
C: 'a,
A: 'a, {
hub: &'a YouTube<C, NC, A>,
_delegate: Option<&'a mut Delegate>,
_part: Option<String>,
}
impl<'a, C, NC, A> ChannelSectionInsertMethodBuilder<'a, C, NC, A> where NC: hyper::net::NetworkConnector, C: BorrowMut<hyper::Client<NC>> + 'a, A: oauth2::GetToken {
/// Perform the operation you have build so far.
/// TODO: Build actual call
pub fn doit(mut self) -> () {
let mut params: Vec<(&str, String)> = Vec::with_capacity(1);
if self._part.is_none() {
self._part = Some("parts from request value".to_string());
}
if self._delegate.is_some() {
self._delegate.as_mut().unwrap().connection_error(hyper::HttpError::HttpStatusError);
}
}
pub fn delegate(mut self, new_value: &'a mut Delegate) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
self._delegate = Some(new_value);
self
}
}
pub trait Delegate {
/// Called whenever there is an HttpError, usually if there are network problems.
///
/// Return retry information.
fn connection_error(&mut self, hyper::HttpError) -> oauth2::Retry {
oauth2::Retry::Abort
}
}
#[derive(Default)]
pub struct DefaultDelegate;
impl Delegate for DefaultDelegate {}

View File

@@ -1,3 +0,0 @@
pub use self::service::*;
mod service;

View File

@@ -1,126 +0,0 @@
use std::cell::RefCell;
use std::borrow::BorrowMut;
use std::marker::PhantomData;
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)]
pub struct Video {
pub snippet: Option<VideoSnippet>,
pub recordingDetails: Option<VideoRecordingDetails>,
pub status: Option<VideoStatus>,
}
#[allow(non_snake_case)]
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
pub struct VideoSnippet {
pub categoryId: String,
pub description: String,
pub tags: Vec<String>,
pub title: String,
pub status: Option<VideoStatus>,
pub recordingDetails: Option<VideoRecordingDetails>,
}
impl Video {
fn parts(&self) -> String {
let mut res = String::new();
if self.status.is_some() {
res = res + "status,";
}
if self.recordingDetails.is_some() {
res = res + "recordingDetails";
}
if self.snippet.is_some() {
res = res + "snippet,";
}
res
}
}
#[allow(non_snake_case)]
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
pub struct VideoStatus {
pub privacyStatus: String,
pub embeddable: bool,
pub license: String,
pub publicStatsViewable: bool,
pub publishAt: String,
}
#[allow(non_snake_case)]
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
pub struct VideoRecordingDetails {
locationDescription: String,
recordingDate: String,
}
#[allow(non_snake_case)]
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
pub struct GeoPoint {
altitude: f64,
latitude: f64,
longitude: f64,
}
/// The videos service - provides actual functionality through builders.
pub struct Service<'a, C, NC, A>
where NC: 'a,
C: 'a,
A: 'a, {
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(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 {
hub: self.hub,
video: video.clone(),
parts: parts.to_string(),
}
}
}
pub struct VideosInsertBuilder<'a, C, NC, A>
where NC: 'a,
C: 'a,
A: 'a {
hub: &'a YouTube<C, NC, A>,
video: Video,
parts: String,
}
impl<'a, C, NC, A> VideosInsertBuilder<'a, C, NC, A>
where NC: hyper::net::NetworkConnector,
C: BorrowMut<hyper::Client<NC>> + 'a,
A: oauth2::GetToken + 'a {
}
#[cfg(test)]
mod tests {
use std::default::Default;
use super::*;
}

View File

@@ -1,6 +1,8 @@
#![feature(core,io)]
#![allow(dead_code)]
//! library with code shared by all generated implementations
extern crate hyper;
extern crate mime;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "yup-oauth2" as oauth2;
@@ -9,4 +11,26 @@ mod cmn;
/// This module is for testing only, its code is used in mako templates
#[cfg(test)]
mod dev;
mod tests {
extern crate "yup-hyper-mock" as hyper_mock;
use super::cmn::*;
use self::hyper_mock::*;
use std::io::Read;
use std::default::Default;
#[test]
fn multi_part_reader() {
let mut r1 = MockStream::with_input(b"foo");
let mut r2 = MockStream::with_input(b"bar");
let mut mpr: MultiPartReader = Default::default();
mpr.add_part(&mut r1, 50, &"application/json".parse().unwrap())
.add_part(&mut r2, 25, &"application/plain".parse().unwrap());
let mut res = String::new();
assert_eq!(mpr.read_to_string(&mut res), Ok(57));
println!("{}", res);
assert!(false);
}
}