<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write, Result};

// The same as in the Rust's standard library.
const DEFAULT_BUF_SIZE: usize = 8 * 1024;

pub fn copy_until<R, W, P>(reader: &mut R, writer: &mut W, mut pred: P)
    -> Result<()>
where
    R: Read,
    W: Write,
    P: FnMut(&R, &W) -> bool,
{
    let mut buf = [0; DEFAULT_BUF_SIZE];
    loop {
        use std::io::ErrorKind::*;
        let len = match reader.read(&mut buf[..]) {
            Ok(0) => break,
            Ok(len) => len,
            Err(ref error) if error.kind() == Interrupted => continue,
            Err(error) => return Err(error),
        };

        writer.write_all(&buf[..len])?;
        if pred(reader, writer) {
            break;
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_copy_until_with_empty_buffer() {
        let mut reader: &[u8] = b"";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| false).is_ok());
        assert_eq!(writer, b"");
    }

    #[test]
    fn test_copy_until_end() {
        let mut reader: &[u8] = b"foobar";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| false).is_ok());
        assert_eq!(writer, b"foobar");
    }

    #[test]
    fn test_copy_until_specific_size() {
        let limit = 4 * 1024 * 1024;

        // An infinite stream of zeros (see explanation below).
        struct Null;

        impl std::io::Read for Null {

            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
                // TODO: Rewriter with `slice::fill` once it stabilizes.
                for item in buf.iter_mut() {
                    *item = 0;
                }

                Ok(buf.len() as usize)
            }
        }

        let mut reader = Null;
        let mut writer = vec!();

        // This should verify that copying eventually stops after the condition
        // is met since the reader is inifite.
        assert! {
            copy_until(&mut reader, &mut writer, |_, writer| {
                writer.len() > limit
            }).is_ok()
        }

        assert!(writer.iter().all(|item| *item == 0));
        assert!(writer.len() > limit);
    }
}
<commit_msg>Write documentation for the I/O module.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Extensions to the standard I/O utilities.
//!
//! This module essentially provides useful, RRG-specific utilities that are not
//! available in `std::io`, mostly operating on the standard `Read` and `Write`
//! traits.

use std::io::{Read, Write, Result};

// The same as in the Rust's standard library.
const DEFAULT_BUF_SIZE: usize = 8 * 1024;

/// Copies contents of one buffer into the other until a condition is met.
///
/// This function should behave similarly to the standard `std::io::copy` with
/// the difference that it can be given a condition when copying should stop.
///
/// Note that the predicate is not checked after each copied byte. Therefore,
/// there are not guarantees about when exactly and how often it will be called.
/// In particular, the input stream can finish even before the predicate is
/// called at all.
///
/// # Errors
///
/// The errors is reported immediately if there is an error when reading from
/// the input or writing to the output.
///
/// Like `std::io::copy`, this function will retry instances of `Interrupted`
/// errors.
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let mut rand = File::open("/dev/random").unwrap();
/// let mut buf = vec!();
///
/// rrg::io::copy_until(&mut rand, &mut buf, |_, writer| writer.len() >= 1024);
///
/// println!("random bytes: {:?}", buf);
/// ```
pub fn copy_until<R, W, P>(reader: &mut R, writer: &mut W, mut pred: P)
    -> Result<()>
where
    R: Read,
    W: Write,
    P: FnMut(&R, &W) -> bool,
{
    let mut buf = [0; DEFAULT_BUF_SIZE];
    loop {
        use std::io::ErrorKind::*;
        let len = match reader.read(&mut buf[..]) {
            Ok(0) => break,
            Ok(len) => len,
            Err(ref error) if error.kind() == Interrupted => continue,
            Err(error) => return Err(error),
        };

        writer.write_all(&buf[..len])?;
        if pred(reader, writer) {
            break;
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_copy_until_with_empty_buffer() {
        let mut reader: &[u8] = b"";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| false).is_ok());
        assert_eq!(writer, b"");
    }

    #[test]
    fn test_copy_until_end() {
        let mut reader: &[u8] = b"foobar";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| false).is_ok());
        assert_eq!(writer, b"foobar");
    }

    #[test]
    fn test_copy_until_specific_size() {
        let limit = 4 * 1024 * 1024;

        // An infinite stream of zeros (see explanation below).
        struct Null;

        impl std::io::Read for Null {

            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
                // TODO: Rewriter with `slice::fill` once it stabilizes.
                for item in buf.iter_mut() {
                    *item = 0;
                }

                Ok(buf.len() as usize)
            }
        }

        let mut reader = Null;
        let mut writer = vec!();

        // This should verify that copying eventually stops after the condition
        // is met since the reader is inifite.
        assert! {
            copy_until(&mut reader, &mut writer, |_, writer| {
                writer.len() > limit
            }).is_ok()
        }

        assert!(writer.iter().all(|item| *item == 0));
        assert!(writer.len() > limit);
    }
}
<|endoftext|>


<commit_before>use futures::Stream;
use futures::future::Future;
use futures::sync::oneshot;
use hyper;
use hyper::{Method, Request};
use hyper::header::UserAgent;
use hyper_rustls::HttpsConnector;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use serde_json;
use std::collections::HashMap;
use tokio_core::reactor::Remote;

use errors;
use errors::*;

pub struct HTTPClient {
    api_base: String,
    headers: HashMap<&'static str, String>,
    core_remote: Remote,
}

struct InternalResp {
    data: hyper::Chunk,
    headers: hyper::Headers,
    status: hyper::StatusCode,
}

pub struct HTTPResponse<T> {
    pub item: T,
    pub headers: hyper::Headers,
    pub status: hyper::StatusCode,
}

type InternalResponseResult = Result<InternalResp>;
type FutureInternalResponse = oneshot::Receiver<InternalResponseResult>;

pub type FutureResult<T> = Box<Future<Item = T, Error = errors::Error> + Send + 'static>;

impl HTTPClient {
    pub fn new(core_remote: Remote, api_base: &str) -> HTTPClient {
        HTTPClient {
            api_base: api_base.into(),
            headers: HashMap::new(),
            core_remote: core_remote,
        }
    }

    pub fn with_headers(self, headers: HashMap<&'static str, String>) -> HTTPClient {
        let mut c = self;
        c.headers = headers;
        c
    }

