<commit_before>#![allow(unstable)]

extern crate curl;
extern crate "rustc-serialize" as rustc_serialize;

use std::fmt;
use std::io::{self, fs, MemReader, MemWriter, File};
use std::collections::HashMap;
use std::io::util::ChainedReader;
use std::result;

use curl::http;
use curl::http::handle::Method::{Put, Get, Delete};
use curl::http::handle::{Method, Request};
use rustc_serialize::json;

pub struct Registry {
    host: String,
    token: Option<String>,
    handle: http::Handle,
}

pub type Result<T> = result::Result<T, Error>;

#[derive(PartialEq, Copy)]
pub enum Auth {
    Authorized,
    Unauthorized
}

pub enum Error {
    Curl(curl::ErrCode),
    NotOkResponse(http::Response),
    NonUtf8Body,
    Api(Vec<String>),
    Unauthorized,
    TokenMissing,
    Io(io::IoError),
}

#[derive(RustcDecodable)]
pub struct Crate {
    pub name: String,
    pub description: Option<String>,
    pub max_version: String
}

#[derive(RustcEncodable)]
pub struct NewCrate {
    pub name: String,
    pub vers: String,
    pub deps: Vec<NewCrateDependency>,
    pub features: HashMap<String, Vec<String>>,
    pub authors: Vec<String>,
    pub description: Option<String>,
    pub documentation: Option<String>,
    pub homepage: Option<String>,
    pub readme: Option<String>,
    pub keywords: Vec<String>,
    pub license: Option<String>,
    pub license_file: Option<String>,
    pub repository: Option<String>,
}

#[derive(RustcEncodable)]
pub struct NewCrateDependency {
    pub optional: bool,
    pub default_features: bool,
    pub name: String,
    pub features: Vec<String>,
    pub version_req: String,
    pub target: Option<String>,
    pub kind: String,
}

#[derive(RustcDecodable)]
pub struct User {
    pub id: u32,
    pub login: String,
    pub avatar: String,
    pub email: Option<String>,
    pub name: Option<String>,
}

#[derive(RustcDecodable)] struct R { ok: bool }
#[derive(RustcDecodable)] struct ApiErrorList { errors: Vec<ApiError> }
#[derive(RustcDecodable)] struct ApiError { detail: String }
#[derive(RustcEncodable)] struct OwnersReq<'a> { users: &'a [&'a str] }
#[derive(RustcDecodable)] struct Users { users: Vec<User> }
#[derive(RustcDecodable)] struct Crates { crates: Vec<Crate> }

impl Registry {
    pub fn new(host: String, token: Option<String>) -> Registry {
        Registry::new_handle(host, token, http::Handle::new())
    }

    pub fn new_handle(host: String, token: Option<String>,
                      handle: http::Handle) -> Registry {
        Registry {
            host: host,
            token: token,
            handle: handle,
        }
    }

