<commit_before>
// https://users.rust-lang.org/t/what-am-i-doing-wrong-go-program-is-12x-faster-than-rust/5692/13
// https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html
// https://serde.rs/codegen-hybrid.html

// FIXME: Use https://github.com/nox/serde_urlencoded
// FIXME: Make sure that all structs above Issue are not using `String`s instead of `Enum`s.
// FIXME: Harmonize the different state enums (e.g. IssueState, MergeRequestState, Authors)
// FIXME: Move all enums here (ListingOrderBy, ListingSort, etc.)
// FIXME: Use a type for sha1
// FIXME: Use chrono crate for dates
// FIXME: Use unsigned integers where it makes sense (id, iid, etc.)
// FIXME: Verify all `match` in push_str() in build_query(): They should contain all members.
// FIXME: Get rid of build_query(), use serde's Serialize instead.
// FIXME: Write nicer wrappers, getting rid of Listing.

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ListingSort {
    #[serde(rename = "asc")]
    Asc,
    #[serde(rename = "desc")]
    Desc,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ListingVisibility {
    #[serde(rename = "public")]
    Public,
    #[serde(rename = "internal")]
    Internal,
    #[serde(rename = "private")]
    Private,
}

#[derive(Debug, Serialize, Deserialize)]
enum IssueState {
    #[serde(rename = "opened")]
    Opened,

    #[serde(rename = "closed")]
    Closed,
}

#[derive(Debug, Serialize, Deserialize)]
enum UserState {
    #[serde(rename = "active")]
    Active,

    #[serde(rename = "blocked")]
    Blocked,
}

#[derive(Debug, Serialize, Deserialize)]
enum MilestoneState {
    #[serde(rename = "active")]
    Active,

    #[serde(rename = "closed")]
    Closed,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Version {
    pub version: String,
    pub revision: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Group {
    pub id: i64,
    pub name: String,
    pub path: String,
    pub description: String,
    pub visibility_level: i64,
    pub lfs_enabled: bool,
    pub avatar_url: Option<String>,
    pub web_url: String,
    pub request_access_enabled: bool,
}

pub type Groups = Vec<Group>;

#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectOwner {
    pub name: String,
    pub username: String,
    pub id: i64,
    pub state: String,
    pub avatar_url: String,
    pub web_url: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectNamespaceAvatar {
    url: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectNamespace {
    id: i64,
    name: String,
    path: String,
    owner_id: Option<i64>,  // FIXME: Why would a project not have this?
    created_at: String,  // FIXME: Date instead?
    updated_at: String,  // FIXME: Date instead?
    description: String,
    avatar: Option<ProjectNamespaceAvatar>,
    membership_lock: Option<bool>,
    share_with_group_lock: bool,
    visibility_level: i64,
    request_access_enabled: bool,
    ldap_sync_status: Option<String>,
    ldap_sync_error: Option<String>,  // FIXME: Is String the proper type?
    ldap_sync_last_update_at: Option<String>,  // FIXME: Is String the proper type?
    ldap_sync_last_successful_update_at: Option<String>,  // FIXME: Is String the proper type?
    ldap_sync_last_sync_at: Option<String>,  // FIXME: Is String the proper type?
    deleted_at: Option<String>,  // FIXME: Is String the proper type?
    lfs_enabled: Option<String>,  // FIXME: Is String the proper type?
    repository_size_limit: Option<String>  // FIXME: Is String the proper type?
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectForkedFrom {
    id: i64,
    http_url_to_repo: String,
    web_url: String,
    name: String,
    name_with_namespace: String,
    path: String,
    path_with_namespace: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectAccess {
    access_level: i64,
    notification_level: i64,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectPermissions {
    project_access: Option<ProjectAccess>,
    group_access: Option<ProjectAccess>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectSharedWithGroup {
    group_id: i64,
    group_name: String,
    group_access_level: i64,
}

// https://users.rust-lang.org/t/what-am-i-doing-wrong-go-program-is-12x-faster-than-rust/5692/13
// https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html
#[derive(Debug, Serialize, Deserialize)]
pub struct Project {
    id: i64,
    description: String,
    default_branch: Option<String>,
    tag_list: Vec<String>,
    public: bool,
    archived: bool,
    visibility_level: i64,
    ssh_url_to_repo: String,
    http_url_to_repo: String,
    web_url: String,
    // owner: Option<ProjectOwner>,  // FIXME: Why would a project not have an owner?
    name: String,
    name_with_namespace: String,
    path: String,
    path_with_namespace: String,
    container_registry_enabled: Option<bool>,
    issues_enabled: bool,
    merge_requests_enabled: bool,
    wiki_enabled: bool,
    builds_enabled: bool,
    snippets_enabled: bool,
    created_at: String,  // FIXME: Date instead?
    last_activity_at: String,  // FIXME: Date instead?
    shared_runners_enabled: bool,
    lfs_enabled: bool,
    creator_id: i64,
    namespace: ProjectNamespace,
    forked_from_project: Option<ProjectForkedFrom>,
    avatar_url: Option<String>,
    star_count: i64,
    forks_count: i64,
    open_issues_count: i64,
    runners_token: Option<String>,
    public_builds: bool,
    shared_with_groups: Vec<ProjectSharedWithGroup>,
    only_allow_merge_if_build_succeeds: bool,
    request_access_enabled: bool,
    only_allow_merge_if_all_discussions_are_resolved: Option<bool>,  // FIXME: Is bool the proper type?
    approvals_before_merge: Option<i64>,
    permissions: Option<ProjectPermissions>,
}

pub type Projects = Vec<Project>;

#[derive(Debug, Serialize, Deserialize)]
struct Milestone {
    id: i64,
    iid: i64,
    project_id: i64,
    title: String,
    description: String,
    state: MilestoneState,
    created_at: String,  // FIXME: Use date type?
    updated_at: String,  // FIXME: Use date type?
    due_date: Option<String>  // FIXME: Use date type?
}

#[derive(Debug, Serialize, Deserialize)]
struct User {
    name: String,
    username: String,
    id: i64,
    state: UserState,
    avatar_url: Option<String>,
    web_url: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Issue {
    id: i64,
    iid: i64,
    project_id: i64,
    title: String,
    description: String,
    state: IssueState,
    created_at: String,  // FIXME: Use date type?
    updated_at: String,  // FIXME: Use date type?
    labels: Vec<String>,
    milestone: Option<Milestone>,
    assignee: Option<User>,
    author: User,
    subscribed: bool,
    user_notes_count: i64,
    upvotes: i64,
    downvotes: i64,
    due_date: Option<String>,  // FIXME: Use date type?
    confidential: bool,
    web_url: Option<String>
}

pub type Issues = Vec<Issue>;
<commit_msg>Let `Project` and `ProjectNamespace` derive `Default`<commit_after>
// https://users.rust-lang.org/t/what-am-i-doing-wrong-go-program-is-12x-faster-than-rust/5692/13
// https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html
// https://serde.rs/codegen-hybrid.html

// FIXME: Use https://github.com/nox/serde_urlencoded
// FIXME: Make sure that all structs above Issue are not using `String`s instead of `Enum`s.
// FIXME: Harmonize the different state enums (e.g. IssueState, MergeRequestState, Authors)
// FIXME: Move all enums here (ListingOrderBy, ListingSort, etc.)
// FIXME: Use a type for sha1
// FIXME: Use chrono crate for dates
// FIXME: Use unsigned integers where it makes sense (id, iid, etc.)
// FIXME: Verify all `match` in push_str() in build_query(): They should contain all members.
// FIXME: Get rid of build_query(), use serde's Serialize instead.
// FIXME: Write nicer wrappers, getting rid of Listing.

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ListingSort {
    #[serde(rename = "asc")]
    Asc,
    #[serde(rename = "desc")]
    Desc,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ListingVisibility {
    #[serde(rename = "public")]
    Public,
    #[serde(rename = "internal")]
    Internal,
    #[serde(rename = "private")]
    Private,
}

#[derive(Debug, Serialize, Deserialize)]
enum IssueState {
    #[serde(rename = "opened")]
    Opened,

    #[serde(rename = "closed")]
    Closed,
}

#[derive(Debug, Serialize, Deserialize)]
enum UserState {
    #[serde(rename = "active")]
    Active,

    #[serde(rename = "blocked")]
    Blocked,
}

#[derive(Debug, Serialize, Deserialize)]
enum MilestoneState {
    #[serde(rename = "active")]
    Active,

    #[serde(rename = "closed")]
    Closed,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Version {
    pub version: String,
    pub revision: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Group {
    pub id: i64,
    pub name: String,
    pub path: String,
    pub description: String,
    pub visibility_level: i64,
    pub lfs_enabled: bool,
    pub avatar_url: Option<String>,
    pub web_url: String,
    pub request_access_enabled: bool,
}

pub type Groups = Vec<Group>;

#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectOwner {
    pub name: String,
    pub username: String,
    pub id: i64,
    pub state: String,
    pub avatar_url: String,
    pub web_url: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectNamespaceAvatar {
    url: Option<String>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
struct ProjectNamespace {
    id: i64,
    name: String,
    path: String,
    owner_id: Option<i64>,  // FIXME: Why would a project not have this?
    created_at: String,  // FIXME: Date instead?
    updated_at: String,  // FIXME: Date instead?
    description: String,
    avatar: Option<ProjectNamespaceAvatar>,
    membership_lock: Option<bool>,
    share_with_group_lock: bool,
    visibility_level: i64,
    request_access_enabled: bool,
    ldap_sync_status: Option<String>,
    ldap_sync_error: Option<String>,  // FIXME: Is String the proper type?
    ldap_sync_last_update_at: Option<String>,  // FIXME: Is String the proper type?
    ldap_sync_last_successful_update_at: Option<String>,  // FIXME: Is String the proper type?
    ldap_sync_last_sync_at: Option<String>,  // FIXME: Is String the proper type?
    deleted_at: Option<String>,  // FIXME: Is String the proper type?
    lfs_enabled: Option<String>,  // FIXME: Is String the proper type?
    repository_size_limit: Option<String>  // FIXME: Is String the proper type?
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectForkedFrom {
    id: i64,
    http_url_to_repo: String,
    web_url: String,
    name: String,
    name_with_namespace: String,
    path: String,
    path_with_namespace: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectAccess {
    access_level: i64,
    notification_level: i64,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectPermissions {
    project_access: Option<ProjectAccess>,
    group_access: Option<ProjectAccess>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ProjectSharedWithGroup {
    group_id: i64,
    group_name: String,
    group_access_level: i64,
}

// https://users.rust-lang.org/t/what-am-i-doing-wrong-go-program-is-12x-faster-than-rust/5692/13
// https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Project {
    id: i64,
    description: String,
    default_branch: Option<String>,
    tag_list: Vec<String>,
    public: bool,
    archived: bool,
    visibility_level: i64,
    ssh_url_to_repo: String,
    http_url_to_repo: String,
    web_url: String,
    // owner: Option<ProjectOwner>,  // FIXME: Why would a project not have an owner?
    name: String,
    name_with_namespace: String,
    path: String,
    path_with_namespace: String,
    container_registry_enabled: Option<bool>,
    issues_enabled: bool,
    merge_requests_enabled: bool,
    wiki_enabled: bool,
    builds_enabled: bool,
    snippets_enabled: bool,
    created_at: String,  // FIXME: Date instead?
    last_activity_at: String,  // FIXME: Date instead?
    shared_runners_enabled: bool,
    lfs_enabled: bool,
    creator_id: i64,
    namespace: ProjectNamespace,
    forked_from_project: Option<ProjectForkedFrom>,
    avatar_url: Option<String>,
    star_count: i64,
    forks_count: i64,
    open_issues_count: i64,
    runners_token: Option<String>,
    public_builds: bool,
    shared_with_groups: Vec<ProjectSharedWithGroup>,
    only_allow_merge_if_build_succeeds: bool,
    request_access_enabled: bool,
    only_allow_merge_if_all_discussions_are_resolved: Option<bool>,  // FIXME: Is bool the proper type?
    approvals_before_merge: Option<i64>,
    permissions: Option<ProjectPermissions>,
}

pub type Projects = Vec<Project>;

#[derive(Debug, Serialize, Deserialize)]
struct Milestone {
    id: i64,
    iid: i64,
    project_id: i64,
    title: String,
    description: String,
    state: MilestoneState,
    created_at: String,  // FIXME: Use date type?
    updated_at: String,  // FIXME: Use date type?
    due_date: Option<String>  // FIXME: Use date type?
}

#[derive(Debug, Serialize, Deserialize)]
struct User {
    name: String,
    username: String,
    id: i64,
    state: UserState,
    avatar_url: Option<String>,
    web_url: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Issue {
    id: i64,
    iid: i64,
    project_id: i64,
    title: String,
    description: String,
    state: IssueState,
    created_at: String,  // FIXME: Use date type?
    updated_at: String,  // FIXME: Use date type?
    labels: Vec<String>,
    milestone: Option<Milestone>,
    assignee: Option<User>,
    author: User,
    subscribed: bool,
    user_notes_count: i64,
    upvotes: i64,
    downvotes: i64,
    due_date: Option<String>,  // FIXME: Use date type?
    confidential: bool,
    web_url: Option<String>
}

pub type Issues = Vec<Issue>;
<|endoftext|>


<commit_before>//! Server Requests
//!
//! These are requests that a `hyper::Server` receives, and include its method,
//! target URI, headers, and message body.
use std::io::{self, Read};
use std::net::SocketAddr;
use std::time::Duration;

use buffer::BufReader;
use net::NetworkStream;
use version::{HttpVersion};
use method::Method;
use header::{Headers, ContentLength, TransferEncoding};
use http::h1::{self, Incoming, HttpReader};
use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader};
use uri::RequestUri;

/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
pub struct Request<'a, 'b: 'a> {
    /// The IP address of the remote connection.
    pub remote_addr: SocketAddr,
    /// The `Method`, such as `Get`, `Post`, etc.
    pub method: Method,
    /// The headers of the incoming request.
    pub headers: Headers,
    /// The target request-uri for this request.
    pub uri: RequestUri,
    /// The version of HTTP for this request.
    pub version: HttpVersion,
    body: HttpReader<&'a mut BufReader<&'b mut NetworkStream>>
}

impl<'a, 'b: 'a> Request<'a, 'b> {
    /// Create a new Request, reading the StartLine and Headers so they are
    /// immediately useful.
    pub fn new(mut stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr)
        -> ::Result<Request<'a, 'b>> {

        let Incoming { version, subject: (method, uri), headers } = try!(h1::parse_request(stream));
        debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
        debug!("{:?}", headers);

        let body = if headers.has::<ContentLength>() {
            match headers.get::<ContentLength>() {
                Some(&ContentLength(len)) => SizedReader(stream, len),
                None => unreachable!()
            }
        } else if headers.has::<TransferEncoding>() {
            todo!("check for Transfer-Encoding: chunked");
            ChunkedReader(stream, None)
        } else {
            EmptyReader(stream)
        };

        Ok(Request {
            remote_addr: addr,
            method: method,
            uri: uri,
            headers: headers,
            version: version,
            body: body
        })
    }

    /// Set the read timeout of the underlying NetworkStream.
    #[inline]
    pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
        self.body.get_ref().get_ref().set_read_timeout(timeout)
    }

    /// Get a reference to the underlying `NetworkStream`.
    #[inline]
    pub fn downcast_ref<T: NetworkStream>(&self) -> Option<&T> {
        self.body.get_ref().get_ref().downcast_ref()
    }

    /// Get a reference to the underlying Ssl stream, if connected
    /// over HTTPS.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate hyper;
    /// # #[cfg(feature = "openssl")]
    /// extern crate openssl;
    /// # #[cfg(feature = "openssl")]
    /// use openssl::ssl::SslStream;
    /// use hyper::net::HttpStream;
    /// # fn main() {}
    /// # #[cfg(feature = "openssl")]
    /// # fn doc_ssl(req: hyper::server::Request) {
    /// let maybe_ssl = req.ssl::<SslStream<HttpStream>>();
    /// # }
    /// ```
    #[inline]
    pub fn ssl<T: NetworkStream>(&self) -> Option<&T> {
        use ::net::HttpsStream;
        match self.downcast_ref() {
            Some(&HttpsStream::Https(ref s)) => Some(s),
            _ => None
        }
    }

    /// Deconstruct a Request into its constituent parts.
    #[inline]
    pub fn deconstruct(self) -> (SocketAddr, Method, Headers,
                                 RequestUri, HttpVersion,
                                 HttpReader<&'a mut BufReader<&'b mut NetworkStream>>) {
        (self.remote_addr, self.method, self.headers,
         self.uri, self.version, self.body)
    }
}

impl<'a, 'b> Read for Request<'a, 'b> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.body.read(buf)
    }
}

#[cfg(test)]
mod tests {
    use buffer::BufReader;
    use header::{Host, TransferEncoding, Encoding};
    use net::NetworkStream;
    use mock::MockStream;
    use super::Request;

    use std::io::{self, Read};
    use std::net::SocketAddr;

    fn sock(s: &str) -> SocketAddr {
        s.parse().unwrap()
    }

    fn read_to_string(mut req: Request) -> io::Result<String> {
        let mut s = String::new();
        try!(req.read_to_string(&mut s));
        Ok(s)
    }

    #[test]
    fn test_get_empty_body() {
        let mut mock = MockStream::with_input(b"\
            GET / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            \r\n\
            I'm a bad request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
    }

    #[test]
    fn test_head_empty_body() {
        let mut mock = MockStream::with_input(b"\
            HEAD / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            \r\n\
            I'm a bad request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
    }

    #[test]
    fn test_post_empty_body() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            \r\n\
            I'm a bad request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
    }

    #[test]
    fn test_parse_chunked_request() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            1\r\n\
            q\r\n\
            2\r\n\
            we\r\n\
            2\r\n\
            rt\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        // The headers are correct?
        match req.headers.get::<Host>() {
            Some(host) => {
                assert_eq!("example.domain", host.hostname);
            },
            None => panic!("Host header expected!"),
        };
        match req.headers.get::<TransferEncoding>() {
            Some(encodings) => {
                assert_eq!(1, encodings.len());
                assert_eq!(Encoding::Chunked, encodings[0]);
            }
            None => panic!("Transfer-Encoding: chunked expected!"),
        };
        // The content is correctly read?
        assert_eq!(read_to_string(req).unwrap(), "qwert".to_owned());
    }

    /// Tests that when a chunk size is not a valid radix-16 number, an error
    /// is returned.
    #[test]
    fn test_invalid_chunk_size_not_hex_digit() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            X\r\n\
            1\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        assert!(read_to_string(req).is_err());
    }

    /// Tests that when a chunk size contains an invalid extension, an error is
    /// returned.
    #[test]
    fn test_invalid_chunk_size_extension() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            1 this is an invalid extension\r\n\
            1\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        assert!(read_to_string(req).is_err());
    }

    /// Tests that when a valid extension that contains a digit is appended to
    /// the chunk size, the chunk is correctly read.
    #[test]
    fn test_chunk_size_with_extension() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            1;this is an extension with a digit 1\r\n\
            1\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        assert_eq!(read_to_string(req).unwrap(), "1".to_owned());
    }

}
<commit_msg>test(server): Add test for GET request with body<commit_after>//! Server Requests
//!
//! These are requests that a `hyper::Server` receives, and include its method,
//! target URI, headers, and message body.
use std::io::{self, Read};
use std::net::SocketAddr;
use std::time::Duration;

use buffer::BufReader;
use net::NetworkStream;
use version::{HttpVersion};
use method::Method;
use header::{Headers, ContentLength, TransferEncoding};
use http::h1::{self, Incoming, HttpReader};
use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader};
use uri::RequestUri;

/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
pub struct Request<'a, 'b: 'a> {
    /// The IP address of the remote connection.
    pub remote_addr: SocketAddr,
    /// The `Method`, such as `Get`, `Post`, etc.
    pub method: Method,
    /// The headers of the incoming request.
    pub headers: Headers,
    /// The target request-uri for this request.
    pub uri: RequestUri,
    /// The version of HTTP for this request.
    pub version: HttpVersion,
    body: HttpReader<&'a mut BufReader<&'b mut NetworkStream>>
}

impl<'a, 'b: 'a> Request<'a, 'b> {
    /// Create a new Request, reading the StartLine and Headers so they are
    /// immediately useful.
    pub fn new(mut stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr)
        -> ::Result<Request<'a, 'b>> {

        let Incoming { version, subject: (method, uri), headers } = try!(h1::parse_request(stream));
        debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
        debug!("{:?}", headers);

        let body = if headers.has::<ContentLength>() {
            match headers.get::<ContentLength>() {
                Some(&ContentLength(len)) => SizedReader(stream, len),
                None => unreachable!()
            }
        } else if headers.has::<TransferEncoding>() {
            todo!("check for Transfer-Encoding: chunked");
            ChunkedReader(stream, None)
        } else {
            EmptyReader(stream)
        };

        Ok(Request {
            remote_addr: addr,
            method: method,
            uri: uri,
            headers: headers,
            version: version,
            body: body
        })
    }

    /// Set the read timeout of the underlying NetworkStream.
    #[inline]
    pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
        self.body.get_ref().get_ref().set_read_timeout(timeout)
    }

    /// Get a reference to the underlying `NetworkStream`.
    #[inline]
    pub fn downcast_ref<T: NetworkStream>(&self) -> Option<&T> {
        self.body.get_ref().get_ref().downcast_ref()
    }

    /// Get a reference to the underlying Ssl stream, if connected
    /// over HTTPS.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate hyper;
    /// # #[cfg(feature = "openssl")]
    /// extern crate openssl;
    /// # #[cfg(feature = "openssl")]
    /// use openssl::ssl::SslStream;
    /// use hyper::net::HttpStream;
    /// # fn main() {}
    /// # #[cfg(feature = "openssl")]
    /// # fn doc_ssl(req: hyper::server::Request) {
    /// let maybe_ssl = req.ssl::<SslStream<HttpStream>>();
    /// # }
    /// ```
    #[inline]
    pub fn ssl<T: NetworkStream>(&self) -> Option<&T> {
        use ::net::HttpsStream;
        match self.downcast_ref() {
            Some(&HttpsStream::Https(ref s)) => Some(s),
            _ => None
        }
    }

    /// Deconstruct a Request into its constituent parts.
    #[inline]
    pub fn deconstruct(self) -> (SocketAddr, Method, Headers,
                                 RequestUri, HttpVersion,
                                 HttpReader<&'a mut BufReader<&'b mut NetworkStream>>) {
        (self.remote_addr, self.method, self.headers,
         self.uri, self.version, self.body)
    }
}

impl<'a, 'b> Read for Request<'a, 'b> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.body.read(buf)
    }
}

#[cfg(test)]
mod tests {
    use buffer::BufReader;
    use header::{Host, TransferEncoding, Encoding};
    use net::NetworkStream;
    use mock::MockStream;
    use super::Request;

    use std::io::{self, Read};
    use std::net::SocketAddr;

    fn sock(s: &str) -> SocketAddr {
        s.parse().unwrap()
    }

    fn read_to_string(mut req: Request) -> io::Result<String> {
        let mut s = String::new();
        try!(req.read_to_string(&mut s));
        Ok(s)
    }

    #[test]
    fn test_get_empty_body() {
        let mut mock = MockStream::with_input(b"\
            GET / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            \r\n\
            I'm a bad request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
    }

    #[test]
    fn test_get_with_body() {
        let mut mock = MockStream::with_input(b"\
            GET / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Content-Length: 19\r\n\
            \r\n\
            I'm a good request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "I'm a good request.".to_owned());
    }

    #[test]
    fn test_head_empty_body() {
        let mut mock = MockStream::with_input(b"\
            HEAD / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            \r\n\
            I'm a bad request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
    }

    #[test]
    fn test_post_empty_body() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            \r\n\
            I'm a bad request.\r\n\
        ");

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
    }

    #[test]
    fn test_parse_chunked_request() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            1\r\n\
            q\r\n\
            2\r\n\
            we\r\n\
            2\r\n\
            rt\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        // The headers are correct?
        match req.headers.get::<Host>() {
            Some(host) => {
                assert_eq!("example.domain", host.hostname);
            },
            None => panic!("Host header expected!"),
        };
        match req.headers.get::<TransferEncoding>() {
            Some(encodings) => {
                assert_eq!(1, encodings.len());
                assert_eq!(Encoding::Chunked, encodings[0]);
            }
            None => panic!("Transfer-Encoding: chunked expected!"),
        };
        // The content is correctly read?
        assert_eq!(read_to_string(req).unwrap(), "qwert".to_owned());
    }

    /// Tests that when a chunk size is not a valid radix-16 number, an error
    /// is returned.
    #[test]
    fn test_invalid_chunk_size_not_hex_digit() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            X\r\n\
            1\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        assert!(read_to_string(req).is_err());
    }

    /// Tests that when a chunk size contains an invalid extension, an error is
    /// returned.
    #[test]
    fn test_invalid_chunk_size_extension() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            1 this is an invalid extension\r\n\
            1\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        assert!(read_to_string(req).is_err());
    }

    /// Tests that when a valid extension that contains a digit is appended to
    /// the chunk size, the chunk is correctly read.
    #[test]
    fn test_chunk_size_with_extension() {
        let mut mock = MockStream::with_input(b"\
            POST / HTTP/1.1\r\n\
            Host: example.domain\r\n\
            Transfer-Encoding: chunked\r\n\
            \r\n\
            1;this is an extension with a digit 1\r\n\
            1\r\n\
            0\r\n\
            \r\n"
        );

        // FIXME: Use Type ascription
        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);

        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();

        assert_eq!(read_to_string(req).unwrap(), "1".to_owned());
    }

}
<|endoftext|>


