<commit_before>use byteorder::ReadBytesExt;
use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
#[cfg(feature = "unix_socket")]
use unix_socket::UnixStream;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, RawSocket};

use {SslMode, ConnectParams, ConnectTarget};
use error::{ConnectError};
use io::{NegotiateSsl, StreamWrapper};
use message::{self, WriteMessage};
use message::FrontendMessage::SslRequest;

const DEFAULT_PORT: u16 = 5432;

/// A connection to the Postgres server.
///
/// It implements `Read`, `Write` and `StreamWrapper`, as well as `AsRawFd` on
/// Unix platforms and `AsRawSocket` on Windows platforms.
pub struct Stream(InternalStream);

impl Read for Stream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl StreamWrapper for Stream {
    fn get_ref(&self) -> &Stream {
        self
    }

    fn get_mut(&mut self) -> &mut Stream {
        self
    }
}

#[cfg(unix)]
impl AsRawFd for Stream {
    fn as_raw_fd(&self) -> RawFd {
        match self.0 {
            InternalStream::Tcp(ref s) => s.as_raw_fd(),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref s) => s.as_raw_fd(),
        }
    }
}

#[cfg(windows)]
impl AsRawSocket for Stream {
    fn as_raw_socket(&self) -> RawSocket {
        // Unix sockets aren't supported on windows, so no need to match
        match self.0 {
            InternalStream::Tcp(ref s) => s.as_raw_socket(),
        }
    }
}

enum InternalStream {
    Tcp(TcpStream),
    #[cfg(feature = "unix_socket")]
    Unix(UnixStream),
}

impl Read for InternalStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            InternalStream::Tcp(ref mut s) => s.read(buf),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref mut s) => s.read(buf),
        }
    }
}

impl Write for InternalStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            InternalStream::Tcp(ref mut s) => s.write(buf),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref mut s) => s.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            InternalStream::Tcp(ref mut s) => s.flush(),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref mut s) => s.flush(),
        }
    }
}

fn open_socket(params: &ConnectParams) -> Result<InternalStream, ConnectError> {
    let port = params.port.unwrap_or(DEFAULT_PORT);
    match params.target {
        ConnectTarget::Tcp(ref host) => {
            Ok(try!(TcpStream::connect(&(&**host, port)).map(InternalStream::Tcp)))
        }
        #[cfg(feature = "unix_socket")]
        ConnectTarget::Unix(ref path) => {
            let mut path = path.clone();
            path.push(&format!(".s.PGSQL.{}", port));
            Ok(try!(UnixStream::connect(&path).map(InternalStream::Unix)))
        }
    }
}

pub fn initialize_stream(params: &ConnectParams, ssl: &SslMode)
                            -> Result<Box<StreamWrapper>, ConnectError> {
    let mut socket = Stream(try!(open_socket(params)));

    let (ssl_required, negotiator) = match *ssl {
        SslMode::None => return Ok(Box::new(socket)),
        SslMode::Prefer(ref negotiator) => (false, negotiator),
        SslMode::Require(ref negotiator) => (true, negotiator),
    };

    try!(socket.write_message(&SslRequest { code: message::SSL_CODE }));
    try!(socket.flush());

    if try!(socket.read_u8()) == 'N' as u8 {
        if ssl_required {
            return Err(ConnectError::NoSslSupport);
        } else {
            return Ok(Box::new(socket));
        }
    }

    // Postgres doesn't support SSL over unix sockets
    let host = match params.target {
        ConnectTarget::Tcp(ref host) => host,
        #[cfg(feature = "unix_socket")]
        ConnectTarget::Unix(_) => return Err(ConnectError::IoError(::bad_response()))
    };

    match negotiator.negotiate_ssl(host, socket) {
        Ok(stream) => Ok(stream),
        Err(err) => Err(ConnectError::SslError(err))
    }
}
<commit_msg>cleanup<commit_after>use byteorder::ReadBytesExt;
use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
#[cfg(feature = "unix_socket")]
use unix_socket::UnixStream;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, RawSocket};

use {SslMode, ConnectParams, ConnectTarget};
use error::{ConnectError};
use io::{NegotiateSsl, StreamWrapper};
use message::{self, WriteMessage};
use message::FrontendMessage::SslRequest;

const DEFAULT_PORT: u16 = 5432;

/// A connection to the Postgres server.
///
/// It implements `Read`, `Write` and `StreamWrapper`, as well as `AsRawFd` on
/// Unix platforms and `AsRawSocket` on Windows platforms.
pub struct Stream(InternalStream);

impl Read for Stream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl StreamWrapper for Stream {
    fn get_ref(&self) -> &Stream {
        self
    }

    fn get_mut(&mut self) -> &mut Stream {
        self
    }
}

#[cfg(unix)]
impl AsRawFd for Stream {
    fn as_raw_fd(&self) -> RawFd {
        match self.0 {
            InternalStream::Tcp(ref s) => s.as_raw_fd(),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref s) => s.as_raw_fd(),
        }
    }
}

#[cfg(windows)]
impl AsRawSocket for Stream {
    fn as_raw_socket(&self) -> RawSocket {
        // Unix sockets aren't supported on windows, so no need to match
        match self.0 {
            InternalStream::Tcp(ref s) => s.as_raw_socket(),
        }
    }
}

enum InternalStream {
    Tcp(TcpStream),
    #[cfg(feature = "unix_socket")]
    Unix(UnixStream),
}

impl Read for InternalStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            InternalStream::Tcp(ref mut s) => s.read(buf),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref mut s) => s.read(buf),
        }
    }
}

impl Write for InternalStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            InternalStream::Tcp(ref mut s) => s.write(buf),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref mut s) => s.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            InternalStream::Tcp(ref mut s) => s.flush(),
            #[cfg(feature = "unix_socket")]
            InternalStream::Unix(ref mut s) => s.flush(),
        }
    }
}

fn open_socket(params: &ConnectParams) -> Result<InternalStream, ConnectError> {
    let port = params.port.unwrap_or(DEFAULT_PORT);
    match params.target {
        ConnectTarget::Tcp(ref host) => {
            Ok(try!(TcpStream::connect(&(&**host, port)).map(InternalStream::Tcp)))
        }
        #[cfg(feature = "unix_socket")]
        ConnectTarget::Unix(ref path) => {
            let path = path.join(&format!(".s.PGSQL.{}", port));
            Ok(try!(UnixStream::connect(&path).map(InternalStream::Unix)))
        }
    }
}

pub fn initialize_stream(params: &ConnectParams, ssl: &SslMode)
                            -> Result<Box<StreamWrapper>, ConnectError> {
    let mut socket = Stream(try!(open_socket(params)));

    let (ssl_required, negotiator) = match *ssl {
        SslMode::None => return Ok(Box::new(socket)),
        SslMode::Prefer(ref negotiator) => (false, negotiator),
        SslMode::Require(ref negotiator) => (true, negotiator),
    };

    try!(socket.write_message(&SslRequest { code: message::SSL_CODE }));
    try!(socket.flush());

    if try!(socket.read_u8()) == 'N' as u8 {
        if ssl_required {
            return Err(ConnectError::NoSslSupport);
        } else {
            return Ok(Box::new(socket));
        }
    }

    // Postgres doesn't support SSL over unix sockets
    let host = match params.target {
        ConnectTarget::Tcp(ref host) => host,
        #[cfg(feature = "unix_socket")]
        ConnectTarget::Unix(_) => return Err(ConnectError::IoError(::bad_response()))
    };

    match negotiator.negotiate_ssl(host, socket) {
        Ok(stream) => Ok(stream),
        Err(err) => Err(ConnectError::SslError(err))
    }
}
<|endoftext|>


<commit_before>extern crate para_bm;

use std::time::Duration;
use std::thread::Thread;

use para_bm::{sleep_ns};

fn fast () {
    sleep_ns(1000);
}

fn slow () {
    sleep_ns(1000000000);
}

fn my_bench<F> (f: F) where F: Fn() {

    // first "throw away" run
    let mut d = Duration::span(|| {
        f();
    });
    let ns = d.num_nanoseconds().unwrap();
    println!("num nano seconds first run {}", ns);

    let mut sum = 0;
    // iterate to get more cache hits, etc...
    for _ in 0 .. 10 {
        d = Duration::span(|| {
            f();
        });
        let ns = d.num_nanoseconds().unwrap();
        sum = sum + ns;
    }

    let avg = (sum as f64) / 10.0;
    println!("average after first run {}", avg);
}

fn main () {

    println!("Running Threaded slow");
    my_bench(||{
        let threads_holder = (0 .. 3).map(|ii| {
            Thread::scoped(move || {
                slow();
                println!("Done slow thread {}", ii);
            })
        }).collect::<Vec<_>>();
        println!("done spawning");
    });

    /*
    println!("Running Non-Threaded fast");
    my_bench(||{
        fast();
    });
    */

}
<commit_msg>thread and non-thread<commit_after>extern crate para_bm;

use std::time::Duration;
use std::thread::Thread;

use para_bm::{sleep_ns};

fn fast () {
    sleep_ns(1000);
}

fn slow () {
    sleep_ns(1000000000);
}