    pub fn add_owners(&mut self, krate: &str, owners: &[&str]) -> Result<()> {
        let body = json::encode(&OwnersReq { users: owners });
        let body = try!(self.put(format!("/crates/{}/owners", krate),
                                 body.as_bytes()));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    pub fn remove_owners(&mut self, krate: &str, owners: &[&str]) -> Result<()> {
        let body = json::encode(&OwnersReq { users: owners });
        let body = try!(self.delete(format!("/crates/{}/owners", krate),
                                    Some(body.as_bytes())));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    pub fn list_owners(&mut self, krate: &str) -> Result<Vec<User>> {
        let body = try!(self.get(format!("/crates/{}/owners", krate)));
        Ok(json::decode::<Users>(body.as_slice()).unwrap().users)
    }

    pub fn publish(&mut self, krate: &NewCrate, tarball: &Path) -> Result<()> {
        let json = json::encode(krate);
        // Prepare the body. The format of the upload request is:
        //
        //      <le u32 of json>
        //      <json request> (metadata for the package)
        //      <le u32 of tarball>
        //      <source tarball>
        let stat = try!(fs::stat(tarball).map_err(Error::Io));
        let header = {
            let mut w = MemWriter::new();
            w.write_le_u32(json.len() as u32).unwrap();
            w.write_str(json.as_slice()).unwrap();
            w.write_le_u32(stat.size as u32).unwrap();
            MemReader::new(w.into_inner())
        };
        let tarball = try!(File::open(tarball).map_err(Error::Io));
        let size = stat.size as usize + header.get_ref().len();
        let mut body = ChainedReader::new(vec![Box::new(header) as Box<Reader>,
                                               Box::new(tarball) as Box<Reader>].into_iter());

        let url = format!("{}/api/v1/crates/new", self.host);

        let token = try!(self.token.as_ref().ok_or(Error::TokenMissing)).as_slice();
        let request = self.handle.put(url, &mut body)
            .content_length(size)
            .header("Accept", "application/json")
            .header("Authorization", token);
        let response = handle(request.exec());
        let _body = try!(response);
        Ok(())
    }

    pub fn search(&mut self, query: &str) -> Result<Vec<Crate>> {
        let body = try!(self.req(format!("/crates?q={}", query), None, Get, Auth::Unauthorized));

        Ok(json::decode::<Crates>(body.as_slice()).unwrap().crates)
    }

    pub fn yank(&mut self, krate: &str, version: &str) -> Result<()> {
        let body = try!(self.delete(format!("/crates/{}/{}/yank", krate, version),
                                    None));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    pub fn unyank(&mut self, krate: &str, version: &str) -> Result<()> {
        let body = try!(self.put(format!("/crates/{}/{}/unyank", krate, version),
                                 &[]));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    fn put(&mut self, path: String, b: &[u8]) -> Result<String> {
        self.req(path, Some(b), Put, Auth::Authorized)
    }

    fn get(&mut self, path: String) -> Result<String> {
        self.req(path, None, Get, Auth::Authorized)
    }

    fn delete(&mut self, path: String, b: Option<&[u8]>) -> Result<String> {
        self.req(path, b, Delete, Auth::Authorized)
    }

    fn req(&mut self, path: String, body: Option<&[u8]>,
           method: Method, authorized: Auth) -> Result<String> {
        let mut req = Request::new(&mut self.handle, method)
                              .uri(format!("{}/api/v1{}", self.host, path))
                              .header("Accept", "application/json")
                              .content_type("application/json");

        if authorized == Auth::Authorized {
            let token = try!(self.token.as_ref().ok_or(Error::TokenMissing)).as_slice();
            req = req.header("Authorization", token);
        }
        match body {
            Some(b) => req = req.body(b),
            None => {}
        }
        handle(req.exec())
    }
}

fn handle(response: result::Result<http::Response, curl::ErrCode>)
          -> Result<String> {
    let response = try!(response.map_err(Error::Curl));
    match response.get_code() {
        0 => {} // file upload url sometimes
        200 => {}
        403 => return Err(Error::Unauthorized),
        _ => return Err(Error::NotOkResponse(response))
    }

    let body = match String::from_utf8(response.move_body()) {
        Ok(body) => body,
        Err(..) => return Err(Error::NonUtf8Body),
    };
    match json::decode::<ApiErrorList>(body.as_slice()) {
        Ok(errors) => {
            return Err(Error::Api(errors.errors.into_iter().map(|s| s.detail)
                                        .collect()))
        }
        Err(..) => {}
    }
    Ok(body)
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::NonUtf8Body => write!(f, "reponse body was not utf-8"),
            Error::Curl(ref err) => write!(f, "http error: {}", err),
            Error::NotOkResponse(ref resp) => {
                write!(f, "failed to get a 200 OK response: {}", resp)
            }
            Error::Api(ref errs) => {
                write!(f, "api errors: {}", errs.connect(", "))
            }
            Error::Unauthorized => write!(f, "unauthorized API access"),
            Error::TokenMissing => write!(f, "no upload token found, please run `cargo login`"),
            Error::Io(ref e) => write!(f, "io error: {}", e),
        }
    }
}
<commit_msg>fix build on recent rust: use `unwrap` on `encode`<commit_after>#![allow(unstable)]

extern crate curl;
extern crate "rustc-serialize" as rustc_serialize;

use std::fmt;
use std::io::{self, fs, MemReader, MemWriter, File};
use std::collections::HashMap;
use std::io::util::ChainedReader;
use std::result;

use curl::http;
use curl::http::handle::Method::{Put, Get, Delete};
use curl::http::handle::{Method, Request};
use rustc_serialize::json;

pub struct Registry {
    host: String,
    token: Option<String>,
    handle: http::Handle,
}

pub type Result<T> = result::Result<T, Error>;

#[derive(PartialEq, Copy)]
pub enum Auth {
    Authorized,
    Unauthorized
}

pub enum Error {
    Curl(curl::ErrCode),
    NotOkResponse(http::Response),
    NonUtf8Body,
    Api(Vec<String>),
    Unauthorized,
    TokenMissing,
    Io(io::IoError),
}

#[derive(RustcDecodable)]
pub struct Crate {
    pub name: String,
    pub description: Option<String>,
    pub max_version: String
}

#[derive(RustcEncodable)]
pub struct NewCrate {
    pub name: String,
    pub vers: String,
    pub deps: Vec<NewCrateDependency>,
    pub features: HashMap<String, Vec<String>>,
    pub authors: Vec<String>,
    pub description: Option<String>,
    pub documentation: Option<String>,
    pub homepage: Option<String>,
    pub readme: Option<String>,
    pub keywords: Vec<String>,
    pub license: Option<String>,
    pub license_file: Option<String>,
    pub repository: Option<String>,
}

#[derive(RustcEncodable)]
pub struct NewCrateDependency {
    pub optional: bool,
    pub default_features: bool,
    pub name: String,
    pub features: Vec<String>,
    pub version_req: String,
    pub target: Option<String>,
    pub kind: String,
}

#[derive(RustcDecodable)]
pub struct User {
    pub id: u32,
    pub login: String,
    pub avatar: String,
    pub email: Option<String>,
    pub name: Option<String>,
}

#[derive(RustcDecodable)] struct R { ok: bool }
#[derive(RustcDecodable)] struct ApiErrorList { errors: Vec<ApiError> }
#[derive(RustcDecodable)] struct ApiError { detail: String }
#[derive(RustcEncodable)] struct OwnersReq<'a> { users: &'a [&'a str] }
#[derive(RustcDecodable)] struct Users { users: Vec<User> }
#[derive(RustcDecodable)] struct Crates { crates: Vec<Crate> }

impl Registry {
    pub fn new(host: String, token: Option<String>) -> Registry {
        Registry::new_handle(host, token, http::Handle::new())
    }

    pub fn new_handle(host: String, token: Option<String>,
                      handle: http::Handle) -> Registry {
        Registry {
            host: host,
            token: token,
            handle: handle,
        }
    }

    pub fn add_owners(&mut self, krate: &str, owners: &[&str]) -> Result<()> {
        let body = json::encode(&OwnersReq { users: owners }).unwrap();
        let body = try!(self.put(format!("/crates/{}/owners", krate),
                                 body.as_bytes()));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    pub fn remove_owners(&mut self, krate: &str, owners: &[&str]) -> Result<()> {
        let body = json::encode(&OwnersReq { users: owners }).unwrap();
        let body = try!(self.delete(format!("/crates/{}/owners", krate),
                                    Some(body.as_bytes())));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    pub fn list_owners(&mut self, krate: &str) -> Result<Vec<User>> {
        let body = try!(self.get(format!("/crates/{}/owners", krate)));
        Ok(json::decode::<Users>(body.as_slice()).unwrap().users)
    }

    pub fn publish(&mut self, krate: &NewCrate, tarball: &Path) -> Result<()> {
        let json = json::encode(krate).unwrap();
        // Prepare the body. The format of the upload request is:
        //
        //      <le u32 of json>
        //      <json request> (metadata for the package)
        //      <le u32 of tarball>
        //      <source tarball>
        let stat = try!(fs::stat(tarball).map_err(Error::Io));
        let header = {
            let mut w = MemWriter::new();
            w.write_le_u32(json.len() as u32).unwrap();
            w.write_str(json.as_slice()).unwrap();
            w.write_le_u32(stat.size as u32).unwrap();
            MemReader::new(w.into_inner())
        };
        let tarball = try!(File::open(tarball).map_err(Error::Io));
        let size = stat.size as usize + header.get_ref().len();
        let mut body = ChainedReader::new(vec![Box::new(header) as Box<Reader>,
                                               Box::new(tarball) as Box<Reader>].into_iter());

        let url = format!("{}/api/v1/crates/new", self.host);

        let token = try!(self.token.as_ref().ok_or(Error::TokenMissing)).as_slice();
        let request = self.handle.put(url, &mut body)
            .content_length(size)
            .header("Accept", "application/json")
            .header("Authorization", token);
        let response = handle(request.exec());
        let _body = try!(response);
        Ok(())
    }

    pub fn search(&mut self, query: &str) -> Result<Vec<Crate>> {
        let body = try!(self.req(format!("/crates?q={}", query), None, Get, Auth::Unauthorized));

        Ok(json::decode::<Crates>(body.as_slice()).unwrap().crates)
    }

    pub fn yank(&mut self, krate: &str, version: &str) -> Result<()> {
        let body = try!(self.delete(format!("/crates/{}/{}/yank", krate, version),
                                    None));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    pub fn unyank(&mut self, krate: &str, version: &str) -> Result<()> {
        let body = try!(self.put(format!("/crates/{}/{}/unyank", krate, version),
                                 &[]));
        assert!(json::decode::<R>(body.as_slice()).unwrap().ok);
        Ok(())
    }

    fn put(&mut self, path: String, b: &[u8]) -> Result<String> {
        self.req(path, Some(b), Put, Auth::Authorized)
    }

    fn get(&mut self, path: String) -> Result<String> {
        self.req(path, None, Get, Auth::Authorized)
    }

    fn delete(&mut self, path: String, b: Option<&[u8]>) -> Result<String> {
        self.req(path, b, Delete, Auth::Authorized)
    }

    fn req(&mut self, path: String, body: Option<&[u8]>,
           method: Method, authorized: Auth) -> Result<String> {
        let mut req = Request::new(&mut self.handle, method)
                              .uri(format!("{}/api/v1{}", self.host, path))
                              .header("Accept", "application/json")
                              .content_type("application/json");

        if authorized == Auth::Authorized {
            let token = try!(self.token.as_ref().ok_or(Error::TokenMissing)).as_slice();
            req = req.header("Authorization", token);
        }
        match body {
            Some(b) => req = req.body(b),
            None => {}
        }
        handle(req.exec())
    }
}

fn handle(response: result::Result<http::Response, curl::ErrCode>)
          -> Result<String> {
    let response = try!(response.map_err(Error::Curl));
    match response.get_code() {
        0 => {} // file upload url sometimes
        200 => {}
        403 => return Err(Error::Unauthorized),
        _ => return Err(Error::NotOkResponse(response))
    }

    let body = match String::from_utf8(response.move_body()) {
        Ok(body) => body,
        Err(..) => return Err(Error::NonUtf8Body),
    };
    match json::decode::<ApiErrorList>(body.as_slice()) {
        Ok(errors) => {
            return Err(Error::Api(errors.errors.into_iter().map(|s| s.detail)
                                        .collect()))
        }
        Err(..) => {}
    }
    Ok(body)
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::NonUtf8Body => write!(f, "reponse body was not utf-8"),
            Error::Curl(ref err) => write!(f, "http error: {}", err),
            Error::NotOkResponse(ref resp) => {
                write!(f, "failed to get a 200 OK response: {}", resp)
            }
            Error::Api(ref errs) => {
                write!(f, "api errors: {}", errs.connect(", "))
            }
            Error::Unauthorized => write!(f, "unauthorized API access"),
            Error::TokenMissing => write!(f, "no upload token found, please run `cargo login`"),
            Error::Io(ref e) => write!(f, "io error: {}", e),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! retry
//!
//! this is an abstraction over the regular http get request. it allows you to
//! have a request retry until it succeeds, with a configurable number of
//! of attempts and a backoff strategy. It also takes care of automatically
//! deserializing responses and handles headers in a sane way.

use std::borrow::Cow;
use std::io::Read;
use std::time::Duration;

use reqwest::header;
use reqwest::{self, Method, Request};

use serde;
use serde_json;
use serde_xml_rs;

use errors::*;
use retry::Retry;

use retry::raw_deserializer;

pub trait Deserializer {
    fn deserialize<T, R>(&self, R) -> Result<T>
    where
        T: for<'de> serde::Deserialize<'de>,
        R: Read;
    fn content_type(&self) -> header::HeaderValue;
}

#[derive(Debug, Clone, Copy)]
pub struct Xml;

impl Deserializer for Xml {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
    where
        T: for<'de> serde::Deserialize<'de>,
        R: Read,
    {
        serde_xml_rs::deserialize(r).chain_err(|| "failed xml deserialization")
    }
    fn content_type(&self) -> header::HeaderValue {
        header::HeaderValue::from_static("text/xml; charset=utf-8")
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Json;

impl Deserializer for Json {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
    where
        T: serde::de::DeserializeOwned,
        R: Read,
    {
        serde_json::from_reader(r).chain_err(|| "failed json deserialization")
    }
    fn content_type(&self) -> header::HeaderValue {
        header::HeaderValue::from_static("application/json")
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Raw;

impl Deserializer for Raw {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
    where
        T: for<'de> serde::Deserialize<'de>,
        R: Read,
    {
        raw_deserializer::from_reader(r).chain_err(|| "failed raw deserialization")
    }
    fn content_type(&self) -> header::HeaderValue {
        header::HeaderValue::from_static("text/plain; charset=utf-8")
    }
}

#[derive(Debug, Clone)]
pub struct Client {
    client: reqwest::Client,
    headers: header::HeaderMap,
    retry: Retry,
    return_on_404: bool,
}

impl Client {
    pub fn try_new() -> Result<Self> {
        let client = reqwest::Client::builder()
            .build()
            .chain_err(|| "failed to initialize client")?;
        Ok(Client {
            client,
            headers: header::HeaderMap::new(),
            retry: Retry::new(),
            return_on_404: false,
        })
    }

    pub fn header(mut self, k: header::HeaderName, v: header::HeaderValue) -> Self {
        self.headers.append(k, v);
        self
    }

    pub fn initial_backoff(mut self, initial_backoff: Duration) -> Self {
        self.retry = self.retry.initial_backoff(initial_backoff);
        self
    }

    pub fn max_backoff(mut self, max_backoff: Duration) -> Self {
        self.retry = self.retry.max_backoff(max_backoff);
        self
    }

    /// max_attempts will panic if the argument is greater than 500
    pub fn max_attempts(mut self, max_attempts: u32) -> Self {
        self.retry = self.retry.max_attempts(max_attempts);
        self
    }

    pub fn return_on_404(mut self, return_on_404: bool) -> Self {
        self.return_on_404 = return_on_404;
        self
    }

    pub fn get<D>(&self, d: D, url: String) -> RequestBuilder<D>
    where
        D: Deserializer,
    {
        RequestBuilder {
            url,
            body: None,
            d,
            client: self.client.clone(),
            headers: self.headers.clone(),
            retry: self.retry.clone(),
            return_on_404: self.return_on_404,
        }
    }

    pub fn post<D>(&self, d: D, url: String, body: Option<Cow<str>>) -> RequestBuilder<D>
    where
        D: Deserializer,
    {
        RequestBuilder {
            url,
            body: body.map(|b| b.into_owned()),
            d,
            client: self.client.clone(),
            headers: self.headers.clone(),
            retry: self.retry.clone(),
            return_on_404: self.return_on_404,
        }
    }
}

pub struct RequestBuilder<D>
where
    D: Deserializer,
{
    url: String,
    body: Option<String>,
    d: D,
    client: reqwest::Client,
    headers: header::HeaderMap,
    retry: Retry,
    return_on_404: bool,
}

impl<D> RequestBuilder<D>
where
    D: Deserializer,
{
    pub fn header(mut self, k: header::HeaderName, v: header::HeaderValue) -> Self {
        self.headers.append(k, v);
        self
    }

    pub fn send<T>(self) -> Result<Option<T>>
    where
        T: for<'de> serde::Deserialize<'de>,
    {
        let url = reqwest::Url::parse(self.url.as_str()).chain_err(|| "failed to parse uri")?;
        let mut req = Request::new(Method::GET, url);
        req.headers_mut().extend(self.headers.clone().into_iter());

        self.retry.clone().retry(|attempt| {
            info!("Fetching {}: Attempt #{}", req.url(), attempt + 1);
            self.dispatch_request(&req)
        })
    }

    pub fn dispatch_post(self) -> Result<reqwest::StatusCode> {
        let url = reqwest::Url::parse(self.url.as_str()).chain_err(|| "failed to parse uri")?;

        self.retry.clone().retry(|attempt| {
            let mut builder = reqwest::Client::new()
                .post(url.clone())
                .headers(self.headers.clone())
                .header(header::CONTENT_TYPE, self.d.content_type());
            if let Some(ref content) = self.body {
                builder = builder.body(content.clone());
            };
            let req = builder
                .build()
                .chain_err(|| "failed to build POST request")?;

            info!("Posting {}: Attempt #{}", req.url(), attempt + 1);
            let status = self
                .client
                .execute(req)
                .chain_err(|| "failed to POST request")?
                .status();
            if status.is_success() {
                Ok(status)
            } else {
                Err(format!("POST failed: {}", status).into())
            }
        })
    }

    fn dispatch_request<T>(&self, req: &Request) -> Result<Option<T>>
    where
        T: for<'de> serde::Deserialize<'de>,
    {
        match self.client.execute(clone_request(req)) {
            Ok(resp) => match (resp.status(), self.return_on_404) {
                (reqwest::StatusCode::OK, _) => {
                    info!("Fetch successful");
                    self.d
                        .deserialize(resp)
                        .map(Some)
                        .chain_err(|| "failed to deserialize data")
                }
                (reqwest::StatusCode::NOT_FOUND, true) => {
                    info!("Fetch failed with 404: resource not found");
                    Ok(None)
                }
                (s, _) => {
                    info!("Failed to fetch: {}", s);
                    Err(format!("failed to fetch: {}", s).into())
                }
            },
            Err(e) => {
                info!("Failed to fetch: {}", e);
                Err(Error::with_chain(e, "failed to fetch"))
            }
        }
    }
}

/// Reqwests Request struct doesn't implement `Clone`,
/// so we have to do it here.
fn clone_request(req: &Request) -> Request {
    let mut newreq = Request::new(req.method().clone(), req.url().clone());
    newreq
        .headers_mut()
        .extend(req.headers().clone().into_iter());
    newreq
}
<commit_msg>retry: fix clippy warning<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! retry
//!
//! this is an abstraction over the regular http get request. it allows you to
//! have a request retry until it succeeds, with a configurable number of
//! of attempts and a backoff strategy. It also takes care of automatically
//! deserializing responses and handles headers in a sane way.

use std::borrow::Cow;
use std::io::Read;
use std::time::Duration;

use reqwest::header;
use reqwest::{self, Method, Request};

use serde;
use serde_json;
use serde_xml_rs;

use errors::*;
use retry::Retry;

use retry::raw_deserializer;

pub trait Deserializer {
    fn deserialize<T, R>(&self, R) -> Result<T>
    where
        T: for<'de> serde::Deserialize<'de>,
        R: Read;
    fn content_type(&self) -> header::HeaderValue;
}

#[derive(Debug, Clone, Copy)]
pub struct Xml;

impl Deserializer for Xml {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
    where
        T: for<'de> serde::Deserialize<'de>,
        R: Read,
    {
        serde_xml_rs::deserialize(r).chain_err(|| "failed xml deserialization")
    }
    fn content_type(&self) -> header::HeaderValue {
        header::HeaderValue::from_static("text/xml; charset=utf-8")
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Json;

impl Deserializer for Json {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
    where
        T: serde::de::DeserializeOwned,
        R: Read,
    {
        serde_json::from_reader(r).chain_err(|| "failed json deserialization")
    }
    fn content_type(&self) -> header::HeaderValue {
        header::HeaderValue::from_static("application/json")
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Raw;

impl Deserializer for Raw {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
    where
        T: for<'de> serde::Deserialize<'de>,
        R: Read,
    {
        raw_deserializer::from_reader(r).chain_err(|| "failed raw deserialization")
    }
    fn content_type(&self) -> header::HeaderValue {
        header::HeaderValue::from_static("text/plain; charset=utf-8")
    }
}

#[derive(Debug, Clone)]
pub struct Client {
    client: reqwest::Client,
    headers: header::HeaderMap,
    retry: Retry,
    return_on_404: bool,
}

impl Client {
    pub fn try_new() -> Result<Self> {
        let client = reqwest::Client::builder()
            .build()
            .chain_err(|| "failed to initialize client")?;
        Ok(Client {
            client,
            headers: header::HeaderMap::new(),
            retry: Retry::new(),
            return_on_404: false,
        })
    }

    pub fn header(mut self, k: header::HeaderName, v: header::HeaderValue) -> Self {
        self.headers.append(k, v);
        self
    }

    pub fn initial_backoff(mut self, initial_backoff: Duration) -> Self {
        self.retry = self.retry.initial_backoff(initial_backoff);
        self
    }

    pub fn max_backoff(mut self, max_backoff: Duration) -> Self {
        self.retry = self.retry.max_backoff(max_backoff);
        self
    }

    /// max_attempts will panic if the argument is greater than 500
    pub fn max_attempts(mut self, max_attempts: u32) -> Self {
        self.retry = self.retry.max_attempts(max_attempts);
        self
    }

    pub fn return_on_404(mut self, return_on_404: bool) -> Self {
        self.return_on_404 = return_on_404;
        self
    }

    pub fn get<D>(&self, d: D, url: String) -> RequestBuilder<D>
    where
        D: Deserializer,
    {
        RequestBuilder {
            url,
            body: None,
            d,
            client: self.client.clone(),
            headers: self.headers.clone(),
            retry: self.retry.clone(),
            return_on_404: self.return_on_404,
        }
    }

    pub fn post<D>(&self, d: D, url: String, body: Option<Cow<str>>) -> RequestBuilder<D>
    where
        D: Deserializer,
    {
        RequestBuilder {
            url,
            body: body.map(Cow::into_owned),
            d,
            client: self.client.clone(),
            headers: self.headers.clone(),
            retry: self.retry.clone(),
            return_on_404: self.return_on_404,
        }
    }
}

pub struct RequestBuilder<D>
where
    D: Deserializer,
{
    url: String,
    body: Option<String>,
    d: D,
    client: reqwest::Client,
    headers: header::HeaderMap,
    retry: Retry,
    return_on_404: bool,
}

impl<D> RequestBuilder<D>
where
    D: Deserializer,
{
    pub fn header(mut self, k: header::HeaderName, v: header::HeaderValue) -> Self {
        self.headers.append(k, v);
        self
    }

    pub fn send<T>(self) -> Result<Option<T>>
    where
        T: for<'de> serde::Deserialize<'de>,
    {
        let url = reqwest::Url::parse(self.url.as_str()).chain_err(|| "failed to parse uri")?;
        let mut req = Request::new(Method::GET, url);
        req.headers_mut().extend(self.headers.clone().into_iter());

        self.retry.clone().retry(|attempt| {
            info!("Fetching {}: Attempt #{}", req.url(), attempt + 1);
            self.dispatch_request(&req)
        })
    }

    pub fn dispatch_post(self) -> Result<reqwest::StatusCode> {
        let url = reqwest::Url::parse(self.url.as_str()).chain_err(|| "failed to parse uri")?;

        self.retry.clone().retry(|attempt| {
            let mut builder = reqwest::Client::new()
                .post(url.clone())
                .headers(self.headers.clone())
                .header(header::CONTENT_TYPE, self.d.content_type());
            if let Some(ref content) = self.body {
                builder = builder.body(content.clone());
            };
            let req = builder
                .build()
                .chain_err(|| "failed to build POST request")?;

            info!("Posting {}: Attempt #{}", req.url(), attempt + 1);
            let status = self
                .client
                .execute(req)
                .chain_err(|| "failed to POST request")?
                .status();
            if status.is_success() {
                Ok(status)
            } else {
                Err(format!("POST failed: {}", status).into())
            }
        })
    }

    fn dispatch_request<T>(&self, req: &Request) -> Result<Option<T>>
    where
        T: for<'de> serde::Deserialize<'de>,
    {
        match self.client.execute(clone_request(req)) {
            Ok(resp) => match (resp.status(), self.return_on_404) {
                (reqwest::StatusCode::OK, _) => {
                    info!("Fetch successful");
                    self.d
                        .deserialize(resp)
                        .map(Some)
                        .chain_err(|| "failed to deserialize data")
                }
                (reqwest::StatusCode::NOT_FOUND, true) => {
                    info!("Fetch failed with 404: resource not found");
                    Ok(None)
                }
                (s, _) => {
                    info!("Failed to fetch: {}", s);
                    Err(format!("failed to fetch: {}", s).into())
                }
            },
            Err(e) => {
                info!("Failed to fetch: {}", e);
                Err(Error::with_chain(e, "failed to fetch"))
            }
        }
    }
}

/// Reqwests Request struct doesn't implement `Clone`,
/// so we have to do it here.
fn clone_request(req: &Request) -> Request {
    let mut newreq = Request::new(req.method().clone(), req.url().clone());
    newreq
        .headers_mut()
        .extend(req.headers().clone().into_iter());
    newreq
}
<|endoftext|>


<commit_before>// The Nature Of Code
// Daniel Shiffman
// http://natureofcode.com
//
// Example 3-01: Angular Motion(using rotate())

use nannou::prelude::*;

fn main() {
    nannou::app(model).update(update).run();
}

struct Model {
    angle: f32,
    angle_velocity: f32,
    angle_acceleration: f32,
}

fn model(app: &App) -> Model {
    let angle = 0.0;
    let angle_velocity = 0.0;
    let angle_acceleration = -0.0001;
    app.new_window().size(800, 200).view(view).build().unwrap();
    Model {
        angle,
        angle_velocity,
        angle_acceleration,
    }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    model.angle += model.angle_velocity;
    model.angle_velocity += model.angle_acceleration;
}

fn view(app: &App, model: &Model, frame: Frame) {
    // Begin drawing
    let draw = app.draw();
    draw.rect()
        .wh(app.window_rect().wh())
        .color(WHITE);

    draw.line()
        .start(pt2(-60.0, 0.0))
        .end(pt2(60.0, 0.0))
        .color(rgb8(0, 0, 0))
        .stroke_weight(2.0)
        .rotate(model.angle);

    draw.ellipse()
        .xy(pt2(60.0, 0.0)
        .rotate(model.angle))
        .w_h(16.0, 16.0)
        .gray(0.5)
        .stroke_weight(2.0)
        .stroke_color(rgb8(0, 0, 0));

    draw.ellipse()
        .xy(pt2(-60.0, 0.0)
        .rotate(model.angle))
        .w_h(16.0, 16.0)
        .gray(0.5)
        .stroke_weight(2.0)
        .stroke_color(rgb8(0, 0, 0));

    // Write the result of our drawing to the window's frame.
    draw.to_frame(app, &frame).unwrap();
}
<commit_msg>ran rustfmt<commit_after>// The Nature Of Code
// Daniel Shiffman
// http://natureofcode.com
//
// Example 3-01: Angular Motion(using rotate())

use nannou::prelude::*;

fn main() {
    nannou::app(model).update(update).run();
}

struct Model {
    angle: f32,
    angle_velocity: f32,
    angle_acceleration: f32,
}

fn model(app: &App) -> Model {
    let angle = 0.0;
    let angle_velocity = 0.0;
    let angle_acceleration = -0.0001;
    app.new_window().size(800, 200).view(view).build().unwrap();
    Model {
        angle,
        angle_velocity,
        angle_acceleration,
    }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    model.angle += model.angle_velocity;
    model.angle_velocity += model.angle_acceleration;
}

fn view(app: &App, model: &Model, frame: Frame) {
    // Begin drawing
    let draw = app.draw();
    draw.rect().wh(app.window_rect().wh()).color(WHITE);

    draw.line()
        .start(pt2(-60.0, 0.0))
        .end(pt2(60.0, 0.0))
        .color(rgb8(0, 0, 0))
        .stroke_weight(2.0)
        .rotate(model.angle);

    draw.ellipse()
        .xy(pt2(60.0, 0.0).rotate(model.angle))
        .w_h(16.0, 16.0)
        .gray(0.5)
        .stroke_weight(2.0)
        .stroke_color(rgb8(0, 0, 0));

    draw.ellipse()
        .xy(pt2(-60.0, 0.0).rotate(model.angle))
        .w_h(16.0, 16.0)
        .gray(0.5)
        .stroke_weight(2.0)
        .stroke_color(rgb8(0, 0, 0));

    // Write the result of our drawing to the window's frame.
    draw.to_frame(app, &frame).unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use assert_cmd::Command;
use proc_macro2::Span;
use quote::quote;
use std::{
    borrow::Cow,
    fs::File,
    io::Write,
    path::{Path, PathBuf},
};
use syn::Token;
use tempdir::TempDir;

static INPUT_H: &str = indoc::indoc! {"
    inline int DoMath(int a) {
        return a * 3;
    }

    struct First {
        First() {}
        int foo;
    };

    struct Second {
        Second(const First& a) {}
        int bar;
    };
"};

enum Input {
    Header(String),
    ReproCase(PathBuf),
}

#[test]
fn test_reduce_direct_header() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(|header, _| Ok(Input::Header(header.into())), false)
}

#[test]
fn test_reduce_direct_repro_case() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            let config = format!(
                "#include \"{}\" generate_all!() block!(\"First\")
            safety!(unsafe_ffi)",
                header
            );
            let json = serde_json::json!({
                "header": INPUT_H,
                "config": config
            });
            let repropath = demo_code_dir.join("repro.json");
            let f = File::create(&repropath)?;
            serde_json::to_writer(f, &json)?;
            Ok(Input::ReproCase(repropath))
        },
        false,
    )
}

#[test]
#[ignore] // takes absolutely ages but you can run using cargo test -- --ignored
fn test_reduce_preprocessed_repro_case() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            write_minimal_rs_code(header, demo_code_dir);
            let repro = demo_code_dir.join("autocxx-repro.json");
            let mut cmd = Command::cargo_bin("autocxx-gen")?;
            cmd.arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg(demo_code_dir.join("main.rs"))
                .env("AUTOCXX_REPRO_CASE", repro.to_str().unwrap())
                .arg("--outdir")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--gen-cpp")
                .arg("--suppress-system-headers")
                .assert()
                .success();
            Ok(Input::ReproCase(repro))
        },
        false,
    )
}

#[test]
#[ignore] // takes absolutely ages but you can run using cargo test -- --ignored
fn test_reduce_preprocessed() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            write_minimal_rs_code(header, demo_code_dir);
            let prepro = demo_code_dir.join("autocxx-preprocessed.h");
            let mut cmd = Command::cargo_bin("autocxx-gen")?;
            cmd.arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg(demo_code_dir.join("main.rs"))
                .env("AUTOCXX_PREPROCESS", prepro.to_str().unwrap())
                .arg("--outdir")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--gen-cpp")
                .arg("--suppress-system-headers")
                .assert()
                .success();
            Ok(Input::Header("autocxx-preprocessed.h".into()))
        },
        false,
    )
}

#[test]
#[ignore] // takes absolutely ages but you can run using cargo test -- --ignored
fn test_reduce_preprocessed_include_cxx_h() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            write_minimal_rs_code(header, demo_code_dir);
            let prepro = demo_code_dir.join("autocxx-preprocessed.h");
            let mut cmd = Command::cargo_bin("autocxx-gen")?;
            cmd.arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg(demo_code_dir.join("main.rs"))
                .env("AUTOCXX_PREPROCESS", prepro.to_str().unwrap())
                .arg("--outdir")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--gen-cpp")
                .arg("--suppress-system-headers")
                .assert()
                .success();
            Ok(Input::Header("autocxx-preprocessed.h".into()))
        },
        true,
    )
}

fn write_minimal_rs_code(header: &str, demo_code_dir: &Path) {
    let hexathorpe = Token![#](Span::call_site());
    write_to_file(
        demo_code_dir,
        "main.rs",
        quote! {
            autocxx::include_cpp! {
                #hexathorpe include #header
                generate_all!()
                block!("First")
                safety!(unsafe_ffi)
            }
        }
        .to_string()
        .as_bytes(),
    );
}

fn do_reduce<F>(get_repro_case: F, include_cxx_h: bool) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnOnce(&str, &Path) -> Result<Input, Box<dyn std::error::Error>>,
{
    if creduce_is_broken() {
        return Ok(());
    }
    let tmp_dir = TempDir::new("example")?;
    let demo_code_dir = tmp_dir.path().join("demo");
    std::fs::create_dir(&demo_code_dir).unwrap();
    let input_header = if include_cxx_h {
        Cow::Owned(format!("#include \"cxx.h\"\n{}", INPUT_H))
    } else {
        Cow::Borrowed(INPUT_H)
    };
    write_to_file(&demo_code_dir, "input.h", input_header.as_bytes());
    write_to_file(&demo_code_dir, "cxx.h", cxx_gen::HEADER.as_bytes());
    let output_path = tmp_dir.path().join("min.h");
    let repro_case = get_repro_case("input.h", &demo_code_dir)?;
    let mut cmd = Command::cargo_bin("autocxx-reduce")?;
    let mut cmd = cmd
        .arg("-o")
        .arg(output_path.to_str().unwrap())
        .arg("-p")
        .arg("type marked as blocked")
        .arg("-k");
    match repro_case {
        Input::Header(header_name) => {
            cmd = cmd
                .arg("file")
                .arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--header")
                .arg(header_name)
                .arg("-d")
                .arg("generate_all!()")
                .arg("-d")
                .arg("block!(\"First\")");
        }
        Input::ReproCase(repro_case) => {
            cmd = cmd.arg("repro").arg("-r").arg(repro_case);
        }
    }
    eprintln!("Running {:?}", cmd);
    let o = cmd.output()?;
    println!("Reduce output: {}", std::str::from_utf8(&o.stdout).unwrap());
    println!("Reduce error: {}", std::str::from_utf8(&o.stderr).unwrap());
    if !o.status.success() {
        panic!("autocxx-reduce returned non-zero result code");
    }
    let minimized = std::fs::read_to_string(output_path)?;
    assert!(minimized.contains("First"));
    assert!(!minimized.contains("DoMath"));
    Ok(())
}

fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
    let path = dir.join(filename);
    let mut f = File::create(&path).expect("Unable to create file");
    f.write_all(content).expect("Unable to write file");
}

fn creduce_is_broken() -> bool {
    // On some machines, creduce immediately segfaults
    Command::new("creduce").arg("--version").ok().is_err()
}
<commit_msg>Move away from generate_all in reduce tests.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use assert_cmd::Command;
use proc_macro2::Span;
use quote::quote;
use std::{
    borrow::Cow,
    fs::File,
    io::Write,
    path::{Path, PathBuf},
};
use syn::Token;
use tempdir::TempDir;

static INPUT_H: &str = indoc::indoc! {"
    inline int DoMath(int a) {
        return a * 3;
    }

    struct First {
        First() {}
        int foo;
    };

    struct Second {
        Second(const First& a) {}
        int bar;
    };

    struct WithMethods {
        First get_first();
        Second get_second();
        int a;
    };
"};

enum Input {
    Header(String),
    ReproCase(PathBuf),
}

#[test]
fn test_reduce_direct_header() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(|header, _| Ok(Input::Header(header.into())), false)
}

#[test]
fn test_reduce_direct_repro_case() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            let config = format!(
                "#include \"{}\" generate_all!() block!(\"First\")
            safety!(unsafe_ffi)",
                header
            );
            let json = serde_json::json!({
                "header": INPUT_H,
                "config": config
            });
            let repropath = demo_code_dir.join("repro.json");
            let f = File::create(&repropath)?;
            serde_json::to_writer(f, &json)?;
            Ok(Input::ReproCase(repropath))
        },
        false,
    )
}