    pub fn get<T>(&self, path: &str) -> Result<T>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de::<T, ()>(Method::Get, path, None).map(|res| res.item)
    }

    pub fn get_async<T>(&self, path: &str) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async::<T, ()>(Method::Get, path, None)
    }

    pub fn post<T, U: Serialize>(&self, path: &str, body: &U) -> Result<T>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de::<T, U>(Method::Post, path, Some(body)).map(|res| res.item)
    }

    pub fn post_async<T, U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async::<T, U>(Method::Post, path, Some(body))
    }

    pub fn post_void<U: Serialize>(&self, path: &str, body: &U) -> Result<()> {
        self.request_void::<U>(Method::Post, path, Some(body)).map(|_| ())
    }

    pub fn post_void_async<U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<()>> {
        self.request_void_async::<U>(Method::Post, path, Some(body))
    }

    pub fn put<T, U: Serialize>(&self, path: &str, body: &U) -> Result<T>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de::<T, U>(Method::Put, path, Some(body)).map(|res| res.item)
    }

    pub fn put_async<T, U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async::<T, U>(Method::Put, path, Some(body))
    }

    pub fn put_void<U: Serialize>(&self, path: &str, body: &U) -> Result<()> {
        self.request_void::<U>(Method::Put, path, Some(body)).map(|_| ())
    }

    pub fn put_void_async<U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<()>> {
        self.request_void_async::<U>(Method::Put, path, Some(body))
    }

    pub fn delete_void(&self, path: &str) -> Result<()> {
        self.request_void::<()>(Method::Delete, path, None).map(|_| ())
    }

    pub fn delete_void_async(&self, path: &str) -> FutureResult<HTTPResponse<()>> {
        self.request_void_async::<()>(Method::Delete, path, None)
    }

    // `spawn` is necesary for driving futures returned by async methods and any combinations
    // applied on top of them w/o needing to `wait` on the result
    pub fn spawn<F>(&self, fut: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        self.core_remote.spawn(move |_| fut);
    }

    pub fn request_de<T, U: Serialize>(&self, method: Method, path: &str, body: Option<&U>) -> Result<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async(method, path, body).wait().map_err(|e| {
            Error::from(format!("Error waiting for HTTP response: {}", e))
        })
    }

    pub fn request_void<U: Serialize>(&self, method: Method, path: &str, body: Option<&U>) -> Result<HTTPResponse<()>> {
        self.request_void_async(method, path, body).wait().map_err(|e| {
            Error::from(format!("Error waiting for HTTP response: {}", e))
        })
    }

    pub fn request_de_async<T, U: Serialize>(
        &self,
        method: Method,
        path: &str,
        body: Option<&U>,
    ) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        let path = path.to_string();
        Box::new(
            self.request_async(method, &path, body)
                .or_else(|_| Err("HTTP Request was cancelled".into()))
                .and_then(move |res| {
                    res.and_then(|res| {
                        if res.status.is_redirection() {
                            warn!(
                                "Received redirection when expected to receive data to deserialize. \
                                 Request: {}; Headers: {}",
                                path,
                                res.headers
                            );
                        }

                        serde_json::from_slice::<T>(&res.data)
                            .map_err(|e| {
                                format!(
                                    "Error parsing response: {}\n---\n{}\n---",
                                    e,
                                    String::from_utf8_lossy(&res.data)
                                ).into()
                            })
                            .map(|obj| {
                                HTTPResponse {
                                    item: obj,
                                    headers: res.headers,
                                    status: res.status,
                                }
                            })
                    })
                }),
        )
    }

    pub fn request_void_async<U: Serialize>(
        &self,
        method: Method,
        path: &str,
        body: Option<&U>,
    ) -> FutureResult<HTTPResponse<()>> {
        Box::new(
            self.request_async(method, path, body)
                .or_else(|_| Err("HTTP Request was cancelled".into()))
                .and_then(|res| {
                    res.map(|r| {
                        HTTPResponse {
                            item: (),
                            headers: r.headers,
                            status: r.status,
                        }
                    })
                }),
        )
    }

    fn request_async<U: Serialize>(&self, method: Method, path: &str, body: Option<&U>) -> FutureInternalResponse {
        let (tx, rx) = oneshot::channel::<InternalResponseResult>();

        let send_future = |it| if let Err(_) = tx.send(it) {
            error!("Error sending on future channel");
        };

        let url;
        if path.is_empty() {
            url = self.api_base.clone();
        } else if path.starts_with("http://") || path.starts_with("https://") {
            url = path.to_string();
        } else if path.starts_with("/") {
            url = self.api_base.clone() + path;
        } else {
            url = self.api_base.clone() + "/" + path;
        }
        let url: hyper::Uri = match url.parse() {
            Ok(u) => u,
            Err(e) => {
                send_future(Err(format!("Error parsing url: {}: {}", url, e).into()));
                return rx;
            }
        };

        let headers = self.headers.clone();

        let mut req = Request::new(method, url);
        req.headers_mut().set(UserAgent::new("octobot"));

        for (k, v) in &headers {
            req.headers_mut().set_raw(k.clone(), v.clone());
        }

        if let Some(body) = body {
            let body_json = match serde_json::to_string(&body) {
                Ok(j) => j,
                Err(e) => {
                    send_future(Err(format!("Error json-encoding body: {}", e).into()));
                    return rx;
                }
            };
            req.set_body(body_json)
        }

        let path = path.to_string();

        self.core_remote.spawn(move |handle| {
            // TODO: I wonder if these objects are expensive to create and we should be sharing them across requests?
            let https = HttpsConnector::new(4, &handle);
            let client = hyper::Client::configure().connector(https).build(&handle);

            client
                .request(req)
                .map_err(|e| {
                    error!("Error in HTTP request: {}", e);
                })
                .and_then(|res| {
                    let status = res.status();
                    let headers = res.headers().clone();
                    res.body()
                        .concat2()
                        .map_err(|e| {
                            error!("Error in HTTP request: {}", e);
                        })
                        .map(move |buffer| {
                            debug!("Response: HTTP {}\n---\n{}\n---", status, String::from_utf8_lossy(&buffer));
                            if !status.is_success() && !status.is_redirection() {
                                send_future(Err(
                                    format!(
                                        "Failed request to {}: HTTP {}\n---\n{}\n---",
                                        path,
                                        status,
                                        String::from_utf8_lossy(&buffer)
                                    ).into(),
                                ));
                            } else {
                                send_future(Ok(InternalResp {
                                    data: buffer,
                                    headers: headers,
                                    status: status,
                                }));
                            }
                        })
                })
        });

        rx
    }
}
<commit_msg>Add method to get raw HTTP response data (#203)<commit_after>use futures::Stream;
use futures::future::Future;
use futures::sync::oneshot;
use hyper;
use hyper::{Method, Request};
use hyper::header::UserAgent;
use hyper_rustls::HttpsConnector;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use serde_json;
use std::collections::HashMap;
use tokio_core::reactor::Remote;

use errors;
use errors::*;

pub struct HTTPClient {
    api_base: String,
    headers: HashMap<&'static str, String>,
    core_remote: Remote,
}

struct InternalResp {
    data: hyper::Chunk,
    headers: hyper::Headers,
    status: hyper::StatusCode,
}

pub struct HTTPResponse<T> {
    pub item: T,
    pub headers: hyper::Headers,
    pub status: hyper::StatusCode,
}

type InternalResponseResult = Result<InternalResp>;
type FutureInternalResponse = oneshot::Receiver<InternalResponseResult>;

pub type FutureResult<T> = Box<Future<Item = T, Error = errors::Error> + Send + 'static>;

impl HTTPClient {
    pub fn new(core_remote: Remote, api_base: &str) -> HTTPClient {
        HTTPClient {
            api_base: api_base.into(),
            headers: HashMap::new(),
            core_remote: core_remote,
        }
    }

    pub fn with_headers(self, headers: HashMap<&'static str, String>) -> HTTPClient {
        let mut c = self;
        c.headers = headers;
        c
    }

    pub fn get<T>(&self, path: &str) -> Result<T>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de::<T, ()>(Method::Get, path, None).map(|res| res.item)
    }

    pub fn get_async<T>(&self, path: &str) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async::<T, ()>(Method::Get, path, None)
    }

    pub fn post<T, U: Serialize>(&self, path: &str, body: &U) -> Result<T>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de::<T, U>(Method::Post, path, Some(body)).map(|res| res.item)
    }

    pub fn post_async<T, U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async::<T, U>(Method::Post, path, Some(body))
    }

    pub fn post_void<U: Serialize>(&self, path: &str, body: &U) -> Result<()> {
        self.request_void::<U>(Method::Post, path, Some(body)).map(|_| ())
    }

    pub fn post_void_async<U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<()>> {
        self.request_void_async::<U>(Method::Post, path, Some(body))
    }

    pub fn put<T, U: Serialize>(&self, path: &str, body: &U) -> Result<T>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de::<T, U>(Method::Put, path, Some(body)).map(|res| res.item)
    }

    pub fn put_async<T, U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async::<T, U>(Method::Put, path, Some(body))
    }

    pub fn put_void<U: Serialize>(&self, path: &str, body: &U) -> Result<()> {
        self.request_void::<U>(Method::Put, path, Some(body)).map(|_| ())
    }

    pub fn put_void_async<U: Serialize>(&self, path: &str, body: &U) -> FutureResult<HTTPResponse<()>> {
        self.request_void_async::<U>(Method::Put, path, Some(body))
    }

    pub fn delete_void(&self, path: &str) -> Result<()> {
        self.request_void::<()>(Method::Delete, path, None).map(|_| ())
    }

    pub fn delete_void_async(&self, path: &str) -> FutureResult<HTTPResponse<()>> {
        self.request_void_async::<()>(Method::Delete, path, None)
    }

    // `spawn` is necesary for driving futures returned by async methods and any combinations
    // applied on top of them w/o needing to `wait` on the result
    pub fn spawn<F>(&self, fut: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        self.core_remote.spawn(move |_| fut);
    }

    pub fn request_de<T, U: Serialize>(&self, method: Method, path: &str, body: Option<&U>) -> Result<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        self.request_de_async(method, path, body).wait().map_err(|e| {
            Error::from(format!("Error waiting for HTTP response: {}", e))
        })
    }

    pub fn request_raw<U: Serialize>(
        &self,
        method: Method,
        path: &str,
        body: Option<&U>,
    ) -> Result<HTTPResponse<Vec<u8>>> {
        self.request_raw_async(method, path, body).wait().map_err(|e| {
            Error::from(format!("Error waiting for HTTP response: {}", e))
        })
    }

    pub fn request_void<U: Serialize>(&self, method: Method, path: &str, body: Option<&U>) -> Result<HTTPResponse<()>> {
        self.request_void_async(method, path, body).wait().map_err(|e| {
            Error::from(format!("Error waiting for HTTP response: {}", e))
        })
    }

    pub fn request_de_async<T, U: Serialize>(
        &self,
        method: Method,
        path: &str,
        body: Option<&U>,
    ) -> FutureResult<HTTPResponse<T>>
    where
        T: DeserializeOwned + Send + 'static,
    {
        let path = path.to_string();
        Box::new(
            self.request_async(method, &path, body)
                .or_else(|_| Err("HTTP Request was cancelled".into()))
                .and_then(move |res| {
                    res.and_then(|res| {
                        if res.status.is_redirection() {
                            warn!(
                                "Received redirection when expected to receive data to deserialize. \
                                 Request: {}; Headers: {}",
                                path,
                                res.headers
                            );
                        }

                        serde_json::from_slice::<T>(&res.data)
                            .map_err(|e| {
                                format!(
                                    "Error parsing response: {}\n---\n{}\n---",
                                    e,
                                    String::from_utf8_lossy(&res.data)
                                ).into()
                            })
                            .map(|obj| {
                                HTTPResponse {
                                    item: obj,
                                    headers: res.headers,
                                    status: res.status,
                                }
                            })
                    })
                }),
        )
    }

    pub fn request_void_async<U: Serialize>(
        &self,
        method: Method,
        path: &str,
        body: Option<&U>,
    ) -> FutureResult<HTTPResponse<()>> {
        Box::new(
            self.request_async(method, path, body)
                .or_else(|_| Err("HTTP Request was cancelled".into()))
                .and_then(|res| {
                    res.map(|r| {
                        HTTPResponse {
                            item: (),
                            headers: r.headers,
                            status: r.status,
                        }
                    })
                }),
        )
    }

    pub fn request_raw_async<U: Serialize>(
        &self,
        method: Method,
        path: &str,
        body: Option<&U>,
    ) -> FutureResult<HTTPResponse<Vec<u8>>> {
        Box::new(
            self.request_async(method, path, body)
                .or_else(|_| Err("HTTP Request was cancelled".into()))
                .and_then(|res| {
                    res.map(|r| {
                        HTTPResponse {
                            item: r.data.to_vec(),
                            headers: r.headers,
                            status: r.status,
                        }
                    })
                }),
        )
    }

    fn request_async<U: Serialize>(&self, method: Method, path: &str, body: Option<&U>) -> FutureInternalResponse {
        let (tx, rx) = oneshot::channel::<InternalResponseResult>();

        let send_future = |it| if let Err(_) = tx.send(it) {
            error!("Error sending on future channel");
        };

        let url;
        if path.is_empty() {
            url = self.api_base.clone();
        } else if path.starts_with("http://") || path.starts_with("https://") {
            url = path.to_string();
        } else if path.starts_with("/") {
            url = self.api_base.clone() + path;
        } else {
            url = self.api_base.clone() + "/" + path;
        }
        let url: hyper::Uri = match url.parse() {
            Ok(u) => u,
            Err(e) => {
                send_future(Err(format!("Error parsing url: {}: {}", url, e).into()));
                return rx;
            }
        };

        let headers = self.headers.clone();

        let mut req = Request::new(method, url);
        req.headers_mut().set(UserAgent::new("octobot"));

        for (k, v) in &headers {
            req.headers_mut().set_raw(k.clone(), v.clone());
        }

        if let Some(body) = body {
            let body_json = match serde_json::to_string(&body) {
                Ok(j) => j,
                Err(e) => {
                    send_future(Err(format!("Error json-encoding body: {}", e).into()));
                    return rx;
                }
            };
            req.set_body(body_json)
        }

        let path = path.to_string();

        self.core_remote.spawn(move |handle| {
            // TODO: I wonder if these objects are expensive to create and we should be sharing them across requests?
            let https = HttpsConnector::new(4, &handle);
            let client = hyper::Client::configure().connector(https).build(&handle);

            client
                .request(req)
                .map_err(|e| {
                    error!("Error in HTTP request: {}", e);
                })
                .and_then(|res| {
                    let status = res.status();
                    let headers = res.headers().clone();
                    res.body()
                        .concat2()
                        .map_err(|e| {
                            error!("Error in HTTP request: {}", e);
                        })
                        .map(move |buffer| {
                            debug!("Response: HTTP {}\n---\n{}\n---", status, String::from_utf8_lossy(&buffer));
                            if !status.is_success() && !status.is_redirection() {
                                send_future(Err(
                                    format!(
                                        "Failed request to {}: HTTP {}\n---\n{}\n---",
                                        path,
                                        status,
                                        String::from_utf8_lossy(&buffer)
                                    ).into(),
                                ));
                            } else {
                                send_future(Ok(InternalResp {
                                    data: buffer,
                                    headers: headers,
                                    status: status,
                                }));
                            }
                        })
                })
        });

        rx
    }
}
<|endoftext|>