fn my_bench<F> (f: F) where F: Fn() {

    // first "throw away" run
    let mut d = Duration::span(|| {
        f();
    });
    let ns = d.num_nanoseconds().unwrap();
    println!("num nano seconds first run {}", ns);

    let mut sum = 0;
    // iterate to get more cache hits, etc...
    for _ in 0 .. 10 {
        d = Duration::span(|| {
            f();
        });
        let ns = d.num_nanoseconds().unwrap();
        sum = sum + ns;
    }

    let avg = (sum as f64) / 10.0;
    println!("average after first run {}", avg);
}

fn main () {

    println!("Running Threaded slow");
    my_bench(||{
        let threads_holder = (0 .. 3).map(|ii| {
            Thread::scoped(move || {
                slow();
            })
        }).collect::<Vec<_>>();
    });
    println!("Done threaded slow");

    println!("Running serial slow");
    my_bench(||{
        let serial_holder = (0 .. 3).map(|ii| {
            slow();
        }).collect::<Vec<_>>();
    });
}
<|endoftext|>


<commit_before>//
// Copyright 2021 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.
//

//! Type, constant and Wasm host function definitions for the Oak-Functions application
//! binary interface (ABI).

pub mod proto {
    include!(concat!(env!("OUT_DIR"), "/oak.functions.abi.rs"));
    include!(concat!(env!("OUT_DIR"), "/oak.functions.lookup_data.rs"));
    include!(concat!(env!("OUT_DIR"), "/oak.functions.invocation.rs"));

    impl Response {
        /// Creates a new instance of Response.
        ///
        /// Sets the `status` and `body` to the given status and body, and sets the
        /// `length` to the length of the body.
        pub fn create(status: StatusCode, body: Vec<u8>) -> Self {
            Response {
                status: status as i32,
                body: body.clone(),
                length: body.len() as u64,
            }
        }

        /// Returns the body of the response, excluding any trailing 0s.
        ///
        /// Uses the effective length of the body, in `self.length`, to remove the trailing 0s.
        /// Returns as error if `self.length` cannot be converted to `usize` due to an overflow.
        pub fn body(&self) -> Result<&[u8], std::num::TryFromIntError> {
            use std::convert::TryFrom;

            let length = usize::try_from(self.length)?;
            Ok(&self.body.as_slice()[..length])
        }
    }
}

// TODO(#1963): Add tests, in an example.

// The Oak-Functions ABI primarily consists of a collection of Wasm host functions in the
// "oak_functions" module that are made available to WebAssembly modules running as Oak-Functions
// workloads.
// See https://rustwasm.github.io/book/reference/js-ffi.html
#[link(wasm_import_module = "oak_functions")]
extern "C" {
    /// See [`read_request`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#read_request).
    pub fn read_request(buf_ptr_ptr: *mut *mut u8, buf_len_ptr: *mut usize) -> u32;

    /// See [`write_response`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#write_response).
    pub fn write_response(buf_ptr: *const u8, buf_len: usize) -> u32;

    /// See [`write_log_message`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#write_log_message).
    pub fn write_log_message(buf_ptr: *const u8, buf_len: usize) -> u32;

    /// See [`report_metric`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#report_metric).
    pub fn report_metric(buf_ptr: *const u8, buf_len: usize, value: i64) -> u32;

    /// See [`storage_get_item`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#storage_get_item).
    pub fn storage_get_item(
        key_ptr: *const u8,
        key_len: usize,
        value_ptr_ptr: *mut *mut u8,
        value_len_ptr: *mut usize,
    ) -> u32;

    /// See [`tf_model_infer`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#tf_model_infer).
    pub fn tf_model_infer(
        input_ptr: *const u8,
        input_len: usize,
        inference_ptr_ptr: *mut *mut u8,
        inference_len_ptr: *mut usize,
    ) -> u32;
}
<commit_msg>Add definitions of Wasm host functions based on channels (#2426)<commit_after>//
// Copyright 2021 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.
//

//! Type, constant and Wasm host function definitions for the Oak-Functions application
//! binary interface (ABI).

type ChannelHandle = u32;
type StatusCode = u32;

pub mod proto {
    include!(concat!(env!("OUT_DIR"), "/oak.functions.abi.rs"));
    include!(concat!(env!("OUT_DIR"), "/oak.functions.lookup_data.rs"));
    include!(concat!(env!("OUT_DIR"), "/oak.functions.invocation.rs"));

    impl Response {
        /// Creates a new instance of Response.
        ///
        /// Sets the `status` and `body` to the given status and body, and sets the
        /// `length` to the length of the body.
        pub fn create(status: StatusCode, body: Vec<u8>) -> Self {
            Response {
                status: status as i32,
                body: body.clone(),
                length: body.len() as u64,
            }
        }

        /// Returns the body of the response, excluding any trailing 0s.
        ///
        /// Uses the effective length of the body, in `self.length`, to remove the trailing 0s.
        /// Returns as error if `self.length` cannot be converted to `usize` due to an overflow.
        pub fn body(&self) -> Result<&[u8], std::num::TryFromIntError> {
            use std::convert::TryFrom;

            let length = usize::try_from(self.length)?;
            Ok(&self.body.as_slice()[..length])
        }
    }
}

// TODO(#1963): Add tests, in an example.

// The Oak-Functions ABI primarily consists of a collection of Wasm host functions in the
// "oak_functions" module that are made available to WebAssembly modules running as Oak-Functions
// workloads.
// See https://rustwasm.github.io/book/reference/js-ffi.html
#[link(wasm_import_module = "oak_functions")]
extern "C" {
    /// See [`read_request`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#read_request).
    pub fn read_request(buf_ptr_ptr: *mut *mut u8, buf_len_ptr: *mut usize) -> u32;

    /// See [`write_response`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#write_response).
    pub fn write_response(buf_ptr: *const u8, buf_len: usize) -> u32;

    /// See [`write_log_message`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#write_log_message).
    pub fn write_log_message(buf_ptr: *const u8, buf_len: usize) -> u32;

    /// See [`report_metric`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#report_metric).
    pub fn report_metric(buf_ptr: *const u8, buf_len: usize, value: i64) -> u32;

    /// See [`storage_get_item`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#storage_get_item).
    pub fn storage_get_item(
        key_ptr: *const u8,
        key_len: usize,
        value_ptr_ptr: *mut *mut u8,
        value_len_ptr: *mut usize,
    ) -> u32;

    /// See [`tf_model_infer`](https://github.com/project-oak/oak/blob/main/docs/oak_functions_abi.md#tf_model_infer).
    pub fn tf_model_infer(
        input_ptr: *const u8,
        input_len: usize,
        inference_ptr_ptr: *mut *mut u8,
        inference_len_ptr: *mut usize,
    ) -> u32;

    /// Allocates a buffer on the callers memory to write a message from the channel with
    /// `channel_handle`. After a successful call `dest_buf_ptr_ptr` holds the address of the
    /// buffer and `dest_buf_len_ptr` holds its length. The caller can then read
    /// the message from `dest_buf_ptr_ptr`.
    ///
    /// Returns a status code to indicate success. In particular, returns immediately
    /// with an appropriate status code, if no message is available on the channel.
    pub fn channel_read(
        channel_handle: ChannelHandle,
        dest_buf_ptr_ptr: *mut *mut u8,
        dest_buf_len_ptr: *mut usize,
    ) -> StatusCode;

    /// Writes a message, i.e., `src_buf_len` bytes, from `scr_buf_ptr` into the channel with
    /// `channel_handle`.
    ///
    /// Returns a status code to indicate success.
    pub fn channel_write(
        channel_handle: ChannelHandle,
        src_buf_ptr: *const u8,
        src_buf_len: usize,
    ) -> StatusCode;

    /// Waits until at least one of the channels from the channel handles in the buffer at
    /// `channel_handle_buf_ptr` has a message to read, or
    /// until the `deadline_ms` expires. After a successful call to `channel_wait`, the buffer at
    /// `ready_channel_handle_buf_ptr` holds the channel handles with at least one message to read.
    /// Both, `channel_handle_buf_len` and `ready_channel_handle_buf_len` hold the length of the
    /// respective buffers in bytes.
    ///
    /// Returns a status code to indicate success or deadline expiration.
    pub fn channel_wait(
        channel_handle_buf_ptr: *const ChannelHandle,
        channel_handle_buf_len: usize,
        ready_channel_handle_buf_ptr: *mut *mut i32,
        ready_channel_handle_buf_len: *mut usize,
        deadline_ms: u32,
    ) -> StatusCode;
}
<|endoftext|>


<commit_before>// Copyright 2018 Dmitry Tantsur <divius.inside@gmail.com>
//
// 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.

//! Session structure definition.

use std::cell::Ref;

use reqwest::{Body, Method, RequestBuilder as ReqwestRB, Response, Url};
use reqwest::header::{Header, Headers};
use serde::Serialize;
use serde::de::DeserializeOwned;

use super::Result;
use super::auth::AuthMethod;
use super::common::ApiVersion;
use super::utils;

/// Information about API endpoint.
#[derive(Clone, Debug)]
pub struct ServiceInfo {
    /// Root endpoint.
    pub root_url: Url,
    /// Current API version (if supported).
    pub current_version: Option<ApiVersion>,
    /// Minimum API version (if supported).
    pub minimum_version: Option<ApiVersion>
}

/// Trait representing a service type.
pub trait ServiceType {
    /// Service type to pass to the catalog.
    fn catalog_type() -> &'static str;

    /// Get basic service information.
    fn service_info(endpoint: Url, auth: &AuthMethod) -> Result<ServiceInfo>;

    /// Return headers to set for this API version.
    fn api_version_headers(_version: ApiVersion) -> Option<Headers> { None }
}

/// An HTTP request builder.
///
/// This is a thin wrapper around reqwest's RequestBuilder with error handling.
#[derive(Debug)]
pub struct RequestBuilder {
    inner: ReqwestRB,
}

impl RequestBuilder {
    /// Create a RequestBuilder by wrapping a reqwest's one.
    pub fn new(inner: ReqwestRB) -> RequestBuilder {
        RequestBuilder {
            inner: inner
        }
    }

    /// Access to the inner object.
    pub fn inner_mut(&mut self) -> &mut ReqwestRB {
        &mut self.inner
    }

    /// Take the inner object out.
    pub fn into_inner(self) -> ReqwestRB {
        self.inner
    }

    /// Add a Header to this Request.
    pub fn header<H: Header>(&mut self, header: H) -> &mut RequestBuilder {
        let _ = self.inner.header(header);
        self
    }

    /// Add a set of Headers to the existing ones on this Request.
    pub fn headers(&mut self, headers: Headers) -> &mut RequestBuilder {
        let _ = self.inner.headers(headers);
        self
    }

    /// Set the request body.
    pub fn body<T: Into<Body>>(&mut self, body: T) -> &mut RequestBuilder {
        let _ = self.inner.body(body);
        self
    }

    /// Modify the query string of the URL.
    pub fn query<T: Serialize>(&mut self, query: &T) -> &mut RequestBuilder {
        let _ = self.inner.query(query);
        self
    }

    /// Send a JSON body.
    pub fn json<T: Serialize>(&mut self, json: &T) -> &mut RequestBuilder {
        let _ = self.inner.json(json);
        self
    }

    /// Construct the Request and sends it the target URL, returning a Response.
    pub fn send(&mut self) -> Result<Response> {
        _log(self.inner.send()?).error_for_status().map_err(From::from)
    }

    /// Construct the Request, send it and receive a JSON.
    pub fn receive_json<T: DeserializeOwned>(&mut self) -> Result<T> {
        _log(self.inner.send()?).error_for_status()?.json().map_err(From::from)
    }
}

#[inline]
fn _log(resp: Response) -> Response {
    trace!("HTTP request to {} returned {}", resp.url(), resp.status());
    resp
}

/// An OpenStack API session.
///
/// The session object serves as a wrapper around an HTTP(s) client, handling
/// authentication, accessing the service catalog and token refresh.
///
/// The session object also owns the endpoint interface to use.
#[derive(Debug, Clone)]
pub struct Session {
    auth: Box<AuthMethod>,
    cached_info: utils::MapCache<&'static str, ServiceInfo>,
    endpoint_interface: String
}

impl Session {
    /// Create a new session with a given authentication plugin.
    ///
    /// The resulting session will use the default endpoint interface (usually,
    /// public).
    pub fn new<Auth: AuthMethod + 'static>(auth_method: Auth) -> Session {
        let ep = auth_method.default_endpoint_interface();
        Session {
            auth: Box::new(auth_method),
            cached_info: utils::MapCache::new(),
            endpoint_interface: ep
        }
    }

    /// Convert this session into one using the given endpoint interface.
    pub fn with_endpoint_interface<S>(self, endpoint_interface: S)
            -> Session where S: Into<String> {
        Session {
            auth: self.auth,
            // ServiceInfo has to be refreshed
            cached_info: utils::MapCache::new(),
            endpoint_interface: endpoint_interface.into()
        }
    }

    /// Get a reference to the authentication method in use.
    pub fn auth_method(&self) -> &AuthMethod {
        self.auth.as_ref()
    }

    /// Get a mutable reference to the authentication method in use.
    pub fn auth_method_mut(&mut self) -> &mut AuthMethod {
        self.auth.as_mut()
    }

    /// Get service info for the given service.
    pub fn get_service_info<Srv>(&self) -> Result<ServiceInfo>
            where Srv: ServiceType {
        let info = self.get_service_info_ref::<Srv>()?;
        Ok(info.clone())
    }

    /// Construct and endpoint for the given service from the path.
    pub fn get_endpoint<Srv: ServiceType>(&self, path: &[&str])
            -> Result<Url> {
        let info = self.get_service_info_ref::<Srv>()?;
        Ok(utils::url::extend(info.root_url.clone(), path))
    }

    /// Make an HTTP request to the given service.
    pub fn request<Srv: ServiceType>(&self, method: Method, path: &[&str],
                                     api_version: Option<ApiVersion>)
            -> Result<RequestBuilder> {
        let url = self.get_endpoint::<Srv>(path)?;
        trace!("Sending HTTP {} request to {} with API version {:?}",
               method, url, api_version);
        let maybe_headers = api_version.and_then(|ver| {
            Srv::api_version_headers(ver)
        });
        let mut builder = self.auth.request(method, url)?;
        if let Some(headers) = maybe_headers {
            let _unused = builder.headers(headers);
        }
        Ok(builder)
    }

    fn ensure_service_info<Srv>(&self) -> Result<()> where Srv: ServiceType {
        self.cached_info.ensure_value(Srv::catalog_type(), |_| {
            self.get_catalog_endpoint(Srv::catalog_type())
                .and_then(|ep| Srv::service_info(ep, self.auth_method()))
        })?;

        Ok(())
    }

    fn get_catalog_endpoint<S>(&self, service_type: S) -> Result<Url>
            where S: Into<String> {
        self.auth.get_endpoint(service_type.into(),
                               Some(self.endpoint_interface.clone()))
    }

    fn get_service_info_ref<Srv>(&self)
            -> Result<Ref<ServiceInfo>> where Srv: ServiceType {
        self.ensure_service_info::<Srv>()?;
        Ok(self.cached_info.get_ref(&Srv::catalog_type()).unwrap())
    }
}