#[test]
#[ignore] // takes absolutely ages but you can run using cargo test -- --ignored
fn test_reduce_preprocessed_repro_case() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            write_minimal_rs_code(header, demo_code_dir);
            let repro = demo_code_dir.join("autocxx-repro.json");
            let mut cmd = Command::cargo_bin("autocxx-gen")?;
            cmd.arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg(demo_code_dir.join("main.rs"))
                .env("AUTOCXX_REPRO_CASE", repro.to_str().unwrap())
                .arg("--outdir")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--gen-cpp")
                .arg("--suppress-system-headers")
                .assert()
                .success();
            Ok(Input::ReproCase(repro))
        },
        false,
    )
}

#[test]
#[ignore] // takes absolutely ages but you can run using cargo test -- --ignored
fn test_reduce_preprocessed() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            write_minimal_rs_code(header, demo_code_dir);
            let prepro = demo_code_dir.join("autocxx-preprocessed.h");
            let mut cmd = Command::cargo_bin("autocxx-gen")?;
            cmd.arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg(demo_code_dir.join("main.rs"))
                .env("AUTOCXX_PREPROCESS", prepro.to_str().unwrap())
                .arg("--outdir")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--gen-cpp")
                .arg("--suppress-system-headers")
                .assert()
                .success();
            Ok(Input::Header("autocxx-preprocessed.h".into()))
        },
        false,
    )
}