<commit_before>extern crate ftp;

use ftp::FtpStream;
use ftp::types::FileType;
use std::fs::File;
use std::io::Write;

fn main() {
    // connect to the server
    let mut ftp = FtpStream::connect("kernel.org:21").unwrap();
    ftp.login("anonymous", "").unwrap();

    // change working directory
    ftp.cwd("/pub/linux/kernel").unwrap();

    // list files in the current directory
    match ftp.list(None) {
        Ok(output) => println!("{}", output.join("")),
        Err(err) => panic!(err)
    }

    // download a file a write it on the disk
    ftp.transfer_type(FileType::Binary).unwrap();
    ftp.retr("README", |stream| {
        let mut file = File::create("README").unwrap();
        let mut buf = [0; 2048];
        loop {
            match stream.read(&mut buf) {
                Ok(0) => break,
                Ok(n) => file.write_all(&buf[0..n]).unwrap(),
                Err(err) => panic!(err)
            };
        }
        Ok(())
    }).unwrap();
}

#[cfg(test)]
mod test {
    use ftp::FtpStream;

    fn connect() -> FtpStream {
        let mut ftp = FtpStream::connect("kernel.org:21").unwrap();
        ftp.login("anonymous", "").unwrap();
        return ftp;
    }

    #[ignore]
    #[test]
    fn test_cwd() {
        let mut ftp = connect();
        // make sure the current directory is /
        assert_eq!(ftp.pwd().unwrap(), "/");
        ftp.cwd("/pub/linux/kernel").unwrap();
        assert_eq!(ftp.pwd().unwrap(), "/pub/linux/kernel");
    }

    #[ignore]
    #[test]
    fn test_list_dir() {
        let mut ftp = connect();
        assert_eq!(
            ftp.list(Some("/")).unwrap().join(""),
            "drwxr-xr-x    9 ftp      ftp          4096 Dec 01  2011 pub");
    }
}
<commit_msg>ftp: simplify writing file when downloading it<commit_after>#![feature(conservative_impl_trait)]

extern crate ftp;

use ftp::FtpStream;
use ftp::types::{Result,FileType};
use std::fs::File;
use std::io::{Read,Write};

fn write_file(filename: &str) -> impl Fn(&mut Read) -> Result<()> {
    let filename = filename.to_string();
    move |stream| {
        let mut file = File::create(&filename).unwrap();
        let mut buf = [0; 2048];
        loop {
            match stream.read(&mut buf) {
                Ok(0) => break,
                Ok(n) => file.write_all(&buf[0..n]).unwrap(),
                Err(err) => panic!(err)
            };
        }
        Ok(())
    }
}

fn main() {
    // connect to the server
    let mut ftp = FtpStream::connect("kernel.org:21").unwrap();
    ftp.login("anonymous", "").unwrap();

    // change working directory
    ftp.cwd("/pub/linux/kernel").unwrap();

    // list files in the current directory
    match ftp.list(None) {
        Ok(output) => println!("{}", output.join("")),
        Err(err) => panic!(err)
    }

    // download a file a write it on the disk
    ftp.transfer_type(FileType::Binary).unwrap();
    ftp.retr("README", write_file("README")).unwrap();
}

#[cfg(test)]
mod test {
    use ftp::FtpStream;

    fn connect() -> FtpStream {
        let mut ftp = FtpStream::connect("kernel.org:21").unwrap();
        ftp.login("anonymous", "").unwrap();
        return ftp;
    }

    #[ignore]
    #[test]
    fn test_cwd() {
        let mut ftp = connect();
        // make sure the current directory is /
        assert_eq!(ftp.pwd().unwrap(), "/");
        ftp.cwd("/pub/linux/kernel").unwrap();
        assert_eq!(ftp.pwd().unwrap(), "/pub/linux/kernel");
    }

    #[ignore]
    #[test]
    fn test_list_dir() {
        let mut ftp = connect();
        assert_eq!(
            ftp.list(Some("/")).unwrap().join(""),
            "drwxr-xr-x    9 ftp      ftp          4096 Dec 01  2011 pub");
    }
}
<|endoftext|>


<commit_before>use msg::rosgraph_msgs::{Clock as ClockMsg, Log};
use msg::std_msgs::Header;
use time::{Duration, Time};
use serde::{Deserialize, Serialize};
use std::sync::{mpsc, Arc};
use std::time;
use super::clock::{Clock, Rate, RealClock, SimulatedClock};
use super::master::{self, Master, Topic};
use super::slave::Slave;
use super::error::{ErrorKind, Result, ResultExt};
use super::super::rosxmlrpc::Response;
use super::naming::{self, Resolver};
use super::raii::{Publisher, Service, Subscriber};
use super::resolve;
use tcpros::{Client, Message, ServicePair, ServiceResult};
use xml_rpc;
use yaml_rust::{Yaml, YamlLoader};

pub struct Ros {
    master: Arc<Master>,
    slave: Arc<Slave>,
    hostname: String,
    resolver: Resolver,
    name: String,
    clock: Arc<Clock>,
    static_subs: Vec<Subscriber>,
    logger: Option<Publisher<Log>>,
    shutdown_rx: Option<mpsc::Receiver<()>>,
}

impl Ros {
    pub fn new(name: &str) -> Result<Ros> {
        let namespace = resolve::namespace();
        let master_uri = resolve::master();
        let hostname = resolve::hostname();
        let name = resolve::name(name);
        let mut ros = Ros::new_raw(&master_uri, &hostname, &namespace, &name)?;
        for (src, dest) in resolve::mappings() {
            ros.map(&src, &dest)?;
        }
        for (src, dest) in resolve::params() {
            let data = YamlLoader::load_from_str(&dest)
                .chain_err(|| format!("Failed to load YAML: {}", dest))?
                .into_iter()
                .next()
                .ok_or_else(|| format!("Failed to load YAML: {}", dest))?;
            let param = ros.param(&src)
                .ok_or_else(|| format!("Failed to resolve name: {}", src))?;
            param.set_raw(yaml_to_xmlrpc(data)?)?;
        }

        if ros.param("/use_sim_time")
            .and_then(|v| v.get().ok())
            .unwrap_or(false)
        {
            let clock = Arc::new(SimulatedClock::default());
            let ros_clock = Arc::clone(&clock);
            let sub = ros.subscribe::<ClockMsg, _>("/clock", move |v| clock.trigger(v.clock))
                .chain_err(|| "Failed to subscribe to simulated clock")?;
            ros.static_subs.push(sub);
            ros.clock = ros_clock;
        }

        ros.logger = Some(ros.publish("/rosout")?);

        Ok(ros)
    }

    fn new_raw(master_uri: &str, hostname: &str, namespace: &str, name: &str) -> Result<Ros> {
        let namespace = namespace.trim_right_matches('/');

        if name.contains('/') {
            bail!(ErrorKind::Naming(
                naming::error::ErrorKind::IllegalCharacter(name.into()),
            ));
        }

        let name = format!("{}/{}", namespace, name);
        let resolver = Resolver::new(&name)?;

        let (shutdown_tx, shutdown_rx) = mpsc::channel();

        let slave = Slave::new(master_uri, hostname, 0, &name, shutdown_tx)?;
        let master = Master::new(master_uri, &name, slave.uri());

        Ok(Ros {
            master: Arc::new(master),
            slave: Arc::new(slave),
            hostname: String::from(hostname),
            resolver: resolver,
            name: name,
            clock: Arc::new(RealClock::default()),
            static_subs: Vec::new(),
            logger: None,
            shutdown_rx: Some(shutdown_rx),
        })
    }

    fn map(&mut self, source: &str, destination: &str) -> Result<()> {
        self.resolver.map(source, destination).map_err(|v| v.into())
    }

    #[inline]
    pub fn uri(&self) -> &str {
        self.slave.uri()
    }

    #[inline]
    pub fn name(&self) -> &str {
        &self.name
    }

    #[inline]
    pub fn hostname(&self) -> &str {
        &self.hostname
    }

    #[inline]
    pub fn now(&self) -> Time {
        self.clock.now()
    }

    #[inline]
    pub fn sleep(&self, d: Duration) {
        self.clock.sleep(d);
    }

    pub fn rate(&self, rate: f64) -> Rate {
        let nanos = 1_000_000_000.0 / rate;
        Rate::new(
            Arc::clone(&self.clock),
            self.now(),
            Duration::from_nanos(nanos as i64),
        )
    }

    #[inline]
    pub fn is_ok(&self) -> bool {
        if let Some(ref rx) = self.shutdown_rx {
            rx.try_recv() == Err(mpsc::TryRecvError::Empty)
        } else {
            return false;
        }
    }

    #[inline]
    pub fn spin(&mut self) -> Spinner {
        Spinner {
            shutdown_rx: self.shutdown_rx.take(),
        }
    }

    pub fn param(&self, name: &str) -> Option<Parameter> {
        self.resolver.translate(name).ok().map(|v| Parameter {
            master: Arc::clone(&self.master),
            name: v,
        })
    }

    pub fn parameters(&self) -> Response<Vec<String>> {
        self.master.get_param_names()
    }