#[cfg(test)]
mod test {
    use super::super::utils;

    #[test]
    fn test_session_new() {
        let s = utils::test::new_session(utils::test::URL);
        let ep = s.get_catalog_endpoint("fake").unwrap();
        assert_eq!(&ep.to_string(), utils::test::URL);
    }

    #[test]
    fn test_session_get_endpoint() {
        let s = utils::test::new_session(utils::test::URL);
        let ep = s.get_endpoint::<utils::test::FakeServiceType>(&[])
            .unwrap();
        assert_eq!(&ep.to_string(), utils::test::URL);
    }

    #[test]
    fn test_session_get_endpoint_with_path() {
        let s = utils::test::new_session(utils::test::URL);
        let ep = s.get_endpoint::<utils::test::FakeServiceType>(&["foo", "bar"])
            .unwrap();
        assert_eq!(ep.to_string(), format!("{}foo/bar", utils::test::URL));
    }
}
<commit_msg>chore(session): log actual error message<commit_after>// Copyright 2018 Dmitry Tantsur <divius.inside@gmail.com>
//
// 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.

//! Session structure definition.

use std::cell::Ref;

use log;
use reqwest::{Body, Method, RequestBuilder as ReqwestRB, Response, Url};
use reqwest::header::{Header, Headers};
use serde::Serialize;
use serde::de::DeserializeOwned;

use super::Result;
use super::auth::AuthMethod;
use super::common::ApiVersion;
use super::utils;

/// Information about API endpoint.
#[derive(Clone, Debug)]
pub struct ServiceInfo {
    /// Root endpoint.
    pub root_url: Url,
    /// Current API version (if supported).
    pub current_version: Option<ApiVersion>,
    /// Minimum API version (if supported).
    pub minimum_version: Option<ApiVersion>
}

/// Trait representing a service type.
pub trait ServiceType {
    /// Service type to pass to the catalog.
    fn catalog_type() -> &'static str;

    /// Get basic service information.
    fn service_info(endpoint: Url, auth: &AuthMethod) -> Result<ServiceInfo>;

    /// Return headers to set for this API version.
    fn api_version_headers(_version: ApiVersion) -> Option<Headers> { None }
}

/// An HTTP request builder.
///
/// This is a thin wrapper around reqwest's RequestBuilder with error handling.
#[derive(Debug)]
pub struct RequestBuilder {
    inner: ReqwestRB,
}

impl RequestBuilder {
    /// Create a RequestBuilder by wrapping a reqwest's one.
    pub fn new(inner: ReqwestRB) -> RequestBuilder {
        RequestBuilder {
            inner: inner
        }
    }

    /// Access to the inner object.
    pub fn inner_mut(&mut self) -> &mut ReqwestRB {
        &mut self.inner
    }

    /// Take the inner object out.
    pub fn into_inner(self) -> ReqwestRB {
        self.inner
    }

    /// Add a Header to this Request.
    pub fn header<H: Header>(&mut self, header: H) -> &mut RequestBuilder {
        let _ = self.inner.header(header);
        self
    }

    /// Add a set of Headers to the existing ones on this Request.
    pub fn headers(&mut self, headers: Headers) -> &mut RequestBuilder {
        let _ = self.inner.headers(headers);
        self
    }

    /// Set the request body.
    pub fn body<T: Into<Body>>(&mut self, body: T) -> &mut RequestBuilder {
        let _ = self.inner.body(body);
        self
    }

    /// Modify the query string of the URL.
    pub fn query<T: Serialize>(&mut self, query: &T) -> &mut RequestBuilder {
        let _ = self.inner.query(query);
        self
    }