<commit_before>#[feature(macro_rules)];
#[crate_id = "codegen#0.1"];

use std::os;
use std::io::buffered::BufferedWriter;
use std::io::File;
use std::io;
use std::io::fs::mkdir_recursive;
use std::path::GenericPath;
pub mod branchify;
pub mod keycode;
pub mod scancode;

fn main() {
    let args = os::args();
    match args.len() {
        0 => {
            println("usage: codegen [keycode|scancode].rs destdir");
            os::set_exit_status(1);
        },
        3 => {
            let output_dir = GenericPath::new(args[2].clone());
            // TODO: maybe not 0777?
            mkdir_recursive(&output_dir, 0b111_111_111);

            match args[1] {
                ~"keycode.rs" => keycode::generate(&output_dir),
                ~"scancode.rs" => scancode::generate(&output_dir),
                s => {
                    println!("unknown thing-to-generate '{}'", s);
                    os::set_exit_status(1);
                }
            }
        },
        _ => {
            println!("usage: {} [keycode|scancode].rs destdir", args[0]);
            os::set_exit_status(1);
        }
    }
}

pub fn get_writer(output_dir: &Path, filename: &str) -> ~BufferedWriter<File> {
    match File::open_mode(&output_dir.join(filename), io::Truncate, io::Write) {
        Some(writer) => ~BufferedWriter::new(writer),
        None => fail!("Unable to write file"),
    }
}
<commit_msg>Added appropriate use line for println<commit_after>#[feature(macro_rules)];
#[crate_id = "codegen#0.1"];