#[test]
#[ignore] // takes absolutely ages but you can run using cargo test -- --ignored
fn test_reduce_preprocessed_include_cxx_h() -> Result<(), Box<dyn std::error::Error>> {
    do_reduce(
        |header, demo_code_dir| {
            write_minimal_rs_code(header, demo_code_dir);
            let prepro = demo_code_dir.join("autocxx-preprocessed.h");
            let mut cmd = Command::cargo_bin("autocxx-gen")?;
            cmd.arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg(demo_code_dir.join("main.rs"))
                .env("AUTOCXX_PREPROCESS", prepro.to_str().unwrap())
                .arg("--outdir")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--gen-cpp")
                .arg("--suppress-system-headers")
                .assert()
                .success();
            Ok(Input::Header("autocxx-preprocessed.h".into()))
        },
        true,
    )
}

fn write_minimal_rs_code(header: &str, demo_code_dir: &Path) {
    let hexathorpe = Token![#](Span::call_site());
    write_to_file(
        demo_code_dir,
        "main.rs",
        quote! {
            autocxx::include_cpp! {
                #hexathorpe include #header
                generate!("WithMethods")
                block!("First")
                safety!(unsafe_ffi)
            }
        }
        .to_string()
        .as_bytes(),
    );
}

fn do_reduce<F>(get_repro_case: F, include_cxx_h: bool) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnOnce(&str, &Path) -> Result<Input, Box<dyn std::error::Error>>,
{
    if creduce_is_broken() {
        return Ok(());
    }
    let tmp_dir = TempDir::new("example")?;
    let demo_code_dir = tmp_dir.path().join("demo");
    std::fs::create_dir(&demo_code_dir).unwrap();
    let input_header = if include_cxx_h {
        Cow::Owned(format!("#include \"cxx.h\"\n{}", INPUT_H))
    } else {
        Cow::Borrowed(INPUT_H)
    };
    write_to_file(&demo_code_dir, "input.h", input_header.as_bytes());
    write_to_file(&demo_code_dir, "cxx.h", cxx_gen::HEADER.as_bytes());
    let output_path = tmp_dir.path().join("min.h");
    let repro_case = get_repro_case("input.h", &demo_code_dir)?;
    let mut cmd = Command::cargo_bin("autocxx-reduce")?;
    let mut cmd = cmd
        .arg("-o")
        .arg(output_path.to_str().unwrap())
        .arg("-p")
        .arg("type marked as blocked")
        .arg("-k");
    match repro_case {
        Input::Header(header_name) => {
            cmd = cmd
                .arg("file")
                .arg("--inc")
                .arg(demo_code_dir.to_str().unwrap())
                .arg("--header")
                .arg(header_name)
                .arg("-d")
                .arg("generate!(\"WithMethods\")")
                .arg("-d")
                .arg("block!(\"First\")");
        }
        Input::ReproCase(repro_case) => {
            cmd = cmd.arg("repro").arg("-r").arg(repro_case);
        }
    }
    eprintln!("Running {:?}", cmd);
    let o = cmd.output()?;
    println!("Reduce output: {}", std::str::from_utf8(&o.stdout).unwrap());
    println!("Reduce error: {}", std::str::from_utf8(&o.stderr).unwrap());
    if !o.status.success() {
        panic!("autocxx-reduce returned non-zero result code");
    }
    let minimized = std::fs::read_to_string(output_path)?;
    assert!(minimized.contains("First"));
    assert!(!minimized.contains("DoMath"));
    Ok(())
}

fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
    let path = dir.join(filename);
    let mut f = File::create(&path).expect("Unable to create file");
    f.write_all(content).expect("Unable to write file");
}

