<commit_before>/*!

Things for the construction and sending of HTTP requests.

If you want to make a request, `RequestWriter::new` is where you start, and
`RequestWriter.read_response` is where you will send the request and read the response.

```rust
use http::client::RequestWriter;
use http::method::Get;

fn main() {
    let request = RequestWriter::new(Get, from_str("http://example.com/").unwrap());
    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_request, error)) => fail!(":-( {}", error),
    };
    // Now you have a `ResponseReader`; see http::client::response for docs on that.
}
```

If you wish to send a request body (e.g. POST requests), I'm sorry to have to tell you that there is
not *good* support for this yet. However, it can be done; here is an example:

```rust
let data: &[u8];
let mut request: RequestWriter;

request.headers.content_length = Some(data.len());
request.write(data);
let response = match request.read_response() {
    Ok(response) => response,
    Err((_request, error)) => fail!(":-( {}", error),
};
```

*/

use url;
use url::Url;
use method::Method;
use std::io::{IoError, IoResult};
use std::io::net::get_host_addresses;
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use buffer::BufferedStream;
use headers::request::HeaderCollection;
use headers::host::Host;
use connecter::Connecter;

use client::response::ResponseReader;

/*impl ResponseReader {
    {
        let mut buf = [0u8, ..2000];
        match stream.read(buf) {
            None => fail!("Read error :-("),  // conditions for error interception, again
            Some(bytes_read) => {
                println!(str::from_bytes(buf.slice_to(bytes_read)));
            }
        }

        match response {
            Some(response) => Ok(response),
            None => Err(self),
        }
    }
}*/

pub struct RequestWriter<S = super::NetworkStream> {
    // The place to write to (typically a network stream, which is
    // io::net::tcp::TcpStream or an SSL wrapper around that)
    stream: Option<BufferedStream<S>>,
    headers_written: bool,

    /// The originating IP address of the request.
    pub remote_addr: Option<SocketAddr>,

    /// The host name and IP address that the request was sent to; this must always be specified for
    /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host
    /// header was not defined, and so this field will probably be None in such cases.
    //host: Host,  // Now headers.host

    /// The headers sent with the request.
    pub headers: Box<HeaderCollection>,

    /// The HTTP method for the request.
    pub method: Method,

    /// The URL being requested.
    pub url: Url,

    /// Should we use SSL?
    use_ssl: bool,
}

/// Low-level HTTP request writing support
///
/// Moderately hacky, and due to current limitations in the TcpStream arrangement reading cannot
/// take place until writing is completed.
///
/// At present, this only supports making one request per connection.
impl<S: Reader + Writer = super::NetworkStream> RequestWriter<S> {
    /// Create a `RequestWriter` writing to the specified location
    pub fn new(method: Method, url: Url) -> IoResult<RequestWriter<S>> {
        RequestWriter::new_request(method, url, false, true)
    }

    pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult<RequestWriter<S>> {
        let host = match url.port {
            None => Host {
                name: url.host.clone(),
                port: None,
            },
            Some(ref p) => Host {
                name: url.host.clone(),
                port: Some(from_str(p.as_slice()).expect("You didn’t aught to give a bad port!")),
            },
        };

        let remote_addr = try!(url_to_socket_addr(&url));
        info!("using ip address {} for {}", remote_addr.to_str(), url.host.as_slice());

        fn url_to_socket_addr(url: &Url) -> IoResult<SocketAddr> {
            // Just grab the first IPv4 address
            let addrs = try!(get_host_addresses(url.host.as_slice()));
            let addr = addrs.move_iter().find(|&a| {
                match a {
                    Ipv4Addr(..) => true,
                    _ => false
                }
            });

            // TODO: Error handling
            let addr = addr.unwrap();

            // Default to 80, using the port specified or 443 if the protocol is HTTPS.
            let port = match url.port {
                Some(ref p) => from_str(p.as_slice()).expect("You didn’t aught to give a bad port!"),
                // FIXME: case insensitivity?
                None => if url.scheme.as_slice() == "https" { 443 } else { 80 },
            };

            Ok(SocketAddr {
                ip: addr,
                port: port
            })
        }

        let mut request = RequestWriter {
            stream: None,
            headers_written: false,
            remote_addr: Some(remote_addr),
            headers: box HeaderCollection::new(),
            method: method,
            url: url,
            use_ssl: use_ssl,
        };

        if auto_detect_ssl {
            // FIXME: case insensitivity?
            request.use_ssl = request.url.scheme.as_slice() == "https";
        }

        request.headers.host = Some(host);
        Ok(request)
    }
}

impl<S: Connecter + Reader + Writer = super::NetworkStream> RequestWriter<S> {

    /// Connect to the remote host if not already connected.
    pub fn try_connect(&mut self) -> IoResult<()> {
        if self.stream.is_none() {
            self.connect()
        } else {
            Ok(())
        }
    }

    /// Connect to the remote host; fails if already connected.
    /// Returns ``true`` upon success and ``false`` upon failure (also use conditions).
    pub fn connect(&mut self) -> IoResult<()> {
        if !self.stream.is_none() {
            fail!("I don't think you meant to call connect() twice, you know.");
        }

        self.stream = match self.remote_addr {
            Some(addr) => {
                let stream = try!(Connecter::connect(addr, self.url.host.as_slice(), self.use_ssl));
                Some(BufferedStream::new(stream))
            },
            None => fail!("connect() called before remote_addr was set"),
        };
        Ok(())
    }

    /// Write the Request-Line and headers of the response, if we have not already done so.
    pub fn try_write_headers(&mut self) -> IoResult<()> {
        if !self.headers_written {
            self.write_headers()
        } else {
            Ok(())
        }
    }

    /// Write the Status-Line and headers of the response, in preparation for writing the body.
    ///
    /// If the headers have already been written, this will fail. See also `try_write_headers`.
    pub fn write_headers(&mut self) -> IoResult<()> {
        // This marks the beginning of the response (RFC2616 §5)
        if self.headers_written {
            fail!("RequestWriter.write_headers() called, but headers already written");
        }
        if self.stream.is_none() {
            try!(self.connect());
        }

        // Write the Request-Line (RFC2616 §5.1)
        // TODO: get to the point where we can say HTTP/1.1 with good conscience
        try!(write!(self.stream.get_mut_ref() as &mut Writer,
            "{} {}{}{} HTTP/1.0\r\n",
            self.method.to_str(),
            if self.url.path.len()  > 0 { self.url.path.as_slice() } else { "/" },
            if self.url.query.len() > 0 { "?" } else { "" },
            url::query_to_str(&self.url.query)));

        try!(self.headers.write_all(self.stream.get_mut_ref()));
        self.headers_written = true;
        Ok(())
    }

    /**
     * Send the request and construct a `ResponseReader` out of it.
     *
     * If the request sending fails in any way, a condition will be raised; if handled, the original
     * request will be returned as an `Err`.
     */
    pub fn read_response(mut self) -> Result<ResponseReader<S>, (RequestWriter<S>, IoError)> {
        match self.try_write_headers() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.flush() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.stream.take() {
            Some(stream) => ResponseReader::construct(stream, self),
            None => unreachable!(), // TODO: is it genuinely unreachable?
        }
    }
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl<S: Reader + Writer + Connecter = super::NetworkStream> Writer for RequestWriter<S> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        if !self.headers_written {
            try!(self.write_headers());
        }
        // TODO: decide whether using get_mut_ref() is sound
        // (it will cause failure if None)
        self.stream.get_mut_ref().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        // TODO: ditto
        self.stream.get_mut_ref().flush()
    }
}
<commit_msg>The port field of Url is now a number, not a String<commit_after>/*!

Things for the construction and sending of HTTP requests.

If you want to make a request, `RequestWriter::new` is where you start, and
`RequestWriter.read_response` is where you will send the request and read the response.

```rust
use http::client::RequestWriter;
use http::method::Get;

fn main() {
    let request = RequestWriter::new(Get, from_str("http://example.com/").unwrap());
    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_request, error)) => fail!(":-( {}", error),
    };
    // Now you have a `ResponseReader`; see http::client::response for docs on that.
}
```

If you wish to send a request body (e.g. POST requests), I'm sorry to have to tell you that there is
not *good* support for this yet. However, it can be done; here is an example:

```rust
let data: &[u8];
let mut request: RequestWriter;

request.headers.content_length = Some(data.len());
request.write(data);
let response = match request.read_response() {
    Ok(response) => response,
    Err((_request, error)) => fail!(":-( {}", error),
};
```

*/

use url;
use url::Url;
use method::Method;
use std::io::{IoError, IoResult};
use std::io::net::get_host_addresses;
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use buffer::BufferedStream;
use headers::request::HeaderCollection;
use headers::host::Host;
use connecter::Connecter;

use client::response::ResponseReader;

/*impl ResponseReader {
    {
        let mut buf = [0u8, ..2000];
        match stream.read(buf) {
            None => fail!("Read error :-("),  // conditions for error interception, again
            Some(bytes_read) => {
                println!(str::from_bytes(buf.slice_to(bytes_read)));
            }
        }

        match response {
            Some(response) => Ok(response),
            None => Err(self),
        }
    }
}*/