    pub fn state(&self) -> Response<master::SystemState> {
        self.master.get_system_state().map(Into::into)
    }

    pub fn topics(&self) -> Response<Vec<Topic>> {
        self.master
            .get_topic_types()
            .map(|v| v.into_iter().map(Into::into).collect())
    }

    pub fn client<T: ServicePair>(&self, service: &str) -> Result<Client<T>> {
        let name = self.resolver.translate(service)?;
        let uri = self.master.lookup_service(&name)?;
        Ok(Client::new(&self.name, &uri, &name))
    }

    pub fn wait_for_service(&self, service: &str, timeout: Option<time::Duration>) -> Result<()> {
        use rosxmlrpc::ResponseError;
        use std::thread::sleep;

        let name = self.resolver.translate(service)?;
        let now = ::std::time::Instant::now();
        loop {
            let e = match self.master.lookup_service(&name) {
                Ok(_) => return Ok(()),
                Err(e) => e,
            };
            match e {
                ResponseError::Client(ref m) if m == "no provider" => {
                    if let Some(ref timeout) = timeout {
                        if &now.elapsed() > timeout {
                            return Err(ErrorKind::TimeoutError.into());
                        }
                    }
                    sleep(time::Duration::from_millis(100));
                    continue;
                }
                _ => {}
            }
            return Err(e.into());
        }
    }

    pub fn service<T, F>(&mut self, service: &str, handler: F) -> Result<Service>
    where
        T: ServicePair,
        F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
    {
        let name = self.resolver.translate(service)?;
        Service::new::<T, F>(
            Arc::clone(&self.master),
            Arc::clone(&self.slave),
            &self.hostname,
            &name,
            handler,
        )
    }

    pub fn subscribe<T, F>(&mut self, topic: &str, callback: F) -> Result<Subscriber>
    where
        T: Message,
        F: Fn(T) -> () + Send + 'static,
    {
        let name = self.resolver.translate(topic)?;
        Subscriber::new::<T, F>(
            Arc::clone(&self.master),
            Arc::clone(&self.slave),
            &name,
            callback,
        )
    }

    pub fn publish<T>(&mut self, topic: &str) -> Result<Publisher<T>>
    where
        T: Message,
    {
        let name = self.resolver.translate(topic)?;
        Publisher::new(
            Arc::clone(&self.master),
            Arc::clone(&self.slave),
            Arc::clone(&self.clock),
            &self.hostname,
            &name,
        )
    }

    pub fn log(&mut self, level: i8, msg: String, file: &str, line: u32) {
        let logger = &mut match self.logger {
            Some(ref mut v) => v,
            None => return,
        };
        let topics = self.slave
            .publications
            .lock()
            .expect("Failed to acquire lock on mutex")
            .keys()
            .cloned()
            .collect();
        let message = Log {
            header: Header::default(),
            level,
            msg,
            name: self.name.clone(),
            line,
            file: file.into(),
            function: String::default(),
            topics,
        };
        if let Err(err) = logger.send(message) {
            error!("Logging error: {}", err);
        }
    }
}

pub struct Parameter {
    master: Arc<Master>,
    name: String,
}

impl Parameter {
    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn get<'b, T: Deserialize<'b>>(&self) -> Response<T> {
        self.master.get_param::<T>(&self.name)
    }

    pub fn get_raw(&self) -> Response<xml_rpc::Value> {
        self.master.get_param_any(&self.name)
    }

    pub fn set<T: Serialize>(&self, value: &T) -> Response<()> {
        self.master.set_param::<T>(&self.name, value).and(Ok(()))
    }

    pub fn set_raw(&self, value: xml_rpc::Value) -> Response<()> {
        self.master.set_param_any(&self.name, value).and(Ok(()))
    }

    pub fn delete(&self) -> Response<()> {
        self.master.delete_param(&self.name).and(Ok(()))
    }

    pub fn exists(&self) -> Response<bool> {
        self.master.has_param(&self.name)
    }

    pub fn search(&self) -> Response<String> {
        self.master.search_param(&self.name)
    }
}

fn yaml_to_xmlrpc(val: Yaml) -> Result<xml_rpc::Value> {
    Ok(match val {
        Yaml::Real(v) => xml_rpc::Value::Double(v.parse().chain_err(|| "Failed to parse float")?),
        Yaml::Integer(v) => xml_rpc::Value::Int(v as i32),
        Yaml::String(v) => xml_rpc::Value::String(v),
        Yaml::Boolean(v) => xml_rpc::Value::Bool(v),
        Yaml::Array(v) => {
            xml_rpc::Value::Array(v.into_iter().map(yaml_to_xmlrpc).collect::<Result<_>>()?)
        }
        Yaml::Hash(v) => xml_rpc::Value::Struct(v.into_iter()
            .map(|(k, v)| Ok((yaml_to_string(k)?, yaml_to_xmlrpc(v)?)))
            .collect::<Result<_>>()?),
        Yaml::Alias(_) => bail!("Alias is not supported"),
        Yaml::Null => bail!("Illegal null value"),
        Yaml::BadValue => bail!("Bad value provided"),
    })
}

fn yaml_to_string(val: Yaml) -> Result<String> {
    Ok(match val {
        Yaml::Real(v) | Yaml::String(v) => v,
        Yaml::Integer(v) => v.to_string(),
        Yaml::Boolean(true) => "true".into(),
        Yaml::Boolean(false) => "false".into(),
        _ => bail!("Hash keys need to be strings"),
    })
}

pub struct Spinner {
    shutdown_rx: Option<mpsc::Receiver<()>>,
}

impl Drop for Spinner {
    fn drop(&mut self) {
        if let Some(ref rx) = self.shutdown_rx {
            rx.recv().is_ok();
        }
    }
}
<commit_msg>Make leading namespace slash optional<commit_after>use msg::rosgraph_msgs::{Clock as ClockMsg, Log};
use msg::std_msgs::Header;
use time::{Duration, Time};
use serde::{Deserialize, Serialize};
use std::sync::{mpsc, Arc};
use std::time;
use super::clock::{Clock, Rate, RealClock, SimulatedClock};
use super::master::{self, Master, Topic};
use super::slave::Slave;
use super::error::{ErrorKind, Result, ResultExt};
use super::super::rosxmlrpc::Response;
use super::naming::{self, Resolver};
use super::raii::{Publisher, Service, Subscriber};
use super::resolve;
use tcpros::{Client, Message, ServicePair, ServiceResult};
use xml_rpc;
use yaml_rust::{Yaml, YamlLoader};

pub struct Ros {
    master: Arc<Master>,
    slave: Arc<Slave>,
    hostname: String,
    resolver: Resolver,
    name: String,
    clock: Arc<Clock>,
    static_subs: Vec<Subscriber>,
    logger: Option<Publisher<Log>>,
    shutdown_rx: Option<mpsc::Receiver<()>>,
}

impl Ros {
    pub fn new(name: &str) -> Result<Ros> {
        let mut namespace = resolve::namespace();
        if !namespace.starts_with('/') {
            namespace = format!("/{}", namespace);
        }
        let master_uri = resolve::master();
        let hostname = resolve::hostname();
        let name = resolve::name(name);
        let mut ros = Ros::new_raw(&master_uri, &hostname, &namespace, &name)?;
        for (src, dest) in resolve::mappings() {
            ros.map(&src, &dest)?;
        }
        for (src, dest) in resolve::params() {
            let data = YamlLoader::load_from_str(&dest)
                .chain_err(|| format!("Failed to load YAML: {}", dest))?
                .into_iter()
                .next()
                .ok_or_else(|| format!("Failed to load YAML: {}", dest))?;
            let param = ros.param(&src)
                .ok_or_else(|| format!("Failed to resolve name: {}", src))?;
            param.set_raw(yaml_to_xmlrpc(data)?)?;
        }

        if ros.param("/use_sim_time")
            .and_then(|v| v.get().ok())
            .unwrap_or(false)
        {
            let clock = Arc::new(SimulatedClock::default());
            let ros_clock = Arc::clone(&clock);
            let sub = ros.subscribe::<ClockMsg, _>("/clock", move |v| clock.trigger(v.clock))
                .chain_err(|| "Failed to subscribe to simulated clock")?;
            ros.static_subs.push(sub);
            ros.clock = ros_clock;
        }

        ros.logger = Some(ros.publish("/rosout")?);

        Ok(ros)
    }

    fn new_raw(master_uri: &str, hostname: &str, namespace: &str, name: &str) -> Result<Ros> {
        let namespace = namespace.trim_right_matches('/');

        if name.contains('/') {
            bail!(ErrorKind::Naming(
                naming::error::ErrorKind::IllegalCharacter(name.into()),
            ));
        }

        let name = format!("{}/{}", namespace, name);
        let resolver = Resolver::new(&name)?;

        let (shutdown_tx, shutdown_rx) = mpsc::channel();

        let slave = Slave::new(master_uri, hostname, 0, &name, shutdown_tx)?;
        let master = Master::new(master_uri, &name, slave.uri());

        Ok(Ros {
            master: Arc::new(master),
            slave: Arc::new(slave),
            hostname: String::from(hostname),
            resolver: resolver,
            name: name,
            clock: Arc::new(RealClock::default()),
            static_subs: Vec::new(),
            logger: None,
            shutdown_rx: Some(shutdown_rx),
        })
    }

    fn map(&mut self, source: &str, destination: &str) -> Result<()> {
        self.resolver.map(source, destination).map_err(|v| v.into())
    }

    #[inline]
    pub fn uri(&self) -> &str {
        self.slave.uri()
    }

    #[inline]
    pub fn name(&self) -> &str {
        &self.name
    }

    #[inline]
    pub fn hostname(&self) -> &str {
        &self.hostname
    }

    #[inline]
    pub fn now(&self) -> Time {
        self.clock.now()
    }

    #[inline]
    pub fn sleep(&self, d: Duration) {
        self.clock.sleep(d);
    }

    pub fn rate(&self, rate: f64) -> Rate {
        let nanos = 1_000_000_000.0 / rate;
        Rate::new(
            Arc::clone(&self.clock),
            self.now(),
            Duration::from_nanos(nanos as i64),
        )
    }

    #[inline]
    pub fn is_ok(&self) -> bool {
        if let Some(ref rx) = self.shutdown_rx {
            rx.try_recv() == Err(mpsc::TryRecvError::Empty)
        } else {
            return false;
        }
    }