fn creduce_is_broken() -> bool {
    // On some machines, creduce immediately segfaults
    Command::new("creduce").arg("--version").ok().is_err()
}
<|endoftext|>


<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! An utility binary to run Oak Runtime.
//!
//! To invoke, run the following command from the root of the repository:
//!
//! ```
//! cargo run --package=oak_loader -- --application=<APP_CONFIG_PATH>
//! ```

use log::{error, info};
use oak_runtime::{configure_and_run, proto::oak::application::ApplicationConfiguration};
use prost::Message;
use std::{fs::File, io::Read, thread};
use structopt::StructOpt;

#[derive(StructOpt, Clone)]
#[structopt(about = "Oak Loader")]
pub struct Opt {
    #[structopt(long, help = "Application configuration file")]
    application: String,
    // TODO(#806): Make arguments non-optional once TLS support is enabled.
    #[structopt(long, help = "Path to the PEM-encoded CA root certificate")]
    ca_cert: Option<String>,
    #[structopt(long, help = "Path to the private key")]
    private_key: Option<String>,
    #[structopt(long, help = "Path to the PEM-encoded certificate chain")]
    cert_chain: Option<String>,
    #[structopt(
        long,
        default_value = "3030",
        help = "Metrics server port number. Defaults to 3030."
    )]
    metrics_port: u16,
    #[structopt(long, help = "Starts the Runtime without a metrics server.")]
    no_metrics: bool,
    #[structopt(
        long,
        default_value = "1909",
        help = "Introspection server port number. Defaults to 1909."
    )]
    introspect_port: u16,
    #[structopt(long, help = "Starts the Runtime without an introspection server.")]
    no_introspect: bool,
}

fn read_file(filename: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let mut file = File::open(filename)
        .map_err(|error| format!("Failed to open file <{}>: {:?}", filename, error))?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)
        .map_err(|error| format!("Failed to read file <{}>: {:?}", filename, error))?;
    Ok(buffer)
}

fn start_runtime(
    application: String,
    runtime_config: oak_runtime::RuntimeConfiguration,
) -> Result<(), Box<dyn std::error::Error>> {
    info!("Loading Oak Runtime");

    let app_config = {
        let buffer = read_file(&application)?;
        ApplicationConfiguration::decode(&buffer[..])
            .map_err(|error| format!("Failed to decode application configuration: {:?}", error))?
    };

    // Start the Runtime from the given config.
    let (_runtime, _initial_handle) = configure_and_run(app_config, runtime_config)
        .map_err(|error| format!("Runtime error: {:?}", error))?;

    // The Runtime kicks off its own threads for the initial Node and any subsequently created
    // Nodes, so just park the current thread.
    thread::park();
    Ok(())
}

fn main() {
    simple_logger::init().expect("failed to initialize logger");
    let opt = Opt::from_args();
    let application = opt.application.clone();

    // Start the runtime in a new thread
    let runtime_config = oak_runtime::RuntimeConfiguration {
        metrics_port: if opt.no_metrics {
            None
        } else {
            Some(opt.metrics_port)
        },
        introspect_port: if opt.no_introspect {
            None
        } else {
            Some(opt.introspect_port)
        },
    };

    if let Err(e) = start_runtime(application, runtime_config) {
        error!("failed to start Oak Runtime: {}", e);
    }
}
<commit_msg>oak_loader: reorganize into a straight line<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! An utility binary to run Oak Runtime.
//!
//! To invoke, run the following command from the root of the repository:
//!
//! ```
//! cargo run --package=oak_loader -- --application=<APP_CONFIG_PATH>
//! ```

use log::info;
use oak_runtime::{configure_and_run, proto::oak::application::ApplicationConfiguration};
use prost::Message;
use std::{fs::File, io::Read, thread};
use structopt::StructOpt;

#[derive(StructOpt, Clone)]
#[structopt(about = "Oak Loader")]
pub struct Opt {
    #[structopt(long, help = "Application configuration file")]
    application: String,
    // TODO(#806): Make arguments non-optional once TLS support is enabled.
    #[structopt(long, help = "Path to the PEM-encoded CA root certificate")]
    ca_cert: Option<String>,
    #[structopt(long, help = "Path to the private key")]
    private_key: Option<String>,
    #[structopt(long, help = "Path to the PEM-encoded certificate chain")]
    cert_chain: Option<String>,
    #[structopt(
        long,
        default_value = "3030",
        help = "Metrics server port number. Defaults to 3030."
    )]
    metrics_port: u16,
    #[structopt(long, help = "Starts the Runtime without a metrics server.")]
    no_metrics: bool,
    #[structopt(
        long,
        default_value = "1909",
        help = "Introspection server port number. Defaults to 1909."
    )]
    introspect_port: u16,
    #[structopt(long, help = "Starts the Runtime without an introspection server.")]
    no_introspect: bool,
}

fn read_file(filename: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let mut file = File::open(filename)
        .map_err(|error| format!("Failed to open file <{}>: {:?}", filename, error))?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)
        .map_err(|error| format!("Failed to read file <{}>: {:?}", filename, error))?;
    Ok(buffer)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    simple_logger::init().expect("failed to initialize logger");
    let opt = Opt::from_args();

    let app_config_data = read_file(&opt.application)?;
    let app_config = ApplicationConfiguration::decode(&app_config_data[..])?;
    let runtime_config = oak_runtime::RuntimeConfiguration {
        metrics_port: if opt.no_metrics {
            None
        } else {
            Some(opt.metrics_port)
        },
        introspect_port: if opt.no_introspect {
            None
        } else {
            Some(opt.introspect_port)
        },
    };

    // Start the Runtime from the given config.
    info!("starting Runtime");
    let (runtime, initial_handle) = configure_and_run(app_config, runtime_config)
        .map_err(|status| format!("status {:?}", status))?;
    info!("initial write handle {:?}", initial_handle);

    // The Runtime kicks off its own threads for the initial Node and any subsequently created
    // Nodes, so just park the current thread.
    thread::park();

    info!("stop Runtime");
    runtime.stop();
    Ok(())
}
<|endoftext|>


<commit_before>use std::{
    collections::HashSet,
    fs::File,
    future::Future,
    io::{self, prelude::*},
    path::PathBuf,
    pin::Pin,
    str,
    sync::{mpsc, Arc, Mutex},
    task::{Context, Poll},
    thread,
};

use futures_channel::oneshot;
use futures_util::future;
use hyper::{body::to_bytes, Body, Error, Request, Response, Server, StatusCode, Uri};
use tokio::{
    net::{TcpListener, TcpStream},
    runtime,
};

// A "bomb" so when the test task exists we know when to shut down
// the server and fail if the subtask failed.
pub struct Bomb {
    iorx: Sink,
    quittx: Option<oneshot::Sender<()>>,
    #[allow(clippy::type_complexity)]
    thread: Option<thread::JoinHandle<Option<(Vec<u8>, PathBuf)>>>,
}

#[derive(Clone)]
struct Sink(Arc<Mutex<Vec<u8>>>);