    /// Send a JSON body.
    pub fn json<T: Serialize>(&mut self, json: &T) -> &mut RequestBuilder {
        let _ = self.inner.json(json);
        self
    }

    /// Construct the Request and sends it the target URL, returning a Response.
    pub fn send(&mut self) -> Result<Response> {
        _log(self.inner.send()?).error_for_status().map_err(From::from)
    }

    /// Construct the Request, send it and receive a JSON.
    pub fn receive_json<T: DeserializeOwned>(&mut self) -> Result<T> {
        _log(self.inner.send()?).error_for_status()?.json().map_err(From::from)
    }
}

fn _log(mut resp: Response) -> Response {
    if log_enabled!(log::Level::Trace) {
        let details = if resp.status().is_client_error() || resp.status().is_server_error() {
            resp.text().ok()
        } else {
            None
        };

        // TODO(dtantsur): proper error parsing
        trace!("HTTP request to {} returned {}; error: {:?}",
               resp.url(), resp.status(), details);
    }
    resp
}

/// An OpenStack API session.
///
/// The session object serves as a wrapper around an HTTP(s) client, handling
/// authentication, accessing the service catalog and token refresh.
///
/// The session object also owns the endpoint interface to use.
#[derive(Debug, Clone)]
pub struct Session {
    auth: Box<AuthMethod>,
    cached_info: utils::MapCache<&'static str, ServiceInfo>,
    endpoint_interface: String
}

impl Session {
    /// Create a new session with a given authentication plugin.
    ///
    /// The resulting session will use the default endpoint interface (usually,
    /// public).
    pub fn new<Auth: AuthMethod + 'static>(auth_method: Auth) -> Session {
        let ep = auth_method.default_endpoint_interface();
        Session {
            auth: Box::new(auth_method),
            cached_info: utils::MapCache::new(),
            endpoint_interface: ep
        }
    }

    /// Convert this session into one using the given endpoint interface.
    pub fn with_endpoint_interface<S>(self, endpoint_interface: S)
            -> Session where S: Into<String> {
        Session {
            auth: self.auth,
            // ServiceInfo has to be refreshed
            cached_info: utils::MapCache::new(),
            endpoint_interface: endpoint_interface.into()
        }
    }

    /// Get a reference to the authentication method in use.
    pub fn auth_method(&self) -> &AuthMethod {
        self.auth.as_ref()
    }

    /// Get a mutable reference to the authentication method in use.
    pub fn auth_method_mut(&mut self) -> &mut AuthMethod {
        self.auth.as_mut()
    }

    /// Get service info for the given service.
    pub fn get_service_info<Srv>(&self) -> Result<ServiceInfo>
            where Srv: ServiceType {
        let info = self.get_service_info_ref::<Srv>()?;
        Ok(info.clone())
    }

    /// Construct and endpoint for the given service from the path.
    pub fn get_endpoint<Srv: ServiceType>(&self, path: &[&str])
            -> Result<Url> {
        let info = self.get_service_info_ref::<Srv>()?;
        Ok(utils::url::extend(info.root_url.clone(), path))
    }

    /// Make an HTTP request to the given service.
    pub fn request<Srv: ServiceType>(&self, method: Method, path: &[&str],
                                     api_version: Option<ApiVersion>)
            -> Result<RequestBuilder> {
        let url = self.get_endpoint::<Srv>(path)?;
        trace!("Sending HTTP {} request to {} with API version {:?}",
               method, url, api_version);
        let maybe_headers = api_version.and_then(|ver| {
            Srv::api_version_headers(ver)
        });
        let mut builder = self.auth.request(method, url)?;
        if let Some(headers) = maybe_headers {
            let _unused = builder.headers(headers);
        }
        Ok(builder)
    }

    fn ensure_service_info<Srv>(&self) -> Result<()> where Srv: ServiceType {
        self.cached_info.ensure_value(Srv::catalog_type(), |_| {
            self.get_catalog_endpoint(Srv::catalog_type())
                .and_then(|ep| Srv::service_info(ep, self.auth_method()))
        })?;

        Ok(())
    }

    fn get_catalog_endpoint<S>(&self, service_type: S) -> Result<Url>
            where S: Into<String> {
        self.auth.get_endpoint(service_type.into(),
                               Some(self.endpoint_interface.clone()))
    }

    fn get_service_info_ref<Srv>(&self)
            -> Result<Ref<ServiceInfo>> where Srv: ServiceType {
        self.ensure_service_info::<Srv>()?;
        Ok(self.cached_info.get_ref(&Srv::catalog_type()).unwrap())
    }
}

#[cfg(test)]
mod test {
    use super::super::utils;

    #[test]
    fn test_session_new() {
        let s = utils::test::new_session(utils::test::URL);
        let ep = s.get_catalog_endpoint("fake").unwrap();
        assert_eq!(&ep.to_string(), utils::test::URL);
    }

    #[test]
    fn test_session_get_endpoint() {
        let s = utils::test::new_session(utils::test::URL);
        let ep = s.get_endpoint::<utils::test::FakeServiceType>(&[])
            .unwrap();
        assert_eq!(&ep.to_string(), utils::test::URL);
    }