    #[inline]
    pub fn spin(&mut self) -> Spinner {
        Spinner {
            shutdown_rx: self.shutdown_rx.take(),
        }
    }

    pub fn param(&self, name: &str) -> Option<Parameter> {
        self.resolver.translate(name).ok().map(|v| Parameter {
            master: Arc::clone(&self.master),
            name: v,
        })
    }

    pub fn parameters(&self) -> Response<Vec<String>> {
        self.master.get_param_names()
    }

    pub fn state(&self) -> Response<master::SystemState> {
        self.master.get_system_state().map(Into::into)
    }

    pub fn topics(&self) -> Response<Vec<Topic>> {
        self.master
            .get_topic_types()
            .map(|v| v.into_iter().map(Into::into).collect())
    }

    pub fn client<T: ServicePair>(&self, service: &str) -> Result<Client<T>> {
        let name = self.resolver.translate(service)?;
        let uri = self.master.lookup_service(&name)?;
        Ok(Client::new(&self.name, &uri, &name))
    }

    pub fn wait_for_service(&self, service: &str, timeout: Option<time::Duration>) -> Result<()> {
        use rosxmlrpc::ResponseError;
        use std::thread::sleep;

        let name = self.resolver.translate(service)?;
        let now = ::std::time::Instant::now();
        loop {
            let e = match self.master.lookup_service(&name) {
                Ok(_) => return Ok(()),
                Err(e) => e,
            };
            match e {
                ResponseError::Client(ref m) if m == "no provider" => {
                    if let Some(ref timeout) = timeout {
                        if &now.elapsed() > timeout {
                            return Err(ErrorKind::TimeoutError.into());
                        }
                    }
                    sleep(time::Duration::from_millis(100));
                    continue;
                }
                _ => {}
            }
            return Err(e.into());
        }
    }

    pub fn service<T, F>(&mut self, service: &str, handler: F) -> Result<Service>
    where
        T: ServicePair,
        F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
    {
        let name = self.resolver.translate(service)?;
        Service::new::<T, F>(
            Arc::clone(&self.master),
            Arc::clone(&self.slave),
            &self.hostname,
            &name,
            handler,
        )
    }

    pub fn subscribe<T, F>(&mut self, topic: &str, callback: F) -> Result<Subscriber>
    where
        T: Message,
        F: Fn(T) -> () + Send + 'static,
    {
        let name = self.resolver.translate(topic)?;
        Subscriber::new::<T, F>(
            Arc::clone(&self.master),
            Arc::clone(&self.slave),
            &name,
            callback,
        )
    }

    pub fn publish<T>(&mut self, topic: &str) -> Result<Publisher<T>>
    where
        T: Message,
    {
        let name = self.resolver.translate(topic)?;
        Publisher::new(
            Arc::clone(&self.master),
            Arc::clone(&self.slave),
            Arc::clone(&self.clock),
            &self.hostname,
            &name,
        )
    }

    pub fn log(&mut self, level: i8, msg: String, file: &str, line: u32) {
        let logger = &mut match self.logger {
            Some(ref mut v) => v,
            None => return,
        };
        let topics = self.slave
            .publications
            .lock()
            .expect("Failed to acquire lock on mutex")
            .keys()
            .cloned()
            .collect();
        let message = Log {
            header: Header::default(),
            level,
            msg,
            name: self.name.clone(),
            line,
            file: file.into(),
            function: String::default(),
            topics,
        };
        if let Err(err) = logger.send(message) {
            error!("Logging error: {}", err);
        }
    }
}

pub struct Parameter {
    master: Arc<Master>,
    name: String,
}

impl Parameter {
    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn get<'b, T: Deserialize<'b>>(&self) -> Response<T> {
        self.master.get_param::<T>(&self.name)
    }

    pub fn get_raw(&self) -> Response<xml_rpc::Value> {
        self.master.get_param_any(&self.name)
    }

    pub fn set<T: Serialize>(&self, value: &T) -> Response<()> {
        self.master.set_param::<T>(&self.name, value).and(Ok(()))
    }

    pub fn set_raw(&self, value: xml_rpc::Value) -> Response<()> {
        self.master.set_param_any(&self.name, value).and(Ok(()))
    }

    pub fn delete(&self) -> Response<()> {
        self.master.delete_param(&self.name).and(Ok(()))
    }

    pub fn exists(&self) -> Response<bool> {
        self.master.has_param(&self.name)
    }

    pub fn search(&self) -> Response<String> {
        self.master.search_param(&self.name)
    }
}

fn yaml_to_xmlrpc(val: Yaml) -> Result<xml_rpc::Value> {
    Ok(match val {
        Yaml::Real(v) => xml_rpc::Value::Double(v.parse().chain_err(|| "Failed to parse float")?),
        Yaml::Integer(v) => xml_rpc::Value::Int(v as i32),
        Yaml::String(v) => xml_rpc::Value::String(v),
        Yaml::Boolean(v) => xml_rpc::Value::Bool(v),
        Yaml::Array(v) => {
            xml_rpc::Value::Array(v.into_iter().map(yaml_to_xmlrpc).collect::<Result<_>>()?)
        }
        Yaml::Hash(v) => xml_rpc::Value::Struct(v.into_iter()
            .map(|(k, v)| Ok((yaml_to_string(k)?, yaml_to_xmlrpc(v)?)))
            .collect::<Result<_>>()?),
        Yaml::Alias(_) => bail!("Alias is not supported"),
        Yaml::Null => bail!("Illegal null value"),
        Yaml::BadValue => bail!("Bad value provided"),
    })
}

fn yaml_to_string(val: Yaml) -> Result<String> {
    Ok(match val {
        Yaml::Real(v) | Yaml::String(v) => v,
        Yaml::Integer(v) => v.to_string(),
        Yaml::Boolean(true) => "true".into(),
        Yaml::Boolean(false) => "false".into(),
        _ => bail!("Hash keys need to be strings"),
    })
}

pub struct Spinner {
    shutdown_rx: Option<mpsc::Receiver<()>>,
}

impl Drop for Spinner {
    fn drop(&mut self) {
        if let Some(ref rx) = self.shutdown_rx {
            rx.recv().is_ok();
        }
    }
}
<|endoftext|>


<commit_before>use parser::parser::{parse};
use expr::Expr;
use expr::Expr::*;
use expr::Val::*;
use expr::UnOp::*;
use expr::BinOp::*;
use expr::Dec::*;
use state::State;
use runtime_error::RuntimeError;
use std::result;

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

pub struct Interpreter {
  pub state: State,
}