impl<'a> Write for &'a Sink {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        Write::write(&mut *self.0.lock().unwrap(), data)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl Drop for Bomb {
    fn drop(&mut self) {
        drop(self.quittx.take());
        let res = self.thread.take().unwrap().join();
        let stderr = str::from_utf8(&self.iorx.0.lock().unwrap())
            .unwrap()
            .to_string();
        match res {
            Err(..) if !thread::panicking() => panic!("server subtask failed: {}", stderr),
            Err(e) => {
                if !stderr.is_empty() {
                    println!("server subtask failed ({e:?}): {stderr}")
                }
            }
            Ok(_) if thread::panicking() => {}
            Ok(None) => {}
            Ok(Some((data, file))) => {
                assert_ok!(assert_ok!(File::create(&file)).write_all(&data));
            }
        }
    }
}

fn cache_file(name: &str) -> PathBuf {
    PathBuf::from(file!())
        .parent()
        .unwrap()
        .join("http-data")
        .join(name)
}

enum Record {
    Capture(Vec<Exchange>, PathBuf),
    Replay(Vec<Exchange>),
}

pub fn proxy() -> (String, Bomb) {
    let me = thread::current().name().unwrap().to_string();
    let record = dotenv::var("RECORD").is_ok();

    let (url_tx, url_rx) = mpsc::channel();

    let data = cache_file(&me.replace("::", "_"));
    let record = if record && !data.exists() {
        Record::Capture(Vec::new(), data)
    } else if !data.exists() {
        Record::Replay(serde_json::from_slice(b"[]").unwrap())
    } else {
        let mut body = Vec::new();
        assert_ok!(assert_ok!(File::open(&data)).read_to_end(&mut body));
        Record::Replay(serde_json::from_slice(&body).unwrap())
    };

    let sink = Arc::new(Mutex::new(Vec::new()));
    let sink2 = Sink(Arc::clone(&sink));

    let (quittx, quitrx) = oneshot::channel();

    let thread = thread::spawn(move || {
        let rt = assert_ok!(runtime::Builder::new_current_thread().enable_io().build());

        let client = if let Record::Capture(_, _) = record {
            Some(hyper::Client::builder().build(hyper_tls::HttpsConnector::new()))
        } else {
            None
        };

        let listener = assert_ok!(rt.block_on(TcpListener::bind("127.0.0.1:0")));
        url_tx
            .send(format!("http://{}", assert_ok!(listener.local_addr())))
            .unwrap();

        let record = Arc::new(Mutex::new(record));
        let srv = Server::builder(hyper::server::accept::poll_fn(move |cx| {
            listener
                .poll_accept(cx)
                .map(|res| Some(res.map(|(stream, _)| stream)))
        }))
        .serve(Proxy {
            sink: sink2,
            record: Arc::clone(&record),
            client,
        })
        .with_graceful_shutdown(async {
            quitrx.await.ok();
        });

        rt.block_on(srv).ok();

        let record = record.lock().unwrap();
        match *record {
            Record::Capture(ref data, ref path) => {
                let data = assert_ok!(serde_json::to_string_pretty(data));
                Some((data.into_bytes(), path.clone()))
            }
            Record::Replay(ref remaining_exchanges) if !remaining_exchanges.is_empty() =>
                panic!(
                    "The HTTP proxy for this test received fewer requests than expected (remaining: {})",
                    remaining_exchanges.len()
                ),
            Record::Replay(..) => None,
        }
    });

    (
        url_rx.recv().unwrap(),
        Bomb {
            iorx: Sink(sink),
            quittx: Some(quittx),
            thread: Some(thread),
        },
    )
}

#[derive(Clone)]
struct Proxy {
    sink: Sink,
    record: Arc<Mutex<Record>>,
    client: Option<Client>,
}

impl tower_service::Service<Request<Body>> for Proxy {
    type Response = Response<Body>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Response<Body>, Error>> + Send>>;

    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request<Body>) -> Self::Future {
        match *self.record.lock().unwrap() {
            Record::Capture(_, _) => {
                let client = self.client.as_ref().unwrap().clone();
                let record2 = self.record.clone();
                Box::pin(async move {
                    let (response, exchange) = record_http(req, client).await?;
                    if let Record::Capture(ref mut d, _) = *record2.lock().unwrap() {
                        d.push(exchange);
                    }
                    Ok(response)
                })
            }
            Record::Replay(ref mut exchanges) => {
                Box::pin(replay_http(req, exchanges.remove(0), &mut &self.sink))
            }
        }
    }
}

impl<'a> tower_service::Service<&'a TcpStream> for Proxy {
    type Response = Proxy;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Proxy, Error>> + Send + 'static>>;

    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, _: &'a TcpStream) -> Self::Future {
        Box::pin(future::ok(self.clone()))
    }
}

#[derive(Serialize, Deserialize)]
struct Exchange {
    request: RecordedRequest,
    response: RecordedResponse,
}

#[derive(Serialize, Deserialize)]
struct RecordedRequest {
    uri: String,
    method: String,
    headers: HashSet<(String, String)>,
    body: String,
}

#[derive(Serialize, Deserialize)]
struct RecordedResponse {
    status: u16,
    headers: HashSet<(String, String)>,
    body: String,
}

type Client = hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
type ResponseAndExchange = (Response<Body>, Exchange);

async fn record_http(req: Request<Body>, client: Client) -> Result<ResponseAndExchange, Error> {
    // Deconstruct the incoming request and await for the full body
    let (header_parts, body) = req.into_parts();
    let method = header_parts.method;
    let uri = header_parts.uri;
    let headers = header_parts.headers;
    let body = to_bytes(body).await?;

    // Save info on the incoming request for the exchange log
    let request = RecordedRequest {
        uri: uri.to_string(),
        method: method.to_string(),
        headers: headers
            .iter()
            .map(|h| (h.0.as_str().to_string(), h.1.to_str().unwrap().to_string()))
            .collect(),
        body: base64::encode(&body),
    };

    // Construct an outgoing request
    let uri = uri.to_string().replace("http://", "https://");
    let uri = uri.parse::<Uri>().unwrap();
    let mut req = Request::builder()
        .method(method.clone())
        .uri(uri)
        .body(body.into())
        .unwrap();
    *req.headers_mut() = headers.clone();

    // Deconstruct the incoming response and await for the full body
    let hyper_response = client.request(req).await?;
    let status = hyper_response.status();
    let headers = hyper_response.headers().clone();
    let body = to_bytes(hyper_response.into_body()).await?;

    // Save the response for the exchange log
    let response = RecordedResponse {
        status: status.as_u16(),
        headers: headers
            .iter()
            .map(|h| (h.0.as_str().to_string(), h.1.to_str().unwrap().to_string()))
            .collect(),
        body: base64::encode(&body),
    };

    // Construct an outgoing response
    let mut hyper_response = Response::builder()
        .status(status)
        .body(body.into())
        .unwrap();
    *hyper_response.headers_mut() = headers;

    Ok((hyper_response, Exchange { request, response }))
}

fn replay_http(
    req: Request<Body>,
    mut exchange: Exchange,
    stdout: &mut dyn Write,
) -> impl Future<Output = Result<Response<Body>, Error>> + Send {
    static IGNORED_HEADERS: &[&str] = &["authorization", "date", "cache-control", "host"];

    debug!("<- {req:?}");
    assert_eq!(req.uri().to_string(), exchange.request.uri);
    assert_eq!(req.method().to_string(), exchange.request.method);
    assert_ok!(writeln!(
        stdout,
        "expecting: {:?}",
        exchange.request.headers
    ));
    for (name, value) in req.headers().iter() {
        let pair = (
            name.as_str().to_string(),
            value.to_str().unwrap().to_string(),
        );
        assert_ok!(writeln!(stdout, "received: {:?}", pair));
        if name == "user-agent" {
            assert_eq!(value, "crates.io (https://crates.io)");
            continue;
        }
        if IGNORED_HEADERS.contains(&name.as_str()) {
            continue;
        }
        if !exchange.request.headers.remove(&pair) {
            panic!("found {:?} but didn't expect it", pair);
        }
    }
    for (name, value) in exchange.request.headers.drain() {
        if IGNORED_HEADERS.contains(&name.as_str()) {
            continue;
        }
        panic!("didn't find header {:?}", (name, value));
    }

    async {
        let _ = &exchange;
        assert_eq!(
            to_bytes(req.into_body()).await.unwrap(),
            base64::decode(&exchange.request.body).unwrap()
        );

        let mut builder = Response::builder();
        for (key, value) in exchange.response.headers {
            builder = builder.header(key.as_str(), value.as_str());
        }
        let body = base64::decode(exchange.response.body.as_bytes()).unwrap();
        let status = StatusCode::from_u16(exchange.response.status).unwrap();
        let response = builder.status(status).body(body.into()).unwrap();

        debug!("-> {response:?}");
        Ok(response)
    }
}
<commit_msg>Don't auto-generate emtpy http-data files (#5039)<commit_after>use std::{
    collections::HashSet,
    fs::File,
    future::Future,
    io::{self, prelude::*},
    path::PathBuf,
    pin::Pin,
    str,
    sync::{mpsc, Arc, Mutex},
    task::{Context, Poll},
    thread,
};

use futures_channel::oneshot;
use futures_util::future;
use hyper::{body::to_bytes, Body, Error, Request, Response, Server, StatusCode, Uri};
use tokio::{
    net::{TcpListener, TcpStream},
    runtime,
};

// A "bomb" so when the test task exists we know when to shut down
// the server and fail if the subtask failed.
pub struct Bomb {
    iorx: Sink,
    quittx: Option<oneshot::Sender<()>>,
    #[allow(clippy::type_complexity)]
    thread: Option<thread::JoinHandle<Option<(Vec<u8>, PathBuf)>>>,
}

#[derive(Clone)]
struct Sink(Arc<Mutex<Vec<u8>>>);