pub struct RequestWriter<S = super::NetworkStream> {
    // The place to write to (typically a network stream, which is
    // io::net::tcp::TcpStream or an SSL wrapper around that)
    stream: Option<BufferedStream<S>>,
    headers_written: bool,

    /// The originating IP address of the request.
    pub remote_addr: Option<SocketAddr>,

    /// The host name and IP address that the request was sent to; this must always be specified for
    /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host
    /// header was not defined, and so this field will probably be None in such cases.
    //host: Host,  // Now headers.host

    /// The headers sent with the request.
    pub headers: Box<HeaderCollection>,

    /// The HTTP method for the request.
    pub method: Method,

    /// The URL being requested.
    pub url: Url,

    /// Should we use SSL?
    use_ssl: bool,
}

/// Low-level HTTP request writing support
///
/// Moderately hacky, and due to current limitations in the TcpStream arrangement reading cannot
/// take place until writing is completed.
///
/// At present, this only supports making one request per connection.
impl<S: Reader + Writer = super::NetworkStream> RequestWriter<S> {
    /// Create a `RequestWriter` writing to the specified location
    pub fn new(method: Method, url: Url) -> IoResult<RequestWriter<S>> {
        RequestWriter::new_request(method, url, false, true)
    }

    pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult<RequestWriter<S>> {
        let host = match url.port {
            None => Host {
                name: url.host.clone(),
                port: None,
            },
            Some(p) => Host {
                name: url.host.clone(),
                port: Some(p),
            },
        };

        let remote_addr = try!(url_to_socket_addr(&url));
        info!("using ip address {} for {}", remote_addr.to_str(), url.host.as_slice());

        fn url_to_socket_addr(url: &Url) -> IoResult<SocketAddr> {
            // Just grab the first IPv4 address
            let addrs = try!(get_host_addresses(url.host.as_slice()));
            let addr = addrs.move_iter().find(|&a| {
                match a {
                    Ipv4Addr(..) => true,
                    _ => false
                }
            });

            // TODO: Error handling
            let addr = addr.unwrap();

            // Default to 80, using the port specified or 443 if the protocol is HTTPS.
            let port = match url.port {
                Some(p) => p,
                // FIXME: case insensitivity?
                None => if url.scheme.as_slice() == "https" { 443 } else { 80 },
            };

            Ok(SocketAddr {
                ip: addr,
                port: port
            })
        }

        let mut request = RequestWriter {
            stream: None,
            headers_written: false,
            remote_addr: Some(remote_addr),
            headers: box HeaderCollection::new(),
            method: method,
            url: url,
            use_ssl: use_ssl,
        };

        if auto_detect_ssl {
            // FIXME: case insensitivity?
            request.use_ssl = request.url.scheme.as_slice() == "https";
        }

        request.headers.host = Some(host);
        Ok(request)
    }
}

impl<S: Connecter + Reader + Writer = super::NetworkStream> RequestWriter<S> {

    /// Connect to the remote host if not already connected.
    pub fn try_connect(&mut self) -> IoResult<()> {
        if self.stream.is_none() {
            self.connect()
        } else {
            Ok(())
        }
    }

    /// Connect to the remote host; fails if already connected.
    /// Returns ``true`` upon success and ``false`` upon failure (also use conditions).
    pub fn connect(&mut self) -> IoResult<()> {
        if !self.stream.is_none() {
            fail!("I don't think you meant to call connect() twice, you know.");
        }

        self.stream = match self.remote_addr {
            Some(addr) => {
                let stream = try!(Connecter::connect(addr, self.url.host.as_slice(), self.use_ssl));
                Some(BufferedStream::new(stream))
            },
            None => fail!("connect() called before remote_addr was set"),
        };
        Ok(())
    }

    /// Write the Request-Line and headers of the response, if we have not already done so.
    pub fn try_write_headers(&mut self) -> IoResult<()> {
        if !self.headers_written {
            self.write_headers()
        } else {
            Ok(())
        }
    }

    /// Write the Status-Line and headers of the response, in preparation for writing the body.
    ///
    /// If the headers have already been written, this will fail. See also `try_write_headers`.
    pub fn write_headers(&mut self) -> IoResult<()> {
        // This marks the beginning of the response (RFC2616 §5)
        if self.headers_written {
            fail!("RequestWriter.write_headers() called, but headers already written");
        }
        if self.stream.is_none() {
            try!(self.connect());
        }

        // Write the Request-Line (RFC2616 §5.1)
        // TODO: get to the point where we can say HTTP/1.1 with good conscience
        try!(write!(self.stream.get_mut_ref() as &mut Writer,
            "{} {}{}{} HTTP/1.0\r\n",
            self.method.to_str(),
            if self.url.path.len()  > 0 { self.url.path.as_slice() } else { "/" },
            if self.url.query.len() > 0 { "?" } else { "" },
            url::query_to_str(&self.url.query)));

        try!(self.headers.write_all(self.stream.get_mut_ref()));
        self.headers_written = true;
        Ok(())
    }

    /**
     * Send the request and construct a `ResponseReader` out of it.
     *
     * If the request sending fails in any way, a condition will be raised; if handled, the original
     * request will be returned as an `Err`.
     */
    pub fn read_response(mut self) -> Result<ResponseReader<S>, (RequestWriter<S>, IoError)> {
        match self.try_write_headers() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.flush() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.stream.take() {
            Some(stream) => ResponseReader::construct(stream, self),
            None => unreachable!(), // TODO: is it genuinely unreachable?
        }
    }
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl<S: Reader + Writer + Connecter = super::NetworkStream> Writer for RequestWriter<S> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        if !self.headers_written {
            try!(self.write_headers());
        }
        // TODO: decide whether using get_mut_ref() is sound
        // (it will cause failure if None)
        self.stream.get_mut_ref().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        // TODO: ditto
        self.stream.get_mut_ref().flush()
    }
}
<|endoftext|>


<commit_before>use crate::index::diff::Error;
use crate::{Change, CrateVersion};
use bstr::BStr;
use git_repository as git;
use std::collections::BTreeSet;
use std::ops::Range;

#[derive(Default)]
pub(crate) struct Delegate {
    changes: Vec<Change>,
    delete_version_ids: BTreeSet<u64>,
    err: Option<Error>,
}