impl Interpreter {
  pub fn new() -> Interpreter {
    Interpreter {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Val(_) => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, box Val(Bool(b))) => {
        Val(Bool(!b))
      },
      Uop(Neg, box Val(Int(n))) => {
        Val(Int(-1 * n))
      },
      Bop(And, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 && b2))
      },
      Bop(Or, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 || b2))
      },
      Bop(Eq, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 == v2))
      },
      Bop(Ne, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 != v2))
      },
      Bop(Mod, box Val(Int(n1)), box Val(Int(n2))) => {
        // rust % gives the remainder, not modulus
        // let result = ((n1 % n2) + n2) % n2;
        let result = n1.checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?
          .checked_add(n2)
          .ok_or(RuntimeError::IntegerOverflow)?
          .checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?;

        Val(Int(result))
      },
      Bop(Lt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 < n2))
      },
      Bop(Gt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 > n2))
      },
      Bop(Leq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 <= n2))
      },
      Bop(Geq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 >= n2))
      },
      Bop(Plus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_add(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Minus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_sub(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Times, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_mul(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Div, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_div(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Seq, box Val(_), box e1) => {
        e1
      },
      Bop(Assign, box Var(x), box v @ Val(_)) => {
        self.state.assign(x, v.clone())?;
        debug!("done assigning {:?}", self.state.mem);
        v
      },
      Ternary(box Val(Bool(b)), box e1, box e2) => {
        match b {
          true => e1,
          false => e2,
        }
      },
      Decl(DConst, box Var(x), box Val(v1), box e1) => {
        self.state.alloc_const(x, Val(v1))?;
        e1
      },
      Decl(DVar, box Var(x), box Val(v1), box e1) => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x, Val(v1))?;
        e1
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Val(Func(ref name, ref e1, ref xs)) => {
            self.state.begin_scope();

            // alloc the params
            for (xn, en) in xs.iter().zip(es.iter()) {
              self.state.alloc(xn.to_var()?, en.clone())?;
            }

            // alloc the fn body for named functions
            match *name {
              Some(ref s) => self.state.alloc(s.to_var()?, *v1.clone())?,
              _ => {},
            };

            Scope(Box::new(*e1.clone()))
          },
          _ => return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(box v @ Val(_)) => {
        self.state.end_scope();
        v
      },
      While(box Val(Bool(b)), e1o, _, e2o, e3) => {
        match b {
          true => While(e1o.clone(), e1o, e2o.clone(), e2o, e3),
          false => *e3,
        }
      },
      Print(box Val(v)) => {
        println!("{}", v);
        Val(Undefined)
      },
      /**
       * Search Cases
       */
      Bop(op, v1 @ box Val(_), box e2) => {
        Bop(
          op,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(Assign, v1 @ box Var(_), box e2) => {
        Bop(
          Assign,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(op, box e1, e2) => {
        Bop(op, Box::new(self.step(e1)?), e2)
      },
      Uop(op, box e1) => {
        Uop(op, Box::new(self.step(e1)?))
      },
      Ternary(box e1, e2, e3) => {
        Ternary(Box::new(self.step(e1)?), e2, e3)
      },
      While(box e1, e1o, box Val(v), e2o, e3) => {
        While(Box::new(self.step(e1)?), e1o, Box::new(Val(v)), e2o, e3)
      },
      While(e1, e1o, box e2, e2o, e3) => {
        While(e1, e1o, Box::new(self.step(e2)?), e2o, e3)
      },
      Decl(dt, box addr, box e1, e2) => {
        Decl(dt, Box::new(addr.clone()), Box::new(self.step(e1)?), e2)
      },
      FnCall(f @ box Val(Func(_, _, _)), mut args) => {
        // find the first nonvalue arg and call step() on it
        if let Some(index) = args.iter().position(|e| {
          match e {
            &Expr::Val(_) => false,
            _ => true,
          }
        }) {
          args[index] = self.step(args[index].clone())?
        }

        FnCall(f, args)
      },
      FnCall(box e1, args) => {
        FnCall(Box::new(self.step(e1)?), args)
      },
      Scope(box e1) => {
        Scope(Box::new(self.step(e1)?))
      },
      Print(box e1) => {
        Print(Box::new(self.step(e1)?))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = parse(input)?;

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000000000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = self.step(e.clone())?;
      }
    }
  }
}
<commit_msg>avoid destructuring in pattern match when not necessary<commit_after>use parser::parser::{parse};
use expr::Expr;
use expr::Expr::*;
use expr::Val::*;
use expr::UnOp::*;
use expr::BinOp::*;
use expr::Dec::*;
use state::State;
use runtime_error::RuntimeError;
use std::result;

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

pub struct Interpreter {
  pub state: State,
}

impl Interpreter {
  pub fn new() -> Interpreter {
    Interpreter {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Val(_) => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, box Val(Bool(b))) => {
        Val(Bool(!b))
      },
      Uop(Neg, box Val(Int(n))) => {
        Val(Int(-1 * n))
      },
      Bop(And, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 && b2))
      },
      Bop(Or, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 || b2))
      },
      Bop(Eq, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 == v2))
      },
      Bop(Ne, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 != v2))
      },
      Bop(Mod, box Val(Int(n1)), box Val(Int(n2))) => {
        // rust % gives the remainder, not modulus
        // let result = ((n1 % n2) + n2) % n2;
        let result = n1.checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?
          .checked_add(n2)
          .ok_or(RuntimeError::IntegerOverflow)?
          .checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?;

        Val(Int(result))
      },
      Bop(Lt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 < n2))
      },
      Bop(Gt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 > n2))
      },
      Bop(Leq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 <= n2))
      },
      Bop(Geq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 >= n2))
      },
      Bop(Plus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_add(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Minus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_sub(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Times, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_mul(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Div, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_div(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Seq, box Val(_), box e1) => {
        e1
      },
      Bop(Assign, box Var(x), box v @ Val(_)) => {
        self.state.assign(x, v.clone())?;
        debug!("done assigning {:?}", self.state.mem);
        v
      },
      Ternary(box Val(Bool(b)), box e1, box e2) => {
        match b {
          true => e1,
          false => e2,
        }
      },
      Decl(DConst, box Var(x), box v1 @ Val(_), box e1) => {
        self.state.alloc_const(x, v1)?;
        e1
      },
      Decl(DVar, box Var(x), box v1 @ Val(_), box e1) => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x, v1)?;
        e1
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Val(Func(ref name, ref e1, ref xs)) => {
            self.state.begin_scope();

            // alloc the params
            for (xn, en) in xs.iter().zip(es.iter()) {
              self.state.alloc(xn.to_var()?, en.clone())?;
            }

            // alloc the fn body for named functions
            match *name {
              Some(ref s) => self.state.alloc(s.to_var()?, *v1.clone())?,
              _ => {},
            };

            Scope(Box::new(*e1.clone()))
          },
          _ => return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(box v @ Val(_)) => {
        self.state.end_scope();
        v
      },
      While(box Val(Bool(b)), e1o, _, e2o, e3) => {
        match b {
          true => While(e1o.clone(), e1o, e2o.clone(), e2o, e3),
          false => *e3,
        }
      },
      Print(box Val(v)) => {
        println!("{}", v);
        Val(Undefined)
      },
      /**
       * Search Cases
       */
      Bop(op, v1 @ box Val(_), box e2) => {
        Bop(
          op,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(Assign, v1 @ box Var(_), box e2) => {
        Bop(
          Assign,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(op, box e1, e2) => {
        Bop(op, Box::new(self.step(e1)?), e2)
      },
      Uop(op, box e1) => {
        Uop(op, Box::new(self.step(e1)?))
      },
      Ternary(box e1, e2, e3) => {
        Ternary(Box::new(self.step(e1)?), e2, e3)
      },
      While(box e1, e1o, v @ box Val(_), e2o, e3) => {
        While(Box::new(self.step(e1)?), e1o, v, e2o, e3)
      },
      While(e1, e1o, box e2, e2o, e3) => {
        While(e1, e1o, Box::new(self.step(e2)?), e2o, e3)
      },
      Decl(dt, box addr, box e1, e2) => {
        Decl(dt, Box::new(addr.clone()), Box::new(self.step(e1)?), e2)
      },
      FnCall(f @ box Val(Func(_, _, _)), mut args) => {
        // find the first nonvalue arg and call step() on it
        if let Some(index) = args.iter().position(|e| {
          match e {
            &Val(_) => false,
            _ => true,
          }
        }) {
          args[index] = self.step(args[index].clone())?
        }

        FnCall(f, args)
      },
      FnCall(box e1, args) => {
        FnCall(Box::new(self.step(e1)?), args)
      },
      Scope(box e1) => {
        Scope(Box::new(self.step(e1)?))
      },
      Print(box e1) => {
        Print(Box::new(self.step(e1)?))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = parse(input)?;

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000000000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = self.step(e.clone())?;
      }
    }
  }
}
<|endoftext|>


<commit_before>use libc;
use std::ptr::write_bytes;
use time;

use cpu::get_rootset;
use ctxt::get_ctxt;
use mem::ptr::Ptr;
use object::Obj;

const INITIAL_THRESHOLD: usize = 128;
const USED_RATIO: f64 = 0.75;

pub struct Gc {
    memory: Vec<Ptr>,
    bytes_allocated: usize,
    threshold: usize,

    pub duration: u64,
    pub malloc_duration: u64,
    pub collect_duration: u64,
    pub total_allocated: u64,
}

impl Gc {
    pub fn new() -> Gc {
        Gc {
            memory: Vec::new(),
            bytes_allocated: 0,
            threshold: INITIAL_THRESHOLD,
            duration: 0,
            malloc_duration: 0,
            collect_duration: 0,
            total_allocated: 0,
        }
    }

    pub fn alloc(&mut self, size: usize) -> Ptr {
        let alloc_start = time::precise_time_ns();
        let ctxt = get_ctxt();

        if ctxt.args.flag_gc_stress {
            // with --gc-stress collect garbage at every allocation
            // useful for testing
            self.collect();

        // do we pass threshold with this allocation?
        } else if self.bytes_allocated + size > self.threshold {
            // collect garbage
            self.collect();

            // if still more memory than USED_RATIO % of the threshold,
            // we need to increase the threshold
            if (self.bytes_allocated + size) as f64 > self.threshold as f64 * USED_RATIO {
                let saved_threshold = self.threshold;
                self.threshold = (self.threshold as f64 / USED_RATIO) as usize;

                if ctxt.args.flag_gc_dump {
                    println!("GC: increase threshold from {} to {}",
                             saved_threshold, self.threshold);
                }
            }
        }

        let malloc_start = time::precise_time_ns();
        let ptr = unsafe { libc::malloc(size) };
        unsafe { write_bytes(ptr, 0, size); }
        self.malloc_duration += time::precise_time_ns() - malloc_start;

        let ptr = Ptr::new(ptr);

        self.memory.push(ptr);
        self.bytes_allocated += size;
        self.total_allocated += size as u64;

        if ctxt.args.flag_gc_dump {
            println!("GC: allocate {} bytes: {:x} (total {} bytes, threshold {}, {} objects)",
                     size, ptr.raw() as usize,
                     self.bytes_allocated, self.threshold, self.memory.len());
        }

        self.duration += time::precise_time_ns() - alloc_start;

        ptr
    }

    pub fn collect(&mut self) {
        let collect_start = time::precise_time_ns();
        let ctxt = get_ctxt();
        let rootset = get_rootset(ctxt);

        if ctxt.args.flag_gc_dump {
            println!("GC: collect garbage");
        }

        for &ptr in &self.memory {
            let obj = unsafe { &mut *(ptr.raw() as *mut Obj) };
            obj.header_mut().unmark();
        }

        mark_literals();
        mark_rootset(&rootset);
        sweep(self, ctxt.args.flag_gc_dump);

        self.collect_duration += time::precise_time_ns() - collect_start;
    }
}

impl Drop for Gc {
    fn drop(&mut self) {
        for mem in &self.memory {
            unsafe {
                libc::free(mem.raw());
            }
        }
    }
}

fn mark_literals() {
    let ctxt = get_ctxt();
    let literals = ctxt.literals.lock().unwrap();

    for lit in literals.iter() {
        mark_recursive(lit.raw() as usize);
    }
}

fn mark_rootset(rootset: &Vec<usize>) {
    for &root in rootset {
        mark_recursive(root);
    }
}

fn mark_recursive(ptr: usize) {
    if ptr == 0 { return; }
    let obj = unsafe { &mut *(ptr as *mut Obj) };

    if !obj.header().is_marked() {
        obj.header_mut().mark();
        let class = obj.header().class();

        for field in class.all_fields(get_ctxt()) {
            if field.ty.reference_type() {
                let addr = ptr as isize + field.offset as isize;
                let obj = unsafe { *(addr as *const usize) };

                if obj == 0 { return; }
                mark_recursive(obj);
            }
        }
    }
}

fn sweep(gc: &mut Gc, dump: bool) {
    let mut i = 0;

    while i < gc.memory.len() {
        let ptr = gc.memory[i];
        let obj = unsafe { &mut *(ptr.raw() as *mut Obj) };

        if !obj.header().is_marked() {
            let size = obj.size();

            unsafe {
                // TODO: make overwriting memory optional
                write_bytes(ptr.raw() as *mut u8, 0xcc, size);

                libc::free(ptr.raw())
            };

            gc.bytes_allocated -= size;
            gc.memory.remove(i);

            if dump {
                println!("sweep {:x} with {} bytes", ptr.raw() as usize, size);
            }

            continue;
        }

        i += 1;
    }
}
<commit_msg>fixed compile-time error<commit_after>use libc;
use std::ptr::write_bytes;
use time;

use cpu::get_rootset;
use ctxt::get_ctxt;
use mem::ptr::Ptr;
use object::Obj;

const INITIAL_THRESHOLD: usize = 128;
const USED_RATIO: f64 = 0.75;

pub struct Gc {
    memory: Vec<Ptr>,
    bytes_allocated: usize,
    threshold: usize,

    pub duration: u64,
    pub malloc_duration: u64,
    pub collect_duration: u64,
    pub sweep_duration: u64,
    pub total_allocated: u64,
}

impl Gc {
    pub fn new() -> Gc {
        Gc {
            memory: Vec::new(),
            bytes_allocated: 0,
            threshold: INITIAL_THRESHOLD,
            duration: 0,
            malloc_duration: 0,
            collect_duration: 0,
            sweep_duration: 0,
            total_allocated: 0,
        }
    }

    pub fn alloc(&mut self, size: usize) -> Ptr {
        let alloc_start = time::precise_time_ns();
        let ctxt = get_ctxt();

        if ctxt.args.flag_gc_stress {
            // with --gc-stress collect garbage at every allocation
            // useful for testing
            self.collect();

        // do we pass threshold with this allocation?
        } else if self.bytes_allocated + size > self.threshold {
            // collect garbage
            self.collect();

            // if still more memory than USED_RATIO % of the threshold,
            // we need to increase the threshold
            if (self.bytes_allocated + size) as f64 > self.threshold as f64 * USED_RATIO {
                let saved_threshold = self.threshold;
                self.threshold = (self.threshold as f64 / USED_RATIO) as usize;

                if ctxt.args.flag_gc_dump {
                    println!("GC: increase threshold from {} to {}",
                             saved_threshold, self.threshold);
                }
            }
        }

        let malloc_start = time::precise_time_ns();
        let ptr = unsafe { libc::malloc(size) };
        unsafe { write_bytes(ptr, 0, size); }
        self.malloc_duration += time::precise_time_ns() - malloc_start;

        let ptr = Ptr::new(ptr);

        self.memory.push(ptr);
        self.bytes_allocated += size;
        self.total_allocated += size as u64;

        if ctxt.args.flag_gc_dump {
            println!("GC: allocate {} bytes: {:x} (total {} bytes, threshold {}, {} objects)",
                     size, ptr.raw() as usize,
                     self.bytes_allocated, self.threshold, self.memory.len());
        }

        self.duration += time::precise_time_ns() - alloc_start;

        ptr
    }

    pub fn collect(&mut self) {
        let collect_start = time::precise_time_ns();
        let ctxt = get_ctxt();
        let rootset = get_rootset(ctxt);

        if ctxt.args.flag_gc_dump {
            println!("GC: collect garbage");
        }

        for &ptr in &self.memory {
            let obj = unsafe { &mut *(ptr.raw() as *mut Obj) };
            obj.header_mut().unmark();
        }

        mark_literals();
        mark_rootset(&rootset);

        let sweep_start = time::precise_time_ns();
        sweep(self, ctxt.args.flag_gc_dump);
        self.sweep_duration += time::precise_time_ns() - sweep_start;

        self.collect_duration += time::precise_time_ns() - collect_start;
    }
}

impl Drop for Gc {
    fn drop(&mut self) {
        for mem in &self.memory {
            unsafe {
                libc::free(mem.raw());
            }
        }
    }
}

fn mark_literals() {
    let ctxt = get_ctxt();
    let literals = ctxt.literals.lock().unwrap();

    for lit in literals.iter() {
        mark_recursive(lit.raw() as usize);
    }
}

fn mark_rootset(rootset: &Vec<usize>) {
    for &root in rootset {
        mark_recursive(root);
    }
}

fn mark_recursive(ptr: usize) {
    if ptr == 0 { return; }
    let obj = unsafe { &mut *(ptr as *mut Obj) };

    if !obj.header().is_marked() {
        obj.header_mut().mark();
        let class = obj.header().class();

        for field in class.all_fields(get_ctxt()) {
            if field.ty.reference_type() {
                let addr = ptr as isize + field.offset as isize;
                let obj = unsafe { *(addr as *const usize) };

                if obj == 0 { return; }
                mark_recursive(obj);
            }
        }
    }
}

fn sweep(gc: &mut Gc, dump: bool) {
    let mut i = 0;

    while i < gc.memory.len() {
        let ptr = gc.memory[i];
        let obj = unsafe { &mut *(ptr.raw() as *mut Obj) };

        if !obj.header().is_marked() {
            let size = obj.size();

            unsafe {
                // TODO: make overwriting memory optional
                write_bytes(ptr.raw() as *mut u8, 0xcc, size);

                libc::free(ptr.raw())
            };

            gc.bytes_allocated -= size;
            gc.memory.remove(i);

            if dump {
                println!("sweep {:x} with {} bytes", ptr.raw() as usize, size);
            }

            continue;
        }

        i += 1;
    }
}
<|endoftext|>


<commit_before>use std::io::{stdin, Read};
use std::fmt;
use std::fs::File;
use std::sync::Mutex;

use futures::{Future, IntoFuture};
use futures::future::Either;

use vm::{self, ExternModule, Result, Variants};
use vm::future::FutureValue;
use vm::gc::{Gc, Traverseable};
use vm::types::*;
use vm::thread::{Thread, ThreadInternal};
use vm::api::{Array, FutureResult, Generic, Getable, OpaqueValue, OwnedFunction, PrimitiveFuture,
              TypedBytecode, Userdata, VmType, WithVM, IO};
use vm::api::generic::{A, B};
use vm::stack::{StackFrame, State};
use vm::internal::ValuePrinter;

use vm::internal::Value;

use compiler_pipeline::*;

use super::{Compiler, Error};

fn print(s: &str) -> IO<()> {
    print!("{}", s);
    IO::Value(())
}

fn println(s: &str) -> IO<()> {
    println!("{}", s);
    IO::Value(())
}

struct GluonFile(Mutex<File>);

impl Userdata for GluonFile {}

impl fmt::Debug for GluonFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "File")
    }
}

impl VmType for GluonFile {
    type Type = GluonFile;
}

impl Traverseable for GluonFile {
    fn traverse(&self, _: &mut Gc) {}
}

fn open_file(s: &str) -> IO<GluonFile> {
    match File::open(s) {
        Ok(f) => IO::Value(GluonFile(Mutex::new(f))),
        Err(err) => IO::Exception(format!("{}", err)),
    }
}

fn read_file<'vm>(file: WithVM<'vm, &GluonFile>, count: usize) -> IO<Array<'vm, u8>> {
    let WithVM { vm, value: file } = file;
    let mut file = file.0.lock().unwrap();
    let mut buffer = Vec::with_capacity(count);
    unsafe {
        buffer.set_len(count);
        match file.read(&mut *buffer) {
            Ok(bytes_read) => {
                let value = {
                    let mut context = vm.context();
                    match context.alloc(&buffer[..bytes_read]) {
                        Ok(value) => value,
                        Err(err) => return IO::Exception(format!("{}", err)),
                    }
                };
                IO::Value(Getable::from_value(vm, Variants::new(&Value::Array(value))))
            }
            Err(err) => IO::Exception(format!("{}", err)),
        }
    }
}