impl<'a> Write for &'a Sink {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        Write::write(&mut *self.0.lock().unwrap(), data)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl Drop for Bomb {
    fn drop(&mut self) {
        drop(self.quittx.take());
        let res = self.thread.take().unwrap().join();
        let stderr = str::from_utf8(&self.iorx.0.lock().unwrap())
            .unwrap()
            .to_string();
        match res {
            Err(..) if !thread::panicking() => panic!("server subtask failed: {}", stderr),
            Err(e) => {
                if !stderr.is_empty() {
                    println!("server subtask failed ({e:?}): {stderr}")
                }
            }
            Ok(_) if thread::panicking() => {}
            Ok(Some((data, file))) if data != b"[]\n" => {
                assert_ok!(assert_ok!(File::create(&file)).write_all(&data));
            }
            Ok(_) => {}
        }
    }
}

fn cache_file(name: &str) -> PathBuf {
    PathBuf::from(file!())
        .parent()
        .unwrap()
        .join("http-data")
        .join(name)
}

enum Record {
    Capture(Vec<Exchange>, PathBuf),
    Replay(Vec<Exchange>),
}

pub fn proxy() -> (String, Bomb) {
    let me = thread::current().name().unwrap().to_string();
    let record = dotenv::var("RECORD").is_ok();

    let (url_tx, url_rx) = mpsc::channel();

    let data = cache_file(&me.replace("::", "_"));
    let record = if record && !data.exists() {
        Record::Capture(Vec::new(), data)
    } else if !data.exists() {
        Record::Replay(serde_json::from_slice(b"[]").unwrap())
    } else {
        let mut body = Vec::new();
        assert_ok!(assert_ok!(File::open(&data)).read_to_end(&mut body));
        Record::Replay(serde_json::from_slice(&body).unwrap())
    };

    let sink = Arc::new(Mutex::new(Vec::new()));
    let sink2 = Sink(Arc::clone(&sink));

    let (quittx, quitrx) = oneshot::channel();

    let thread = thread::spawn(move || {
        let rt = assert_ok!(runtime::Builder::new_current_thread().enable_io().build());

        let client = if let Record::Capture(_, _) = record {
            Some(hyper::Client::builder().build(hyper_tls::HttpsConnector::new()))
        } else {
            None
        };

        let listener = assert_ok!(rt.block_on(TcpListener::bind("127.0.0.1:0")));
        url_tx
            .send(format!("http://{}", assert_ok!(listener.local_addr())))
            .unwrap();

        let record = Arc::new(Mutex::new(record));
        let srv = Server::builder(hyper::server::accept::poll_fn(move |cx| {
            listener
                .poll_accept(cx)
                .map(|res| Some(res.map(|(stream, _)| stream)))
        }))
        .serve(Proxy {
            sink: sink2,
            record: Arc::clone(&record),
            client,
        })
        .with_graceful_shutdown(async {
            quitrx.await.ok();
        });

        rt.block_on(srv).ok();

        let record = record.lock().unwrap();
        match *record {
            Record::Capture(ref data, ref path) => {
                let data = assert_ok!(serde_json::to_string_pretty(data));
                Some((data.into_bytes(), path.clone()))
            }
            Record::Replay(ref remaining_exchanges) if !remaining_exchanges.is_empty() =>
                panic!(
                    "The HTTP proxy for this test received fewer requests than expected (remaining: {})",
                    remaining_exchanges.len()
                ),
            Record::Replay(..) => None,
        }
    });

    (
        url_rx.recv().unwrap(),
        Bomb {
            iorx: Sink(sink),
            quittx: Some(quittx),
            thread: Some(thread),
        },
    )
}

#[derive(Clone)]
struct Proxy {
    sink: Sink,
    record: Arc<Mutex<Record>>,
    client: Option<Client>,
}

impl tower_service::Service<Request<Body>> for Proxy {
    type Response = Response<Body>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Response<Body>, Error>> + Send>>;

    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request<Body>) -> Self::Future {
        match *self.record.lock().unwrap() {
            Record::Capture(_, _) => {
                let client = self.client.as_ref().unwrap().clone();
                let record2 = self.record.clone();
                Box::pin(async move {
                    let (response, exchange) = record_http(req, client).await?;
                    if let Record::Capture(ref mut d, _) = *record2.lock().unwrap() {
                        d.push(exchange);
                    }
                    Ok(response)
                })
            }
            Record::Replay(ref mut exchanges) => {
                Box::pin(replay_http(req, exchanges.remove(0), &mut &self.sink))
            }
        }
    }
}

impl<'a> tower_service::Service<&'a TcpStream> for Proxy {
    type Response = Proxy;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Proxy, Error>> + Send + 'static>>;

    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, _: &'a TcpStream) -> Self::Future {
        Box::pin(future::ok(self.clone()))
    }
}

#[derive(Serialize, Deserialize)]
struct Exchange {
    request: RecordedRequest,
    response: RecordedResponse,
}

#[derive(Serialize, Deserialize)]
struct RecordedRequest {
    uri: String,
    method: String,
    headers: HashSet<(String, String)>,
    body: String,
}

#[derive(Serialize, Deserialize)]
struct RecordedResponse {
    status: u16,
    headers: HashSet<(String, String)>,
    body: String,
}

type Client = hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
type ResponseAndExchange = (Response<Body>, Exchange);

async fn record_http(req: Request<Body>, client: Client) -> Result<ResponseAndExchange, Error> {
    // Deconstruct the incoming request and await for the full body
    let (header_parts, body) = req.into_parts();
    let method = header_parts.method;
    let uri = header_parts.uri;
    let headers = header_parts.headers;
    let body = to_bytes(body).await?;

    // Save info on the incoming request for the exchange log
    let request = RecordedRequest {
        uri: uri.to_string(),
        method: method.to_string(),
        headers: headers
            .iter()
            .map(|h| (h.0.as_str().to_string(), h.1.to_str().unwrap().to_string()))
            .collect(),
        body: base64::encode(&body),
    };

    // Construct an outgoing request
    let uri = uri.to_string().replace("http://", "https://");
    let uri = uri.parse::<Uri>().unwrap();
    let mut req = Request::builder()
        .method(method.clone())
        .uri(uri)
        .body(body.into())
        .unwrap();
    *req.headers_mut() = headers.clone();

    // Deconstruct the incoming response and await for the full body
    let hyper_response = client.request(req).await?;
    let status = hyper_response.status();
    let headers = hyper_response.headers().clone();
    let body = to_bytes(hyper_response.into_body()).await?;

    // Save the response for the exchange log
    let response = RecordedResponse {
        status: status.as_u16(),
        headers: headers
            .iter()
            .map(|h| (h.0.as_str().to_string(), h.1.to_str().unwrap().to_string()))
            .collect(),
        body: base64::encode(&body),
    };

    // Construct an outgoing response
    let mut hyper_response = Response::builder()
        .status(status)
        .body(body.into())
        .unwrap();
    *hyper_response.headers_mut() = headers;

    Ok((hyper_response, Exchange { request, response }))
}

fn replay_http(
    req: Request<Body>,
    mut exchange: Exchange,
    stdout: &mut dyn Write,
) -> impl Future<Output = Result<Response<Body>, Error>> + Send {
    static IGNORED_HEADERS: &[&str] = &["authorization", "date", "cache-control", "host"];

    debug!("<- {req:?}");
    assert_eq!(req.uri().to_string(), exchange.request.uri);
    assert_eq!(req.method().to_string(), exchange.request.method);
    assert_ok!(writeln!(
        stdout,
        "expecting: {:?}",
        exchange.request.headers
    ));
    for (name, value) in req.headers().iter() {
        let pair = (
            name.as_str().to_string(),
            value.to_str().unwrap().to_string(),
        );
        assert_ok!(writeln!(stdout, "received: {:?}", pair));
        if name == "user-agent" {
            assert_eq!(value, "crates.io (https://crates.io)");
            continue;
        }
        if IGNORED_HEADERS.contains(&name.as_str()) {
            continue;
        }
        if !exchange.request.headers.remove(&pair) {
            panic!("found {:?} but didn't expect it", pair);
        }
    }
    for (name, value) in exchange.request.headers.drain() {
        if IGNORED_HEADERS.contains(&name.as_str()) {
            continue;
        }
        panic!("didn't find header {:?}", (name, value));
    }

    async {
        let _ = &exchange;
        assert_eq!(
            to_bytes(req.into_body()).await.unwrap(),
            base64::decode(&exchange.request.body).unwrap()
        );

        let mut builder = Response::builder();
        for (key, value) in exchange.response.headers {
            builder = builder.header(key.as_str(), value.as_str());
        }
        let body = base64::decode(exchange.response.body.as_bytes()).unwrap();
        let status = StatusCode::from_u16(exchange.response.status).unwrap();
        let response = builder.status(status).body(body.into()).unwrap();

        debug!("-> {response:?}");
        Ok(response)
    }
}
<|endoftext|>


<commit_before>use std::io::Write;

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    let destination = std::path::Path::new(&out_dir).join("reftests.rs");
    let mut f = std::fs::File::create(&destination).unwrap();

    let root_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let reftests_dir = std::path::Path::new(&root_dir).join("../../tests/xc2bit/reftests");
    let files = std::fs::read_dir(reftests_dir).unwrap();

    for file in files {
        let path = file.expect("failed to get path").path();
        if path.extension().expect("bogus reftest filename (doesn't have extension)") == "jed" {
            let path = path.canonicalize().unwrap();

            let id_string = path.file_name().unwrap().to_str().unwrap().chars().map(|x| match x {
                'A'...'Z' | 'a'...'z' | '0'...'9' => x,
                _ => '_',
            }).collect::<String>();

            write!(f, r#"
                #[test]
                fn reftest_{}() {{
                    run_one_reftest("{}");
                }}
                "#, id_string, path.to_str().unwrap()).unwrap();
        }
    }
}
<commit_msg>xc2bit: Hack build.rs so that it uploads<commit_after>use std::io::Write;

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    let destination = std::path::Path::new(&out_dir).join("reftests.rs");
    let mut f = std::fs::File::create(&destination).unwrap();

    let root_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let reftests_dir = std::path::Path::new(&root_dir).join("../../tests/xc2bit/reftests");
    let files = std::fs::read_dir(reftests_dir);

    if let Ok(files) = files {
        for file in files {
            let path = file.expect("failed to get path").path();
            if path.extension().expect("bogus reftest filename (doesn't have extension)") == "jed" {
                let path = path.canonicalize().unwrap();

                let id_string = path.file_name().unwrap().to_str().unwrap().chars().map(|x| match x {
                    'A'...'Z' | 'a'...'z' | '0'...'9' => x,
                    _ => '_',
                }).collect::<String>();

                write!(f, r#"
                    #[test]
                    fn reftest_{}() {{
                        run_one_reftest("{}");
                    }}
                    "#, id_string, path.to_str().unwrap()).unwrap();
            }
        }
    }
}
<|endoftext|>


<commit_before>//! This module provides a microfacet model for transmission (a BTDF), see
//! [Walter et al. 07](https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf)
//! for details.

use std::f32;
use enum_set::EnumSet;

use linalg::{self, Vector};
use film::Colorf;
use bxdf::{self, BxDF, BxDFType};
use bxdf::fresnel::{Dielectric, Fresnel};
use bxdf::microfacet::{MicrofacetDistribution};