    #[test]
    fn test_session_get_endpoint_with_path() {
        let s = utils::test::new_session(utils::test::URL);
        let ep = s.get_endpoint::<utils::test::FakeServiceType>(&["foo", "bar"])
            .unwrap();
        assert_eq!(ep.to_string(), format!("{}foo/bar", utils::test::URL));
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;

use num::BigUint;

use Construct;
use Data;
use Count;
use Of;
use ToIndex;
use ToPos;
use Zero;

/// A discrete space that models a full square of NxN pairs.
///
/// Dimension is natural number, position is `(x, y)`.
pub struct SqPair<T = Data>(PhantomData<T>);

impl<T> Construct for SqPair<T> {
    fn new() -> SqPair<T> {SqPair(PhantomData)}
}

impl Count<usize> for SqPair<Data> {
    type N = usize;
    fn count(&self, dim: &usize) -> usize {dim * dim}
}

impl Count<BigUint> for SqPair<Data> {
    type N = BigUint;
    fn count(&self, dim: &BigUint) -> BigUint {dim * dim}
}

impl<T, U> Count<U> for SqPair<Of<T>>
    where
        T: Construct + Count<U>,
        SqPair: Count<<T as Count<U>>::N, N = <T as Count<U>>::N>
{
    type N = <T as Count<U>>::N;
    fn count(&self, dim: &U) -> Self::N {
        let of: T = Construct::new();
        let data: SqPair<Data> = Construct::new();
        data.count(&of.count(dim))
    }
}

impl Zero<usize, (usize, usize)> for SqPair<Data> {
    fn zero(&self, _: &usize) -> (usize, usize) {(0, 0)}
}

impl<T, U, V>
Zero<U, (V, V)> for SqPair<Of<T>>
    where
        T: Construct + Zero<U, V>
{
    fn zero(&self, dim: &U) -> (V, V) {
        let of: T = Construct::new();
        (of.zero(dim), of.zero(dim))
    }
}

impl ToIndex<usize, (usize, usize)> for SqPair<Data> {
    fn to_index(&self, dim: &usize, &(a, b): &(usize, usize)) -> usize {
        a + b * dim
    }
}

impl<T, U, V>
ToIndex<U, (V, V)> for SqPair<Of<T>>
    where
        T: Construct + ToIndex<U, V> + Count<U, N = usize>
{
    fn to_index(
        &self,
        dim: &U,
        &(ref a, ref b): &(V, V)
    ) -> usize {
        let of: T = Construct::new();
        let data: SqPair<Data> = Construct::new();
        let a = of.to_index(dim, a);
        let b = of.to_index(dim, b);
        data.to_index(&self.count(dim), &(a, b))
    }
}

impl ToPos<usize, (usize, usize)> for SqPair<Data> {
    fn to_pos(&self, dim: &usize, index: usize, pos: &mut (usize, usize)) {
        pos.0 = index % dim;
        pos.1 = index / dim;
    }
}

impl<T, U, V>
ToPos<U, (V, V)> for SqPair<Of<T>>
    where
        T: Construct + Count<U, N = usize> + ToPos<U, V>
{
    fn to_pos(
        &self,
        dim: &U,
        index: usize,
        &mut (ref mut a, ref mut b): &mut (V, V)
    ) {
        let of: T = Construct::new();
        let data: SqPair<Data> = Construct::new();
        let count = of.count(dim);
        let mut pair = (0, 0);
        data.to_pos(&count, index, &mut pair);
        let (pair_a, pair_b) = pair;
        of.to_pos(dim, pair_a, a);
        of.to_pos(dim, pair_b, b);
    }
}
<commit_msg>Impl `Zero<BigUint, (BigUint, BigUint)>` for `SqPair`<commit_after>use std::marker::PhantomData;

use num::BigUint;

use Construct;
use Data;
use Count;
use Of;
use ToIndex;
use ToPos;
use Zero;

/// A discrete space that models a full square of NxN pairs.
///
/// Dimension is natural number, position is `(x, y)`.
pub struct SqPair<T = Data>(PhantomData<T>);

impl<T> Construct for SqPair<T> {
    fn new() -> SqPair<T> {SqPair(PhantomData)}
}

impl Count<usize> for SqPair<Data> {
    type N = usize;
    fn count(&self, dim: &usize) -> usize {dim * dim}
}

impl Count<BigUint> for SqPair<Data> {
    type N = BigUint;
    fn count(&self, dim: &BigUint) -> BigUint {dim * dim}
}

impl<T, U> Count<U> for SqPair<Of<T>>
    where
        T: Construct + Count<U>,
        SqPair: Count<<T as Count<U>>::N, N = <T as Count<U>>::N>
{
    type N = <T as Count<U>>::N;
    fn count(&self, dim: &U) -> Self::N {
        let of: T = Construct::new();
        let data: SqPair<Data> = Construct::new();
        data.count(&of.count(dim))
    }
}

impl Zero<usize, (usize, usize)> for SqPair<Data> {
    fn zero(&self, _: &usize) -> (usize, usize) {(0, 0)}
}

impl Zero<BigUint, (BigUint, BigUint)> for SqPair<Data> {
    fn zero(&self, _: &BigUint) -> (BigUint, BigUint) {(0usize.into(), 0usize.into())}
}

impl<T, U, V>
Zero<U, (V, V)> for SqPair<Of<T>>
    where
        T: Construct + Zero<U, V>
{
    fn zero(&self, dim: &U) -> (V, V) {
        let of: T = Construct::new();
        (of.zero(dim), of.zero(dim))
    }
}

impl ToIndex<usize, (usize, usize)> for SqPair<Data> {
    fn to_index(&self, dim: &usize, &(a, b): &(usize, usize)) -> usize {
        a + b * dim
    }
}

impl<T, U, V>
ToIndex<U, (V, V)> for SqPair<Of<T>>
    where
        T: Construct + ToIndex<U, V> + Count<U, N = usize>
{
    fn to_index(
        &self,
        dim: &U,
        &(ref a, ref b): &(V, V)
    ) -> usize {
        let of: T = Construct::new();
        let data: SqPair<Data> = Construct::new();
        let a = of.to_index(dim, a);
        let b = of.to_index(dim, b);
        data.to_index(&self.count(dim), &(a, b))
    }
}

impl ToPos<usize, (usize, usize)> for SqPair<Data> {
    fn to_pos(&self, dim: &usize, index: usize, pos: &mut (usize, usize)) {
        pos.0 = index % dim;
        pos.1 = index / dim;
    }
}

impl<T, U, V>
ToPos<U, (V, V)> for SqPair<Of<T>>
    where
        T: Construct + Count<U, N = usize> + ToPos<U, V>
{
    fn to_pos(
        &self,
        dim: &U,
        index: usize,
        &mut (ref mut a, ref mut b): &mut (V, V)
    ) {
        let of: T = Construct::new();
        let data: SqPair<Data> = Construct::new();
        let count = of.count(dim);
        let mut pair = (0, 0);
        data.to_pos(&count, index, &mut pair);
        let (pair_a, pair_b) = pair;
        of.to_pos(dim, pair_a, a);
        of.to_pos(dim, pair_b, b);
    }
}
<|endoftext|>


<commit_before>/// A type to represent all supported types for parser 'get'-like functions
#[derive(Clone, Debug, PartialEq)]
pub enum Variant<'a> {
    Bool(bool),
    Bytes(&'a [u8]),
    Str(&'a str),
    U8(u8),
    U16(u16),
    U32(u32),
    U64(u64),
    USize(usize),

    List(Box<Vec<Variant<'a>>>),
}

impl<'a> Variant<'a> {
    pub fn from_slice<T>(v: &'a [T]) -> Variant<'a>
    where
        Variant<'a>: From<&'a T>,
    {
        let v = v.iter().map(|s| Variant::from(s)).collect();
        Variant::List(Box::new(v))
    }

    pub fn from_vec<T>(v: &'a Vec<T>) -> Variant<'a>
    where
        Variant<'a>: From<&'a T>,
    {
        let v = v.iter().map(|s| Variant::from(s)).collect();
        Variant::List(Box::new(v))
    }
}

use std::convert::From;

macro_rules! variant_from_primitive {
    ( $t:ty, $it:expr ) => {
        impl<'a> From<$t> for Variant<'a> {
            fn from(input: $t) -> Self {
                $it(input)
            }
        }
    };
}

variant_from_primitive!(bool, Variant::Bool);
variant_from_primitive!(u8, Variant::U8);
variant_from_primitive!(u16, Variant::U16);
variant_from_primitive!(u32, Variant::U32);
variant_from_primitive!(u64, Variant::U64);
variant_from_primitive!(usize, Variant::USize);
impl<'a> From<&'a [u8]> for Variant<'a> {
    fn from(input: &'a [u8]) -> Self {
        Variant::Bytes(input)
    }
}

impl<'a> From<&'a str> for Variant<'a> {
    fn from(input: &'a str) -> Self {
        Variant::Str(input)
    }
}

impl<'a> From<&'a String> for Variant<'a> {
    fn from(input: &'a String) -> Self {
        Variant::Str(&input)
    }
}

#[cfg(test)]
mod tests {
    use super::Variant;
    use std::mem;

    #[test]
    fn variant_size() {
        // expected: 24: 8 (variant) + 16 (slice)
        println!("sizeof Variant: {}", mem::size_of::<Variant>());
    }
}
<commit_msg>Add macro to help generating get() and keys() for RParser<commit_after>/// A type to represent all supported types for parser 'get'-like functions
#[derive(Clone, Debug, PartialEq)]
pub enum Variant<'a> {
    Bool(bool),
    Bytes(&'a [u8]),
    Str(&'a str),
    U8(u8),
    U16(u16),
    U32(u32),
    U64(u64),
    USize(usize),

    List(Box<Vec<Variant<'a>>>),
}

impl<'a> Variant<'a> {
    pub fn from_slice<T>(v: &'a [T]) -> Variant<'a>
    where
        Variant<'a>: From<&'a T>,
    {
        let v = v.iter().map(|s| Variant::from(s)).collect();
        Variant::List(Box::new(v))
    }

    pub fn from_vec<T>(v: &'a Vec<T>) -> Variant<'a>
    where
        Variant<'a>: From<&'a T>,
    {
        let v = v.iter().map(|s| Variant::from(s)).collect();
        Variant::List(Box::new(v))
    }
}

use std::convert::From;

macro_rules! variant_from_primitive {
    ( $t:ty, $it:expr ) => {
        impl<'a> From<$t> for Variant<'a> {
            fn from(input: $t) -> Self {
                $it(input)
            }
        }
    };
}

variant_from_primitive!(bool, Variant::Bool);
variant_from_primitive!(u8, Variant::U8);
variant_from_primitive!(u16, Variant::U16);
variant_from_primitive!(u32, Variant::U32);
variant_from_primitive!(u64, Variant::U64);
variant_from_primitive!(usize, Variant::USize);
impl<'a> From<&'a [u8]> for Variant<'a> {
    fn from(input: &'a [u8]) -> Self {
        Variant::Bytes(input)
    }
}

impl<'a> From<&'a str> for Variant<'a> {
    fn from(input: &'a str) -> Self {
        Variant::Str(input)
    }
}

impl<'a> From<&'a String> for Variant<'a> {
    fn from(input: &'a String) -> Self {
        Variant::Str(&input)
    }
}

#[macro_export]
macro_rules! gen_get_variants_old {
    ( impl $( $pattern:expr => |$s:ident| $closure:expr ),* ) => {
    };
    ( $t:ident, $( $pattern:expr => |$s:ident| $closure:expr ),* ) => {
        fn get<'b>(&'b self, key: &str) -> Option<Variant<'b>> {
            match key {
                $(
                    $pattern => {
                        let closure = |$s: &'b $t| $closure;
                        closure(self)
                    },
                )*
                _ => None,
            }
        }
    };
}

#[macro_export]
macro_rules! gen_get_variants {
    // Entry point
    ($t:ident, $($body:tt)* ) => {
        fn get<'b>(&'b self, key: &str) -> Option<Variant<'b>> {
            gen_get_variants!{ @gen_match $t {}, self, key, $($body)* }
        }
        fn keys(&self) -> ::std::slice::Iter<&str> {
            gen_get_variants!{ @gen_keys [], $($body)* }
        }
    };
    //
    // MATCH ARMS
    //
    // Simple closure case
    (@gen_match $t:ident {$($arms:tt)*}, $self:ident, $key:ident, $pattern:pat => |$s:ident| $closure:expr, $($tail:tt)*) => {
        gen_get_variants!{
            @gen_match
            $t
            {
                $($arms)*
                $pattern => {
                    let closure = |$s: &'b $t| $closure;
                    closure($self)
                },
            },
            $self,
            $key,
            $($tail)*
        }
    };
    // Shortcut: into
    (@gen_match $t:ident {$($arms:tt)*}, $self:ident, $key:ident, $pattern:ident => into, $($tail:tt)*) => {
        gen_get_variants!{
            @gen_match
            $t
            {
                $($arms)*
                stringify!{$pattern} => {
                    Some($self.$pattern.into())
                },
            },
            $self,
            $key,
            $($tail)*
        }
    };
    // Shortcut: map
    (@gen_match $t:ident {$($arms:tt)*}, $self:ident, $key:ident, $pattern:ident => map, $($tail:tt)*) => {
        gen_get_variants!{
            @gen_match
            $t
            {
                $($arms)*
                stringify!{$pattern} => {
                    $self.$pattern.map(|p| p.into())
                },
            },
            $self,
            $key,
            $($tail)*
        }
    };
    // Shortcut: map_as_ref
    (@gen_match $t:ident {$($arms:tt)*}, $self:ident, $key:ident, $pattern:ident => map_as_ref, $($tail:tt)*) => {
        gen_get_variants!{
            @gen_match
            $t
            {
                $($arms)*
                stringify!{$pattern} => {
                    $self.$pattern.as_ref().map(|p| p.into())
                },
            },
            $self,
            $key,
            $($tail)*
        }
    };
    // Termination rule
    (@gen_match $t:ident {$($arms:tt)*}, $self:ident, $key:ident, /* $($body:tt)* */  $(,)* ) => {
        match $key {
            $($arms)*
            _ => None
        }
    };
    //
    // KEYS
    //
    // Simple closure case
    (@gen_keys [$($arms:tt)*], $pattern:tt => |$s:ident| $closure:expr, $($tail:tt)*) => {
        gen_get_variants!{
            @gen_keys
            [
                $($arms)* $pattern,
            ],
            $($tail)*
        }
    };
    // Shortcut: into
    (@gen_keys [$($arms:tt)*], $pattern:ident => $kw:tt, $($tail:tt)*) => {
        gen_get_variants!{
            @gen_keys
            [
                $($arms)* stringify!{$pattern},
            ],
            $($tail)*
        }
    };
    // Termination rule
    (@gen_keys [$($keys:tt)*], /* $($body:tt)* */  $(,)* ) => {
        [
            $($keys)*
        ].iter()
    };
}