fn read_file_to_string(s: &str) -> IO<String> {
    let mut buffer = String::new();
    match File::open(s).and_then(|mut file| file.read_to_string(&mut buffer)) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

fn read_char() -> IO<char> {
    match stdin().bytes().next() {
        Some(result) => match result {
            Ok(b) => ::std::char::from_u32(b as u32)
                .map(IO::Value)
                .unwrap_or_else(|| IO::Exception("Not a valid char".into())),
            Err(err) => IO::Exception(format!("{}", err)),
        },
        None => IO::Exception("No read".into()),
    }
}

fn read_line() -> IO<String> {
    let mut buffer = String::new();
    match stdin().read_line(&mut buffer) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

/// IO a -> (String -> IO a) -> IO a
fn catch<'vm>(
    action: OpaqueValue<&'vm Thread, IO<A>>,
    mut catch: OwnedFunction<fn(String) -> IO<Generic<A>>>,
) -> FutureResult<Box<Future<Item = IO<Generic<A>>, Error = vm::Error> + Send>> {
    let vm = action.vm().root_thread();
    let frame_level = vm.context().stack.get_frames().len();
    let mut action: OwnedFunction<fn(()) -> Generic<A>> =
        unsafe { Getable::from_value(&vm, Variants::new(&action.get_value())) };

    let future = action.call_async(()).then(move |result| match result {
        Ok(value) => Either::A(Ok(IO::Value(value)).into_future()),
        Err(err) => {
            {
                let mut context = vm.context();
                let mut stack = StackFrame::current(&mut context.stack);
                while stack.stack.get_frames().len() > frame_level {
                    if stack.exit_scope().is_err() {
                        return Either::A(Ok(IO::Exception("Unknown error".into())).into_future());
                    }
                }
            }
            Either::B(catch.call_async(format!("{}", err)).then(|result| {
                Ok(match result {
                    Ok(value) => value,
                    Err(err) => IO::Exception(format!("{}", err)),
                })
            }))
        }
    });

    FutureResult(Box::new(future))
}

fn clear_frames(err: Error, mut stack: StackFrame) -> IO<String> {
    let frame_level = stack
        .stack
        .get_frames()
        .iter()
        .rposition(|frame| frame.state == State::Lock)
        .unwrap_or(0);

    let fmt = match err {
        Error::VM(vm::Error::Panic(_)) => {
            let trace = stack.stack.stacktrace(frame_level);
            format!("{}\n{}", err, trace)
        }
        _ => format!("{}", err),
    };
    while let Ok(_) = stack.exit_scope() {}
    IO::Exception(fmt)
}

fn run_expr(WithVM { vm, value: expr }: WithVM<&str>) -> PrimitiveFuture<IO<String>> {
    let vm = vm.root_thread();

    let vm1 = vm.clone();
    let future = expr.run_expr(&mut Compiler::new(), vm1, "<top>", expr, None)
        .then(move |run_result| {
            let mut context = vm.context();
            let stack = StackFrame::current(&mut context.stack);
            FutureValue::sync(Ok(match run_result {
                Ok(execute_value) => {
                    let env = vm.global_env().get_env();
                    let typ = execute_value.typ;
                    IO::Value(format!(
                        "{} : {}",
                        ValuePrinter::new(&*env, &typ, *execute_value.value).width(80),
                        typ
                    ))
                }
                Err(err) => clear_frames(err, stack),
            }))
        });

    future.boxed()
}

fn load_script(
    WithVM { vm, value: name }: WithVM<&str>,
    expr: &str,
) -> PrimitiveFuture<IO<String>> {
    let vm1 = vm.root_thread();
    let vm = vm.root_thread();
    let name = name.to_string();
    let future = expr.load_script(&mut Compiler::new(), vm1, &name, expr, None)
        .then(move |run_result| {
            let mut context = vm.context();
            let stack = StackFrame::current(&mut context.stack);
            let io = match run_result {
                Ok(()) => IO::Value(format!("Loaded {}", name)),
                Err(err) => clear_frames(err, stack),
            };
            Ok(io).into()
        });
    future.boxed()
}

mod std {
    pub mod io {
        pub use io as prim;
    }
}

pub fn load(vm: &Thread) -> Result<ExternModule> {
    vm.register_type::<GluonFile>("File", &[])?;

    // flat_map f m : (a -> IO b) -> IO a -> IO b
    //     = f (m ())
    let flat_map = vec![
        // [f, m, ()]       Initial stack
        Call(1),     // [f, m_ret]       Call m ()
        PushInt(0),  // [f, m_ret, ()]   Add a dummy argument ()
        TailCall(2), /* [f_ret]          Call f m_ret () */
    ];

    type FlatMap = fn(fn(A) -> IO<B>, IO<A>) -> IO<B>;
    type Wrap = fn(A) -> IO<A>;

    let wrap = vec![Pop(1)];

    use self::std;

    // IO functions
    ExternModule::new(
        vm,
        record! {
            flat_map => TypedBytecode::<FlatMap>::new("std.io.prim.flat_map", 3, flat_map),
            wrap => TypedBytecode::<Wrap>::new("std.io.prim.wrap", 2, wrap),
            open_file => primitive!(1 std::io::prim::open_file),
            read_file => primitive!(2 std::io::prim::read_file),
            read_file_to_string => primitive!(1 std::io::prim::read_file_to_string),
            read_char => primitive!(0 std::io::prim::read_char),
            read_line => primitive!(0 std::io::prim::read_line),
            print => primitive!(1 std::io::prim::print),
            println => primitive!(1 std::io::prim::println),
            catch => primitive!(2 std::io::prim::catch),
            run_expr => primitive!(1 std::io::prim::run_expr),
            load_script => primitive!(2 std::io::prim::load_script),
        },
    )
}
<commit_msg>fix(vm): Run IO expressions with io.run_expr<commit_after>use std::io::{stdin, Read};
use std::fmt;
use std::fs::File;
use std::sync::Mutex;

use futures::{Future, IntoFuture};
use futures::future::Either;

use vm::{self, ExternModule, Result, Variants};
use vm::future::FutureValue;
use vm::gc::{Gc, Traverseable};
use vm::types::*;
use vm::thread::{Thread, ThreadInternal};
use vm::api::{Array, FutureResult, Generic, Getable, OpaqueValue, OwnedFunction, PrimitiveFuture,
              TypedBytecode, Userdata, VmType, WithVM, IO};
use vm::api::generic::{A, B};
use vm::stack::{StackFrame, State};
use vm::internal::ValuePrinter;

use vm::internal::Value;

use compiler_pipeline::*;

use super::{Compiler, Error};

fn print(s: &str) -> IO<()> {
    print!("{}", s);
    IO::Value(())
}

fn println(s: &str) -> IO<()> {
    println!("{}", s);
    IO::Value(())
}

struct GluonFile(Mutex<File>);

impl Userdata for GluonFile {}

impl fmt::Debug for GluonFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "File")
    }
}