/// Struct providing the microfacet BTDF, implemented as described in
/// [Walter et al. 07](https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf)
pub struct MicrofacetTransmission {
    reflectance: Colorf,
    fresnel: Dielectric,
    /// Microfacet distribution describing the structure of the microfacets of
    /// the material
    microfacet: Box<MicrofacetDistribution + Send + Sync>,
}

impl MicrofacetTransmission {
    /// Create a new transmissive microfacet BRDF
    pub fn new(c: &Colorf, fresnel: Dielectric,
               microfacet: Box<MicrofacetDistribution + Send + Sync>) -> MicrofacetTransmission {
        MicrofacetTransmission { reflectance: *c, fresnel: fresnel, microfacet: microfacet }
    }
    /// Convenience method for getting `eta_i` and `eta_t` in the right order for if
    /// we're entering or exiting this material based on the direction of the outgoing
    /// ray.
    fn eta_for_interaction(&self, w_o: &Vector) -> (f32, f32) {
        if bxdf::cos_theta(w_o) > 0.0 {
            (self.fresnel.eta_i, self.fresnel.eta_t)
        } else {
            (self.fresnel.eta_t, self.fresnel.eta_i)
        }
    }
    /// Compute the Jacobian for the change of variables (see [Walter et al 07] section 4.2),
    /// here we compute equation 17 in that section.
    fn jacobian(w_o: &Vector, w_i: &Vector, w_h: &Vector, eta: (f32, f32)) -> f32 {
        let wi_dot_h = linalg::dot(w_i, w_h);
        let wo_dot_h = linalg::dot(w_o, w_h);
        f32::powf(eta.1, 2.0) * f32::abs(wo_dot_h) / f32::powf(eta.0 * wi_dot_h + eta.1 * wo_dot_h, 2.0)
    }
    fn half_vector(w_o: &Vector, w_i: &Vector, eta: (f32, f32)) -> Option<Vector> {
        let mut w_h = -eta.0 * *w_o - eta.1 * *w_i;
        if w_h.x == 0.0 && w_h.y == 0.0 && w_h.z == 0.0 {
            None
        } else {
            Some(w_h.normalized())
        }
    }
}

impl BxDF for MicrofacetTransmission {
    fn bxdf_type(&self) -> EnumSet<BxDFType> {
        let mut e = EnumSet::new();
        e.insert(BxDFType::Glossy);
        e.insert(BxDFType::Transmission);
        e
    }
    // etaA is on the outside, etaB is on the inside
    // so etaA = eta.0, etaB = eta.1
    // in walter eta al eta_i is incident side, eta_o/eta_t is transmitted side
    // so eta_i = eta.0, eta_o & eta_t = eta.1
    fn eval(&self, w_o: &Vector, w_i: &Vector) -> Colorf {
        if bxdf::same_hemisphere(w_o, w_i) {
            return Colorf::black();
        }
        let cos_to = bxdf::cos_theta(w_o);
        let cos_ti = bxdf::cos_theta(w_i);
        if cos_to == 0.0 || cos_ti == 0.0 {
            return Colorf::black();
        }
        let eta = self.eta_for_interaction(w_o);
        if let Some(w_h) = MicrofacetTransmission::half_vector(w_o, w_i, eta) {
            let d = self.microfacet.normal_distribution(&w_h);
            let f = Colorf::broadcast(1.0) - self.fresnel.fresnel(linalg::dot(w_o, &w_h));
            let g = self.microfacet.shadowing_masking(w_i, w_o, &w_h);
            let wi_dot_h = linalg::dot(w_i, &w_h);
            let jacobian = MicrofacetTransmission::jacobian(w_o, w_i, &w_h, eta);
            self.reflectance * (f32::abs(wi_dot_h) / (f32::abs(w_i.z) * f32::abs(w_o.z)))
                * (f * g * d) * jacobian
        } else {
            Colorf::black()
        }
    }
    // NOTE: w_o is in the material we're coming from, w_i is in the material we're entering
    fn sample(&self, w_o: &Vector, samples: &(f32, f32)) -> (Colorf, Vector, f32) {
        let mut w_h = self.microfacet.sample(w_o, samples);
        if !bxdf::same_hemisphere(w_o, &w_h) {
            w_h = -w_h;
        }
        let eta = self.eta_for_interaction(w_o);
        if let Some(w_i) = linalg::refract(w_o, &w_h, eta.0, eta.1) {
            if bxdf::same_hemisphere(w_o, &w_i) {
                (Colorf::black(), Vector::broadcast(0.0), 0.0)
            } else {
                let pdf = self.microfacet.pdf(&w_h)
                    * MicrofacetTransmission::jacobian(w_o, &w_i, &w_h, eta);
                (self.eval(w_o, &w_i), w_i, pdf)
            }
        } else {
            (Colorf::black(), Vector::broadcast(0.0), 0.0)
        }
    }
    fn pdf(&self, w_o: &Vector, w_i: &Vector) -> f32 {
        if bxdf::same_hemisphere(w_o, w_i) {
            0.0
        } else {
            let eta = self.eta_for_interaction(w_o);
            if let Some(w_h) = MicrofacetTransmission::half_vector(w_o, w_i, eta) {
                self.microfacet.pdf(&w_h) * MicrofacetTransmission::jacobian(w_o, w_i, &w_h, eta)
            } else {
                0.0
            }
        }
    }
}

<commit_msg>Clean up some comments<commit_after>//! This module provides a microfacet model for transmission (a BTDF), see
//! [Walter et al. 07](https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf)
//! for details.

use std::f32;
use enum_set::EnumSet;

use linalg::{self, Vector};
use film::Colorf;
use bxdf::{self, BxDF, BxDFType};
use bxdf::fresnel::{Dielectric, Fresnel};
use bxdf::microfacet::{MicrofacetDistribution};

/// Struct providing the microfacet BTDF, implemented as described in
/// [Walter et al. 07](https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf)
pub struct MicrofacetTransmission {
    reflectance: Colorf,
    fresnel: Dielectric,
    /// Microfacet distribution describing the structure of the microfacets of
    /// the material
    microfacet: Box<MicrofacetDistribution + Send + Sync>,
}

impl MicrofacetTransmission {
    /// Create a new transmissive microfacet BRDF
    pub fn new(c: &Colorf, fresnel: Dielectric,
               microfacet: Box<MicrofacetDistribution + Send + Sync>) -> MicrofacetTransmission {
        MicrofacetTransmission { reflectance: *c, fresnel: fresnel, microfacet: microfacet }
    }
    /// Convenience method for getting `eta_i` and `eta_t` in the right order for if
    /// we're entering or exiting this material based on the direction of the outgoing
    /// ray.
    fn eta_for_interaction(&self, w_o: &Vector) -> (f32, f32) {
        if bxdf::cos_theta(w_o) > 0.0 {
            (self.fresnel.eta_i, self.fresnel.eta_t)
        } else {
            (self.fresnel.eta_t, self.fresnel.eta_i)
        }
    }
    /// Compute the Jacobian for the change of variables (see [Walter et al 07] section 4.2),
    /// here we compute equation 17 in that section.
    fn jacobian(w_o: &Vector, w_i: &Vector, w_h: &Vector, eta: (f32, f32)) -> f32 {
        let wi_dot_h = linalg::dot(w_i, w_h);
        let wo_dot_h = linalg::dot(w_o, w_h);
        f32::powf(eta.1, 2.0) * f32::abs(wo_dot_h) / f32::powf(eta.0 * wi_dot_h + eta.1 * wo_dot_h, 2.0)
    }
    fn half_vector(w_o: &Vector, w_i: &Vector, eta: (f32, f32)) -> Option<Vector> {
        let mut w_h = -eta.0 * *w_o - eta.1 * *w_i;
        if w_h.x == 0.0 && w_h.y == 0.0 && w_h.z == 0.0 {
            None
        } else {
            Some(w_h.normalized())
        }
    }
}

impl BxDF for MicrofacetTransmission {
    fn bxdf_type(&self) -> EnumSet<BxDFType> {
        let mut e = EnumSet::new();
        e.insert(BxDFType::Glossy);
        e.insert(BxDFType::Transmission);
        e
    }
    fn eval(&self, w_o: &Vector, w_i: &Vector) -> Colorf {
        if bxdf::same_hemisphere(w_o, w_i) {
            return Colorf::black();
        }
        let cos_to = bxdf::cos_theta(w_o);
        let cos_ti = bxdf::cos_theta(w_i);
        if cos_to == 0.0 || cos_ti == 0.0 {
            return Colorf::black();
        }
        let eta = self.eta_for_interaction(w_o);
        if let Some(w_h) = MicrofacetTransmission::half_vector(w_o, w_i, eta) {
            let d = self.microfacet.normal_distribution(&w_h);
            let f = Colorf::broadcast(1.0) - self.fresnel.fresnel(linalg::dot(w_o, &w_h));
            let g = self.microfacet.shadowing_masking(w_i, w_o, &w_h);
            let wi_dot_h = linalg::dot(w_i, &w_h);
            let jacobian = MicrofacetTransmission::jacobian(w_o, w_i, &w_h, eta);
            self.reflectance * (f32::abs(wi_dot_h) / (f32::abs(w_i.z) * f32::abs(w_o.z)))
                * (f * g * d) * jacobian
        } else {
            Colorf::black()
        }
    }
    fn sample(&self, w_o: &Vector, samples: &(f32, f32)) -> (Colorf, Vector, f32) {
        let mut w_h = self.microfacet.sample(w_o, samples);
        if !bxdf::same_hemisphere(w_o, &w_h) {
            w_h = -w_h;
        }
        let eta = self.eta_for_interaction(w_o);
        if let Some(w_i) = linalg::refract(w_o, &w_h, eta.0, eta.1) {
            if bxdf::same_hemisphere(w_o, &w_i) {
                (Colorf::black(), Vector::broadcast(0.0), 0.0)
            } else {
                let pdf = self.microfacet.pdf(&w_h)
                    * MicrofacetTransmission::jacobian(w_o, &w_i, &w_h, eta);
                (self.eval(w_o, &w_i), w_i, pdf)
            }
        } else {
            (Colorf::black(), Vector::broadcast(0.0), 0.0)
        }
    }
    fn pdf(&self, w_o: &Vector, w_i: &Vector) -> f32 {
        if bxdf::same_hemisphere(w_o, w_i) {
            0.0
        } else {
            let eta = self.eta_for_interaction(w_o);
            if let Some(w_h) = MicrofacetTransmission::half_vector(w_o, w_i, eta) {
                self.microfacet.pdf(&w_h) * MicrofacetTransmission::jacobian(w_o, w_i, &w_h, eta)
            } else {
                0.0
            }
        }
    }
}

<|endoftext|>