#[cfg(test)]
mod tests {
    use super::Variant;
    use std::mem;

    #[test]
    fn variant_size() {
        // expected: 24: 8 (variant) + 16 (slice)
        println!("sizeof Variant: {}", mem::size_of::<Variant>());
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use conduit::{Request, Response};
use conduit_router::RequestParams;
use diesel;
use diesel::pg::{Pg, PgConnection};
use diesel::pg::upsert::*;
use diesel::prelude::*;
use semver;
use serde_json;
use time::{Duration, Timespec, now_utc, strptime};
use url;

use Crate;
use app::RequestApp;
use db::RequestTransaction;
use dependency::{Dependency, EncodableDependency};
use download::{VersionDownload, EncodableVersionDownload};
use git;
use owner::{rights, Rights};
use schema::*;
use user::RequestUser;
use util::errors::CargoError;
use util::{RequestUtils, CargoResult, human};
use license_exprs;

// This is necessary to allow joining version to both crates and readme_rendering
// in the render-readmes script.
enable_multi_table_joins!(crates, readme_rendering);

// Queryable has a custom implementation below
#[derive(Clone, Identifiable, Associations, Debug)]
#[belongs_to(Crate)]
pub struct Version {
    pub id: i32,
    pub crate_id: i32,
    pub num: semver::Version,
    pub updated_at: Timespec,
    pub created_at: Timespec,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub yanked: bool,
    pub license: Option<String>,
}

#[derive(Insertable, Debug)]
#[table_name = "versions"]
pub struct NewVersion {
    crate_id: i32,
    num: String,
    features: String,
    license: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct EncodableVersion {
    pub id: i32,
    #[serde(rename = "crate")]
    pub krate: String,
    pub num: String,
    pub dl_path: String,
    pub readme_path: String,
    pub updated_at: String,
    pub created_at: String,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub yanked: bool,
    pub license: Option<String>,
    pub links: VersionLinks,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct VersionLinks {
    pub dependencies: String,
    pub version_downloads: String,
    pub authors: String,
}

#[derive(Insertable, Identifiable, Queryable, Associations, Debug, Clone, Copy)]
#[belongs_to(Version)]
#[table_name = "readme_rendering"]
#[primary_key(version_id)]
struct ReadmeRendering {
    version_id: i32,
    rendered_at: Timespec,
}

impl Version {
    pub fn encodable(self, crate_name: &str) -> EncodableVersion {
        let Version {
            id,
            num,
            updated_at,
            created_at,
            downloads,
            features,
            yanked,
            license,
            ..
        } = self;
        let num = num.to_string();
        EncodableVersion {
            dl_path: format!("/api/v1/crates/{}/{}/download", crate_name, num),
            readme_path: format!("/api/v1/crates/{}/{}/readme", crate_name, num),
            num: num.clone(),
            id: id,
            krate: crate_name.to_string(),
            updated_at: ::encode_time(updated_at),
            created_at: ::encode_time(created_at),
            downloads: downloads,
            features: features,
            yanked: yanked,
            license: license,
            links: VersionLinks {
                dependencies: format!("/api/v1/crates/{}/{}/dependencies", crate_name, num),
                version_downloads: format!("/api/v1/crates/{}/{}/downloads", crate_name, num),
                authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num),
            },
        }
    }

    /// Returns (dependency, crate dependency name)
    pub fn dependencies(&self, conn: &PgConnection) -> QueryResult<Vec<(Dependency, String)>> {
        Dependency::belonging_to(self)
            .inner_join(crates::table)
            .select((dependencies::all_columns, crates::name))
            .order((dependencies::optional, crates::name))
            .load(conn)
    }

    pub fn max<T>(versions: T) -> semver::Version
    where
        T: IntoIterator<Item = semver::Version>,
    {
        versions.into_iter().max().unwrap_or_else(|| {
            semver::Version {
                major: 0,
                minor: 0,
                patch: 0,
                pre: vec![],
                build: vec![],
            }
        })
    }

    pub fn record_readme_rendering(&self, conn: &PgConnection) -> CargoResult<()> {
        let rendered = ReadmeRendering {
            version_id: self.id,
            rendered_at: ::now(),
        };

        diesel::insert(&rendered.on_conflict(
            readme_rendering::version_id,
            do_update().set(
                readme_rendering::rendered_at.eq(
                    excluded(
                        readme_rendering::rendered_at,
                    ),
                ),
            ),
        )).into(readme_rendering::table)
            .execute(&*conn)?;
        Ok(())
    }
}

impl NewVersion {
    pub fn new(
        crate_id: i32,
        num: &semver::Version,
        features: &HashMap<String, Vec<String>>,
        license: Option<String>,
        license_file: Option<&str>,
    ) -> CargoResult<Self> {
        let features = serde_json::to_string(features)?;

        let mut new_version = NewVersion {
            crate_id: crate_id,
            num: num.to_string(),
            features: features,
            license: license,
        };

        new_version.validate_license(license_file)?;

        Ok(new_version)
    }

    pub fn save(&self, conn: &PgConnection, authors: &[String]) -> CargoResult<Version> {
        use diesel::{select, insert};
        use diesel::expression::dsl::exists;
        use schema::versions::dsl::*;

        let already_uploaded = versions.filter(crate_id.eq(self.crate_id)).filter(
            num.eq(&self.num),
        );
        if select(exists(already_uploaded)).get_result(conn)? {
            return Err(human(&format_args!(
                "crate version `{}` is already \
                 uploaded",
                self.num
            )));
        }

        conn.transaction(|| {
            let version = insert(self).into(versions).get_result::<Version>(conn)?;

            let new_authors = authors
                .iter()
                .map(|s| {
                    NewAuthor {
                        version_id: version.id,
                        name: &*s,
                    }
                })
                .collect::<Vec<_>>();

            insert(&new_authors).into(version_authors::table).execute(
                conn,
            )?;
            Ok(version)
        })
    }

    fn validate_license(&mut self, license_file: Option<&str>) -> CargoResult<()> {
        if let Some(ref license) = self.license {
            for part in license.split('/') {
                license_exprs::validate_license_expr(part).map_err(|e| {
                    human(&format_args!(
                        "{}; see http://opensource.org/licenses \
                         for options, and http://spdx.org/licenses/ \
                         for their identifiers",
                        e
                    ))
                })?;
            }
        } else if license_file.is_some() {
            // If no license is given, but a license file is given, flag this
            // crate as having a nonstandard license. Note that we don't
            // actually do anything else with license_file currently.
            self.license = Some(String::from("non-standard"));
        }
        Ok(())
    }
}

#[derive(Insertable, Debug)]
#[table_name = "version_authors"]
struct NewAuthor<'a> {
    version_id: i32,
    name: &'a str,
}

impl Queryable<versions::SqlType, Pg> for Version {
    type Row = (i32, i32, String, Timespec, Timespec, i32, Option<String>, bool, Option<String>);

    fn build(row: Self::Row) -> Self {
        let features = row.6
            .map(|s| serde_json::from_str(&s).unwrap())
            .unwrap_or_else(HashMap::new);
        Version {
            id: row.0,
            crate_id: row.1,
            num: semver::Version::parse(&row.2).unwrap(),
            updated_at: row.3,
            created_at: row.4,
            downloads: row.5,
            features: features,
            yanked: row.7,
            license: row.8,
        }
    }
}

/// Handles the `GET /versions` route.
// FIXME: where/how is this used?
pub fn index(req: &mut Request) -> CargoResult<Response> {
    use diesel::expression::dsl::any;
    let conn = req.db_conn()?;

    // Extract all ids requested.
    let query = url::form_urlencoded::parse(req.query_string().unwrap_or("").as_bytes());
    let ids = query
        .filter_map(|(ref a, ref b)| if *a == "ids[]" {
            b.parse().ok()
        } else {
            None
        })
        .collect::<Vec<i32>>();

    let versions = versions::table
        .inner_join(crates::table)
        .select((versions::all_columns, crates::name))
        .filter(versions::id.eq(any(ids)))
        .load::<(Version, String)>(&*conn)?
        .into_iter()
        .map(|(version, crate_name)| version.encodable(&crate_name))
        .collect();

    #[derive(Serialize)]
    struct R {
        versions: Vec<EncodableVersion>,
    }
    Ok(req.json(&R { versions: versions }))
}

/// Handles the `GET /versions/:version_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
    let (version, krate) = match req.params().find("crate_id") {
        Some(..) => version_and_crate(req)?,
        None => {
            let id = &req.params()["version_id"];
            let id = id.parse().unwrap_or(0);
            let conn = req.db_conn()?;
            versions::table
                .find(id)
                .inner_join(crates::table)
                .select((versions::all_columns, ::krate::ALL_COLUMNS))
                .first(&*conn)?
        }
    };

    #[derive(Serialize)]
    struct R {
        version: EncodableVersion,
    }
    Ok(req.json(&R { version: version.encodable(&krate.name) }))
}

fn version_and_crate(req: &mut Request) -> CargoResult<(Version, Crate)> {
    let crate_name = &req.params()["crate_id"];
    let semver = &req.params()["version"];
    if semver::Version::parse(semver).is_err() {
        return Err(human(&format_args!("invalid semver: {}", semver)));
    };
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let version = Version::belonging_to(&krate)
        .filter(versions::num.eq(semver))
        .first(&*conn)
        .map_err(|_| {
            human(&format_args!(
                "crate `{}` does not have a version `{}`",
                crate_name,
                semver
            ))
        })?;
    Ok((version, krate))
}

/// Handles the `GET /crates/:crate_id/:version/dependencies` route.
pub fn dependencies(req: &mut Request) -> CargoResult<Response> {
    let (version, _) = version_and_crate(req)?;
    let conn = req.db_conn()?;
    let deps = version.dependencies(&*conn)?;
    let deps = deps.into_iter()
        .map(|(dep, crate_name)| dep.encodable(&crate_name, None))
        .collect();

    #[derive(Serialize)]
    struct R {
        dependencies: Vec<EncodableDependency>,
    }
    Ok(req.json(&R { dependencies: deps }))
}

/// Handles the `GET /crates/:crate_id/:version/downloads` route.
pub fn downloads(req: &mut Request) -> CargoResult<Response> {
    use diesel::expression::dsl::date;
    let (version, _) = version_and_crate(req)?;
    let conn = req.db_conn()?;
    let cutoff_end_date = req.query()
        .get("before_date")
        .and_then(|d| strptime(d, "%Y-%m-%d").ok())
        .unwrap_or_else(now_utc)
        .to_timespec();
    let cutoff_start_date = cutoff_end_date + Duration::days(-89);

    let downloads = VersionDownload::belonging_to(&version)
        .filter(version_downloads::date.between(
            date(cutoff_start_date)..
                date(cutoff_end_date),
        ))
        .order(version_downloads::date)
        .load(&*conn)?
        .into_iter()
        .map(VersionDownload::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        version_downloads: Vec<EncodableVersionDownload>,
    }
    Ok(req.json(&R { version_downloads: downloads }))
}

/// Handles the `GET /crates/:crate_id/:version/authors` route.
pub fn authors(req: &mut Request) -> CargoResult<Response> {
    let (version, _) = version_and_crate(req)?;
    let conn = req.db_conn()?;
    let names = version_authors::table
        .filter(version_authors::version_id.eq(version.id))
        .select(version_authors::name)
        .order(version_authors::name)
        .load(&*conn)?;

    // It was imagined that we wold associate authors with users.
    // This was never implemented. This complicated return struct
    // is all that is left, hear for backwards compatibility.
    #[derive(Serialize)]
    struct R {
        users: Vec<::user::EncodablePublicUser>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        names: Vec<String>,
    }
    Ok(req.json(&R {
        users: vec![],
        meta: Meta { names: names },
    }))
}

/// Handles the `DELETE /crates/:crate_id/:version/yank` route.
/// This does not delete a crate version, it makes the crate
/// version accessible only to crates that already have a
/// `Cargo.lock` containing this version.
///
/// Notes:
/// Crate deletion is not implemented to avoid breaking builds,
/// and the goal of yanking a crate is to prevent crates
/// beginning to depend on the yanked crate version.
pub fn yank(req: &mut Request) -> CargoResult<Response> {
    modify_yank(req, true)
}

/// Handles the `PUT /crates/:crate_id/:version/unyank` route.
pub fn unyank(req: &mut Request) -> CargoResult<Response> {
    modify_yank(req, false)
}

/// Changes `yanked` flag on a crate version record
fn modify_yank(req: &mut Request, yanked: bool) -> CargoResult<Response> {
    let (version, krate) = version_and_crate(req)?;
    let user = req.user()?;
    let conn = req.db_conn()?;
    let owners = krate.owners(&conn)?;
    if rights(req.app(), &owners, user)? < Rights::Publish {
        return Err(human("must already be an owner to yank or unyank"));
    }

    if version.yanked != yanked {
        conn.transaction::<_, Box<CargoError>, _>(|| {
            diesel::update(&version)
                .set(versions::yanked.eq(yanked))
                .execute(&*conn)?;
            git::yank(&**req.app(), &krate.name, &version.num, yanked)?;
            Ok(())
        })?;
    }

    #[derive(Serialize)]
    struct R {
        ok: bool,
    }
    Ok(req.json(&R { ok: true }))
}
<commit_msg>cargo fmt<commit_after>use std::collections::HashMap;

use conduit::{Request, Response};
use conduit_router::RequestParams;
use diesel;
use diesel::pg::{Pg, PgConnection};
use diesel::pg::upsert::*;
use diesel::prelude::*;
use semver;
use serde_json;
use time::{Duration, Timespec, now_utc, strptime};
use url;

use Crate;
use app::RequestApp;
use db::RequestTransaction;
use dependency::{Dependency, EncodableDependency};
use download::{VersionDownload, EncodableVersionDownload};
use git;
use owner::{rights, Rights};
use schema::*;
use user::RequestUser;
use util::errors::CargoError;
use util::{RequestUtils, CargoResult, human};
use license_exprs;

// This is necessary to allow joining version to both crates and readme_rendering
// in the render-readmes script.
enable_multi_table_joins!(crates, readme_rendering);

// Queryable has a custom implementation below
#[derive(Clone, Identifiable, Associations, Debug)]
#[belongs_to(Crate)]
pub struct Version {
    pub id: i32,
    pub crate_id: i32,
    pub num: semver::Version,
    pub updated_at: Timespec,
    pub created_at: Timespec,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub yanked: bool,
    pub license: Option<String>,
}

#[derive(Insertable, Debug)]
#[table_name = "versions"]
pub struct NewVersion {
    crate_id: i32,
    num: String,
    features: String,
    license: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct EncodableVersion {
    pub id: i32,
    #[serde(rename = "crate")]
    pub krate: String,
    pub num: String,
    pub dl_path: String,
    pub readme_path: String,
    pub updated_at: String,
    pub created_at: String,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub yanked: bool,
    pub license: Option<String>,
    pub links: VersionLinks,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct VersionLinks {
    pub dependencies: String,
    pub version_downloads: String,
    pub authors: String,
}

#[derive(Insertable, Identifiable, Queryable, Associations, Debug, Clone, Copy)]
#[belongs_to(Version)]
#[table_name = "readme_rendering"]
#[primary_key(version_id)]
struct ReadmeRendering {
    version_id: i32,
    rendered_at: Timespec,
}

impl Version {
    pub fn encodable(self, crate_name: &str) -> EncodableVersion {
        let Version {
            id,
            num,
            updated_at,
            created_at,
            downloads,
            features,
            yanked,
            license,
            ..
        } = self;
        let num = num.to_string();
        EncodableVersion {
            dl_path: format!("/api/v1/crates/{}/{}/download", crate_name, num),
            readme_path: format!("/api/v1/crates/{}/{}/readme", crate_name, num),
            num: num.clone(),
            id: id,
            krate: crate_name.to_string(),
            updated_at: ::encode_time(updated_at),
            created_at: ::encode_time(created_at),
            downloads: downloads,
            features: features,
            yanked: yanked,
            license: license,
            links: VersionLinks {
                dependencies: format!("/api/v1/crates/{}/{}/dependencies", crate_name, num),
                version_downloads: format!("/api/v1/crates/{}/{}/downloads", crate_name, num),
                authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num),
            },
        }
    }

    /// Returns (dependency, crate dependency name)
    pub fn dependencies(&self, conn: &PgConnection) -> QueryResult<Vec<(Dependency, String)>> {
        Dependency::belonging_to(self)
            .inner_join(crates::table)
            .select((dependencies::all_columns, crates::name))
            .order((dependencies::optional, crates::name))
            .load(conn)
    }

    pub fn max<T>(versions: T) -> semver::Version
    where
        T: IntoIterator<Item = semver::Version>,
    {
        versions.into_iter().max().unwrap_or_else(|| {
            semver::Version {
                major: 0,
                minor: 0,
                patch: 0,
                pre: vec![],
                build: vec![],
            }
        })
    }

    pub fn record_readme_rendering(&self, conn: &PgConnection) -> CargoResult<()> {
        let rendered = ReadmeRendering {
            version_id: self.id,
            rendered_at: ::now(),
        };

        diesel::insert(&rendered.on_conflict(
            readme_rendering::version_id,
            do_update().set(readme_rendering::rendered_at.eq(
                excluded(
                    readme_rendering::rendered_at,
                ),
            )),
        )).into(readme_rendering::table)
            .execute(&*conn)?;
        Ok(())
    }
}

impl NewVersion {
    pub fn new(
        crate_id: i32,
        num: &semver::Version,
        features: &HashMap<String, Vec<String>>,
        license: Option<String>,
        license_file: Option<&str>,
    ) -> CargoResult<Self> {
        let features = serde_json::to_string(features)?;

        let mut new_version = NewVersion {
            crate_id: crate_id,
            num: num.to_string(),
            features: features,
            license: license,
        };

        new_version.validate_license(license_file)?;

        Ok(new_version)
    }