impl VmType for GluonFile {
    type Type = GluonFile;
}

impl Traverseable for GluonFile {
    fn traverse(&self, _: &mut Gc) {}
}

fn open_file(s: &str) -> IO<GluonFile> {
    match File::open(s) {
        Ok(f) => IO::Value(GluonFile(Mutex::new(f))),
        Err(err) => IO::Exception(format!("{}", err)),
    }
}

fn read_file<'vm>(file: WithVM<'vm, &GluonFile>, count: usize) -> IO<Array<'vm, u8>> {
    let WithVM { vm, value: file } = file;
    let mut file = file.0.lock().unwrap();
    let mut buffer = Vec::with_capacity(count);
    unsafe {
        buffer.set_len(count);
        match file.read(&mut *buffer) {
            Ok(bytes_read) => {
                let value = {
                    let mut context = vm.context();
                    match context.alloc(&buffer[..bytes_read]) {
                        Ok(value) => value,
                        Err(err) => return IO::Exception(format!("{}", err)),
                    }
                };
                IO::Value(Getable::from_value(vm, Variants::new(&Value::Array(value))))
            }
            Err(err) => IO::Exception(format!("{}", err)),
        }
    }
}

fn read_file_to_string(s: &str) -> IO<String> {
    let mut buffer = String::new();
    match File::open(s).and_then(|mut file| file.read_to_string(&mut buffer)) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

fn read_char() -> IO<char> {
    match stdin().bytes().next() {
        Some(result) => match result {
            Ok(b) => ::std::char::from_u32(b as u32)
                .map(IO::Value)
                .unwrap_or_else(|| IO::Exception("Not a valid char".into())),
            Err(err) => IO::Exception(format!("{}", err)),
        },
        None => IO::Exception("No read".into()),
    }
}

fn read_line() -> IO<String> {
    let mut buffer = String::new();
    match stdin().read_line(&mut buffer) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

/// IO a -> (String -> IO a) -> IO a
fn catch<'vm>(
    action: OpaqueValue<&'vm Thread, IO<A>>,
    mut catch: OwnedFunction<fn(String) -> IO<Generic<A>>>,
) -> FutureResult<Box<Future<Item = IO<Generic<A>>, Error = vm::Error> + Send>> {
    let vm = action.vm().root_thread();
    let frame_level = vm.context().stack.get_frames().len();
    let mut action: OwnedFunction<fn(()) -> Generic<A>> =
        unsafe { Getable::from_value(&vm, Variants::new(&action.get_value())) };

    let future = action.call_async(()).then(move |result| match result {
        Ok(value) => Either::A(Ok(IO::Value(value)).into_future()),
        Err(err) => {
            {
                let mut context = vm.context();
                let mut stack = StackFrame::current(&mut context.stack);
                while stack.stack.get_frames().len() > frame_level {
                    if stack.exit_scope().is_err() {
                        return Either::A(Ok(IO::Exception("Unknown error".into())).into_future());
                    }
                }
            }
            Either::B(catch.call_async(format!("{}", err)).then(|result| {
                Ok(match result {
                    Ok(value) => value,
                    Err(err) => IO::Exception(format!("{}", err)),
                })
            }))
        }
    });

    FutureResult(Box::new(future))
}

fn clear_frames(err: Error, mut stack: StackFrame) -> IO<String> {
    let frame_level = stack
        .stack
        .get_frames()
        .iter()
        .rposition(|frame| frame.state == State::Lock)
        .unwrap_or(0);

    let fmt = match err {
        Error::VM(vm::Error::Panic(_)) => {
            let trace = stack.stack.stacktrace(frame_level);
            format!("{}\n{}", err, trace)
        }
        _ => format!("{}", err),
    };
    while let Ok(_) = stack.exit_scope() {}
    IO::Exception(fmt)
}

fn run_expr(WithVM { vm, value: expr }: WithVM<&str>) -> PrimitiveFuture<IO<String>> {
    let vm = vm.root_thread();

    let vm1 = vm.clone();
    let future = expr.run_expr(&mut Compiler::new().run_io(true), vm1, "<top>", expr, None)
        .then(move |run_result| {
            let mut context = vm.context();
            let stack = StackFrame::current(&mut context.stack);
            FutureValue::sync(Ok(match run_result {
                Ok(execute_value) => {
                    let env = vm.global_env().get_env();
                    let typ = execute_value.typ;
                    IO::Value(format!(
                        "{} : {}",
                        ValuePrinter::new(&*env, &typ, *execute_value.value).width(80),
                        typ
                    ))
                }
                Err(err) => clear_frames(err, stack),
            }))
        });

    future.boxed()
}

fn load_script(
    WithVM { vm, value: name }: WithVM<&str>,
    expr: &str,
) -> PrimitiveFuture<IO<String>> {
    let vm1 = vm.root_thread();
    let vm = vm.root_thread();
    let name = name.to_string();
    let future = expr.load_script(&mut Compiler::new(), vm1, &name, expr, None)
        .then(move |run_result| {
            let mut context = vm.context();
            let stack = StackFrame::current(&mut context.stack);
            let io = match run_result {
                Ok(()) => IO::Value(format!("Loaded {}", name)),
                Err(err) => clear_frames(err, stack),
            };
            Ok(io).into()
        });
    future.boxed()
}

mod std {
    pub mod io {
        pub use io as prim;
    }
}

pub fn load(vm: &Thread) -> Result<ExternModule> {
    vm.register_type::<GluonFile>("File", &[])?;

    // flat_map f m : (a -> IO b) -> IO a -> IO b
    //     = f (m ())
    let flat_map = vec![
        // [f, m, ()]       Initial stack
        Call(1),     // [f, m_ret]       Call m ()
        PushInt(0),  // [f, m_ret, ()]   Add a dummy argument ()
        TailCall(2), /* [f_ret]          Call f m_ret () */
    ];

    type FlatMap = fn(fn(A) -> IO<B>, IO<A>) -> IO<B>;
    type Wrap = fn(A) -> IO<A>;

    let wrap = vec![Pop(1)];

    use self::std;

    // IO functions
    ExternModule::new(
        vm,
        record! {
            flat_map => TypedBytecode::<FlatMap>::new("std.io.prim.flat_map", 3, flat_map),
            wrap => TypedBytecode::<Wrap>::new("std.io.prim.wrap", 2, wrap),
            open_file => primitive!(1 std::io::prim::open_file),
            read_file => primitive!(2 std::io::prim::read_file),
            read_file_to_string => primitive!(1 std::io::prim::read_file_to_string),
            read_char => primitive!(0 std::io::prim::read_char),
            read_line => primitive!(0 std::io::prim::read_line),
            print => primitive!(1 std::io::prim::print),
            println => primitive!(1 std::io::prim::println),
            catch => primitive!(2 std::io::prim::catch),
            run_expr => primitive!(1 std::io::prim::run_expr),
            load_script => primitive!(2 std::io::prim::load_script),
        },
    )
}
<|endoftext|>