use std::os;
use std::io::buffered::BufferedWriter;
use std::io::File;
use std::io;
use std::io::stdio::println;
use std::io::fs::mkdir_recursive;
use std::path::GenericPath;
pub mod branchify;
pub mod keycode;
pub mod scancode;

fn main() {
    let args = os::args();
    match args.len() {
        0 => {
            println("usage: codegen [keycode|scancode].rs destdir");
            os::set_exit_status(1);
        },
        3 => {
            let output_dir = GenericPath::new(args[2].clone());
            // TODO: maybe not 0777?
            mkdir_recursive(&output_dir, 0b111_111_111);

            match args[1] {
                ~"keycode.rs" => keycode::generate(&output_dir),
                ~"scancode.rs" => scancode::generate(&output_dir),
                s => {
                    println!("unknown thing-to-generate '{}'", s);
                    os::set_exit_status(1);
                }
            }
        },
        _ => {
            println!("usage: {} [keycode|scancode].rs destdir", args[0]);
            os::set_exit_status(1);
        }
    }
}

pub fn get_writer(output_dir: &Path, filename: &str) -> ~BufferedWriter<File> {
    match File::open_mode(&output_dir.join(filename), io::Truncate, io::Write) {
        Some(writer) => ~BufferedWriter::new(writer),
        None => fail!("Unable to write file"),
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

use core::mem;
use cc3200_sys::socket::{Family, Protocol, SocketError, SocketType, RawSocket, sl_Socket,
                         sl_Close, sl_Connect, sl_Send, sl_Recv, SlSockAddrIn_t, SlSockAddr_t,
                         sl_Htonl, sl_Htons};
use simplelink::SimpleLink;
use smallhttp::traits::{Channel, ChannelError};

#[derive(Clone)]
pub struct SocketChannel {
    inner: RawSocket,
}

impl SocketChannel {
    pub fn new() -> Option<Self> {
        let socket = unsafe {
            sl_Socket(Family::AF_INET,
                      SocketType::SOCK_STREAM,
                      Protocol::IPPROTO_TCP)
        };
        if socket >= 0 {
            Some(SocketChannel { inner: socket })
        } else {
            None
        }

    }
}

impl Drop for SocketChannel {
    fn drop(&mut self) {
        unsafe {
            sl_Close(self.inner);
        }
    }
}

impl Channel for SocketChannel {
    // Opens a channel to the given host:port destination, with TLS support is needed.
    fn open(&mut self, host: &str, port: u16, tls: bool) -> Result<(), ChannelError> {
        // Convert the host name into a socket address.
        let addr = match SimpleLink::netapp_get_host_by_name(host) {
            Ok(addr) => addr,
            Err(_) => return Err(ChannelError::InvalidHostName),
        };

        // TODO: use getsockopt to set the TLS options is tls is true.

        let inaddr = SlSockAddrIn_t {
            sin_family: Family::AF_INET,
            sin_port: unsafe { sl_Htons(port) },
            sin_addr: unsafe { sl_Htonl(addr) },
            sin_zero: [0; 8],
        };
        let sockaddr =
            unsafe { ::core::intrinsics::transmute::<SlSockAddrIn_t, SlSockAddr_t>(inaddr) };
        let ret = unsafe {
            sl_Connect(self.inner,
                       &sockaddr as *const SlSockAddr_t,
                       mem::size_of::<SlSockAddrIn_t>() as i16)
        };

        if ret != SocketError::SOC_OK {
            debug!("Unable to connect to {:?} : {:?}", sockaddr, ret);
            Err(ChannelError::UnableToConnect)
        } else {
            Ok(())
        }
    }

    // Tries to send `len` bytes. Returns the number of bytes successfully sent,
    // or an error.
    fn send(&mut self, data: &[u8], len: usize) -> Result<usize, ChannelError> {
        assert!(len < i16::max_value() as usize);
        let ret = unsafe {
            sl_Send(self.inner, data.as_ptr(), len as i16, 0 /* flags */)
        };
        // Rustc doesn't seem to return a SizeOrError but just a i16...
        if ret >= 0 {
            Ok(ret as usize)
        } else {
            debug!("Unable to send: {:?}", ret);
            Err(ChannelError::SomethingWentWrong)
        }
    }

    // Tries to receive at most `max_len` bytes. Returns the number of bytes successfully received,
    // or an error.
    fn recv(&mut self, data: &mut [u8], max_len: usize) -> Result<usize, ChannelError> {
        assert!(max_len < i16::max_value() as usize);
        let ret = unsafe {
            sl_Recv(self.inner,
                    data.as_ptr() as *mut u8,
                    max_len as i16,
                    0 /* flags */)
        };
        // Rustc doesn't seem to return a SizeOrError but just a i16...
        if ret > 0 {
            Ok(ret as usize)
        } else if ret == 0 {
            Err(ChannelError::EndOfStream)
        } else {
            Err(ChannelError::SomethingWentWrong)
        }
    }
}
<commit_msg>Don't ignore TLS silently (#52)<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

use core::mem;
use cc3200_sys::socket::{Family, Protocol, SocketError, SocketType, RawSocket, sl_Socket,
                         sl_Close, sl_Connect, sl_Send, sl_Recv, SlSockAddrIn_t, SlSockAddr_t,
                         sl_Htonl, sl_Htons};
use simplelink::SimpleLink;
use smallhttp::traits::{Channel, ChannelError};

#[derive(Clone)]
pub struct SocketChannel {
    inner: RawSocket,
}

impl SocketChannel {
    pub fn new() -> Option<Self> {
        let socket = unsafe {
            sl_Socket(Family::AF_INET,
                      SocketType::SOCK_STREAM,
                      Protocol::IPPROTO_TCP)
        };
        if socket >= 0 {
            Some(SocketChannel { inner: socket })
        } else {
            None
        }

    }
}

impl Drop for SocketChannel {
    fn drop(&mut self) {
        unsafe {
            sl_Close(self.inner);
        }
    }
}

impl Channel for SocketChannel {
    // Opens a channel to the given host:port destination, with TLS support is needed.
    fn open(&mut self, host: &str, port: u16, tls: bool) -> Result<(), ChannelError> {
        // Convert the host name into a socket address.
        let addr = match SimpleLink::netapp_get_host_by_name(host) {
            Ok(addr) => addr,
            Err(_) => return Err(ChannelError::InvalidHostName),
        };

        // TODO: use getsockopt to set the TLS options if tls is true.
        if tls {
            return Err(ChannelError::TlsUnsupported);
        }

        let inaddr = SlSockAddrIn_t {
            sin_family: Family::AF_INET,
            sin_port: unsafe { sl_Htons(port) },
            sin_addr: unsafe { sl_Htonl(addr) },
            sin_zero: [0; 8],
        };
        let sockaddr =
            unsafe { ::core::intrinsics::transmute::<SlSockAddrIn_t, SlSockAddr_t>(inaddr) };
        let ret = unsafe {
            sl_Connect(self.inner,
                       &sockaddr as *const SlSockAddr_t,
                       mem::size_of::<SlSockAddrIn_t>() as i16)
        };

        if ret != SocketError::SOC_OK {
            debug!("Unable to connect to {:?} : {:?}", sockaddr, ret);
            Err(ChannelError::UnableToConnect)
        } else {
            Ok(())
        }
    }

    // Tries to send `len` bytes. Returns the number of bytes successfully sent,
    // or an error.
    fn send(&mut self, data: &[u8], len: usize) -> Result<usize, ChannelError> {
        assert!(len < i16::max_value() as usize);
        let ret = unsafe {
            sl_Send(self.inner, data.as_ptr(), len as i16, 0 /* flags */)
        };
        // Rustc doesn't seem to return a SizeOrError but just a i16...
        if ret >= 0 {
            Ok(ret as usize)
        } else {
            debug!("Unable to send: {:?}", ret);
            Err(ChannelError::SomethingWentWrong)
        }
    }

    // Tries to receive at most `max_len` bytes. Returns the number of bytes successfully received,
    // or an error.
    fn recv(&mut self, data: &mut [u8], max_len: usize) -> Result<usize, ChannelError> {
        assert!(max_len < i16::max_value() as usize);
        let ret = unsafe {
            sl_Recv(self.inner,
                    data.as_ptr() as *mut u8,
                    max_len as i16,
                    0 /* flags */)
        };
        // Rustc doesn't seem to return a SizeOrError but just a i16...
        if ret > 0 {
            Ok(ret as usize)
        } else if ret == 0 {
            Err(ChannelError::EndOfStream)
        } else {
            Err(ChannelError::SomethingWentWrong)
        }
    }
}
<|endoftext|>


<commit_before>use crate::utils::printing;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

pub fn clone(path: &PathBuf, url: &str) {
    printing::progress(format!("Cloning {} repo.", url));

    match Command::new("git")
        .current_dir(&path)
        .arg("clone")
        .arg(url)
        .output()
    {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn pull_all(path: &PathBuf) {
    printing::progress(format!(
        "Upgrading all repos in {}.",
        path.to_string_lossy()
    ));
    match fs::read_dir(path) {
        Ok(paths) => {
            for path in paths {
                match Command::new("git")
                    .current_dir(match path {
                        Ok(path) => path.path(),
                        Err(error) => panic!("There was a problem: {:?}", error),
                    })
                    .arg("pull")
                    .output()
                {
                    Ok(_) => (),
                    Err(error) => panic!("There was a problem: {:?}", error),
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

pub fn check_all(path: &PathBuf) {
    printing::progress(format!("Checking all repos in {}.", path.to_string_lossy()));
    match fs::read_dir(path) {
        Ok(paths) => {
            let mut all_repos_clean = true;

            for path in paths {
                let path = match &path {
                    Ok(path) => path.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let status_check = match Command::new("git")
                    .current_dir(&path)
                    .arg("status")
                    .arg("--porcelain")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let unpushed_check = match Command::new("git")
                    .current_dir(&path)
                    .arg("log")
                    .arg("@{u}..")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                if !status_check.is_empty() || !unpushed_check.is_empty() {
                    all_repos_clean = false;
                    printing::error(format!("A dirty repo was found: {}", &path.display()));
                }
            }

            if !all_repos_clean {
                panic!("Repos are dirty.");
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}
<commit_msg>Fix variable shadowing<commit_after>use crate::utils::printing;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

pub fn clone(path: &PathBuf, url: &str) {
    printing::progress(format!("Cloning {} repo.", url));

    match Command::new("git")
        .current_dir(&path)
        .arg("clone")
        .arg(url)
        .output()
    {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn pull_all(path: &PathBuf) {
    printing::progress(format!(
        "Upgrading all repos in {}.",
        path.to_string_lossy()
    ));
    match fs::read_dir(path) {
        Ok(dirs) => {
            for dir in dirs {
                match Command::new("git")
                    .current_dir(match dir {
                        Ok(dir) => dir.path(),
                        Err(error) => panic!("There was a problem: {:?}", error),
                    })
                    .arg("pull")
                    .output()
                {
                    Ok(_) => (),
                    Err(error) => panic!("There was a problem: {:?}", error),
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

pub fn check_all(path: &PathBuf) {
    printing::progress(format!("Checking all repos in {}.", path.to_string_lossy()));
    match fs::read_dir(path) {
        Ok(dirs) => {
            let mut all_repos_clean = true;

            for dir in dirs {
                let dir = match &dir {
                    Ok(dir) => dir.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let status_check = match Command::new("git")
                    .current_dir(&dir)
                    .arg("status")
                    .arg("--porcelain")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let unpushed_check = match Command::new("git")
                    .current_dir(&dir)
                    .arg("log")
                    .arg("@{u}..")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                if !status_check.is_empty() || !unpushed_check.is_empty() {
                    all_repos_clean = false;
                    printing::error(format!("A dirty repo was found: {}", &dir.display()));
                }
            }

            if !all_repos_clean {
                panic!("Repos are dirty.");
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}
<|endoftext|>


<commit_before>use libc::c_void;
use parse_code;
use std::io::{self, Read};
use zstd_sys;

struct DecoderContext {
    s: *mut zstd_sys::ZSTD_DStream,
}

impl Default for DecoderContext {
    fn default() -> Self {
        DecoderContext { s: unsafe { zstd_sys::ZSTD_createDStream() } }
    }
}

impl Drop for DecoderContext {
    fn drop(&mut self) {
        let code = unsafe { zstd_sys::ZSTD_freeDStream(self.s) };
        parse_code(code).unwrap();
    }
}

/// A decoder that decompress input data from another `Read`.
///
/// This allows to read a stream of compressed data
/// (good for files or heavy network stream).
pub struct Decoder<R: Read> {
    // input reader (compressed data)
    reader: R,
    // input buffer
    buffer: Vec<u8>,
    // we already read everything in the buffer up to that point
    offset: usize,
    // decompression context
    context: DecoderContext,

    // `true` if we should stop after the first frame.
    single_frame: bool,
}

impl<R: Read> Decoder<R> {
    /// Creates a new decoder.
    pub fn new(reader: R) -> io::Result<Self> {
        Self::with_dictionary(reader, &[])
    }

    /// Sets this `Decoder` to stop after the first frame.
    ///
    /// By default, it keeps concatenating frames until EOF is reached.
    pub fn single_frame(mut self) -> Self {
        self.single_frame = true;
        self
    }

    /// Creates a new decoder, using an existing dictionary.
    ///
    /// The dictionary must be the same as the one used during compression.
    pub fn with_dictionary(reader: R, dictionary: &[u8]) -> io::Result<Self> {

        let buffer_size = unsafe { zstd_sys::ZSTD_DStreamInSize() };

        let context = DecoderContext::default();
        parse_code(unsafe {
            zstd_sys::ZSTD_initDStream_usingDict(context.s,
                                           dictionary.as_ptr() as *const c_void,
                                           dictionary.len())
        })?;

        let decoder = Decoder {
            reader: reader,
            buffer: Vec::with_capacity(buffer_size),
            offset: 0,
            context: context,
            single_frame: false,
        };

        Ok(decoder)
    }

    fn reinit(&mut self) -> io::Result<()> {
        parse_code(unsafe { zstd_sys::ZSTD_resetDStream(self.context.s) })?;
        Ok(())
    }

    /// Recommendation for the size of the output buffer.
    pub fn recommended_output_size() -> usize {
        unsafe { zstd_sys::ZSTD_DStreamOutSize() }
    }

    /// Acquire a reference to the underlying reader.
    pub fn get_ref(&self) -> &R {
        &self.reader
    }

    /// Acquire a mutable reference to the underlying reader.
    ///
    /// Note that mutation of the reader may result in surprising results if
    /// this decoder is continued to be used.
    pub fn get_mut(&mut self) -> &mut R {
        &mut self.reader
    }

    /// Return the inner `Read`.
    ///
    /// Calling `finish()` is not *required* after reading a stream -
    /// just use it if you need to get the `Read` back.
    pub fn finish(self) -> R {
        self.reader
    }

    // Attemps to refill the input buffer.
    fn refill_buffer(&mut self, in_buffer: &mut zstd_sys::ZSTD_inBuffer)
                     -> io::Result<bool> {

        // We need moar data!
        // Make a nice clean buffer
        let buffer_size = self.buffer.capacity();
        unsafe {
            self.buffer.set_len(buffer_size);
        }

        // And FILL IT!
        let read = self.reader.read(&mut self.buffer)?;
        unsafe {
            self.buffer.set_len(read);
        }
        in_buffer.pos = 0;
        in_buffer.size = read;
        // So we can't read anything: input is exhausted.

        Ok(read > 0)
    }
}

impl<R: Read> Read for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {

        let mut in_buffer = zstd_sys::ZSTD_inBuffer {
            src: self.buffer.as_ptr() as *const c_void,
            size: self.buffer.len(),
            pos: self.offset,
        };

        if self.offset > self.buffer.capacity() {
            // If we've reached the end of the frame before,
            // don't even try to read more.
            return Ok(0);
        }

        let mut out_buffer = zstd_sys::ZSTD_outBuffer {
            dst: buf.as_mut_ptr() as *mut c_void,
            size: buf.len(),
            pos: 0,
        };
        while out_buffer.pos != buf.len() {

            let input_exhausted = in_buffer.pos == in_buffer.size &&
                                  !self.refill_buffer(&mut in_buffer)?;

            let res = unsafe {
                let code =
                    zstd_sys::ZSTD_decompressStream(self.context.s,
                                              &mut out_buffer as *mut zstd_sys::ZSTD_outBuffer,
                                              &mut in_buffer as *mut zstd_sys::ZSTD_inBuffer);
                parse_code(code)?
            };

            if res > 1 && input_exhausted {
                // zstd keeps asking for more, but we're short on data!
                return Err(io::Error::new(io::ErrorKind::UnexpectedEof,
                                          "incomplete frame"));
            }

            if res == 0 {
                // This means that the current frame ended.

                // Remember it, so we don't try to read the next one.
                // Also bail if there's no more input to decopress.
                if self.single_frame ||
                   (in_buffer.pos == in_buffer.size &&
                    !self.refill_buffer(&mut in_buffer)?) {
                    // We're out.
                    in_buffer.pos = self.buffer.capacity() + 1;
                    break;
                } else {
                    // Start a new frame baby!
                    self.reinit()?;
                }
            }
        }
        self.offset = in_buffer.pos;
        Ok(out_buffer.pos)
    }
}
<commit_msg>added AsyncRead implementation for Decoder<commit_after>use libc::c_void;
use parse_code;
use std::io::{self, Read};
use zstd_sys;

#[cfg(feature = "tokio")]
use tokio_io::AsyncRead;

struct DecoderContext {
    s: *mut zstd_sys::ZSTD_DStream,
}

impl Default for DecoderContext {
    fn default() -> Self {
        DecoderContext { s: unsafe { zstd_sys::ZSTD_createDStream() } }
    }
}

impl Drop for DecoderContext {
    fn drop(&mut self) {
        let code = unsafe { zstd_sys::ZSTD_freeDStream(self.s) };
        parse_code(code).unwrap();
    }
}

/// A decoder that decompress input data from another `Read`.
///
/// This allows to read a stream of compressed data
/// (good for files or heavy network stream).
pub struct Decoder<R: Read> {
    // input reader (compressed data)
    reader: R,
    // input buffer
    buffer: Vec<u8>,
    // we already read everything in the buffer up to that point
    offset: usize,
    // decompression context
    context: DecoderContext,

    // `true` if we should stop after the first frame.
    single_frame: bool,
}

impl<R: Read> Decoder<R> {
    /// Creates a new decoder.
    pub fn new(reader: R) -> io::Result<Self> {
        Self::with_dictionary(reader, &[])
    }

    /// Sets this `Decoder` to stop after the first frame.
    ///
    /// By default, it keeps concatenating frames until EOF is reached.
    pub fn single_frame(mut self) -> Self {
        self.single_frame = true;
        self
    }

    /// Creates a new decoder, using an existing dictionary.
    ///
    /// The dictionary must be the same as the one used during compression.
    pub fn with_dictionary(reader: R, dictionary: &[u8]) -> io::Result<Self> {

        let buffer_size = unsafe { zstd_sys::ZSTD_DStreamInSize() };

        let context = DecoderContext::default();
        parse_code(unsafe {
            zstd_sys::ZSTD_initDStream_usingDict(context.s,
                                           dictionary.as_ptr() as *const c_void,
                                           dictionary.len())
        })?;

        let decoder = Decoder {
            reader: reader,
            buffer: Vec::with_capacity(buffer_size),
            offset: 0,
            context: context,
            single_frame: false,
        };

        Ok(decoder)
    }

    fn reinit(&mut self) -> io::Result<()> {
        parse_code(unsafe { zstd_sys::ZSTD_resetDStream(self.context.s) })?;
        Ok(())
    }

    /// Recommendation for the size of the output buffer.
    pub fn recommended_output_size() -> usize {
        unsafe { zstd_sys::ZSTD_DStreamOutSize() }
    }

    /// Acquire a reference to the underlying reader.
    pub fn get_ref(&self) -> &R {
        &self.reader
    }

    /// Acquire a mutable reference to the underlying reader.
    ///
    /// Note that mutation of the reader may result in surprising results if
    /// this decoder is continued to be used.
    pub fn get_mut(&mut self) -> &mut R {
        &mut self.reader
    }

    /// Return the inner `Read`.
    ///
    /// Calling `finish()` is not *required* after reading a stream -
    /// just use it if you need to get the `Read` back.
    pub fn finish(self) -> R {
        self.reader
    }

    // Attemps to refill the input buffer.
    fn refill_buffer(&mut self, in_buffer: &mut zstd_sys::ZSTD_inBuffer)
                     -> io::Result<bool> {

        // We need moar data!
        // Make a nice clean buffer
        let buffer_size = self.buffer.capacity();
        unsafe {
            self.buffer.set_len(buffer_size);
        }

        // And FILL IT!
        let read = self.reader.read(&mut self.buffer)?;
        unsafe {
            self.buffer.set_len(read);
        }
        in_buffer.pos = 0;
        in_buffer.size = read;
        // So we can't read anything: input is exhausted.

        Ok(read > 0)
    }
}

impl<R: Read> Read for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {

        let mut in_buffer = zstd_sys::ZSTD_inBuffer {
            src: self.buffer.as_ptr() as *const c_void,
            size: self.buffer.len(),
            pos: self.offset,
        };

        if self.offset > self.buffer.capacity() {
            // If we've reached the end of the frame before,
            // don't even try to read more.
            return Ok(0);
        }

        let mut out_buffer = zstd_sys::ZSTD_outBuffer {
            dst: buf.as_mut_ptr() as *mut c_void,
            size: buf.len(),
            pos: 0,
        };
        while out_buffer.pos != buf.len() {

            let input_exhausted = in_buffer.pos == in_buffer.size &&
                                  !self.refill_buffer(&mut in_buffer)?;

            let res = unsafe {
                let code =
                    zstd_sys::ZSTD_decompressStream(self.context.s,
                                              &mut out_buffer as *mut zstd_sys::ZSTD_outBuffer,
                                              &mut in_buffer as *mut zstd_sys::ZSTD_inBuffer);
                parse_code(code)?
            };

            if res > 1 && input_exhausted {
                // zstd keeps asking for more, but we're short on data!
                return Err(io::Error::new(io::ErrorKind::UnexpectedEof,
                                          "incomplete frame"));
            }

            if res == 0 {
                // This means that the current frame ended.

                // Remember it, so we don't try to read the next one.
                // Also bail if there's no more input to decopress.
                if self.single_frame ||
                   (in_buffer.pos == in_buffer.size &&
                    !self.refill_buffer(&mut in_buffer)?) {
                    // We're out.
                    in_buffer.pos = self.buffer.capacity() + 1;
                    break;
                } else {
                    // Start a new frame baby!
                    self.reinit()?;
                }
            }
        }
        self.offset = in_buffer.pos;
        Ok(out_buffer.pos)
    }
}

#[cfg(feature = "tokio")]
impl<R: AsyncRead> AsyncRead for Decoder<R> {
    unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
        false
    }
}
<|endoftext|>


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

//! Foundation bits exposing the Compute API.

use std::collections::HashMap;
use std::fmt::Debug;

use reqwest::RequestBuilder;
use serde::Serialize;
use serde_json;

use super::super::common::protocol::Ref;
use super::super::common::{self, ApiVersion};
use super::super::session::{RequestBuilderExt, ServiceType, Session};
use super::super::utils::{self, ResultExt};
use super::super::Result;
use super::protocol;

const API_VERSION_KEYPAIR_TYPE: ApiVersion = ApiVersion(2, 2);
const API_VERSION_SERVER_DESCRIPTION: ApiVersion = ApiVersion(2, 19);
const API_VERSION_KEYPAIR_PAGINATION: ApiVersion = ApiVersion(2, 35);
const API_VERSION_FLAVOR_DESCRIPTION: ApiVersion = ApiVersion(2, 55);
const API_VERSION_FLAVOR_EXTRA_SPECS: ApiVersion = ApiVersion(2, 61);

/// Extensions for Session.
pub trait V2API {
    /// Create a key pair.
    fn create_keypair(&self, request: protocol::KeyPairCreate) -> Result<protocol::KeyPair>;

    /// Create a server.
    fn create_server(&self, request: protocol::ServerCreate) -> Result<Ref>;

    /// Delete a key pair.
    fn delete_keypair<S: AsRef<str>>(&self, name: S) -> Result<()>;

    /// Delete a server.
    fn delete_server<S: AsRef<str>>(&self, id: S) -> Result<()>;

    /// Get a flavor by its ID.
    fn get_extra_specs_by_flavor_id<S: AsRef<str>>(&self, id: S)
        -> Result<HashMap<String, String>>;

    /// Get a flavor.
    fn get_flavor<S: AsRef<str>>(&self, id_or_name: S) -> Result<protocol::Flavor> {
        let s = id_or_name.as_ref();
        self.get_flavor_by_id(s)
            .if_not_found_then(|| self.get_flavor_by_name(s))
    }

    /// Get a flavor by its ID.
    fn get_flavor_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Flavor>;

    /// Get a flavor by its name.
    fn get_flavor_by_name<S: AsRef<str>>(&self, name: S) -> Result<protocol::Flavor>;

    /// Get a key pair by its nam.e
    fn get_keypair<S: AsRef<str>>(&self, name: S) -> Result<protocol::KeyPair>;

    /// Get a server.
    fn get_server<S: AsRef<str>>(&self, id_or_name: S) -> Result<protocol::Server> {
        let s = id_or_name.as_ref();
        self.get_server_by_id(s)
            .if_not_found_then(|| self.get_server_by_name(s))
    }

    /// Get a server by its ID.
    fn get_server_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Server>;

    /// Get a server by its ID.
    fn get_server_by_name<S: AsRef<str>>(&self, id: S) -> Result<protocol::Server>;

    /// List flavors.
    fn list_flavors<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>>;

    /// List flavors with details.
    fn list_flavors_detail<Q: Serialize + Debug>(&self, query: &Q)
        -> Result<Vec<protocol::Flavor>>;

    /// List key pairs.
    fn list_keypairs<Q: Serialize + Debug>(&self, query: &Q) -> Result<Vec<protocol::KeyPair>>;

    /// List servers.
    fn list_servers<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>>;

    /// List servers with details.
    fn list_servers_detail<Q: Serialize + Debug>(&self, query: &Q)
        -> Result<Vec<protocol::Server>>;

    /// Pick the highest API version or None if neither is supported.
    fn pick_compute_api_version(&self, versions: &[ApiVersion]) -> Result<Option<ApiVersion>>;

    /// Run an action while providing some arguments.
    fn server_action_with_args<S1, S2, Q>(&self, id: S1, action: S2, args: Q) -> Result<()>
    where
        S1: AsRef<str>,
        S2: AsRef<str>,
        Q: Serialize + Debug;

    /// Run an action on the server.
    fn server_simple_action<S1, S2>(&self, id: S1, action: S2) -> Result<()>
    where
        S1: AsRef<str>,
        S2: AsRef<str>,
    {
        self.server_action_with_args(id, action, serde_json::Value::Null)
    }

    /// Whether the given compute API version is supported by the server.
    fn supports_compute_api_version(&self, version: ApiVersion) -> Result<bool>;

    /// Whether key pair pagination is supported.
    fn supports_keypair_pagination(&self) -> Result<bool> {
        self.supports_compute_api_version(API_VERSION_KEYPAIR_PAGINATION)
    }
}

/// Service type of Compute API V2.
#[derive(Copy, Clone, Debug)]
pub struct V2;

const SERVICE_TYPE: &str = "compute";

fn flavor_api_version<T: V2API>(api: &T) -> Result<Option<ApiVersion>> {
    api.pick_compute_api_version(&[
        API_VERSION_FLAVOR_DESCRIPTION,
        API_VERSION_FLAVOR_EXTRA_SPECS,
    ])
}

impl V2API for Session {
    fn create_keypair(&self, request: protocol::KeyPairCreate) -> Result<protocol::KeyPair> {
        let version = if request.key_type.is_some() {
            Some(API_VERSION_KEYPAIR_TYPE)
        } else {
            None
        };

        debug!("Creating a key pair with {:?}", request);
        let body = protocol::KeyPairCreateRoot { keypair: request };
        let keypair = self
            .post::<V2>(&["os-keypairs"], version)?
            .json(&body)
            .receive_json::<protocol::KeyPairRoot>()?
            .keypair;
        debug!("Created key pair {:?}", keypair);
        Ok(keypair)
    }

    fn create_server(&self, request: protocol::ServerCreate) -> Result<Ref> {
        debug!("Creating a server with {:?}", request);
        let body = protocol::ServerCreateRoot { server: request };
        let server = self
            .post::<V2>(&["servers"], None)?
            .json(&body)
            .receive_json::<protocol::CreatedServerRoot>()?
            .server;
        trace!("Requested creation of server {:?}", server);
        Ok(server)
    }

    fn delete_keypair<S: AsRef<str>>(&self, name: S) -> Result<()> {
        debug!("Deleting key pair {}", name.as_ref());
        self.delete::<V2>(&["os-keypairs", name.as_ref()], None)?
            .commit()?;
        debug!("Key pair {} was deleted", name.as_ref());
        Ok(())
    }

    fn delete_server<S: AsRef<str>>(&self, id: S) -> Result<()> {
        trace!("Deleting server {}", id.as_ref());
        self.delete::<V2>(&["servers", id.as_ref()], None)?
            .commit()?;
        debug!("Successfully requested deletion of server {}", id.as_ref());
        Ok(())
    }

    fn get_extra_specs_by_flavor_id<S: AsRef<str>>(
        &self,
        id: S,
    ) -> Result<HashMap<String, String>> {
        trace!("Get compute extra specs by ID {}", id.as_ref());
        let extra_specs = self
            .get::<V2>(&["flavors", id.as_ref(), "os-extra_specs"], None)?
            .receive_json::<protocol::ExtraSpecsRoot>()?
            .extra_specs;
        trace!("Received {:?}", extra_specs);
        Ok(extra_specs)
    }

    fn get_flavor_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Flavor> {
        trace!("Get compute flavor by ID {}", id.as_ref());
        let version = flavor_api_version(self)?;
        let flavor = self
            .get::<V2>(&["flavors", id.as_ref()], version)?
            .receive_json::<protocol::FlavorRoot>()?
            .flavor;
        trace!("Received {:?}", flavor);
        Ok(flavor)
    }

    fn get_flavor_by_name<S: AsRef<str>>(&self, name: S) -> Result<protocol::Flavor> {
        trace!("Get compute flavor by name {}", name.as_ref());
        let items = self
            .get::<V2>(&["flavors"], None)?
            .receive_json::<protocol::FlavorsRoot>()?
            .flavors
            .into_iter()
            .filter(|item| item.name == name.as_ref());
        utils::one(
            items,
            "Flavor with given name or ID not found",
            "Too many flavors found with given name",
        )
        .and_then(|item| self.get_flavor_by_id(item.id))
    }

    fn get_keypair<S: AsRef<str>>(&self, name: S) -> Result<protocol::KeyPair> {
        trace!("Get compute key pair by name {}", name.as_ref());
        let ver = self.pick_compute_api_version(&[API_VERSION_KEYPAIR_TYPE])?;
        let keypair = self
            .get::<V2>(&["os-keypairs", name.as_ref()], ver)?
            .receive_json::<protocol::KeyPairRoot>()?
            .keypair;
        trace!("Received {:?}", keypair);
        Ok(keypair)
    }

    fn get_server_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Server> {
        trace!("Get compute server with ID {}", id.as_ref());
        let version = self.pick_compute_api_version(&[API_VERSION_SERVER_DESCRIPTION])?;
        let server = self
            .get::<V2>(&["servers", id.as_ref()], version)?
            .receive_json::<protocol::ServerRoot>()?
            .server;
        trace!("Received {:?}", server);
        Ok(server)
    }

    fn get_server_by_name<S: AsRef<str>>(&self, name: S) -> Result<protocol::Server> {
        trace!("Get compute server with name {}", name.as_ref());
        let items = self
            .get::<V2>(&["servers"], None)?
            .query(&[("name", name.as_ref())])
            .receive_json::<protocol::ServersRoot>()?
            .servers
            .into_iter()
            .filter(|item| item.name == name.as_ref());
        utils::one(
            items,
            "Server with given name or ID not found",
            "Too many servers found with given name",
        )
        .and_then(|item| self.get_server_by_id(item.id))
    }

    fn list_flavors<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>> {
        trace!("Listing compute flavors with {:?}", query);
        let result = self
            .get::<V2>(&["flavors"], None)?
            .query(query)
            .receive_json::<protocol::FlavorsRoot>()?
            .flavors;
        trace!("Received flavors: {:?}", result);
        Ok(result)
    }

    fn list_flavors_detail<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<protocol::Flavor>> {
        trace!("Listing compute flavors with {:?}", query);
        let version = self.pick_compute_api_version(&[API_VERSION_FLAVOR_EXTRA_SPECS])?;
        let result = self
            .get::<V2>(&["flavors", "detail"], version)?
            .query(query)
            .receive_json::<protocol::FlavorsDetailRoot>()?
            .flavors;
        trace!("Received flavors: {:?}", result);
        Ok(result)
    }

    fn list_keypairs<Q: Serialize + Debug>(&self, query: &Q) -> Result<Vec<protocol::KeyPair>> {
        trace!("Listing compute key pairs with {:?}", query);
        let ver = self.pick_compute_api_version(&[
            API_VERSION_KEYPAIR_TYPE,
            API_VERSION_KEYPAIR_PAGINATION,
        ])?;
        let result = self
            .get::<V2>(&["os-keypairs"], ver)?
            .query(query)
            .receive_json::<protocol::KeyPairsRoot>()?
            .keypairs
            .into_iter()
            .map(|item| item.keypair)
            .collect::<Vec<_>>();
        trace!("Received key pairs: {:?}", result);
        Ok(result)
    }

    fn list_servers<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>> {
        trace!("Listing compute servers with {:?}", query);
        let result = self
            .get::<V2>(&["servers"], None)?
            .query(query)
            .receive_json::<protocol::ServersRoot>()?
            .servers;
        trace!("Received servers: {:?}", result);
        Ok(result)
    }

    fn list_servers_detail<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<protocol::Server>> {
        trace!("Listing compute servers with {:?}", query);
        let version = self.pick_compute_api_version(&[API_VERSION_SERVER_DESCRIPTION])?;
        let result = self
            .get::<V2>(&["servers", "detail"], version)?
            .query(query)
            .receive_json::<protocol::ServersDetailRoot>()?
            .servers;
        trace!("Received servers: {:?}", result);
        Ok(result)
    }

    fn pick_compute_api_version(&self, versions: &[ApiVersion]) -> Result<Option<ApiVersion>> {
        let info = self.get_service_info_ref::<V2>()?;
        Ok(versions
            .into_iter()
            .filter(|item| info.supports_api_version(**item))
            .max()
            .cloned())
    }

    fn server_action_with_args<S1, S2, Q>(&self, id: S1, action: S2, args: Q) -> Result<()>
    where
        S1: AsRef<str>,
        S2: AsRef<str>,
        Q: Serialize + Debug,
    {
        trace!(
            "Running {} on server {} with args {:?}",
            action.as_ref(),
            id.as_ref(),
            args
        );
        let mut body = HashMap::new();
        let _ = body.insert(action.as_ref(), args);
        self.post::<V2>(&["servers", id.as_ref(), "action"], None)?
            .json(&body)
            .commit()?;
        debug!(
            "Successfully ran {} on server {}",
            action.as_ref(),
            id.as_ref()
        );
        Ok(())
    }

    fn supports_compute_api_version(&self, version: ApiVersion) -> Result<bool> {
        let info = self.get_service_info_ref::<V2>()?;
        Ok(info.supports_api_version(version))
    }
}

impl ServiceType for V2 {
    fn catalog_type() -> &'static str {
        SERVICE_TYPE
    }

    fn major_version_supported(version: ApiVersion) -> bool {
        version.0 == 2
    }

    fn set_api_version_headers(
        request: RequestBuilder,
        version: ApiVersion,
    ) -> Result<RequestBuilder> {
        // TODO: new-style header support
        Ok(request.header("x-openstack-nova-api-version", version.to_string()))
    }
}
<commit_msg>chore: make clippy happy again<commit_after>// Copyright 2017 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.

//! Foundation bits exposing the Compute API.

use std::collections::HashMap;
use std::fmt::Debug;

use reqwest::RequestBuilder;
use serde::Serialize;
use serde_json;

use super::super::common::protocol::Ref;
use super::super::common::{self, ApiVersion};
use super::super::session::{RequestBuilderExt, ServiceType, Session};
use super::super::utils::{self, ResultExt};
use super::super::Result;
use super::protocol;

const API_VERSION_KEYPAIR_TYPE: ApiVersion = ApiVersion(2, 2);
const API_VERSION_SERVER_DESCRIPTION: ApiVersion = ApiVersion(2, 19);
const API_VERSION_KEYPAIR_PAGINATION: ApiVersion = ApiVersion(2, 35);
const API_VERSION_FLAVOR_DESCRIPTION: ApiVersion = ApiVersion(2, 55);
const API_VERSION_FLAVOR_EXTRA_SPECS: ApiVersion = ApiVersion(2, 61);

/// Extensions for Session.
pub trait V2API {
    /// Create a key pair.
    fn create_keypair(&self, request: protocol::KeyPairCreate) -> Result<protocol::KeyPair>;

    /// Create a server.
    fn create_server(&self, request: protocol::ServerCreate) -> Result<Ref>;

    /// Delete a key pair.
    fn delete_keypair<S: AsRef<str>>(&self, name: S) -> Result<()>;

    /// Delete a server.
    fn delete_server<S: AsRef<str>>(&self, id: S) -> Result<()>;

    /// Get a flavor by its ID.
    fn get_extra_specs_by_flavor_id<S: AsRef<str>>(&self, id: S)
        -> Result<HashMap<String, String>>;

    /// Get a flavor.
    fn get_flavor<S: AsRef<str>>(&self, id_or_name: S) -> Result<protocol::Flavor> {
        let s = id_or_name.as_ref();
        self.get_flavor_by_id(s)
            .if_not_found_then(|| self.get_flavor_by_name(s))
    }

    /// Get a flavor by its ID.
    fn get_flavor_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Flavor>;

    /// Get a flavor by its name.
    fn get_flavor_by_name<S: AsRef<str>>(&self, name: S) -> Result<protocol::Flavor>;

    /// Get a key pair by its nam.e
    fn get_keypair<S: AsRef<str>>(&self, name: S) -> Result<protocol::KeyPair>;

    /// Get a server.
    fn get_server<S: AsRef<str>>(&self, id_or_name: S) -> Result<protocol::Server> {
        let s = id_or_name.as_ref();
        self.get_server_by_id(s)
            .if_not_found_then(|| self.get_server_by_name(s))
    }

    /// Get a server by its ID.
    fn get_server_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Server>;

    /// Get a server by its ID.
    fn get_server_by_name<S: AsRef<str>>(&self, id: S) -> Result<protocol::Server>;

    /// List flavors.
    fn list_flavors<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>>;

    /// List flavors with details.
    fn list_flavors_detail<Q: Serialize + Debug>(&self, query: &Q)
        -> Result<Vec<protocol::Flavor>>;

    /// List key pairs.
    fn list_keypairs<Q: Serialize + Debug>(&self, query: &Q) -> Result<Vec<protocol::KeyPair>>;

    /// List servers.
    fn list_servers<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>>;

    /// List servers with details.
    fn list_servers_detail<Q: Serialize + Debug>(&self, query: &Q)
        -> Result<Vec<protocol::Server>>;

    /// Pick the highest API version or None if neither is supported.
    fn pick_compute_api_version(&self, versions: &[ApiVersion]) -> Result<Option<ApiVersion>>;

    /// Run an action while providing some arguments.
    fn server_action_with_args<S1, S2, Q>(&self, id: S1, action: S2, args: Q) -> Result<()>
    where
        S1: AsRef<str>,
        S2: AsRef<str>,
        Q: Serialize + Debug;

    /// Run an action on the server.
    fn server_simple_action<S1, S2>(&self, id: S1, action: S2) -> Result<()>
    where
        S1: AsRef<str>,
        S2: AsRef<str>,
    {
        self.server_action_with_args(id, action, serde_json::Value::Null)
    }

    /// Whether the given compute API version is supported by the server.
    fn supports_compute_api_version(&self, version: ApiVersion) -> Result<bool>;

    /// Whether key pair pagination is supported.
    fn supports_keypair_pagination(&self) -> Result<bool> {
        self.supports_compute_api_version(API_VERSION_KEYPAIR_PAGINATION)
    }
}

/// Service type of Compute API V2.
#[derive(Copy, Clone, Debug)]
pub struct V2;

const SERVICE_TYPE: &str = "compute";

fn flavor_api_version<T: V2API>(api: &T) -> Result<Option<ApiVersion>> {
    api.pick_compute_api_version(&[
        API_VERSION_FLAVOR_DESCRIPTION,
        API_VERSION_FLAVOR_EXTRA_SPECS,
    ])
}

impl V2API for Session {
    fn create_keypair(&self, request: protocol::KeyPairCreate) -> Result<protocol::KeyPair> {
        let version = if request.key_type.is_some() {
            Some(API_VERSION_KEYPAIR_TYPE)
        } else {
            None
        };

        debug!("Creating a key pair with {:?}", request);
        let body = protocol::KeyPairCreateRoot { keypair: request };
        let keypair = self
            .post::<V2>(&["os-keypairs"], version)?
            .json(&body)
            .receive_json::<protocol::KeyPairRoot>()?
            .keypair;
        debug!("Created key pair {:?}", keypair);
        Ok(keypair)
    }

    fn create_server(&self, request: protocol::ServerCreate) -> Result<Ref> {
        debug!("Creating a server with {:?}", request);
        let body = protocol::ServerCreateRoot { server: request };
        let server = self
            .post::<V2>(&["servers"], None)?
            .json(&body)
            .receive_json::<protocol::CreatedServerRoot>()?
            .server;
        trace!("Requested creation of server {:?}", server);
        Ok(server)
    }

    fn delete_keypair<S: AsRef<str>>(&self, name: S) -> Result<()> {
        debug!("Deleting key pair {}", name.as_ref());
        self.delete::<V2>(&["os-keypairs", name.as_ref()], None)?
            .commit()?;
        debug!("Key pair {} was deleted", name.as_ref());
        Ok(())
    }

    fn delete_server<S: AsRef<str>>(&self, id: S) -> Result<()> {
        trace!("Deleting server {}", id.as_ref());
        self.delete::<V2>(&["servers", id.as_ref()], None)?
            .commit()?;
        debug!("Successfully requested deletion of server {}", id.as_ref());
        Ok(())
    }

    fn get_extra_specs_by_flavor_id<S: AsRef<str>>(
        &self,
        id: S,
    ) -> Result<HashMap<String, String>> {
        trace!("Get compute extra specs by ID {}", id.as_ref());
        let extra_specs = self
            .get::<V2>(&["flavors", id.as_ref(), "os-extra_specs"], None)?
            .receive_json::<protocol::ExtraSpecsRoot>()?
            .extra_specs;
        trace!("Received {:?}", extra_specs);
        Ok(extra_specs)
    }

    fn get_flavor_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Flavor> {
        trace!("Get compute flavor by ID {}", id.as_ref());
        let version = flavor_api_version(self)?;
        let flavor = self
            .get::<V2>(&["flavors", id.as_ref()], version)?
            .receive_json::<protocol::FlavorRoot>()?
            .flavor;
        trace!("Received {:?}", flavor);
        Ok(flavor)
    }

    fn get_flavor_by_name<S: AsRef<str>>(&self, name: S) -> Result<protocol::Flavor> {
        trace!("Get compute flavor by name {}", name.as_ref());
        let items = self
            .get::<V2>(&["flavors"], None)?
            .receive_json::<protocol::FlavorsRoot>()?
            .flavors
            .into_iter()
            .filter(|item| item.name == name.as_ref());
        utils::one(
            items,
            "Flavor with given name or ID not found",
            "Too many flavors found with given name",
        )
        .and_then(|item| self.get_flavor_by_id(item.id))
    }

    fn get_keypair<S: AsRef<str>>(&self, name: S) -> Result<protocol::KeyPair> {
        trace!("Get compute key pair by name {}", name.as_ref());
        let ver = self.pick_compute_api_version(&[API_VERSION_KEYPAIR_TYPE])?;
        let keypair = self
            .get::<V2>(&["os-keypairs", name.as_ref()], ver)?
            .receive_json::<protocol::KeyPairRoot>()?
            .keypair;
        trace!("Received {:?}", keypair);
        Ok(keypair)
    }

    fn get_server_by_id<S: AsRef<str>>(&self, id: S) -> Result<protocol::Server> {
        trace!("Get compute server with ID {}", id.as_ref());
        let version = self.pick_compute_api_version(&[API_VERSION_SERVER_DESCRIPTION])?;
        let server = self
            .get::<V2>(&["servers", id.as_ref()], version)?
            .receive_json::<protocol::ServerRoot>()?
            .server;
        trace!("Received {:?}", server);
        Ok(server)
    }

    fn get_server_by_name<S: AsRef<str>>(&self, name: S) -> Result<protocol::Server> {
        trace!("Get compute server with name {}", name.as_ref());
        let items = self
            .get::<V2>(&["servers"], None)?
            .query(&[("name", name.as_ref())])
            .receive_json::<protocol::ServersRoot>()?
            .servers
            .into_iter()
            .filter(|item| item.name == name.as_ref());
        utils::one(
            items,
            "Server with given name or ID not found",
            "Too many servers found with given name",
        )
        .and_then(|item| self.get_server_by_id(item.id))
    }

    fn list_flavors<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>> {
        trace!("Listing compute flavors with {:?}", query);
        let result = self
            .get::<V2>(&["flavors"], None)?
            .query(query)
            .receive_json::<protocol::FlavorsRoot>()?
            .flavors;
        trace!("Received flavors: {:?}", result);
        Ok(result)
    }

    fn list_flavors_detail<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<protocol::Flavor>> {
        trace!("Listing compute flavors with {:?}", query);
        let version = self.pick_compute_api_version(&[API_VERSION_FLAVOR_EXTRA_SPECS])?;
        let result = self
            .get::<V2>(&["flavors", "detail"], version)?
            .query(query)
            .receive_json::<protocol::FlavorsDetailRoot>()?
            .flavors;
        trace!("Received flavors: {:?}", result);
        Ok(result)
    }

    fn list_keypairs<Q: Serialize + Debug>(&self, query: &Q) -> Result<Vec<protocol::KeyPair>> {
        trace!("Listing compute key pairs with {:?}", query);
        let ver = self.pick_compute_api_version(&[
            API_VERSION_KEYPAIR_TYPE,
            API_VERSION_KEYPAIR_PAGINATION,
        ])?;
        let result = self
            .get::<V2>(&["os-keypairs"], ver)?
            .query(query)
            .receive_json::<protocol::KeyPairsRoot>()?
            .keypairs
            .into_iter()
            .map(|item| item.keypair)
            .collect::<Vec<_>>();
        trace!("Received key pairs: {:?}", result);
        Ok(result)
    }

    fn list_servers<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<common::protocol::IdAndName>> {
        trace!("Listing compute servers with {:?}", query);
        let result = self
            .get::<V2>(&["servers"], None)?
            .query(query)
            .receive_json::<protocol::ServersRoot>()?
            .servers;
        trace!("Received servers: {:?}", result);
        Ok(result)
    }

    fn list_servers_detail<Q: Serialize + Debug>(
        &self,
        query: &Q,
    ) -> Result<Vec<protocol::Server>> {
        trace!("Listing compute servers with {:?}", query);
        let version = self.pick_compute_api_version(&[API_VERSION_SERVER_DESCRIPTION])?;
        let result = self
            .get::<V2>(&["servers", "detail"], version)?
            .query(query)
            .receive_json::<protocol::ServersDetailRoot>()?
            .servers;
        trace!("Received servers: {:?}", result);
        Ok(result)
    }

    fn pick_compute_api_version(&self, versions: &[ApiVersion]) -> Result<Option<ApiVersion>> {
        let info = self.get_service_info_ref::<V2>()?;
        Ok(versions
            .iter()
            .filter(|item| info.supports_api_version(**item))
            .max()
            .cloned())
    }

    fn server_action_with_args<S1, S2, Q>(&self, id: S1, action: S2, args: Q) -> Result<()>
    where
        S1: AsRef<str>,
        S2: AsRef<str>,
        Q: Serialize + Debug,
    {
        trace!(
            "Running {} on server {} with args {:?}",
            action.as_ref(),
            id.as_ref(),
            args
        );
        let mut body = HashMap::new();
        let _ = body.insert(action.as_ref(), args);
        self.post::<V2>(&["servers", id.as_ref(), "action"], None)?
            .json(&body)
            .commit()?;
        debug!(
            "Successfully ran {} on server {}",
            action.as_ref(),
            id.as_ref()
        );
        Ok(())
    }

    fn supports_compute_api_version(&self, version: ApiVersion) -> Result<bool> {
        let info = self.get_service_info_ref::<V2>()?;
        Ok(info.supports_api_version(version))
    }
}

impl ServiceType for V2 {
    fn catalog_type() -> &'static str {
        SERVICE_TYPE
    }

    fn major_version_supported(version: ApiVersion) -> bool {
        version.0 == 2
    }

    fn set_api_version_headers(
        request: RequestBuilder,
        version: ApiVersion,
    ) -> Result<RequestBuilder> {
        // TODO: new-style header support
        Ok(request.header("x-openstack-nova-api-version", version.to_string()))
    }
}
<|endoftext|>