    pub fn save(&self, conn: &PgConnection, authors: &[String]) -> CargoResult<Version> {
        use diesel::{select, insert};
        use diesel::expression::dsl::exists;
        use schema::versions::dsl::*;

        let already_uploaded = versions.filter(crate_id.eq(self.crate_id)).filter(
            num.eq(&self.num),
        );
        if select(exists(already_uploaded)).get_result(conn)? {
            return Err(human(&format_args!(
                "crate version `{}` is already \
                 uploaded",
                self.num
            )));
        }

        conn.transaction(|| {
            let version = insert(self).into(versions).get_result::<Version>(conn)?;

            let new_authors = authors
                .iter()
                .map(|s| {
                    NewAuthor {
                        version_id: version.id,
                        name: &*s,
                    }
                })
                .collect::<Vec<_>>();

            insert(&new_authors).into(version_authors::table).execute(
                conn,
            )?;
            Ok(version)
        })
    }

    fn validate_license(&mut self, license_file: Option<&str>) -> CargoResult<()> {
        if let Some(ref license) = self.license {
            for part in license.split('/') {
                license_exprs::validate_license_expr(part).map_err(|e| {
                    human(&format_args!(
                        "{}; see http://opensource.org/licenses \
                         for options, and http://spdx.org/licenses/ \
                         for their identifiers",
                        e
                    ))
                })?;
            }
        } else if license_file.is_some() {
            // If no license is given, but a license file is given, flag this
            // crate as having a nonstandard license. Note that we don't
            // actually do anything else with license_file currently.
            self.license = Some(String::from("non-standard"));
        }
        Ok(())
    }
}

#[derive(Insertable, Debug)]
#[table_name = "version_authors"]
struct NewAuthor<'a> {
    version_id: i32,
    name: &'a str,
}

impl Queryable<versions::SqlType, Pg> for Version {
    type Row = (i32, i32, String, Timespec, Timespec, i32, Option<String>, bool, Option<String>);