impl Delegate {
    pub fn handle(
        &mut self,
        change: git::object::tree::diff::Change<'_, '_, '_>,
    ) -> Result<git::object::tree::diff::Action, Error> {
        use git::bstr::ByteSlice;
        use git::object::tree::diff::change::Event::*;
        use git::objs::tree::EntryMode::*;
        fn entry_data(
            entry: git::objs::tree::EntryMode,
            id: git::Id<'_>,
        ) -> Result<Option<git::Object<'_>>, Error> {
            matches!(entry, Blob | BlobExecutable)
                .then(|| id.object())
                .transpose()
                .map_err(Into::into)
        }
        if change.location.contains(&b'.') {
            return Ok(Default::default());
        }
        match change.event {
            Addition { entry_mode, id } => {
                if let Some(obj) = entry_data(entry_mode, id)? {
                    for line in (&obj.data).lines() {
                        let version = version_from_json_line(line, change.location)?;
                        self.changes.push(if version.yanked {
                            Change::Yanked(version)
                        } else {
                            Change::Added(version)
                        });
                    }
                }
            }
            Deletion { entry_mode, .. } => {
                if entry_mode.is_no_tree() {
                    self.changes.push(Change::Deleted {
                        name: change.location.to_string(),
                    });
                }
            }
            Modification { .. } => {
                if let Some(diff) = change.event.diff().transpose()? {
                    let location = change.location;

                    let input = diff.line_tokens();
                    let mut err = None;
                    git::diff::blob::diff(
                        diff.algo,
                        &input,
                        |before: Range<u32>, after: Range<u32>| {
                            if err.is_some() {
                                return;
                            }
                            let mut lines_before = input.before
                                [before.start as usize..before.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut lines_after = input.after
                                [after.start as usize..after.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            loop {
                                match (lines_before.peek().is_some(), lines_after.peek().is_some())
                                {
                                    (true, false) => {
                                        for removed in lines_before {
                                            match version_from_json_line(removed, location) {
                                                Ok(version) => {
                                                    self.delete_version_ids.insert(version.id());
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                    (false, true) => {
                                        for inserted in lines_after {
                                            match version_from_json_line(inserted, location) {
                                                Ok(version) => {
                                                    self.changes.push(if version.yanked {
                                                        Change::Yanked(version)
                                                    } else {
                                                        Change::Added(version)
                                                    });
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                    (true, true) => {
                                        for (removed, inserted) in
                                            lines_before.by_ref().zip(lines_after.by_ref())
                                        {
                                            match version_from_json_line(inserted, location)
                                                .and_then(|inserted| {
                                                    version_from_json_line(removed, location)
                                                        .map(|removed| (removed, inserted))
                                                }) {
                                                Ok((removed, inserted)) => {
                                                    if removed.yanked != inserted.yanked {
                                                        self.changes.push(if inserted.yanked {
                                                            Change::Yanked(inserted)
                                                        } else {
                                                            Change::Added(inserted)
                                                        });
                                                    }
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    (false, false) => break,
                                }
                            }
                        },
                    );
                    if let Some(err) = err {
                        return Err(err.into());
                    }
                }
            }
        }
        Ok(Default::default())
    }

    pub fn into_result(mut self) -> Result<Vec<Change>, Error> {
        match self.err {
            Some(err) => Err(err),
            None => {
                if !self.delete_version_ids.is_empty() {
                    let deleted_version_ids = &self.delete_version_ids;
                    self.changes.retain(|change| match change {
                        Change::Added(v) | Change::Yanked(v) => {
                            !deleted_version_ids.contains(&v.id())
                        }
                        Change::Deleted { .. } => true,
                    })
                }
                Ok(self.changes)
            }
        }
    }
}

fn version_from_json_line(line: &[u8], file_name: &BStr) -> Result<CrateVersion, Error> {
    serde_json::from_slice(line).map_err(|err| Error::VersionDecode {
        source: err,
        file_name: file_name.into(),
        line: line.into(),
    })
}
<commit_msg>refactor (#26)<commit_after>use crate::index::diff::Error;
use crate::{Change, CrateVersion};
use bstr::BStr;
use git_repository as git;
use std::collections::BTreeSet;
use std::ops::Range;

#[derive(Default)]
pub(crate) struct Delegate {
    changes: Vec<Change>,
    delete_version_ids: BTreeSet<u64>,
    err: Option<Error>,
}

impl Delegate {
    pub fn handle(
        &mut self,
        change: git::object::tree::diff::Change<'_, '_, '_>,
    ) -> Result<git::object::tree::diff::Action, Error> {
        use git::bstr::ByteSlice;
        use git::object::tree::diff::change::Event::*;
        use git::objs::tree::EntryMode::*;
        fn entry_data(
            entry: git::objs::tree::EntryMode,
            id: git::Id<'_>,
        ) -> Result<Option<git::Object<'_>>, Error> {
            matches!(entry, Blob | BlobExecutable)
                .then(|| id.object())
                .transpose()
                .map_err(Into::into)
        }
        if change.location.contains(&b'.') {
            return Ok(Default::default());
        }
        match change.event {
            Addition { entry_mode, id } => {
                if let Some(obj) = entry_data(entry_mode, id)? {
                    for line in (&obj.data).lines() {
                        let version = version_from_json_line(line, change.location)?;
                        self.changes.push(if version.yanked {
                            Change::Yanked(version)
                        } else {
                            Change::Added(version)
                        });
                    }
                }
            }
            Deletion { entry_mode, .. } => {
                if entry_mode.is_no_tree() {
                    self.changes.push(Change::Deleted {
                        name: change.location.to_string(),
                    });
                }
            }
            Modification { .. } => {
                if let Some(diff) = change.event.diff().transpose()? {
                    let location = change.location;

                    let input = diff.line_tokens();
                    let mut err = None;
                    git::diff::blob::diff(
                        diff.algo,
                        &input,
                        |before: Range<u32>, after: Range<u32>| {
                            if err.is_some() {
                                return;
                            }
                            let mut lines_before = input.before
                                [before.start as usize..before.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut lines_after = input.after
                                [after.start as usize..after.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut remember = |version: CrateVersion| {
                                self.changes.push(if version.yanked {
                                    Change::Yanked(version)
                                } else {
                                    Change::Added(version)
                                });
                            };
                            loop {
                                match (lines_before.peek().is_some(), lines_after.peek().is_some())
                                {
                                    (true, false) => {
                                        for removed in lines_before {
                                            match version_from_json_line(removed, location) {
                                                Ok(version) => {
                                                    self.delete_version_ids.insert(version.id());
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                    (false, true) => {
                                        for inserted in lines_after {
                                            match version_from_json_line(inserted, location) {
                                                Ok(version) => remember(version),
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                    (true, true) => {
                                        for (removed, inserted) in
                                            lines_before.by_ref().zip(lines_after.by_ref())
                                        {
                                            match version_from_json_line(inserted, location)
                                                .and_then(|inserted| {
                                                    version_from_json_line(removed, location)
                                                        .map(|removed| (removed, inserted))
                                                }) {
                                                Ok((removed_version, inserted_version)) => {
                                                    if removed_version.yanked
                                                        != inserted_version.yanked
                                                    {
                                                        remember(inserted_version);
                                                    }
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    (false, false) => break,
                                }
                            }
                        },
                    );
                    if let Some(err) = err {
                        return Err(err.into());
                    }
                }
            }
        }
        Ok(Default::default())
    }

    pub fn into_result(mut self) -> Result<Vec<Change>, Error> {
        match self.err {
            Some(err) => Err(err),
            None => {
                if !self.delete_version_ids.is_empty() {
                    let deleted_version_ids = &self.delete_version_ids;
                    self.changes.retain(|change| match change {
                        Change::Added(v) | Change::Yanked(v) => {
                            !deleted_version_ids.contains(&v.id())
                        }
                        Change::Deleted { .. } => true,
                    })
                }
                Ok(self.changes)
            }
        }
    }
}

fn version_from_json_line(line: &[u8], file_name: &BStr) -> Result<CrateVersion, Error> {
    serde_json::from_slice(line).map_err(|err| Error::VersionDecode {
        source: err,
        file_name: file_name.into(),
        line: line.into(),
    })
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::collections::HashMap;
use parking_lot::RwLock;
use elements::Module;
use interpreter::Error;
use interpreter::env::{self, env_module};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface};

/// Program instance. Program is a set of instantiated modules.
pub struct ProgramInstance {
	/// Shared data reference.
	essence: Arc<ProgramInstanceEssence>,
}

/// Program instance essence.
pub struct ProgramInstanceEssence {
	/// Loaded modules.
	modules: RwLock<HashMap<String, Arc<ModuleInstanceInterface>>>,
}

impl ProgramInstance {
	/// Create new program instance.
	pub fn new() -> Result<Self, Error> {
		ProgramInstance::with_env_params(env::EnvParams::default())
	}

	/// Create new program instance with custom env module params (mostly memory)
	pub fn with_env_params(params: env::EnvParams) -> Result<Self, Error> {
		Ok(ProgramInstance {
			essence: Arc::new(ProgramInstanceEssence::with_env_params(params)?),
		})
	}

	/// Instantiate module with validation.
	pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstance>, Error> {
		let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?;
		module_instance.instantiate(true, externals)?;

		let module_instance = Arc::new(module_instance);
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		module_instance.run_start_function()?;
		Ok(module_instance)
		/*let module_instance = Arc::new(ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?);
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		// replace existing module with the same name with new one
		match module_instance.instantiate(true, externals) {
			Ok(()) => Ok(module_instance),
			Err(err) => {
				self.essence.modules.write().remove(name.into());
				Err(err)
			}
		}*/
	}

	/// Insert instantiated module.
	pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstance>) -> Result<Arc<ModuleInstance>, Error> {
		// replace existing module with the same name with new one
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		Ok(module_instance)
	}

	/// Get one of the modules by name
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
		self.essence.module(name)
	}
}

impl ProgramInstanceEssence {
	/// Create new program essence.
	pub fn new() -> Result<Self, Error> {
		ProgramInstanceEssence::with_env_params(env::EnvParams::default())
	}

	pub fn with_env_params(env_params: env::EnvParams) -> Result<Self, Error> {
		let mut modules = HashMap::new();
		let env_module: Arc<ModuleInstanceInterface> = Arc::new(env_module(env_params)?);
		modules.insert("env".into(), env_module);
		Ok(ProgramInstanceEssence {
			modules: RwLock::new(modules),
		})
	}

	/// Get module reference.
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
		self.modules.read().get(name).cloned()
	}
}
<commit_msg>remove comment<commit_after>use std::sync::Arc;
use std::collections::HashMap;
use parking_lot::RwLock;
use elements::Module;
use interpreter::Error;
use interpreter::env::{self, env_module};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface};

/// Program instance. Program is a set of instantiated modules.
pub struct ProgramInstance {
	/// Shared data reference.
	essence: Arc<ProgramInstanceEssence>,
}

/// Program instance essence.
pub struct ProgramInstanceEssence {
	/// Loaded modules.
	modules: RwLock<HashMap<String, Arc<ModuleInstanceInterface>>>,
}

impl ProgramInstance {
	/// Create new program instance.
	pub fn new() -> Result<Self, Error> {
		ProgramInstance::with_env_params(env::EnvParams::default())
	}

	/// Create new program instance with custom env module params (mostly memory)
	pub fn with_env_params(params: env::EnvParams) -> Result<Self, Error> {
		Ok(ProgramInstance {
			essence: Arc::new(ProgramInstanceEssence::with_env_params(params)?),
		})
	}

	/// Instantiate module with validation.
	pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstance>, Error> {
		let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?;
		module_instance.instantiate(true, externals)?;

		let module_instance = Arc::new(module_instance);
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		module_instance.run_start_function()?;
		Ok(module_instance)
	}

	/// Insert instantiated module.
	pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstance>) -> Result<Arc<ModuleInstance>, Error> {
		// replace existing module with the same name with new one
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		Ok(module_instance)
	}

	/// Get one of the modules by name
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
		self.essence.module(name)
	}
}

impl ProgramInstanceEssence {
	/// Create new program essence.
	pub fn new() -> Result<Self, Error> {
		ProgramInstanceEssence::with_env_params(env::EnvParams::default())
	}

	pub fn with_env_params(env_params: env::EnvParams) -> Result<Self, Error> {
		let mut modules = HashMap::new();
		let env_module: Arc<ModuleInstanceInterface> = Arc::new(env_module(env_params)?);
		modules.insert("env".into(), env_module);
		Ok(ProgramInstanceEssence {
			modules: RwLock::new(modules),
		})
	}

	/// Get module reference.
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
		self.modules.read().get(name).cloned()
	}
}
<|endoftext|>


<commit_before>use syntax::ast::*;
use syntax::codemap::{Span, Spanned};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::ptr::P;

pub fn expand(cx: &mut ExtCtxt, span: Span, item: &Annotatable,
                          push: &mut FnMut(Annotatable), trait_name: &str) {
    let trait_name = trait_name.to_string();
    let method_name = trait_name.to_lowercase();
    let method_ident = cx.ident_of(&method_name);
    // Get the that is wrapped by the newtype and do some checks
    let result = match *item {
        Annotatable::Item(ref x) => {
            match x.node {
                ItemKind::Struct(VariantData::Tuple(ref fields, _), _) => {
                    Some((x.ident, cx.ty_ident(span, x.ident), tuple_content(cx, span, x, fields, method_name)))
                },

                ItemKind::Struct(VariantData::Struct(ref fields, _), _) => {
                    Some((x.ident, cx.ty_ident(span, x.ident), struct_content(cx, span, x, fields, method_name)))
                },

                ItemKind::Enum(ref definition, _) => {
                    let input_type = x.ident;
                    Some((x.ident, quote_ty!(cx, Result<$input_type, &'static str>), enum_content(cx, span, x, definition, method_name)))
                },

                _ => None,
            }
        },
        _ => None,
    };

    let (input_type, output_type, block) = match result {
        Some(x) => x,
        _ => {
            cx.span_fatal(span, &format!("only structs can use `derive({})`", trait_name))
        },
    };

    let trait_path = cx.path_global(span, cx.std_path(&["ops", &trait_name]));

    let code = quote_item!(cx,
        impl $trait_path for $input_type {
            type Output = $output_type;
            fn $method_ident(self, rhs: $input_type) -> $output_type {
                $block
            }
        }
    ).unwrap();

    push(Annotatable::Item(code));

}

fn tuple_content(cx: &mut ExtCtxt, span: Span, item: &P<Item>, fields: &Vec<StructField>, method_name: String) -> P<Expr> {
    let type_name = item.ident;
    let mut exprs: Vec<P<Expr>>= vec![];

    for i in 0..fields.len() {
        let i = &i.to_string();
        exprs.push(cx.parse_expr(format!("self.{}.{}(rhs.{})", i, method_name, i)));
    }

    cx.expr_call_ident(span, type_name, exprs)
}

fn struct_content(cx: &mut ExtCtxt, span: Span, item: &P<Item>, fields: &Vec<StructField>, method_name: String) -> P<Expr> {
    let type_name = item.ident;
    let mut filled_fields = vec![];

    for field in fields {
        let (field_id, field_name) = match field.ident {
            Some(x) => (x, x.name.as_str()),
            _ => unreachable!(),
        };
        filled_fields.push(cx.field_imm(span, field_id,
                                        cx.parse_expr(format!("self.{}.{}(rhs.{})", field_name, method_name, field_name))));
    }

    cx.expr_struct_ident(span, type_name, filled_fields)
}

fn enum_content(cx: &mut ExtCtxt, span: Span, item: &P<Item>, enum_def: &EnumDef, method_name: String) -> P<Expr> {
    let mut matches: Vec<Arm> = vec![];
    let enum_ident = item.ident;

    // Add paterns for the same enum types for self and rhs
    for variant in &enum_def.variants {
        let ident = variant.node.name;
        let type_path = quote_path!(cx, $enum_ident::$ident);

        match variant.node.data {
            VariantData::Tuple(ref fields, _)  => {
                // The patern that is outputted should look like this:
                // (TypePath(left_vars), TypePath(right_vars) => Ok(TypePath(exprs))
                let mut left_vars = vec![];
                let mut right_vars = vec![];
                let mut exprs = vec![];

                for i in 0..fields.len() {
                    left_vars.push(cx.pat_ident(span, cx.ident_of(&format!("__l_{}", i))));
                    right_vars.push(cx.pat_ident(span, cx.ident_of(&format!("__r_{}", i))));
                    exprs.push(cx.parse_expr(format!("__l_{}.{}(__r_{})", i, method_name, i)));
                }

                let left_patern = cx.pat_tuple_struct(span, type_path.clone(), left_vars);
                let right_patern = cx.pat_tuple_struct(span, type_path.clone(), right_vars);
                let new_tuple = cx.expr_call(span, cx.expr_path(type_path.clone()), exprs);
                matches.push(quote_arm!(cx, ($left_patern, $right_patern) => Ok($new_tuple),));
            },
            VariantData::Struct(ref fields, _) => {
                // The patern that is outputted should look like this:
                // (TypePath{left_vars}, TypePath{right_vars} => Ok(TypePath{filled_fields})
                let mut left_vars = vec![];
                let mut right_vars = vec![];
                let mut filled_fields = vec![];

                for field in fields {
                    let (field_id, field_name) = match field.ident {
                        Some(x) => (x, x.name.as_str()),
                        _ => unreachable!(),
                    };
                    filled_fields.push(cx.field_imm(
                            span, field_id,
                            cx.parse_expr(format!("__l_{}.{}(__r_{})", field_name, method_name, field_name))));
                    left_vars.push(fieldpat_str(cx, span, field_id, &format!("__l_{}", field_name)));
                    right_vars.push(fieldpat_str(cx, span, field_id, &format!("__r_{}", field_name)));
                }

                let left_patern = cx.pat_struct(span, type_path.clone(), left_vars);
                let right_patern = cx.pat_struct(span, type_path.clone(), right_vars);
                let new_tuple = cx.expr_struct(span, type_path.clone(), filled_fields);
                matches.push(quote_arm!(cx, ($left_patern, $right_patern) => Ok($new_tuple),));
            }
            VariantData::Unit(_) =>  {
                matches.push(quote_arm!(cx, ($type_path, $type_path) => Err("Cannot add unit types together"), ));
            }

        }
    }

    // Other combinations should result in an error
    matches.push(quote_arm!(cx, _ => Err("Trying to add mismatched enum types"), ));
    quote_expr!(cx, match (self, rhs) { $matches })
}

fn fieldpat_str(cx: &mut ExtCtxt, span: Span, field_id: Ident, pat: &str) -> Spanned<FieldPat> {
    Spanned{
        span: span,
        node: FieldPat{
            ident: field_id,
            pat: cx.pat_ident(span, cx.ident_of(pat)),
            is_shorthand: false,
        },
    }
}
<commit_msg>Macros 1.1: Remove legacy add_like the new one is fully functional<commit_after><|endoftext|>


<commit_before>use std::fmt::Debug;

use tokio::time;

use crate::radians::{self, Radians};
use crate::ipc_protocol::{ProtocolClient, RotationDirection};
use crate::renderer_server::TurtleId;
use crate::{Turtle, Color, Point, Speed};

/// Any distance value (positive or negative)
pub type Distance = f64;

/// An angle value without a unit
///
/// The unit with which this angle will be interpreted depends on whether the Turtle is set to use
/// degrees or radians. See the [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
/// [`use_radians()`](struct.Turtle.html#method.use_radians) methods for more information.
pub type Angle = f64;

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum AngleUnit {
    Degrees,
    Radians,
}

impl AngleUnit {
    fn to_radians(self, angle: Angle) -> Radians {
        match self {
            AngleUnit::Degrees => Radians::from_degrees_value(angle),
            AngleUnit::Radians => Radians::from_radians_value(angle),
        }
    }

    fn to_angle(self, angle: Radians) -> Angle {
        match self {
            AngleUnit::Degrees => angle.to_degrees(),
            AngleUnit::Radians => angle.to_radians(),
        }
    }
}

pub struct AsyncTurtle {
    client: ProtocolClient,
    id: TurtleId,
    angle_unit: AngleUnit,
}

impl From<Turtle> for AsyncTurtle {
    fn from(turtle: Turtle) -> Self {
        turtle.into_async()
    }
}

impl AsyncTurtle {
    pub async fn new() -> Self {
        // This needs to be called as close to the start of the program as possible. We call it
        // here since Turtle::new() or AsyncTurtle::new() are commonly called at the beginning
        // of many programs that use the turtle crate.
        crate::start();

        let client = ProtocolClient::new().await
            .expect("unable to create renderer client");
        Self::with_client(client).await
    }

    /// Creates a new turtle using the given client
    pub(crate) async fn with_client(client: ProtocolClient) -> Self {
        let id = client.create_turtle().await;
        let angle_unit = AngleUnit::Degrees;

        Self {client, id, angle_unit}
    }

    pub async fn forward(&mut self, distance: Distance) {
        self.client.move_forward(self.id, distance).await
    }

    pub async fn backward(&mut self, distance: Distance) {
        // Moving backwards is essentially moving forwards with a negative distance
        self.client.move_forward(self.id, -distance).await
    }

    pub async fn right(&mut self, angle: Angle) {
        let angle = self.angle_unit.to_radians(angle);
        self.client.rotate_in_place(self.id, angle, RotationDirection::Clockwise).await
    }

    pub async fn left(&mut self, angle: Angle) {
        let angle = self.angle_unit.to_radians(angle);
        self.client.rotate_in_place(self.id, angle, RotationDirection::Counterclockwise).await
    }

    pub async fn wait(&mut self, secs: f64) {
        // This method *needs* exclusive access (`&mut self`) since otherwise another thread could
        // potentially call another method on the same turtle while it is supposed to be waiting.

        if !secs.is_normal() {
            return;
        }

        time::delay_for(time::Duration::from_millis((secs * 1000.0) as u64)).await
    }

    pub fn into_sync(self) -> Turtle {
        self.into()
    }

    pub async fn speed(&self) -> Speed {
        self.client.turtle_speed(self.id).await
    }

    pub fn set_speed<S: Into<Speed>>(&mut self, speed: S) {
        self.client.turtle_set_speed(self.id, speed.into())
    }

    pub async fn position(&self) -> Point {
        self.client.turtle_position(self.id).await
    }

    pub async fn go_to<P: Into<Point>>(&mut self, position: P) {
        self.client.move_to(self.id, position.into()).await
    }

    pub async fn set_x(&mut self, x: f64) {
        let Point {x: _, y} = self.position().await;
        self.go_to(Point {x, y}).await
    }

    pub async fn set_y(&mut self, y: f64) {
        let Point {x, y: _} = self.position().await;
        self.go_to(Point {x, y}).await
    }

    pub async fn home(&mut self) {
        self.client.move_to(self.id, Point::origin()).await;
        self.client.turtle_reset_heading(self.id);
    }

    pub async fn heading(&self) -> Angle {
        let heading = self.client.turtle_heading(self.id).await;
        self.angle_unit.to_angle(heading)
    }

    pub async fn set_heading(&mut self, angle: Angle) {
        if !angle.is_finite() {
            return;
        }

        let angle = self.angle_unit.to_radians(angle);

        let heading = self.client.turtle_heading(self.id).await;
        // Find the amount we need to turn to reach the target heading based on our current heading
        let angle = angle - heading;
        // Normalize the angle to be between -180 and 179 so that we rotate as little as possible
        // Formula from: https://stackoverflow.com/a/24234924/551904
        let angle = angle - radians::TWO_PI * ((angle + radians::PI) / radians::TWO_PI).floor();

        self.client.rotate_in_place(self.id, angle, RotationDirection::Counterclockwise).await
    }

    pub fn is_using_degrees(&self) -> bool {
        self.angle_unit == AngleUnit::Degrees
    }

    pub fn is_using_radians(&self) -> bool {
        self.angle_unit == AngleUnit::Radians
    }

    pub fn use_degrees(&mut self) {
        self.angle_unit = AngleUnit::Degrees;
    }

    pub fn use_radians(&mut self) {
        self.angle_unit = AngleUnit::Radians;
    }

    pub async fn is_pen_down(&self) -> bool {
        self.client.turtle_pen_is_enabled(self.id).await
    }

    pub fn pen_down(&mut self) {
        self.client.turtle_pen_set_is_enabled(self.id, true)
    }

    pub fn pen_up(&mut self) {
        self.client.turtle_pen_set_is_enabled(self.id, false)
    }

    pub async fn pen_size(&self) -> f64 {
        self.client.turtle_pen_thickness(self.id).await
    }

    pub fn set_pen_size(&mut self, thickness: f64) {
        assert!(
            thickness >= 0.0 && thickness.is_finite(),
            "Invalid thickness: {}. The pen thickness must be greater than or equal to zero",
            thickness
        );

        self.client.turtle_pen_set_thickness(self.id, thickness)
    }

    pub async fn pen_color(&self) -> Color {
        self.client.turtle_pen_color(self.id).await
    }

    pub fn set_pen_color<C: Into<Color> + Copy + Debug>(&mut self, color: C) {
        let pen_color = color.into();
        assert!(
            pen_color.is_valid(),
            "Invalid color: {:?}. See the color module documentation for more information.",
            color
        );
        self.client.turtle_pen_set_color(self.id, pen_color)
    }

    pub async fn fill_color(&self) -> Color {
        self.client.turtle_fill_color(self.id).await
    }

    pub fn set_fill_color<C: Into<Color> + Copy + Debug>(&mut self, color: C) {
        let fill_color = color.into();
        assert!(
            fill_color.is_valid(),
            "Invalid color: {:?}. See the color module documentation for more information.",
            color
        );
        self.client.turtle_set_fill_color(self.id, fill_color)
    }

    pub async fn is_filling(&self) -> bool {
        self.client.turtle_is_filling(self.id).await
    }

    pub fn begin_fill(&mut self) {
        self.client.begin_fill(self.id)
    }

    pub fn end_fill(&mut self) {
        self.client.end_fill(self.id)
    }

    pub async fn is_visible(&self) -> bool {
        self.client.turtle_is_visible(self.id).await
    }

    pub fn hide(&mut self) {
        self.client.turtle_set_is_visible(self.id, false)
    }

    pub fn show(&mut self) {
        self.client.turtle_set_is_visible(self.id, true)
    }

    pub fn reset(&mut self) {
        self.clear();
        self.client.reset_turtle(self.id);
    }

    pub fn clear(&mut self) {
        self.client.clear_turtle(self.id)
    }

    pub async fn turn_towards<P: Into<Point>>(&mut self, target: P) {
        let target: Point = target.into();
        if !target.is_finite() {
            return;
        }

        let position = self.position().await;

        // If the target is (approximately) on the turtle don't turn
        if (target - position).is_not_normal() {
            return;
        }

        let heading = self.client.turtle_heading(self.id).await;

        // Calculate the target angle to reach
        let angle = (target - position).atan2();
        let angle = Radians::from_radians_value(angle);
        // Calculate how much turning will be needed (angle - heading)
        // And clamp it make sure the turtle doesn't turn more than 360 degrees
        let angle = (angle - heading) % radians::TWO_PI;
        // Try to rotate as little as possible
        let angle = if angle.abs() > radians::PI {
            // Use signum to make sure the angle has the right sign
            // And the turtle turns the right way
            -angle.signum() * (radians::TWO_PI - angle.abs())
        } else {
            angle
        };

        self.client.rotate_in_place(self.id, angle, RotationDirection::Counterclockwise).await
    }

    pub async fn wait_for_click(&mut self) {
        use crate::{
            Event::MouseButton,
            event::{PressedState::Pressed, MouseButton::LeftButton},
        };

        loop {
            if let Some(MouseButton(LeftButton, Pressed)) = self.client.poll_event().await {
                break;
            }

            // Sleep for ~1 frame (at 120fps) to avoid pegging the CPU.
            self.wait(1.0 / 120.0).await;
        }
    }

    //TODO: If we move to a shared memory architecture, we wouldn't need to make
    // any request here and thus would not need this method at all. We should
    // think things through before making this method public.
    /// # Stability
    ///
    /// **Warning:** This method exists because it is currently necessary to
    /// do some work asynchronously in order to print out a useful debug
    /// representation for this type. There is no async `Debug` trait. Please
    /// only use this method for debugging. It may be removed in a future
    /// release if we find a way to implement `Debug` trait for this type.
    pub async fn debug(&self) -> impl Debug {
        self.client.debug_turtle(self.id, self.angle_unit).await
    }
}
<commit_msg>Add arc_left and arc_right methods on AsyncTurtle<commit_after>use std::fmt::Debug;

use tokio::time;

use crate::radians::{self, Radians};
use crate::ipc_protocol::{ProtocolClient, RotationDirection};
use crate::renderer_server::TurtleId;
use crate::{Turtle, Color, Point, Speed};

/// Any distance value (positive or negative)
pub type Distance = f64;

/// An angle value without a unit
///
/// The unit with which this angle will be interpreted depends on whether the Turtle is set to use
/// degrees or radians. See the [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
/// [`use_radians()`](struct.Turtle.html#method.use_radians) methods for more information.
pub type Angle = f64;

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum AngleUnit {
    Degrees,
    Radians,
}

impl AngleUnit {
    fn to_radians(self, angle: Angle) -> Radians {
        match self {
            AngleUnit::Degrees => Radians::from_degrees_value(angle),
            AngleUnit::Radians => Radians::from_radians_value(angle),
        }
    }

    fn to_angle(self, angle: Radians) -> Angle {
        match self {
            AngleUnit::Degrees => angle.to_degrees(),
            AngleUnit::Radians => angle.to_radians(),
        }
    }
}

pub struct AsyncTurtle {
    client: ProtocolClient,
    id: TurtleId,
    angle_unit: AngleUnit,
}

impl From<Turtle> for AsyncTurtle {
    fn from(turtle: Turtle) -> Self {
        turtle.into_async()
    }
}

impl AsyncTurtle {
    pub async fn new() -> Self {
        // This needs to be called as close to the start of the program as possible. We call it
        // here since Turtle::new() or AsyncTurtle::new() are commonly called at the beginning
        // of many programs that use the turtle crate.
        crate::start();

        let client = ProtocolClient::new().await
            .expect("unable to create renderer client");
        Self::with_client(client).await
    }

    /// Creates a new turtle using the given client
    pub(crate) async fn with_client(client: ProtocolClient) -> Self {
        let id = client.create_turtle().await;
        let angle_unit = AngleUnit::Degrees;

        Self {client, id, angle_unit}
    }

    pub async fn forward(&mut self, distance: Distance) {
        self.client.move_forward(self.id, distance).await
    }

    pub async fn backward(&mut self, distance: Distance) {
        // Moving backwards is essentially moving forwards with a negative distance
        self.client.move_forward(self.id, -distance).await
    }

    pub async fn right(&mut self, angle: Angle) {
        let angle = self.angle_unit.to_radians(angle);
        self.client.rotate_in_place(self.id, angle, RotationDirection::Clockwise).await
    }

    pub async fn left(&mut self, angle: Angle) {
        let angle = self.angle_unit.to_radians(angle);
        self.client.rotate_in_place(self.id, angle, RotationDirection::Counterclockwise).await
    }

    pub async fn wait(&mut self, secs: f64) {
        // This method *needs* exclusive access (`&mut self`) since otherwise another thread could
        // potentially call another method on the same turtle while it is supposed to be waiting.

        if !secs.is_normal() {
            return;
        }

        time::delay_for(time::Duration::from_millis((secs * 1000.0) as u64)).await
    }

    pub async fn arc_left(&mut self, radius: Distance, extent: Angle) {
        self.client
            .circular_arc(
                self.id,
                radius,
                self.angle_unit.to_radians(extent),
                RotationDirection::Counterclockwise,
            )
            .await
    }

    pub async fn arc_right(&mut self, radius: Distance, extent: Angle) {
        self.client
            .circular_arc(
                self.id,
                radius,
                self.angle_unit.to_radians(extent),
                RotationDirection::Clockwise,
            )
            .await
    }

    pub fn into_sync(self) -> Turtle {
        self.into()
    }

    pub async fn speed(&self) -> Speed {
        self.client.turtle_speed(self.id).await
    }

    pub fn set_speed<S: Into<Speed>>(&mut self, speed: S) {
        self.client.turtle_set_speed(self.id, speed.into())
    }

    pub async fn position(&self) -> Point {
        self.client.turtle_position(self.id).await
    }

    pub async fn go_to<P: Into<Point>>(&mut self, position: P) {
        self.client.move_to(self.id, position.into()).await
    }

    pub async fn set_x(&mut self, x: f64) {
        let Point {x: _, y} = self.position().await;
        self.go_to(Point {x, y}).await
    }

    pub async fn set_y(&mut self, y: f64) {
        let Point {x, y: _} = self.position().await;
        self.go_to(Point {x, y}).await
    }

    pub async fn home(&mut self) {
        self.client.move_to(self.id, Point::origin()).await;
        self.client.turtle_reset_heading(self.id);
    }

    pub async fn heading(&self) -> Angle {
        let heading = self.client.turtle_heading(self.id).await;
        self.angle_unit.to_angle(heading)
    }

    pub async fn set_heading(&mut self, angle: Angle) {
        if !angle.is_finite() {
            return;
        }

        let angle = self.angle_unit.to_radians(angle);

        let heading = self.client.turtle_heading(self.id).await;
        // Find the amount we need to turn to reach the target heading based on our current heading
        let angle = angle - heading;
        // Normalize the angle to be between -180 and 179 so that we rotate as little as possible
        // Formula from: https://stackoverflow.com/a/24234924/551904
        let angle = angle - radians::TWO_PI * ((angle + radians::PI) / radians::TWO_PI).floor();

        self.client.rotate_in_place(self.id, angle, RotationDirection::Counterclockwise).await
    }

    pub fn is_using_degrees(&self) -> bool {
        self.angle_unit == AngleUnit::Degrees
    }

    pub fn is_using_radians(&self) -> bool {
        self.angle_unit == AngleUnit::Radians
    }

    pub fn use_degrees(&mut self) {
        self.angle_unit = AngleUnit::Degrees;
    }

    pub fn use_radians(&mut self) {
        self.angle_unit = AngleUnit::Radians;
    }

    pub async fn is_pen_down(&self) -> bool {
        self.client.turtle_pen_is_enabled(self.id).await
    }

    pub fn pen_down(&mut self) {
        self.client.turtle_pen_set_is_enabled(self.id, true)
    }

    pub fn pen_up(&mut self) {
        self.client.turtle_pen_set_is_enabled(self.id, false)
    }

    pub async fn pen_size(&self) -> f64 {
        self.client.turtle_pen_thickness(self.id).await
    }

    pub fn set_pen_size(&mut self, thickness: f64) {
        assert!(
            thickness >= 0.0 && thickness.is_finite(),
            "Invalid thickness: {}. The pen thickness must be greater than or equal to zero",
            thickness
        );

        self.client.turtle_pen_set_thickness(self.id, thickness)
    }

    pub async fn pen_color(&self) -> Color {
        self.client.turtle_pen_color(self.id).await
    }

    pub fn set_pen_color<C: Into<Color> + Copy + Debug>(&mut self, color: C) {
        let pen_color = color.into();
        assert!(
            pen_color.is_valid(),
            "Invalid color: {:?}. See the color module documentation for more information.",
            color
        );
        self.client.turtle_pen_set_color(self.id, pen_color)
    }

    pub async fn fill_color(&self) -> Color {
        self.client.turtle_fill_color(self.id).await
    }

    pub fn set_fill_color<C: Into<Color> + Copy + Debug>(&mut self, color: C) {
        let fill_color = color.into();
        assert!(
            fill_color.is_valid(),
            "Invalid color: {:?}. See the color module documentation for more information.",
            color
        );
        self.client.turtle_set_fill_color(self.id, fill_color)
    }

    pub async fn is_filling(&self) -> bool {
        self.client.turtle_is_filling(self.id).await
    }

    pub fn begin_fill(&mut self) {
        self.client.begin_fill(self.id)
    }

    pub fn end_fill(&mut self) {
        self.client.end_fill(self.id)
    }

    pub async fn is_visible(&self) -> bool {
        self.client.turtle_is_visible(self.id).await
    }

    pub fn hide(&mut self) {
        self.client.turtle_set_is_visible(self.id, false)
    }

    pub fn show(&mut self) {
        self.client.turtle_set_is_visible(self.id, true)
    }

    pub fn reset(&mut self) {
        self.clear();
        self.client.reset_turtle(self.id);
    }

    pub fn clear(&mut self) {
        self.client.clear_turtle(self.id)
    }

    pub async fn turn_towards<P: Into<Point>>(&mut self, target: P) {
        let target: Point = target.into();
        if !target.is_finite() {
            return;
        }

        let position = self.position().await;

        // If the target is (approximately) on the turtle don't turn
        if (target - position).is_not_normal() {
            return;
        }

        let heading = self.client.turtle_heading(self.id).await;

        // Calculate the target angle to reach
        let angle = (target - position).atan2();
        let angle = Radians::from_radians_value(angle);
        // Calculate how much turning will be needed (angle - heading)
        // And clamp it make sure the turtle doesn't turn more than 360 degrees
        let angle = (angle - heading) % radians::TWO_PI;
        // Try to rotate as little as possible
        let angle = if angle.abs() > radians::PI {
            // Use signum to make sure the angle has the right sign
            // And the turtle turns the right way
            -angle.signum() * (radians::TWO_PI - angle.abs())
        } else {
            angle
        };

        self.client.rotate_in_place(self.id, angle, RotationDirection::Counterclockwise).await
    }

    pub async fn wait_for_click(&mut self) {
        use crate::{
            Event::MouseButton,
            event::{PressedState::Pressed, MouseButton::LeftButton},
        };

        loop {
            if let Some(MouseButton(LeftButton, Pressed)) = self.client.poll_event().await {
                break;
            }

            // Sleep for ~1 frame (at 120fps) to avoid pegging the CPU.
            self.wait(1.0 / 120.0).await;
        }
    }

    //TODO: If we move to a shared memory architecture, we wouldn't need to make
    // any request here and thus would not need this method at all. We should
    // think things through before making this method public.
    /// # Stability
    ///
    /// **Warning:** This method exists because it is currently necessary to
    /// do some work asynchronously in order to print out a useful debug
    /// representation for this type. There is no async `Debug` trait. Please
    /// only use this method for debugging. It may be removed in a future
    /// release if we find a way to implement `Debug` trait for this type.
    pub async fn debug(&self) -> impl Debug {
        self.client.debug_turtle(self.id, self.angle_unit).await
    }
}
<|endoftext|>


<commit_before>extern crate susurrus;
extern crate getopts;
extern crate sodiumoxide;
extern crate rustc_serialize;

use susurrus::session;
use susurrus::noisebox;
use getopts::Options;
use std::env;
use std::io;
use std::io::Read;
use std::fs::OpenOptions;
use rustc_serialize::hex::{FromHex,ToHex};
use std::string::String;

macro_rules! make_fixed {
    ($t:ty, $n:expr, $v:expr) => ({
        let mut res : [$t; $n] = [0; $n];
        assert!($v.len() == $n);
        for i in 0..$n { res[i] = $v[i] }
        res
    })
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn read_keypair<F:io::Read>(file: &mut F) -> io::Result<session::KeyPair> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-secret:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };
    let mut privkey_raw = [0u8; 64];
    try!(file.read(&mut privkey_raw));
    let privkey = match String::from_utf8_lossy(&privkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::KeyPair { pubkey: session::PubKey(make_fixed!(u8, 32, pubkey)),
                          privkey: session::PrivKey(make_fixed!(u8, 32, privkey)) })
}

fn read_pubkey<F:io::Read>(file: &mut F) -> io::Result<session::PubKey> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-public:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::PubKey(make_fixed!(u8, 32, pubkey)))
}

fn main() {
    sodiumoxide::init();

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("e", "encrypt", "sets the mode to encryption");
    opts.optflag("d", "decrypt", "sets the mode to decryption");
    opts.optflag("k", "keygen", "generate a new key pair");
    opts.optflag("p", "pubkey", "extracts public key from key pair");
    opts.optflag("a", "ascii", "ASCII armor encrypted result");
    opts.optopt("i", "input", "sets input file, defaults to stdin", "NAME");
    opts.optopt("o", "output", "set output file, defaults to stdout", "NAME");
    opts.optopt("r", "remote", "specifies file containing remote's public key", "NAME");
    opts.optopt("l", "local", "specifies file containing local key pair", "NAME");
    opts.optopt("t", "type", "specifies the box type to use (one of n, k, or x)", "TYPE");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    let encrypting = match (matches.opt_present("e"), matches.opt_present("d")) {
        (true, true) => {
            println!("Cannot encrypt and decrypt simultaneously");
            return print_usage(&program, opts);
        },
        (true, false) => true,
        (false, true) => false,
        (false, false) => if !matches.opt_present("k") && !matches.opt_present("p") {
            println!("Must specify encryption or decryption");
            return print_usage(&program, opts);
        } else { false }
    };
    let mut output = match matches.opt_str("o") {
        Some(f) => match OpenOptions::new().write(true).create(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Write>,
            Err(e) => return println!("Could not open output file {}: {}", f, e)
        },
        None => Box::new(io::stdout()) as Box<io::Write>
    };
    let mut input = match matches.opt_str("i") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Read>,
            Err(e) => return println!("Could not open input file {}: {}", f, e)
        },
        None => Box::new(io::stdin()) as Box<io::Read>
    };
    let remote = match matches.opt_str("r") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_pubkey(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse remote key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open remote key file {}: {}", f, e)
        },
        None => None
    };
    let local = match matches.opt_str("l") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_keypair(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse local key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open local key file {}: {}", f, e)
        },
        None => None
    };

    if matches.opt_present("k") {
        let kp = session::gen_keypair();
        output.write(&b"noise255-secret:"[..]).unwrap();
        output.write(&kp.pubkey.0[..].to_hex().as_bytes()).unwrap();
        output.write(&kp.privkey.0[..].to_hex().as_bytes()).unwrap();
        output.flush().unwrap();
        return
    }

    if matches.opt_present("p") {
        let local = match local {
            Some(x) => x,
            None => return println!("Key pair must be specified to extract public key")
        };
        output.write(&b"noise255-public:"[..]).unwrap();
        output.write(&local.pubkey.0[..].to_hex().as_bytes()).unwrap();
        output.flush().unwrap();
        return
    }

    let mut buf = vec![];
    input.read_to_end(&mut buf).unwrap();
    let result = if encrypting {
        let remote = match remote {
            Some(x) => x,
            None => return println!("Must specify remote key when encrypting")
        };
        let res = match local {
            Some(x) => noisebox::BoxX::enbox(x, remote, &buf[..]),
            None => noisebox::BoxN::enbox(remote, &buf[..])
        };
        if matches.opt_present("a") {
            let mut out = String::new();
            out.push_str("noise255-message:");
            out.push_str(&res.to_hex()[..]);
            Ok(out.into_bytes())
        } else {
            Ok(res)
        }
    } else {
        let local = match local {
            Some(x) => x,
            None => return println!("Must specify local key when decrypting")
        };
        let msg = if &buf[..17] == &b"noise255-message:"[..] {
            String::from_utf8_lossy(&buf[17..]).from_hex().unwrap()
        } else {
            buf
        };
        match remote {
            Some(x) => noisebox::BoxK::unbox(x, local.clone(), &msg[..]),
            None => Err(())
        }.or(noisebox::BoxN::unbox(local.clone(), &msg[..]))
            .or(noisebox::BoxX::unbox(local, &msg[..]))
    };

    match result {
        Ok(x) => match output.write_all(&x[..]) {
            Ok(()) => (),
            Err(e) => println!("Write failed: {:?}", e)
        },
        Err(e) => println!("Decryption failed: {:?}", e)
    }
    output.flush().unwrap();
}
<commit_msg>Make CLI a bit less messy<commit_after>extern crate susurrus;
extern crate getopts;
extern crate sodiumoxide;
extern crate rustc_serialize;

use susurrus::session;
use susurrus::noisebox;
use getopts::Options;
use std::env;
use std::io;
use std::io::Read;
use std::fs::OpenOptions;
use rustc_serialize::hex::{FromHex,ToHex};
use std::string::String;

macro_rules! make_fixed {
    ($t:ty, $n:expr, $v:expr) => ({
        let mut res : [$t; $n] = [0; $n];
        assert!($v.len() == $n);
        for i in 0..$n { res[i] = $v[i] }
        res
    })
}

fn print_usage(program: &str, opts: Options) {
    let mut brief = format!("Usage: {} <command> [options]", program);
    brief.push_str("\n\nCommands:\n");
    brief.push_str("    box     seals data in an encrypted box\n");
    brief.push_str("    unbox   unseals a box\n");
    brief.push_str("    keygen  generate a new keypair\n");
    brief.push_str("    pubkey  exract public key from keypair");
    print!("{}", opts.usage(&brief));
}

fn read_keypair<F:io::Read>(file: &mut F) -> io::Result<session::KeyPair> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-secret:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };
    let mut privkey_raw = [0u8; 64];
    try!(file.read(&mut privkey_raw));
    let privkey = match String::from_utf8_lossy(&privkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::KeyPair { pubkey: session::PubKey(make_fixed!(u8, 32, pubkey)),
                          privkey: session::PrivKey(make_fixed!(u8, 32, privkey)) })
}

fn read_pubkey<F:io::Read>(file: &mut F) -> io::Result<session::PubKey> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-public:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::PubKey(make_fixed!(u8, 32, pubkey)))
}

fn main() {
    sodiumoxide::init();

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("a", "ascii", "ASCII armor encrypted result");
    opts.optopt("i", "input", "sets input file, defaults to stdin", "NAME");
    opts.optopt("o", "output", "set output file, defaults to stdout", "NAME");
    opts.optopt("r", "remote", "specifies file containing remote's public key", "NAME");
    opts.optopt("l", "local", "specifies file containing local key pair", "NAME");
    opts.optopt("t", "type", "specifies the box type to use (one of n, k, or x)", "TYPE");
    if args.len() < 2 {
        print_usage(&program, opts);
        return;
    }
    let command = args[1].clone();
    let matches = match opts.parse(&args[2..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let mut output = match matches.opt_str("o") {
        Some(f) => match OpenOptions::new().write(true).create(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Write>,
            Err(e) => return println!("Could not open output file {}: {}", f, e)
        },
        None => Box::new(io::stdout()) as Box<io::Write>
    };
    let mut input = match matches.opt_str("i") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Read>,
            Err(e) => return println!("Could not open input file {}: {}", f, e)
        },
        None => Box::new(io::stdin()) as Box<io::Read>
    };
    let remote = match matches.opt_str("r") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_pubkey(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse remote key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open remote key file {}: {}", f, e)
        },
        None => None
    };
    let local = match matches.opt_str("l") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_keypair(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse local key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open local key file {}: {}", f, e)
        },
        None => None
    };

    match &command[..] {
        "keygen" => {
            let kp = session::gen_keypair();
            output.write(&b"noise255-secret:"[..]).unwrap();
            output.write(&kp.pubkey.0[..].to_hex().as_bytes()).unwrap();
            output.write(&kp.privkey.0[..].to_hex().as_bytes()).unwrap();
        }
        "pubkey" => {
            let local = match local {
                Some(x) => x,
                None => return println!("Key pair must be specified to extract public key")
            };
            output.write(&b"noise255-public:"[..]).unwrap();
            output.write(&local.pubkey.0[..].to_hex().as_bytes()).unwrap();
        }
        "box" => {
            let mut buf = vec![];
            input.read_to_end(&mut buf).unwrap();
            let remote = match remote {
                Some(x) => x,
                None => return println!("Must specify remote key when encrypting")
            };
            enum BoxType {
                N, K, X
            }
            let boxtype = match matches.opt_str("t") {
                Some(x) => match x.as_ref() {
                    "n" => BoxType::N,
                    "k" => BoxType::K,
                    "x" => BoxType::X,
                    _ => return println!("Unrecognized box type {}", x),
                },
                None => if local.is_some() {
                    BoxType::X
                } else {
                    BoxType::N
                }
            };
            let res = match boxtype {
                BoxType::N => noisebox::BoxN::enbox(remote, &buf[..]),
                BoxType::K => noisebox::BoxK::enbox(local.unwrap(), remote, &buf[..]),
                BoxType::X => noisebox::BoxX::enbox(local.unwrap(), remote, &buf[..])
            };
            if matches.opt_present("a") {
                let mut out = String::new();
                out.push_str("noise255-message:");
                out.push_str(&res.to_hex()[..]);
                output.write_all(out.as_bytes()).unwrap();
            } else {
                output.write_all(&res[..]).unwrap();
            }
        }
        "unbox" => {
            let mut buf = vec![];
            input.read_to_end(&mut buf).unwrap();
            let local = match local {
                Some(x) => x,
                None => return println!("Must specify local key when decrypting")
            };
            let msg = if &buf[..17] == &b"noise255-message:"[..] {
                String::from_utf8_lossy(&buf[17..]).from_hex().unwrap()
            } else {
                buf
            };
            let result = match remote {
                Some(x) => noisebox::BoxK::unbox(x, local.clone(), &msg[..]),
                None => Err(())
            }.or(noisebox::BoxN::unbox(local.clone(), &msg[..]))
                .or(noisebox::BoxX::unbox(local, &msg[..]));
            match result {
                Ok(x) => match output.write_all(&x[..]) {
                    Ok(()) => (),
                    Err(e) => println!("Write failed: {:?}", e)
                },
                Err(e) => println!("Decryption failed: {:?}", e)
            }
        }
        _ => print_usage(&program, opts)
    }
    output.flush().unwrap();
}
<|endoftext|>


<commit_before>use crate::constants::*;
use itertools::Itertools;

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis.unwrap().split(", ");
    let current_branch = compute_current_branch(&parenthesis_parts);

    // FIXME: Placeholder logic until we have all tests
    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Is there a better way to express this condition than using
    // part.to_string()? Won't that allocate memory and stuff? I want something
    // that just compares the strings for equality.
    if &Some(part.to_string()) == current_branch {
        return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
    }

    // FIXME: Handle "HEAD -> current_branch"

    return part.to_string();
}

fn compute_current_branch(_candidates: &std::str::Split<&str>) -> Option<String> {
    // FIXME: Implement this properly!
    return None;
}
<commit_msg>Test figure-out-current-branch logic<commit_after>use crate::constants::*;
use itertools::Itertools;

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis.unwrap().split(", ");
    let current_branch = compute_current_branch(&parenthesis_parts);

    // FIXME: Placeholder logic until we have all tests
    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Is there a better way to express this condition than using
    // part.to_string()? Won't that allocate memory and stuff? I want something
    // that just compares the strings for equality.
    if &Some(part.to_string()) == current_branch {
        return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
    }

    // FIXME: Handle "HEAD -> current_branch"

    return part.to_string();
}

fn compute_current_branch(_candidates: &std::str::Split<&str>) -> Option<String> {
    // FIXME: Implement this properly!
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD".split(", ")
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(&"tag: 2.20.0, origin/master, origin/HEAD, master".split(", "))
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(&"origin/walles/threaded, walles/threaded".split(", "))
        );

        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(None, compute_current_branch(&"tag: 2.15".split(", ")));

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", "))
        );
    }
}
<|endoftext|>


<commit_before>/// Asserts that constant expressions evaluate to `true`.
///
/// Constant expressions can be ensured to have certain properties via this
/// macro If the expression evaluates to `false`, the file will fail to compile.
/// This is synonymous to [`static_assert` in C++][static_assert].
///
/// # Alternatives
///
/// There also exists [`const_assert_eq`](macro.const_assert_eq.html) for
/// validating whether a sequence of expressions are equal to one another.
///
/// # Examples
///
/// On stable Rust, using the macro requires a unique “label” when used in a
/// module scope:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {}
/// const_assert!(meaning_of_life; 42 == !!42);
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
///
/// const FIVE: usize = 5;
///
/// const_assert!(FIVE * FIVE == 25);
///
/// fn main() {
///     const_assert!(2 + 2 == 4);
///     const_assert!(FIVE - FIVE == 0);
/// }
/// ```
///
/// Some expressions are blatantly false:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert!(1 >= 2);
/// # }
/// ```
///
/// [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert
#[macro_export(local_inner_macros)]
macro_rules! const_assert {
    ($($xs:tt)+) => { _const_assert!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
#[allow(dead_code)]
macro_rules! _const_assert {
    ($($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, eq_op)]
        const _: [(); 0 - !($($xs)&&+) as usize] = [];
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _const_assert {
    ($($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, eq_op)]
        let _ = [(); 0 - !($($xs)&&+) as usize];
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { const_assert!($($xs)+); }
    };
}

/// Asserts that constants are equal in value.
///
/// # Examples
///
/// Works as a shorthand for `const_assert!(a == b)`:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use]
/// # extern crate static_assertions;
/// const TWO: usize = 2;
/// const_assert_eq!(two; TWO * TWO, TWO + TWO, 4);
///
/// fn main() {
///     const NUM: usize = 32;
///     const_assert_eq!(NUM + NUM, 64);
/// }
/// ```
///
/// Just because 2 × 2 = 2 + 2 doesn't mean it holds true for other numbers:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert_eq!(4 + 4, 4 * 4);
/// # }
/// ```
#[macro_export(local_inner_macros)]
macro_rules! const_assert_eq {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($($x == $xs),+);
    };
    ($label:ident; $x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($label; $($x == $xs),+);
    };
}
<commit_msg>Typecheck const_assert! inputs<commit_after>/// Asserts that constant expressions evaluate to `true`.
///
/// Constant expressions can be ensured to have certain properties via this
/// macro If the expression evaluates to `false`, the file will fail to compile.
/// This is synonymous to [`static_assert` in C++][static_assert].
///
/// # Alternatives
///
/// There also exists [`const_assert_eq`](macro.const_assert_eq.html) for
/// validating whether a sequence of expressions are equal to one another.
///
/// # Examples
///
/// Some expressions are blatantly false:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert!(1 >= 2);
/// # }
/// ```
///
/// Inputs are type-checked as booleans:
///
/// ```compile_fail
#[cfg_attr(feature = "nightly", doc = "#![feature(underscore_const_names)]")]
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert!(!0);
/// # }
/// ```
///
/// Despite this being a macro, we see this produces a type error:
///
/// ```txt
///   | const_assert!(!0);
///   |               ^^ expected bool, found integral variable
///   |
///   = note: expected type `bool`
///              found type `{integer}`
/// ```
///
/// On stable Rust, using the macro requires a unique “label” when used in a
/// module scope:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {}
/// const_assert!(meaning_of_life; 42 == !!42);
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
///
/// const FIVE: usize = 5;
///
/// const_assert!(FIVE * FIVE == 25);
///
/// fn main() {
///     const_assert!(2 + 2 == 4);
///     const_assert!(FIVE - FIVE == 0);
/// }
/// ```
///
/// [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert
#[macro_export(local_inner_macros)]
macro_rules! const_assert {
    ($($xs:tt)+) => { _const_assert!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
#[allow(dead_code)]
macro_rules! _const_assert {
    ($($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, eq_op)]
        const _: [(); 0 - !($({ const B: bool = $xs; B })&&+) as usize] = [];
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _const_assert {
    ($($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, eq_op)]
        let _ = [(); 0 - !($({ const B: bool = $xs; B })&&+) as usize];
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { const_assert!($($xs)+); }
    };
}

/// Asserts that constants are equal in value.
///
/// # Examples
///
/// Works as a shorthand for `const_assert!(a == b)`:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use]
/// # extern crate static_assertions;
/// const TWO: usize = 2;
/// const_assert_eq!(two; TWO * TWO, TWO + TWO, 4);
///
/// fn main() {
///     const NUM: usize = 32;
///     const_assert_eq!(NUM + NUM, 64);
/// }
/// ```
///
/// Just because 2 × 2 = 2 + 2 doesn't mean it holds true for other numbers:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert_eq!(4 + 4, 4 * 4);
/// # }
/// ```
#[macro_export(local_inner_macros)]
macro_rules! const_assert_eq {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($($x == $xs),+);
    };
    ($label:ident; $x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($label; $($x == $xs),+);
    };
}
<|endoftext|>