    fn build(row: Self::Row) -> Self {
        let features = row.6
            .map(|s| serde_json::from_str(&s).unwrap())
            .unwrap_or_else(HashMap::new);
        Version {
            id: row.0,
            crate_id: row.1,
            num: semver::Version::parse(&row.2).unwrap(),
            updated_at: row.3,
            created_at: row.4,
            downloads: row.5,
            features: features,
            yanked: row.7,
            license: row.8,
        }
    }
}

/// Handles the `GET /versions` route.
// FIXME: where/how is this used?
pub fn index(req: &mut Request) -> CargoResult<Response> {
    use diesel::expression::dsl::any;
    let conn = req.db_conn()?;

    // Extract all ids requested.
    let query = url::form_urlencoded::parse(req.query_string().unwrap_or("").as_bytes());
    let ids = query
        .filter_map(|(ref a, ref b)| if *a == "ids[]" {
            b.parse().ok()
        } else {
            None
        })
        .collect::<Vec<i32>>();

    let versions = versions::table
        .inner_join(crates::table)
        .select((versions::all_columns, crates::name))
        .filter(versions::id.eq(any(ids)))
        .load::<(Version, String)>(&*conn)?
        .into_iter()
        .map(|(version, crate_name)| version.encodable(&crate_name))
        .collect();

    #[derive(Serialize)]
    struct R {
        versions: Vec<EncodableVersion>,
    }
    Ok(req.json(&R { versions: versions }))
}

/// Handles the `GET /versions/:version_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
    let (version, krate) = match req.params().find("crate_id") {
        Some(..) => version_and_crate(req)?,
        None => {
            let id = &req.params()["version_id"];
            let id = id.parse().unwrap_or(0);
            let conn = req.db_conn()?;
            versions::table
                .find(id)
                .inner_join(crates::table)
                .select((versions::all_columns, ::krate::ALL_COLUMNS))
                .first(&*conn)?
        }
    };

    #[derive(Serialize)]
    struct R {
        version: EncodableVersion,
    }
    Ok(req.json(&R { version: version.encodable(&krate.name) }))
}

fn version_and_crate(req: &mut Request) -> CargoResult<(Version, Crate)> {
    let crate_name = &req.params()["crate_id"];
    let semver = &req.params()["version"];
    if semver::Version::parse(semver).is_err() {
        return Err(human(&format_args!("invalid semver: {}", semver)));
    };
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let version = Version::belonging_to(&krate)
        .filter(versions::num.eq(semver))
        .first(&*conn)
        .map_err(|_| {
            human(&format_args!(
                "crate `{}` does not have a version `{}`",
                crate_name,
                semver
            ))
        })?;
    Ok((version, krate))
}

/// Handles the `GET /crates/:crate_id/:version/dependencies` route.
pub fn dependencies(req: &mut Request) -> CargoResult<Response> {
    let (version, _) = version_and_crate(req)?;
    let conn = req.db_conn()?;
    let deps = version.dependencies(&*conn)?;
    let deps = deps.into_iter()
        .map(|(dep, crate_name)| dep.encodable(&crate_name, None))
        .collect();

    #[derive(Serialize)]
    struct R {
        dependencies: Vec<EncodableDependency>,
    }
    Ok(req.json(&R { dependencies: deps }))
}

/// Handles the `GET /crates/:crate_id/:version/downloads` route.
pub fn downloads(req: &mut Request) -> CargoResult<Response> {
    use diesel::expression::dsl::date;
    let (version, _) = version_and_crate(req)?;
    let conn = req.db_conn()?;
    let cutoff_end_date = req.query()
        .get("before_date")
        .and_then(|d| strptime(d, "%Y-%m-%d").ok())
        .unwrap_or_else(now_utc)
        .to_timespec();
    let cutoff_start_date = cutoff_end_date + Duration::days(-89);

    let downloads = VersionDownload::belonging_to(&version)
        .filter(version_downloads::date.between(
            date(cutoff_start_date)..
                date(cutoff_end_date),
        ))
        .order(version_downloads::date)
        .load(&*conn)?
        .into_iter()
        .map(VersionDownload::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        version_downloads: Vec<EncodableVersionDownload>,
    }
    Ok(req.json(&R { version_downloads: downloads }))
}

/// Handles the `GET /crates/:crate_id/:version/authors` route.
pub fn authors(req: &mut Request) -> CargoResult<Response> {
    let (version, _) = version_and_crate(req)?;
    let conn = req.db_conn()?;
    let names = version_authors::table
        .filter(version_authors::version_id.eq(version.id))
        .select(version_authors::name)
        .order(version_authors::name)
        .load(&*conn)?;

    // It was imagined that we wold associate authors with users.
    // This was never implemented. This complicated return struct
    // is all that is left, hear for backwards compatibility.
    #[derive(Serialize)]
    struct R {
        users: Vec<::user::EncodablePublicUser>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        names: Vec<String>,
    }
    Ok(req.json(&R {
        users: vec![],
        meta: Meta { names: names },
    }))
}

/// Handles the `DELETE /crates/:crate_id/:version/yank` route.
/// This does not delete a crate version, it makes the crate
/// version accessible only to crates that already have a
/// `Cargo.lock` containing this version.
///
/// Notes:
/// Crate deletion is not implemented to avoid breaking builds,
/// and the goal of yanking a crate is to prevent crates
/// beginning to depend on the yanked crate version.
pub fn yank(req: &mut Request) -> CargoResult<Response> {
    modify_yank(req, true)
}

/// Handles the `PUT /crates/:crate_id/:version/unyank` route.
pub fn unyank(req: &mut Request) -> CargoResult<Response> {
    modify_yank(req, false)
}

/// Changes `yanked` flag on a crate version record
fn modify_yank(req: &mut Request, yanked: bool) -> CargoResult<Response> {
    let (version, krate) = version_and_crate(req)?;
    let user = req.user()?;
    let conn = req.db_conn()?;
    let owners = krate.owners(&conn)?;
    if rights(req.app(), &owners, user)? < Rights::Publish {
        return Err(human("must already be an owner to yank or unyank"));
    }

    if version.yanked != yanked {
        conn.transaction::<_, Box<CargoError>, _>(|| {
            diesel::update(&version)
                .set(versions::yanked.eq(yanked))
                .execute(&*conn)?;
            git::yank(&**req.app(), &krate.name, &version.num, yanked)?;
            Ok(())
        })?;
    }

    #[derive(Serialize)]
    struct R {
        ok: bool,
    }
    Ok(req.json(&R { ok: true }))
}
<|endoftext|>