<commit_before>extern crate serde;
extern crate serde_json;
extern crate glob;

#[cfg(feature = "tera_templates")]
mod tera_templates;

#[cfg(feature = "handlebars_templates")]
mod handlebars_templates;

#[macro_use] mod macros;

use self::serde::Serialize;
use self::glob::glob;

use std::path::{Path, PathBuf};
use std::collections::HashMap;

use rocket::config;
use rocket::response::{self, Content, Responder};
use rocket::http::{ContentType, Status};

/// The Template type implements generic support for template rendering in
/// Rocket.
///
/// Templating in Rocket words by first discovering all of the templates inside
/// the template directory. The template directory is configurable via the
/// `template_dir` configuration parameter and defaults to `templates/`. The
/// path set in `template_dir` should be relative to the Rocket configuration
/// file. See the [configuration chapter](https://rocket.rs/guide/overview/#configuration)
/// of the guide for more information on configuration.
///
/// Templates are discovered according to their extension. At present, this
/// library supports the following templates and extensions:
///
/// * **Tera**: `.tera`
/// * **Handlebars**: `.hbs`
///
/// Any file that ends with one of these extension will be discovered and
/// rendered with the corresponding templating engine. The name of the template
/// will be the path to the template file relative to `template_dir` minus at
/// most two extensions. The following are examples of template names (on the
/// right) given that the template is at the path on the left.
///
///   * `{template_dir}/index.html.hbs` => index
///   * `{template_dir}/index.tera` => index
///   * `{template_dir}/index.hbs` => index
///   * `{template_dir}/dir/index.hbs` => dir/index
///   * `{template_dir}/dir/index.html.tera` => dir/index
///   * `{template_dir}/index.template.html.hbs` => index.template
///   * `{template_dir}/subdir/index.template.html.hbs` => subdir/index.template
///
/// The recommended naming scheme is to use two extensions: one for the file
/// type, and one for the template extension. This means that template
/// extensions should look like: `.html.hbs`, `.html.tera`, `.xml.hbs`, etc.
///
/// Templates are rendered with the `render` method. The method takes in the
/// name of a template and a context to render the template with. The context
/// can be any type that implements `Serialize` from
/// [Serde](https://github.com/serde-rs/json) and would serialize to an `Object`
/// value.
///
/// # Usage
///
/// To use, add the `handlebars_templates` feature, the `tera_templates`
/// feature, or both, to the `rocket_contrib` dependencies section of your
/// `Cargo.toml`:
///
/// ```toml,ignore
/// [dependencies.rocket_contrib]
/// version = "*"
/// default-features = false
/// features = ["handlebars_templates", "tera_templates"]
/// ```
///
/// # Examples
///
/// To render a template named "index" with a `HashMap` as the context:
///
/// ```rust
/// use rocket_contrib::Template;
/// use std::collections::HashMap;
///
/// let context: HashMap<&str, &str> = HashMap::new();
/// // ... add key/value pairs to `context` ...
/// let _template = Template::render("index", &context);
/// ```
///
/// The Template type implements Rocket's `Responder` trait, so it can be
/// returned from a request handler directly:
///
/// ```rust,ignore
/// #[get("/")]
/// fn index() -> Template {
///     let context = ...;
///     Template::render("index", &context)
/// }
/// ```
#[derive(Debug)]
pub struct Template(Option<String>, Option<String>);

#[derive(Debug)]
pub struct TemplateInfo {
    /// The complete path, including `template_dir`, to this template.
    full_path: PathBuf,
    /// The complete path, without `template_dir`, to this template.
    path: PathBuf,
    /// The extension for the engine of this template.
    extension: String,
    /// The extension before the engine extension in the template, if any.
    data_type: Option<String>
}

const DEFAULT_TEMPLATE_DIR: &'static str = "templates";

lazy_static! {
    static ref TEMPLATES: HashMap<String, TemplateInfo> = discover_templates();
    static ref TEMPLATE_DIR: String = {
        config::active().map(|config| {
            let dir = config.get_str("template_dir").map_err(|e| {
                if !e.is_not_found() {
                    e.pretty_print();
                    warn_!("Using default directory '{}'", DEFAULT_TEMPLATE_DIR);
                }
            }).unwrap_or(DEFAULT_TEMPLATE_DIR);

            config.root().join(dir).to_string_lossy().into_owned()
        }).unwrap_or(DEFAULT_TEMPLATE_DIR.to_string())
    };
}

impl Template {
    /// Render the template named `name` with the context `context`. The
    /// template is not actually rendered until the response is needed by
    /// Rocket. As such, the `Template` type should be used only as a
    /// `Responder`.
    pub fn render<S, T>(name: S, context: &T) -> Template
        where S: AsRef<str>, T: Serialize
    {
        let name = name.as_ref();
        let template = TEMPLATES.get(name);
        if template.is_none() {
            let names: Vec<_> = TEMPLATES.keys().map(|s| s.as_str()).collect();
            error_!("Template '{}' does not exist.", name);
            info_!("Known templates: {}", names.join(","));
            info_!("Searched in '{}'.", *TEMPLATE_DIR);
            return Template(None, None);
        }

        // Keep this set in-sync with the `engine_set` invocation.
        render_set!(name, template.unwrap(), context,
            "tera_templates" => tera_templates,
            "handlebars_templates" => handlebars_templates,
        );

        unreachable!("A template extension was discovered but not rendered.")
    }
}

impl Responder<'static> for Template {
    fn respond(self) -> response::Result<'static> {
        let content_type = match self.1 {
            Some(ref ext) => ContentType::from_extension(ext),
            None => ContentType::HTML
        };

        match self.0 {
            Some(render) => Content(content_type, render).respond(),
            None => Err(Status::InternalServerError)
        }
    }
}

/// Removes the file path's extension or does nothing if there is none.
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
    let path = path.as_ref();
    let stem = match path.file_stem() {
        Some(stem) => stem,
        None => return path.to_path_buf()
    };

    match path.parent() {
        Some(parent) => parent.join(stem),
        None => PathBuf::from(stem)
    }
}

/// Splits a path into a relative path from TEMPLATE_DIR, a name that
/// may be used to identify the template, and the template's data type.
fn split_path(path: &Path) -> (PathBuf, String, Option<String>) {
    let rel_path = path.strip_prefix(&*TEMPLATE_DIR).unwrap().to_path_buf();
    let path_no_ext = remove_extension(&rel_path);
    let data_type = path_no_ext.extension();
    let mut name = remove_extension(&path_no_ext).to_string_lossy().into_owned();

    // Ensure template name consistency on Windows systems
    if cfg!(windows) {
        name = name.replace("\\", "/");
    }

    (rel_path, name, data_type.map(|d| d.to_string_lossy().into_owned()))
}

/// Returns a HashMap of `TemplateInfo`'s for all of the templates in
/// `TEMPLATE_DIR`. Templates are all files that match one of the extensions for
/// engine's in `engine_set`.
fn discover_templates() -> HashMap<String, TemplateInfo> {
    // Keep this set in-sync with the `render_set` invocation.
    let engines = engine_set![
        "tera_templates" => tera_templates,
        "handlebars_templates" => handlebars_templates,
    ];

    let mut templates = HashMap::new();
    for ext in engines {
        let mut glob_path: PathBuf = [&*TEMPLATE_DIR, "**", "*"].iter().collect();
        glob_path.set_extension(ext);
        for path in glob(glob_path.to_str().unwrap()).unwrap().filter_map(Result::ok) {
            let (rel_path, name, data_type) = split_path(&path);
            templates.insert(name, TemplateInfo {
                full_path: path.to_path_buf(),
                path: rel_path,
                extension: path.extension().unwrap().to_string_lossy().into_owned(),
                data_type: data_type,
            });
        }
    }

    templates
}

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

    /// Combines a `relative_path` and the `TEMPLATE_DIR` path into a full path.
    fn template_path(relative_path: &str) -> PathBuf {
        let mut path = PathBuf::from(&*TEMPLATE_DIR);
        path.push(relative_path);
        path
    }

    /// Returns the template system name, given a relative path to a file.
    fn relative_path_to_name(relative_path: &str) -> String {
        let path = template_path(relative_path);
        let (_, name, _) = split_path(&path);
        name
    }

    #[test]
    fn template_path_index_html() {
        let path = template_path("index.html.hbs");
        let (rel_path, name, data_type) = split_path(&path);

        assert_eq!(rel_path.to_string_lossy(), "index.html.hbs");
        assert_eq!(name, "index");
        assert_eq!(data_type, Some("html".to_owned()));
    }

    #[test]
    fn template_path_subdir_index_html() {
        let path = template_path("subdir/index.html.hbs");
        let (rel_path, name, data_type) = split_path(&path);

        assert_eq!(rel_path.to_string_lossy(), "subdir/index.html.hbs");
        assert_eq!(name, "subdir/index");
        assert_eq!(data_type, Some("html".to_owned()));
    }

    #[test]
    fn template_path_doc_examples() {
        assert_eq!(relative_path_to_name("index.html.hbs"), "index");
        assert_eq!(relative_path_to_name("index.tera"), "index");
        assert_eq!(relative_path_to_name("index.hbs"), "index");
        assert_eq!(relative_path_to_name("dir/index.hbs"), "dir/index");
        assert_eq!(relative_path_to_name("dir/index.html.tera"), "dir/index");
        assert_eq!(relative_path_to_name("index.template.html.hbs"),
                   "index.template");
        assert_eq!(relative_path_to_name("subdir/index.template.html.hbs"),
                   "subdir/index.template");
    }
}
<commit_msg>Improve Template docs and implement Display.<commit_after>extern crate serde;
extern crate serde_json;
extern crate glob;

#[cfg(feature = "tera_templates")]
mod tera_templates;

#[cfg(feature = "handlebars_templates")]
mod handlebars_templates;

#[macro_use] mod macros;

use self::serde::Serialize;
use self::glob::glob;

use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::fmt;

use rocket::config;
use rocket::response::{self, Content, Responder};
use rocket::http::{ContentType, Status};

/// The Template type implements generic support for template rendering in
/// Rocket.
///
/// Templating in Rocket words by first discovering all of the templates inside
/// the template directory. The template directory is configurable via the
/// `template_dir` configuration parameter and defaults to `templates/`. The
/// path set in `template_dir` should be relative to the Rocket configuration
/// file. See the [configuration chapter](https://rocket.rs/guide/overview/#configuration)
/// of the guide for more information on configuration.
///
/// Templates are discovered according to their extension. At present, this
/// library supports the following templates and extensions:
///
/// * **Tera**: `.tera`
/// * **Handlebars**: `.hbs`
///
/// Any file that ends with one of these extension will be discovered and
/// rendered with the corresponding templating engine. The name of the template
/// will be the path to the template file relative to `template_dir` minus at
/// most two extensions. The following are examples of template names (on the
/// right) given that the template is at the path on the left.
///
///   * `{template_dir}/index.html.hbs` => index
///   * `{template_dir}/index.tera` => index
///   * `{template_dir}/index.hbs` => index
///   * `{template_dir}/dir/index.hbs` => dir/index
///   * `{template_dir}/dir/index.html.tera` => dir/index
///   * `{template_dir}/index.template.html.hbs` => index.template
///   * `{template_dir}/subdir/index.template.html.hbs` => subdir/index.template
///
/// The recommended naming scheme is to use two extensions: one for the file
/// type, and one for the template extension. This means that template
/// extensions should look like: `.html.hbs`, `.html.tera`, `.xml.hbs`, etc.
///
/// Templates are rendered with the `render` method. The method takes in the
/// name of a template and a context to render the template with. The context
/// can be any type that implements `Serialize` from
/// [Serde](https://github.com/serde-rs/json) and would serialize to an `Object`
/// value.
///
/// # Usage
///
/// To use, add the `handlebars_templates` feature, the `tera_templates`
/// feature, or both, to the `rocket_contrib` dependencies section of your
/// `Cargo.toml`:
///
/// ```toml,ignore
/// [dependencies.rocket_contrib]
/// version = "*"
/// default-features = false
/// features = ["handlebars_templates", "tera_templates"]
/// ```
///
/// The Template type implements Rocket's `Responder` trait, so it can be
/// returned from a request handler directly:
///
/// ```rust,ignore
/// #[get("/")]
/// fn index() -> Template {
///     let context = ...;
///     Template::render("index", &context)
/// }
/// ```
// Fields are: (optionally rendered template, template extension)
#[derive(Debug)]
pub struct Template(Option<String>, Option<String>);

#[derive(Debug)]
pub struct TemplateInfo {
    /// The complete path, including `template_dir`, to this template.
    full_path: PathBuf,
    /// The complete path, without `template_dir`, to this template.
    path: PathBuf,
    /// The extension for the engine of this template.
    extension: String,
    /// The extension before the engine extension in the template, if any.
    data_type: Option<String>
}

const DEFAULT_TEMPLATE_DIR: &'static str = "templates";

lazy_static! {
    static ref TEMPLATES: HashMap<String, TemplateInfo> = discover_templates();
    static ref TEMPLATE_DIR: String = {
        config::active().map(|config| {
            let dir = config.get_str("template_dir").map_err(|e| {
                if !e.is_not_found() {
                    e.pretty_print();
                    warn_!("Using default directory '{}'", DEFAULT_TEMPLATE_DIR);
                }
            }).unwrap_or(DEFAULT_TEMPLATE_DIR);

            config.root().join(dir).to_string_lossy().into_owned()
        }).unwrap_or(DEFAULT_TEMPLATE_DIR.to_string())
    };
}

impl Template {
    /// Render the template named `name` with the context `context`. The
    /// `context` can be of any type that implements `Serialize`. This is
    /// typically a `HashMap` or a custom `struct`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::HashMap;
    /// use rocket_contrib::Template;
    ///
    /// // Create a `context`. Here, just an empty `HashMap`.
    /// let mut context = HashMap::new();
    ///
    /// # context.insert("test", "test");
    /// let template = Template::render("index", &context);
    /// ```
    pub fn render<S, T>(name: S, context: &T) -> Template
        where S: AsRef<str>, T: Serialize
    {
        let name = name.as_ref();
        let template = TEMPLATES.get(name);
        if template.is_none() {
            let names: Vec<_> = TEMPLATES.keys().map(|s| s.as_str()).collect();
            error_!("Template '{}' does not exist.", name);
            info_!("Known templates: {}", names.join(","));
            info_!("Searched in '{}'.", *TEMPLATE_DIR);
            return Template(None, None);
        }

        // Keep this set in-sync with the `engine_set` invocation.
        render_set!(name, template.unwrap(), context,
            "tera_templates" => tera_templates,
            "handlebars_templates" => handlebars_templates,
        );

        unreachable!("A template extension was discovered but not rendered.")
    }
}

/// Returns a response with the Content-Type derived from the template's
/// extension and a fixed-size body containing the rendered template. If
/// rendering fails, an `Err` of `Status::InternalServerError` is returned.
impl Responder<'static> for Template {
    fn respond(self) -> response::Result<'static> {
        let content_type = match self.1 {
            Some(ref ext) => ContentType::from_extension(ext),
            None => ContentType::HTML
        };

        match self.0 {
            Some(render) => Content(content_type, render).respond(),
            None => Err(Status::InternalServerError)
        }
    }
}

/// Renders `self`. If the template cannot be rendered, nothing is written.
impl fmt::Display for Template {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(ref render) => render.fmt(f),
            None => Ok(())
        }
    }
}

/// Removes the file path's extension or does nothing if there is none.
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
    let path = path.as_ref();
    let stem = match path.file_stem() {
        Some(stem) => stem,
        None => return path.to_path_buf()
    };

    match path.parent() {
        Some(parent) => parent.join(stem),
        None => PathBuf::from(stem)
    }
}

/// Splits a path into a relative path from TEMPLATE_DIR, a name that
/// may be used to identify the template, and the template's data type.
fn split_path(path: &Path) -> (PathBuf, String, Option<String>) {
    let rel_path = path.strip_prefix(&*TEMPLATE_DIR).unwrap().to_path_buf();
    let path_no_ext = remove_extension(&rel_path);
    let data_type = path_no_ext.extension();
    let mut name = remove_extension(&path_no_ext).to_string_lossy().into_owned();

    // Ensure template name consistency on Windows systems
    if cfg!(windows) {
        name = name.replace("\\", "/");
    }

    (rel_path, name, data_type.map(|d| d.to_string_lossy().into_owned()))
}

/// Returns a HashMap of `TemplateInfo`'s for all of the templates in
/// `TEMPLATE_DIR`. Templates are all files that match one of the extensions for
/// engine's in `engine_set`.
fn discover_templates() -> HashMap<String, TemplateInfo> {
    // Keep this set in-sync with the `render_set` invocation.
    let engines = engine_set![
        "tera_templates" => tera_templates,
        "handlebars_templates" => handlebars_templates,
    ];

    let mut templates = HashMap::new();
    for ext in engines {
        let mut glob_path: PathBuf = [&*TEMPLATE_DIR, "**", "*"].iter().collect();
        glob_path.set_extension(ext);
        for path in glob(glob_path.to_str().unwrap()).unwrap().filter_map(Result::ok) {
            let (rel_path, name, data_type) = split_path(&path);
            templates.insert(name, TemplateInfo {
                full_path: path.to_path_buf(),
                path: rel_path,
                extension: path.extension().unwrap().to_string_lossy().into_owned(),
                data_type: data_type,
            });
        }
    }

    templates
}

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

    /// Combines a `relative_path` and the `TEMPLATE_DIR` path into a full path.
    fn template_path(relative_path: &str) -> PathBuf {
        let mut path = PathBuf::from(&*TEMPLATE_DIR);
        path.push(relative_path);
        path
    }

    /// Returns the template system name, given a relative path to a file.
    fn relative_path_to_name(relative_path: &str) -> String {
        let path = template_path(relative_path);
        let (_, name, _) = split_path(&path);
        name
    }

    #[test]
    fn template_path_index_html() {
        let path = template_path("index.html.hbs");
        let (rel_path, name, data_type) = split_path(&path);

        assert_eq!(rel_path.to_string_lossy(), "index.html.hbs");
        assert_eq!(name, "index");
        assert_eq!(data_type, Some("html".to_owned()));
    }

    #[test]
    fn template_path_subdir_index_html() {
        let path = template_path("subdir/index.html.hbs");
        let (rel_path, name, data_type) = split_path(&path);

        assert_eq!(rel_path.to_string_lossy(), "subdir/index.html.hbs");
        assert_eq!(name, "subdir/index");
        assert_eq!(data_type, Some("html".to_owned()));
    }

    #[test]
    fn template_path_doc_examples() {
        assert_eq!(relative_path_to_name("index.html.hbs"), "index");
        assert_eq!(relative_path_to_name("index.tera"), "index");
        assert_eq!(relative_path_to_name("index.hbs"), "index");
        assert_eq!(relative_path_to_name("dir/index.hbs"), "dir/index");
        assert_eq!(relative_path_to_name("dir/index.html.tera"), "dir/index");
        assert_eq!(relative_path_to_name("index.template.html.hbs"),
                   "index.template");
        assert_eq!(relative_path_to_name("subdir/index.template.html.hbs"),
                   "subdir/index.template");
    }
}
<|endoftext|>


<commit_before>use std::{
    io::{self, Error, ErrorKind},
    mem,
    net::{SocketAddr, SocketAddrV4, SocketAddrV6},
    os::unix::io::{AsRawFd, RawFd},
    path::Path,
};

use cfg_if::cfg_if;
use socket2::{Domain, Protocol, Socket, Type};
use tokio::net::{TcpStream, UdpSocket};

use crate::context::Context;

/// Convert `sockaddr_storage` to `SocketAddr`
#[allow(dead_code)]
pub fn sockaddr_to_std(saddr: &libc::sockaddr_storage) -> io::Result<SocketAddr> {
    match saddr.ss_family as libc::c_int {
        libc::AF_INET => unsafe {
            let addr: SocketAddrV4 = mem::transmute_copy(saddr);
            Ok(SocketAddr::V4(addr))
        },
        libc::AF_INET6 => unsafe {
            let addr: SocketAddrV6 = mem::transmute_copy(saddr);
            Ok(SocketAddr::V6(addr))
        },
        _ => {
            let err = Error::new(ErrorKind::InvalidData, "family must be either AF_INET or AF_INET6");
            Err(err)
        }
    }
}

cfg_if! {
    if #[cfg(target_os = "android")] {
        mod uds;

        /// This is a RPC for Android to `protect()` socket for connecting to remote servers
        ///
        /// https://developer.android.com/reference/android/net/VpnService#protect(java.net.Socket)
        ///
        /// More detail could be found in [shadowsocks-android](https://github.com/shadowsocks/shadowsocks-android) project.
        async fn protect<P: AsRef<Path>>(protect_path: P, fd: RawFd) -> io::Result<()> {
            use tokio::io::AsyncReadExt;

            let mut stream = self::uds::UnixStream::connect(protect_path).await?;

            // send fds
            let dummy: [u8; 1] = [1];
            let fds: [RawFd; 1] = [fd];
            stream.send_with_fd(&dummy, &fds).await?;

            // receive the return value
            let mut response = [0; 1];
            stream.read_exact(&mut response).await?;

            if response[0] == 0xFF {
                return Err(Error::new(ErrorKind::Other, "protect() failed"));
            }

            Ok(())
        }
    } else {
        #[inline(always)]
        async fn protect<P: AsRef<Path>>(_protect_path: P, _fd: RawFd) -> io::Result<()> {
            Ok(())
        }
    }
}

/// create a new TCP stream
#[inline(always)]
pub async fn tcp_stream_connect(saddr: &SocketAddr, context: &Context) -> io::Result<TcpStream> {
    let domain = match *saddr {
        SocketAddr::V4(..) => Domain::ipv4(),
        SocketAddr::V6(..) => Domain::ipv6(),
    };

    let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?;
    socket.set_nonblocking(true)?;

    // Any traffic to localhost should not be protected
    // This is a workaround for VPNService
    if cfg!(target_os = "android") && !saddr.ip().is_loopback() {
        if let Some(ref path) = context.config().protect_path {
            protect(path, socket.as_raw_fd()).await?;
        }
    }

    // Set SO_MARK for mark-based routing on Linux (since 2.6.25)
    // NOTE: This will require CAP_NET_ADMIN capability (root in most cases)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    if let Some(mark) = context.config().outbound_fwmark {
        let ret = unsafe {
            libc::setsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_MARK,
                &mark as *const _ as *const _,
                mem::size_of_val(&mark) as libc::socklen_t,
            )
        };
        if ret != 0 {
            return Err(Error::last_os_error());
        }
    }

    // it's important that the socket is protected before connecting
    let stream = socket.into_tcp_stream();
    TcpStream::connect_std(stream, &saddr).await
}

/// Create a `UdpSocket` binded to `addr`
#[inline(always)]
pub async fn create_udp_socket_with_context(addr: &SocketAddr, context: &Context) -> io::Result<UdpSocket> {
    let socket = UdpSocket::bind(addr).await?;

    // Any traffic to localhost should be protected
    // This is a workaround for VPNService
    if cfg!(target_os = "android") && !addr.ip().is_loopback() {
        if let Some(ref path) = context.config().protect_path {
            protect(path, socket.as_raw_fd()).await?;
        }
    }

    Ok(socket)
}

/// Create a `UdpSocket` binded to `addr`
#[inline(always)]
pub async fn create_udp_socket(addr: &SocketAddr) -> io::Result<UdpSocket> {
    UdpSocket::bind(addr).await
}
<commit_msg>Set SO_MARK on Linux for outbound UDP sockets<commit_after>use std::{
    io::{self, Error, ErrorKind},
    mem,
    net::{SocketAddr, SocketAddrV4, SocketAddrV6},
    os::unix::io::{AsRawFd, RawFd},
    path::Path,
};

use cfg_if::cfg_if;
use socket2::{Domain, Protocol, Socket, Type};
use tokio::net::{TcpStream, UdpSocket};

use crate::context::Context;

/// Convert `sockaddr_storage` to `SocketAddr`
#[allow(dead_code)]
pub fn sockaddr_to_std(saddr: &libc::sockaddr_storage) -> io::Result<SocketAddr> {
    match saddr.ss_family as libc::c_int {
        libc::AF_INET => unsafe {
            let addr: SocketAddrV4 = mem::transmute_copy(saddr);
            Ok(SocketAddr::V4(addr))
        },
        libc::AF_INET6 => unsafe {
            let addr: SocketAddrV6 = mem::transmute_copy(saddr);
            Ok(SocketAddr::V6(addr))
        },
        _ => {
            let err = Error::new(ErrorKind::InvalidData, "family must be either AF_INET or AF_INET6");
            Err(err)
        }
    }
}

cfg_if! {
    if #[cfg(target_os = "android")] {
        mod uds;

        /// This is a RPC for Android to `protect()` socket for connecting to remote servers
        ///
        /// https://developer.android.com/reference/android/net/VpnService#protect(java.net.Socket)
        ///
        /// More detail could be found in [shadowsocks-android](https://github.com/shadowsocks/shadowsocks-android) project.
        async fn protect<P: AsRef<Path>>(protect_path: P, fd: RawFd) -> io::Result<()> {
            use tokio::io::AsyncReadExt;

            let mut stream = self::uds::UnixStream::connect(protect_path).await?;

            // send fds
            let dummy: [u8; 1] = [1];
            let fds: [RawFd; 1] = [fd];
            stream.send_with_fd(&dummy, &fds).await?;

            // receive the return value
            let mut response = [0; 1];
            stream.read_exact(&mut response).await?;

            if response[0] == 0xFF {
                return Err(Error::new(ErrorKind::Other, "protect() failed"));
            }

            Ok(())
        }
    } else {
        #[inline(always)]
        async fn protect<P: AsRef<Path>>(_protect_path: P, _fd: RawFd) -> io::Result<()> {
            Ok(())
        }
    }
}

/// create a new TCP stream
#[inline(always)]
pub async fn tcp_stream_connect(saddr: &SocketAddr, context: &Context) -> io::Result<TcpStream> {
    let domain = match *saddr {
        SocketAddr::V4(..) => Domain::ipv4(),
        SocketAddr::V6(..) => Domain::ipv6(),
    };

    let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?;
    socket.set_nonblocking(true)?;

    // Any traffic to localhost should not be protected
    // This is a workaround for VPNService
    if cfg!(target_os = "android") && !saddr.ip().is_loopback() {
        if let Some(ref path) = context.config().protect_path {
            protect(path, socket.as_raw_fd()).await?;
        }
    }

    // Set SO_MARK for mark-based routing on Linux (since 2.6.25)
    // NOTE: This will require CAP_NET_ADMIN capability (root in most cases)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    if let Some(mark) = context.config().outbound_fwmark {
        let ret = unsafe {
            libc::setsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_MARK,
                &mark as *const _ as *const _,
                mem::size_of_val(&mark) as libc::socklen_t,
            )
        };
        if ret != 0 {
            return Err(Error::last_os_error());
        }
    }

    // it's important that the socket is protected before connecting
    let stream = socket.into_tcp_stream();
    TcpStream::connect_std(stream, &saddr).await
}

/// Create a `UdpSocket` binded to `addr`
#[inline(always)]
pub async fn create_udp_socket_with_context(addr: &SocketAddr, context: &Context) -> io::Result<UdpSocket> {
    let socket = UdpSocket::bind(addr).await?;

    // Any traffic to localhost should be protected
    // This is a workaround for VPNService
    if cfg!(target_os = "android") && !addr.ip().is_loopback() {
        if let Some(ref path) = context.config().protect_path {
            protect(path, socket.as_raw_fd()).await?;
        }
    }

    // Set SO_MARK for mark-based routing on Linux (since 2.6.25)
    // NOTE: This will require CAP_NET_ADMIN capability (root in most cases)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    if let Some(mark) = context.config().outbound_fwmark {
        let ret = unsafe {
            libc::setsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_MARK,
                &mark as *const _ as *const _,
                mem::size_of_val(&mark) as libc::socklen_t,
            )
        };
        if ret != 0 {
            return Err(Error::last_os_error());
        }
    }

    Ok(socket)
}

/// Create a `UdpSocket` binded to `addr`
#[inline(always)]
pub async fn create_udp_socket(addr: &SocketAddr) -> io::Result<UdpSocket> {
    UdpSocket::bind(addr).await
}
<|endoftext|>


<commit_before>#![allow(dead_code,
         non_camel_case_types,
         non_upper_case_globals,
         non_snake_case)]

pub enum Task {}

extern "C" {
    pub static mut os_is_initialized: u8;
}

extern "C" {
    fn os_task_yield();
    fn os_task_suspend_self();
    fn os_task_unsuspend(task: *mut Task) -> u8;
    fn os_task_sleep(num_ticks: u32);
}

pub fn yield_cpu() {
    unsafe { os_task_yield() };
}

pub fn suspend_self() {
    unsafe { os_task_suspend_self() };
}

pub fn unsuspend(task: *mut Task) -> u8 {
    unsafe { os_task_unsuspend(task) }
}

pub fn sleep(num_ticks: u32) {
    unsafe { os_task_sleep(num_ticks) }
}

pub struct CriticalSection(u32);

unsafe fn mask_interrupts(mask: u32) -> u32 {
    let old: u32;

    asm!("mrs $0, PRIMASK" : "=r" (old) ::: "volatile");
    asm!("" ::: "memory": "volatile");
    asm!("msr PRIMASK, $0" :: "r" (mask) :: "volatile");

    old
}

impl CriticalSection {
    pub fn start() -> CriticalSection {
        CriticalSection(unsafe { mask_interrupts(1) })
    }
}

impl Drop for CriticalSection {
    fn drop(&mut self) {
        unsafe {
            mask_interrupts(self.0);
        }
    }
}

#[macro_export]
macro_rules! critical {
    ($code:block) => ({
        let __mouros_critical_section = $crate::tasks::CriticalSection::start();

        $code
    })
}
<commit_msg>Implemented a critical section lock structure<commit_after>#![allow(dead_code,
         non_camel_case_types,
         non_upper_case_globals,
         non_snake_case)]

use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};

pub enum Task {}

extern "C" {
    pub static mut os_is_initialized: u8;
}

extern "C" {
    fn os_task_yield();
    fn os_task_suspend_self();
    fn os_task_unsuspend(task: *mut Task) -> u8;
    fn os_task_sleep(num_ticks: u32);
}

pub fn yield_cpu() {
    unsafe { os_task_yield() };
}

pub fn suspend_self() {
    unsafe { os_task_suspend_self() };
}

pub fn unsuspend(task: *mut Task) -> u8 {
    unsafe { os_task_unsuspend(task) }
}

pub fn sleep(num_ticks: u32) {
    unsafe { os_task_sleep(num_ticks) }
}

pub struct CriticalSection(u32);

unsafe fn mask_interrupts(mask: u32) -> u32 {
    let old: u32;

    asm!("mrs $0, PRIMASK" : "=r" (old) ::: "volatile");
    asm!("" ::: "memory": "volatile");
    asm!("msr PRIMASK, $0" :: "r" (mask) :: "volatile");

    old
}

impl CriticalSection {
    pub fn new() -> CriticalSection {
        CriticalSection(unsafe { mask_interrupts(1) })
    }
}

impl Drop for CriticalSection {
    fn drop(&mut self) {
        unsafe {
            mask_interrupts(self.0);
        }
    }
}

#[macro_export]
macro_rules! critical {
    ($code:block) => ({
        let __mouros_critical_section = $crate::tasks::CriticalSection::new();

        $code
    })
}

pub struct CriticalLock<T> {
    data: UnsafeCell<T>,
}

pub struct CriticalLockGuard<'lock, T: 'lock> {
    lock: &'lock CriticalLock<T>,
    crit_section: CriticalSection,
}

impl<T> CriticalLock<T> {
    pub fn new(item: T) -> CriticalLock<T> {
        CriticalLock { data: UnsafeCell::new(item) }
    }

    pub fn lock(&self) -> CriticalLockGuard<T> {
        CriticalLockGuard {
            lock: &self,
            crit_section: CriticalSection::new(),
        }
    }
}

impl<'lock, T> Deref for CriticalLockGuard<'lock, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.lock.data.get() }
    }
}

impl<'lock, T> DerefMut for CriticalLockGuard<'lock, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.lock.data.get() }
    }
}

unsafe impl<T> Send for CriticalLock<T> {}
unsafe impl<T> Sync for CriticalLock<T> {}
<|endoftext|>


<commit_before>use reqwest::Url;
use rustyline::{error::ReadlineError, Editor};
use std::path::PathBuf;

mod cmd;
mod db;
mod parser;
mod script_builder;

use self::db::{Db, DbState};

pub struct Wallet {
    prompt: String,
    url: Url,
    db: Db,
}

impl Wallet {
    pub fn new(home: PathBuf) -> Wallet {
        let db = Db::new(home.join("db"));
        let prompt = (if db.state() == DbState::Locked {
            "locked>> "
        } else {
            "new>> "
        })
        .to_owned();
        Wallet {
            db,
            prompt,
            url: "http://localhost:7777".parse().unwrap(),
        }
    }

    pub fn start(mut self) {
        let mut rl = Editor::<()>::new();
        loop {
            let readline = rl.readline(&self.prompt);
            match readline {
                Ok(line) => {
                    if line.is_empty() {
                        continue;
                    }
                    let mut args = parser::parse_line(&line);

                    match self.process_line(&mut args) {
                        Ok(store_history) => {
                            if store_history {
                                rl.add_history_entry(line);
                            } else {
                                sodiumoxide::utils::memzero(&mut line.into_bytes());
                            }
                        }
                        Err(s) => {
                            println!("{}", s);
                            sodiumoxide::utils::memzero(&mut line.into_bytes());
                        }
                    }

                    for a in args {
                        sodiumoxide::utils::memzero(&mut a.into_bytes());
                    }
                }
                Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
                    println!("Closing wallet...");
                    break;
                }
                Err(err) => {
                    println!("Error reading input: {:?}", err);
                    break;
                }
            }
        }
    }

    fn process_line(&mut self, args: &mut Vec<String>) -> Result<bool, String> {
        if args.is_empty() {
            return Ok(false);
        }
        match &*args[0] {
            "new" => cmd::create_wallet(self, args),
            "unlock" => cmd::unlock(self, args),
            "create_account" => cmd::account::create(self, args),
            "import_account" => cmd::account::import(self, args),
            "get_account" => cmd::account::get(self, args),
            "delete_account" => cmd::account::delete(self, args),
            "list_accounts" => cmd::account::list(self, args),
            "build_script" => cmd::build_script(self, args),
            "decode_tx" => cmd::decode_tx(self, args),
            "sign_tx" => cmd::sign_tx(self, args),
            "unsign_tx" => cmd::unsign_tx(self, args),
            "build_mint_tx" => cmd::build_mint_tx(self, args),
            "get_properties" => cmd::get_properties(self, args),
            "get_block" => cmd::get_block(self, args),
            "help" => {
                Self::print_usage("Displaying help...");
                Ok(true)
            }
            _ => {
                Self::print_usage(&format!("Invalid command: {}", args[0]));
                Ok(true)
            }
        }
    }

    fn print_usage(header: &str) {
        let mut cmds = Vec::<[&str; 2]>::new();
        cmds.push(["help", "Display this help menu"]);
        cmds.push(["new <password>", "Create a new wallet"]);
        cmds.push(["unlock <password>", "Unlock an existing wallet"]);
        cmds.push(["create_account <account>", "Create an account"]);
        cmds.push(["import_account <account> <wif>", "Import an account"]);
        cmds.push(["delete_account <account>", "Delete an existing account"]);
        cmds.push(["get_account <account>", "Retrieve account information"]);
        cmds.push(["list_accounts", "List all accounts"]);
        cmds.push(["build_script <...op>", "Builds a script"]);
        cmds.push([
            "decode_tx <tx_hex>",
            "Decodes a transaction and prints it to console",
        ]);
        cmds.push(["sign_tx <account> <tx_hex>", "Signs a raw transaction"]);
        cmds.push([
            "unsign_tx <sig_pos> <tx_hex>",
            "Removes a signature from a raw transaction",
        ]);
        cmds.push([
            "build_mint_tx <timestamp_offset> <gold_asset> <silver_asset> <owner_script>",
            "Builds a mint transaction",
        ]);
        cmds.push(["get_properties", "Retrieve global network properties"]);
        cmds.push(["get_block <height>", "Retrieve a block from the network"]);

        let mut max_len = 0;
        for cmd in &cmds {
            assert_eq!(cmd.len(), 2);
            let cmd_len = cmd[0].len();
            if cmd_len > max_len {
                max_len = cmd_len;
            }
        }

        println!("{}\n", header);
        for cmd in &cmds {
            let mut c = cmd[0].to_owned();
            if c.len() < max_len {
                for _ in 0..max_len - c.len() {
                    c.push(' ');
                }
            }
            println!("  {}  {}", c, cmd[1]);
        }
        println!();
    }
}
<commit_msg>Print new line before executing wallet command<commit_after>use reqwest::Url;
use rustyline::{error::ReadlineError, Editor};
use std::path::PathBuf;

mod cmd;
mod db;
mod parser;
mod script_builder;

use self::db::{Db, DbState};

pub struct Wallet {
    prompt: String,
    url: Url,
    db: Db,
}

impl Wallet {
    pub fn new(home: PathBuf) -> Wallet {
        let db = Db::new(home.join("db"));
        let prompt = (if db.state() == DbState::Locked {
            "locked>> "
        } else {
            "new>> "
        })
        .to_owned();
        Wallet {
            db,
            prompt,
            url: "http://localhost:7777".parse().unwrap(),
        }
    }

    pub fn start(mut self) {
        let mut rl = Editor::<()>::new();
        loop {
            let readline = rl.readline(&self.prompt);
            match readline {
                Ok(line) => {
                    if line.is_empty() {
                        continue;
                    }
                    let mut args = parser::parse_line(&line);

                    match self.process_line(&mut args) {
                        Ok(store_history) => {
                            if store_history {
                                rl.add_history_entry(line);
                            } else {
                                sodiumoxide::utils::memzero(&mut line.into_bytes());
                            }
                        }
                        Err(s) => {
                            println!("{}", s);
                            sodiumoxide::utils::memzero(&mut line.into_bytes());
                        }
                    }

                    for a in args {
                        sodiumoxide::utils::memzero(&mut a.into_bytes());
                    }
                }
                Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
                    println!("Closing wallet...");
                    break;
                }
                Err(err) => {
                    println!("Error reading input: {:?}", err);
                    break;
                }
            }
        }
    }

    fn process_line(&mut self, args: &mut Vec<String>) -> Result<bool, String> {
        if args.is_empty() {
            return Ok(false);
        }
        println!();
        match &*args[0] {
            "new" => cmd::create_wallet(self, args),
            "unlock" => cmd::unlock(self, args),
            "create_account" => cmd::account::create(self, args),
            "import_account" => cmd::account::import(self, args),
            "get_account" => cmd::account::get(self, args),
            "delete_account" => cmd::account::delete(self, args),
            "list_accounts" => cmd::account::list(self, args),
            "build_script" => cmd::build_script(self, args),
            "decode_tx" => cmd::decode_tx(self, args),
            "sign_tx" => cmd::sign_tx(self, args),
            "unsign_tx" => cmd::unsign_tx(self, args),
            "build_mint_tx" => cmd::build_mint_tx(self, args),
            "get_properties" => cmd::get_properties(self, args),
            "get_block" => cmd::get_block(self, args),
            "help" => {
                Self::print_usage("Displaying help...");
                Ok(true)
            }
            _ => {
                Self::print_usage(&format!("Invalid command: {}", args[0]));
                Ok(true)
            }
        }
    }

    fn print_usage(header: &str) {
        let mut cmds = Vec::<[&str; 2]>::new();
        cmds.push(["help", "Display this help menu"]);
        cmds.push(["new <password>", "Create a new wallet"]);
        cmds.push(["unlock <password>", "Unlock an existing wallet"]);
        cmds.push(["create_account <account>", "Create an account"]);
        cmds.push(["import_account <account> <wif>", "Import an account"]);
        cmds.push(["delete_account <account>", "Delete an existing account"]);
        cmds.push(["get_account <account>", "Retrieve account information"]);
        cmds.push(["list_accounts", "List all accounts"]);
        cmds.push(["build_script <...op>", "Builds a script"]);
        cmds.push([
            "decode_tx <tx_hex>",
            "Decodes a transaction and prints it to console",
        ]);
        cmds.push(["sign_tx <account> <tx_hex>", "Signs a raw transaction"]);
        cmds.push([
            "unsign_tx <sig_pos> <tx_hex>",
            "Removes a signature from a raw transaction",
        ]);
        cmds.push([
            "build_mint_tx <timestamp_offset> <gold_asset> <silver_asset> <owner_script>",
            "Builds a mint transaction",
        ]);
        cmds.push(["get_properties", "Retrieve global network properties"]);
        cmds.push(["get_block <height>", "Retrieve a block from the network"]);

        let mut max_len = 0;
        for cmd in &cmds {
            assert_eq!(cmd.len(), 2);
            let cmd_len = cmd[0].len();
            if cmd_len > max_len {
                max_len = cmd_len;
            }
        }

        println!("{}\n", header);
        for cmd in &cmds {
            let mut c = cmd[0].to_owned();
            if c.len() < max_len {
                for _ in 0..max_len - c.len() {
                    c.push(' ');
                }
            }
            println!("  {}  {}", c, cmd[1]);
        }
        println!();
    }
}
<|endoftext|>


<commit_before>use crate::{common, ninja::get_ninja_exe, pkg, spinner::Spinner, version::Version};
use clap::{Arg, ArgMatches, Command};
use console::Style;
use ditto_config::{read_config, Config, PackageName, CONFIG_FILE_NAME};
use ditto_make::{self as make, BuildNinja, GetWarnings, PackageSources, Sources};
use fs2::FileExt;
use log::{debug, trace};
use miette::{IntoDiagnostic, Result, WrapErr};
use notify::Watcher;
use std::{
    collections::HashMap,
    env::current_exe,
    fs,
    io::{BufRead, BufReader, Write},
    path::{Path, PathBuf},
    process::{self, ExitStatus, Stdio},
    sync::mpsc,
    time::{Duration, Instant},
};

pub static COMPILE_SUBCOMMAND: &str = "compile";

pub fn command<'a>(name: &str) -> Command<'a> {
    Command::new(name).about("Build a project").arg(
        Arg::new("watch")
            .short('w')
            .long("watch")
            .help("Watch files for changes"),
    )
}

pub async fn run(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    if matches.is_present("watch") {
        run_watch(matches, ditto_version).await
    } else {
        let status = run_once(matches, ditto_version).await?;
        process::exit(status.code().unwrap_or(0));
    }
}

struct EventForwarder {
    tx: mpsc::Sender<notify::Result<notify::Event>>,
    debounce_duration: Duration,
    last_run: Option<Instant>,
}

impl EventForwarder {
    fn new(tx: mpsc::Sender<notify::Result<notify::Event>>) -> Self {
        Self {
            tx,
            // Debounce 100ms seems reasonable
            debounce_duration: Duration::from_millis(100),
            last_run: None,
        }
    }
}

impl notify::EventHandler for EventForwarder {
    fn handle_event(&mut self, event: notify::Result<notify::Event>) {
        let now = Instant::now();
        if let Some(last_run) = self.last_run {
            // Debouncing
            if now.duration_since(last_run) > self.debounce_duration {
                if let Err(err) = self.tx.send(event) {
                    log::error!("Error sending notify event: {:?}", err);
                }
                self.last_run = Some(now);
            }
        } else {
            if let Err(err) = self.tx.send(event) {
                log::error!("Error sending notify event: {:?}", err);
            }
            self.last_run = Some(now);
        }
    }
}

pub async fn run_watch(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    // Set up the channel
    let (tx, rx) = mpsc::channel();
    let mut watcher = notify::RecommendedWatcher::new(EventForwarder::new(tx)).into_diagnostic()?;

    // Watch ditto.toml and src/**
    // NOTE not watching packages as that seems wasteful...
    // package source isn't going to be touched the majority of the time?
    // We could consider watching packages that are symlinks (i.e. local)
    watcher
        .watch(
            &PathBuf::from(CONFIG_FILE_NAME),
            notify::RecursiveMode::NonRecursive,
        )
        .into_diagnostic()?;
    watcher
        .watch(
            // TODO use src config value
            &PathBuf::from("src"),
            notify::RecursiveMode::Recursive,
        )
        .into_diagnostic()?;

    // Clear screen initially
    // (other watching tools do this)
    clearscreen::clear()
        .into_diagnostic()
        .wrap_err("error clearing screen")?;

    //let print_done = || {
    //    println!("{}", Style::new().green().bold().apply_to("Done"));
    //};

    if let Err(err) = run_once(matches, ditto_version).await {
        // print the error but don't exit!
        eprintln!("{:?}", err);
    }
    //print_done();

    // Listen for changes...
    loop {
        let event = rx.recv().into_diagnostic()?;

        match event {
            Ok(notify::Event {
                kind: notify::EventKind::Modify(_),
                mut paths,
                ..
            }) if paths.len() == 1 => {
                let path = paths.pop().unwrap();
                let event_path_extension = path.extension().and_then(|ext| ext.to_str());
                // Be selective about what we re-run for.
                // I.e. don't re-run for foreign files etc.
                if matches!(
                    event_path_extension,
                    // ditto source file
                    Some("ditto") |
                    // config file
                    Some("toml")
                ) {
                    clearscreen::clear()
                        .into_diagnostic()
                        .wrap_err("error clearing screen")?;
                    if let Err(err) = run_once(matches, ditto_version).await {
                        // print the error but don't exit!
                        eprintln!("{:?}", err);
                    }
                    //print_done();
                }
            }
            other => {
                log::trace!("Ignoring notify event: {:?}", other);
            }
        }
    }
}

pub async fn run_once(_matches: &ArgMatches, ditto_version: &Version) -> Result<ExitStatus> {
    let config_path: PathBuf = [".", CONFIG_FILE_NAME].iter().collect();
    let config = read_config(&config_path)?;

    // Need to acquire a lock on the build directory as lots of `ditto make`
    // processes running concurrently will cause problems!
    let lock = acquire_lock(&config)?;
    debug!("Lock acquired");

    // Install/remove packages as needed
    // (this is a nicer pattern than requiring a run of a separate CLI command, IMO)
    if !config.dependencies.is_empty() {
        pkg::check_packages_up_to_date(&config)
            .await
            .wrap_err("error checking packages are up to date")?;
    }

    let now = Instant::now(); // for timing

    // Do the work
    let status = make(&config_path, &config, ditto_version)
        .await
        .wrap_err("error running make")?;

    lock.unlock()
        .into_diagnostic()
        .wrap_err("error releasing lock")?;

    debug!("make ran in {}ms", now.elapsed().as_millis());

    Ok(status)
}

async fn make(config_path: &Path, config: &Config, ditto_version: &Version) -> Result<ExitStatus> {
    let (build_ninja, get_warnings) = generate_build_ninja(config_path, config, ditto_version)
        .wrap_err("error generating build.ninja")?;

    trace!("build.ninja generated");

    let mut build_ninja_path = config.ditto_dir.to_path_buf();
    build_ninja_path.push("build");
    build_ninja_path.set_extension("ninja");

    {
        if !config.ditto_dir.exists() {
            fs::create_dir_all(&config.ditto_dir)
                .into_diagnostic()
                .wrap_err(format!(
                    "error creating {}",
                    config.ditto_dir.to_string_lossy()
                ))?;
        }

        let mut handle = fs::File::create(&build_ninja_path)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating ninja build file: {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        handle
            .write_all(build_ninja.into_syntax().as_bytes())
            .into_diagnostic()
            .wrap_err(format!(
                "error writing {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        debug!(
            "build.ninja written to {:?}",
            build_ninja_path.to_string_lossy()
        );
    }

    static NINJA_STATUS_MESSAGE: &str = "__NINJA";

    let ninja_exe = get_ninja_exe().await?;
    let mut child = process::Command::new(&ninja_exe)
        .arg("-f")
        .arg(&build_ninja_path)
        .stdout(Stdio::piped())
        // Mark ninja status messages so we can push them to our own progress spinner
        .env("NINJA_STATUS", NINJA_STATUS_MESSAGE)
        // Don't strip color codes, we'll handle that
        // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961
        .env("CLICOLOR_FORCE", "1")
        // Pass `is_plain` logic down to CLI calls made by ninja
        .env("DITTO_PLAIN", common::is_plain().to_string())
        .spawn()
        .into_diagnostic()
        .wrap_err(format!(
            "error running ninja: {} -f {}",
            ninja_exe,
            build_ninja_path.to_string_lossy()
        ))?;

    let stdout = child.stdout.as_mut().unwrap();
    let stdout_reader = BufReader::new(stdout);
    let mut stdout_lines = stdout_reader.lines();
    if let Some(Ok(first_line)) = stdout_lines.next() {
        // NOTE relying on the format of ninja messages like this could break
        // if DITTO_NINJA is set to a ninja version with a different format
        if first_line.starts_with("ninja: no work to do") {
            // Nothing to do,
            // still need to print warnings though
            let warnings = get_warnings()?;
            if !warnings.is_empty() {
                let warnings_len = warnings.len();
                for (i, warning) in warnings.into_iter().enumerate() {
                    if i == warnings_len - 1 {
                        eprintln!("{:?}", warning);
                    } else {
                        eprint!("{:?}", warning);
                    }
                }
            } else {
                println!("{}", Style::new().white().dim().apply_to("Nothing to do"));
            }
            child
                .wait()
                .into_diagnostic()
                .wrap_err("ninja wasn't running?")
        } else {
            let mut spinner = Spinner::new();
            spinner.set_message(
                first_line
                    .trim_start_matches(NINJA_STATUS_MESSAGE)
                    .to_owned(),
            );

            // Our error/warning reports generally start with a blank line,
            // so we need to replicate that behavior when forwarding ninja
            // output for a consistent experience.
            let mut printed_initial_newline = false;
            while let Some(Ok(line)) = stdout_lines.next() {
                if line.starts_with(NINJA_STATUS_MESSAGE) {
                    spinner.set_message(line.trim_start_matches(NINJA_STATUS_MESSAGE).to_owned());
                } else if line.starts_with("ninja: build stopped: subcommand failed") {
                } else if console::strip_ansi_codes(&line).starts_with("FAILED") {
                    // The following line prints the command that was run (and failed)
                    // so swallow it
                    stdout_lines.next();
                } else {
                    if !printed_initial_newline {
                        spinner.println("\n");
                        printed_initial_newline = true
                    }
                    spinner.println(line);
                }
            }

            let status = child.wait().expect("error waiting for ninja to exit");
            spinner.finish();
            if status.success() {
                // Only print warnings if there wasn't an error
                let warnings = get_warnings()?;
                if !warnings.is_empty() {
                    let warnings_len = warnings.len();
                    for (i, warning) in warnings.into_iter().enumerate() {
                        if i == warnings_len - 1 {
                            eprintln!("{:?}", warning);
                        } else {
                            eprint!("{:?}", warning);
                        }
                    }
                }
            }
            Ok(status)
        }
    } else {
        unreachable!()
    }
}

fn generate_build_ninja(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
) -> Result<(BuildNinja, GetWarnings)> {
    let mut build_dir = config.ditto_dir.to_path_buf();
    build_dir.push("build");
    build_dir.push(&ditto_version.semversion.to_string());

    let ditto_bin = current_exe()
        .into_diagnostic()
        .wrap_err("error getting current executable")?;

    let ditto_sources = find_ditto_files(&config.src_dir)?;

    let sources = Sources {
        config: config_path.to_path_buf(),
        ditto: ditto_sources,
    };

    let package_sources =
        get_package_sources(config).wrap_err("error finding ditto files in packages")?;

    let result = make::generate_build_ninja(
        build_dir,
        ditto_bin,
        &ditto_version.semversion,
        COMPILE_SUBCOMMAND,
        sources,
        package_sources,
    );
    if let Err(ref report) = result {
        // This is a bit brittle, but we want parse errors encountered during
        // build planning to be indistinguishable from parse errors encountered
        // during the actual build
        if report.root_cause().to_string() == "syntax error" {
            //                                  ^^ BEWARE relying on this string is brittle,
            eprintln!("{:?}", report);
            std::process::exit(1);
        }
    }
    result
}

fn get_package_sources(config: &Config) -> Result<PackageSources> {
    let mut package_sources = HashMap::new();
    for path in pkg::list_installed_packages(&pkg::mk_packages_dir(config))? {
        let package_name =
            PackageName::new_unchecked(path.file_name().unwrap().to_string_lossy().into_owned());
        let sources = get_sources_for_dir(&path)?;
        package_sources.insert(package_name, sources);
    }
    Ok(package_sources)
}

fn get_sources_for_dir(dir: &Path) -> Result<Sources> {
    let mut config_path = dir.to_path_buf();
    config_path.push(CONFIG_FILE_NAME);
    let config = read_config(&config_path)?;

    let mut src_dir = dir.to_path_buf();
    src_dir.push(config.src_dir);

    let ditto_sources = find_ditto_files(src_dir)?;
    Ok(Sources {
        config: config_path,
        ditto: ditto_sources,
    })
}

fn find_ditto_files<P: AsRef<Path>>(root: P) -> Result<Vec<PathBuf>> {
    make::find_ditto_files(root.as_ref())
        .into_diagnostic()
        .wrap_err(format!(
            "error finding ditto files in {}",
            root.as_ref().to_string_lossy()
        ))
}

static LOCK_FILE: &str = "_lock";

fn acquire_lock(config: &Config) -> Result<impl FileExt> {
    if !config.ditto_dir.exists() {
        debug!(
            "{} doesn't exist, creating",
            config.ditto_dir.to_string_lossy()
        );

        fs::create_dir_all(&config.ditto_dir)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating {}",
                config.ditto_dir.to_string_lossy()
            ))?;
    }

    let mut lock_file = config.ditto_dir.to_path_buf();
    lock_file.push(LOCK_FILE);

    debug!("Opening lock file at {}", lock_file.to_string_lossy());
    let file = fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&lock_file)
        .into_diagnostic()
        .wrap_err(format!(
            "error opening lock file {}",
            lock_file.to_string_lossy()
        ))?;

    if file.try_lock_exclusive().is_ok() {
        Ok(file)
    } else {
        println!("Waiting for lock...");
        file.lock_exclusive()
            .into_diagnostic()
            .wrap_err("error waiting for lock")?;
        Ok(file)
    }
}
<commit_msg>Fix `make --watch` bailing on build planning parse errors<commit_after>use crate::{common, ninja::get_ninja_exe, pkg, spinner::Spinner, version::Version};
use clap::{Arg, ArgMatches, Command};
use console::Style;
use ditto_config::{read_config, Config, PackageName, CONFIG_FILE_NAME};
use ditto_make::{self as make, BuildNinja, GetWarnings, PackageSources, Sources};
use fs2::FileExt;
use log::{debug, trace};
use miette::{IntoDiagnostic, Result, WrapErr};
use notify::Watcher;
use std::{
    collections::HashMap,
    env::current_exe,
    fs,
    io::{BufRead, BufReader, Write},
    path::{Path, PathBuf},
    process::{self, ExitStatus, Stdio},
    sync::mpsc,
    time::{Duration, Instant},
};

pub static COMPILE_SUBCOMMAND: &str = "compile";

pub fn command<'a>(name: &str) -> Command<'a> {
    Command::new(name).about("Build a project").arg(
        Arg::new("watch")
            .short('w')
            .long("watch")
            .help("Watch files for changes"),
    )
}

pub async fn run(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    if matches.is_present("watch") {
        run_watch(matches, ditto_version).await
    } else {
        let status = run_once(matches, ditto_version).await?;
        process::exit(status.code().unwrap_or(0));
    }
}

struct EventForwarder {
    tx: mpsc::Sender<notify::Result<notify::Event>>,
    debounce_duration: Duration,
    last_run: Option<Instant>,
}

impl EventForwarder {
    fn new(tx: mpsc::Sender<notify::Result<notify::Event>>) -> Self {
        Self {
            tx,
            // Debounce 100ms seems reasonable
            debounce_duration: Duration::from_millis(100),
            last_run: None,
        }
    }
}

impl notify::EventHandler for EventForwarder {
    fn handle_event(&mut self, event: notify::Result<notify::Event>) {
        let now = Instant::now();
        if let Some(last_run) = self.last_run {
            // Debouncing
            if now.duration_since(last_run) > self.debounce_duration {
                if let Err(err) = self.tx.send(event) {
                    log::error!("Error sending notify event: {:?}", err);
                }
                self.last_run = Some(now);
            }
        } else {
            if let Err(err) = self.tx.send(event) {
                log::error!("Error sending notify event: {:?}", err);
            }
            self.last_run = Some(now);
        }
    }
}

pub async fn run_watch(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    // Set up the channel
    let (tx, rx) = mpsc::channel();
    let mut watcher = notify::RecommendedWatcher::new(EventForwarder::new(tx)).into_diagnostic()?;

    // Watch ditto.toml and src/**
    // NOTE not watching packages as that seems wasteful...
    // package source isn't going to be touched the majority of the time?
    // We could consider watching packages that are symlinks (i.e. local)
    watcher
        .watch(
            &PathBuf::from(CONFIG_FILE_NAME),
            notify::RecursiveMode::NonRecursive,
        )
        .into_diagnostic()?;
    watcher
        .watch(
            // TODO use src config value
            &PathBuf::from("src"),
            notify::RecursiveMode::Recursive,
        )
        .into_diagnostic()?;

    // Clear screen initially
    // (other watching tools do this)
    clearscreen::clear()
        .into_diagnostic()
        .wrap_err("error clearing screen")?;

    //let print_done = || {
    //    println!("{}", Style::new().green().bold().apply_to("Done"));
    //};

    if let Err(err) = run_once(matches, ditto_version).await {
        // print the error but don't exit!
        eprintln!("{:?}", err);
    }
    //print_done();

    // Listen for changes...
    loop {
        let event = rx.recv().into_diagnostic()?;

        match event {
            Ok(notify::Event {
                kind: notify::EventKind::Modify(_),
                mut paths,
                ..
            }) if paths.len() == 1 => {
                let path = paths.pop().unwrap();
                let event_path_extension = path.extension().and_then(|ext| ext.to_str());
                // Be selective about what we re-run for.
                // I.e. don't re-run for foreign files etc.
                if matches!(
                    event_path_extension,
                    // ditto source file
                    Some("ditto") |
                    // config file
                    Some("toml")
                ) {
                    clearscreen::clear()
                        .into_diagnostic()
                        .wrap_err("error clearing screen")?;
                    if let Err(err) = run_once(matches, ditto_version).await {
                        // print the error but don't exit!
                        eprintln!("{:?}", err);
                    }
                    //print_done();
                }
            }
            other => {
                log::trace!("Ignoring notify event: {:?}", other);
            }
        }
    }
}

pub async fn run_once(_matches: &ArgMatches, ditto_version: &Version) -> Result<ExitStatus> {
    let config_path: PathBuf = [".", CONFIG_FILE_NAME].iter().collect();
    let config = read_config(&config_path)?;

    // Need to acquire a lock on the build directory as lots of `ditto make`
    // processes running concurrently will cause problems!
    let lock = acquire_lock(&config)?;
    debug!("Lock acquired");

    // Install/remove packages as needed
    // (this is a nicer pattern than requiring a run of a separate CLI command, IMO)
    if !config.dependencies.is_empty() {
        pkg::check_packages_up_to_date(&config)
            .await
            .wrap_err("error checking packages are up to date")?;
    }

    let now = Instant::now(); // for timing

    // Do the work
    let status = make(&config_path, &config, ditto_version).await?;

    lock.unlock()
        .into_diagnostic()
        .wrap_err("error releasing lock")?;

    debug!("make ran in {}ms", now.elapsed().as_millis());

    Ok(status)
}

async fn make(config_path: &Path, config: &Config, ditto_version: &Version) -> Result<ExitStatus> {
    let (build_ninja, get_warnings) = generate_build_ninja(config_path, config, ditto_version)
        .map_err(|err| {
            // This is a bit brittle, but we want parse errors encountered during
            // build planning to be indistinguishable from parse errors encountered
            // during the actual build
            if err.root_cause().to_string() == "syntax error" {
                //                                  ^^ BEWARE relying on this string is brittle!
                err
            } else {
                err.wrap_err("error generating build.ninja")
            }
        })?;

    trace!("build.ninja generated");

    let mut build_ninja_path = config.ditto_dir.to_path_buf();
    build_ninja_path.push("build");
    build_ninja_path.set_extension("ninja");

    {
        if !config.ditto_dir.exists() {
            fs::create_dir_all(&config.ditto_dir)
                .into_diagnostic()
                .wrap_err(format!(
                    "error creating {}",
                    config.ditto_dir.to_string_lossy()
                ))?;
        }

        let mut handle = fs::File::create(&build_ninja_path)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating ninja build file: {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        handle
            .write_all(build_ninja.into_syntax().as_bytes())
            .into_diagnostic()
            .wrap_err(format!(
                "error writing {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        debug!(
            "build.ninja written to {:?}",
            build_ninja_path.to_string_lossy()
        );
    }

    static NINJA_STATUS_MESSAGE: &str = "__NINJA";

    let ninja_exe = get_ninja_exe().await?;
    let mut child = process::Command::new(&ninja_exe)
        .arg("-f")
        .arg(&build_ninja_path)
        .stdout(Stdio::piped())
        // Mark ninja status messages so we can push them to our own progress spinner
        .env("NINJA_STATUS", NINJA_STATUS_MESSAGE)
        // Don't strip color codes, we'll handle that
        // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961
        .env("CLICOLOR_FORCE", "1")
        // Pass `is_plain` logic down to CLI calls made by ninja
        .env("DITTO_PLAIN", common::is_plain().to_string())
        .spawn()
        .into_diagnostic()
        .wrap_err(format!(
            "error running ninja: {} -f {}",
            ninja_exe,
            build_ninja_path.to_string_lossy()
        ))?;

    let stdout = child.stdout.as_mut().unwrap();
    let stdout_reader = BufReader::new(stdout);
    let mut stdout_lines = stdout_reader.lines();
    if let Some(Ok(first_line)) = stdout_lines.next() {
        // NOTE relying on the format of ninja messages like this could break
        // if DITTO_NINJA is set to a ninja version with a different format
        if first_line.starts_with("ninja: no work to do") {
            // Nothing to do,
            // still need to print warnings though
            let warnings = get_warnings()?;
            if !warnings.is_empty() {
                let warnings_len = warnings.len();
                for (i, warning) in warnings.into_iter().enumerate() {
                    if i == warnings_len - 1 {
                        eprintln!("{:?}", warning);
                    } else {
                        eprint!("{:?}", warning);
                    }
                }
            } else {
                println!("{}", Style::new().white().dim().apply_to("Nothing to do"));
            }
            child
                .wait()
                .into_diagnostic()
                .wrap_err("ninja wasn't running?")
        } else {
            let mut spinner = Spinner::new();
            spinner.set_message(
                first_line
                    .trim_start_matches(NINJA_STATUS_MESSAGE)
                    .to_owned(),
            );

            // Our error/warning reports generally start with a blank line,
            // so we need to replicate that behavior when forwarding ninja
            // output for a consistent experience.
            let mut printed_initial_newline = false;
            while let Some(Ok(line)) = stdout_lines.next() {
                if line.starts_with(NINJA_STATUS_MESSAGE) {
                    spinner.set_message(line.trim_start_matches(NINJA_STATUS_MESSAGE).to_owned());
                } else if line.starts_with("ninja: build stopped: subcommand failed") {
                } else if console::strip_ansi_codes(&line).starts_with("FAILED") {
                    // The following line prints the command that was run (and failed)
                    // so swallow it
                    stdout_lines.next();
                } else {
                    if !printed_initial_newline {
                        spinner.println("\n");
                        printed_initial_newline = true
                    }
                    spinner.println(line);
                }
            }

            let status = child.wait().expect("error waiting for ninja to exit");
            spinner.finish();
            if status.success() {
                // Only print warnings if there wasn't an error
                let warnings = get_warnings()?;
                if !warnings.is_empty() {
                    let warnings_len = warnings.len();
                    for (i, warning) in warnings.into_iter().enumerate() {
                        if i == warnings_len - 1 {
                            eprintln!("{:?}", warning);
                        } else {
                            eprint!("{:?}", warning);
                        }
                    }
                }
            }
            Ok(status)
        }
    } else {
        unreachable!()
    }
}

fn generate_build_ninja(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
) -> Result<(BuildNinja, GetWarnings)> {
    let mut build_dir = config.ditto_dir.to_path_buf();
    build_dir.push("build");
    build_dir.push(&ditto_version.semversion.to_string());

    let ditto_bin = current_exe()
        .into_diagnostic()
        .wrap_err("error getting current executable")?;

    let ditto_sources = find_ditto_files(&config.src_dir)?;

    let sources = Sources {
        config: config_path.to_path_buf(),
        ditto: ditto_sources,
    };

    let package_sources =
        get_package_sources(config).wrap_err("error finding ditto files in packages")?;

    make::generate_build_ninja(
        build_dir,
        ditto_bin,
        &ditto_version.semversion,
        COMPILE_SUBCOMMAND,
        sources,
        package_sources,
    )
}

fn get_package_sources(config: &Config) -> Result<PackageSources> {
    let mut package_sources = HashMap::new();
    for path in pkg::list_installed_packages(&pkg::mk_packages_dir(config))? {
        let package_name =
            PackageName::new_unchecked(path.file_name().unwrap().to_string_lossy().into_owned());
        let sources = get_sources_for_dir(&path)?;
        package_sources.insert(package_name, sources);
    }
    Ok(package_sources)
}

fn get_sources_for_dir(dir: &Path) -> Result<Sources> {
    let mut config_path = dir.to_path_buf();
    config_path.push(CONFIG_FILE_NAME);
    let config = read_config(&config_path)?;

    let mut src_dir = dir.to_path_buf();
    src_dir.push(config.src_dir);

    let ditto_sources = find_ditto_files(src_dir)?;
    Ok(Sources {
        config: config_path,
        ditto: ditto_sources,
    })
}

fn find_ditto_files<P: AsRef<Path>>(root: P) -> Result<Vec<PathBuf>> {
    make::find_ditto_files(root.as_ref())
        .into_diagnostic()
        .wrap_err(format!(
            "error finding ditto files in {}",
            root.as_ref().to_string_lossy()
        ))
}

static LOCK_FILE: &str = "_lock";

fn acquire_lock(config: &Config) -> Result<impl FileExt> {
    if !config.ditto_dir.exists() {
        debug!(
            "{} doesn't exist, creating",
            config.ditto_dir.to_string_lossy()
        );

        fs::create_dir_all(&config.ditto_dir)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating {}",
                config.ditto_dir.to_string_lossy()
            ))?;
    }

    let mut lock_file = config.ditto_dir.to_path_buf();
    lock_file.push(LOCK_FILE);

    debug!("Opening lock file at {}", lock_file.to_string_lossy());
    let file = fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&lock_file)
        .into_diagnostic()
        .wrap_err(format!(
            "error opening lock file {}",
            lock_file.to_string_lossy()
        ))?;

    if file.try_lock_exclusive().is_ok() {
        Ok(file)
    } else {
        println!("Waiting for lock...");
        file.lock_exclusive()
            .into_diagnostic()
            .wrap_err("error waiting for lock")?;
        Ok(file)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate galvanic_assert;

use galvanic_assert::*;
use galvanic_assert::matchers::*;

mod get_expectation_for_expression {
    use super::*;

    #[test]
    fn should_get_an_expectation_asserting_an_expression_to_be_true() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(1 == 1);
    }

    #[test]
    fn should_get_an_expectation_asserting_an_expression_to_be_true_and_verify_manually() {
        let e = get_expectation_for!(1 == 1);
        e.verify()
    }

    #[test]
    fn should_get_multiple_expectations_asserting_an_expression_to_be_true() {
        #[allow(unused_variables)]
        let e1 = get_expectation_for!(1 == 1);
        #[allow(unused_variables)]
        let e2 = get_expectation_for!(1 == 1, otherwise "failed");
    }

    #[test]
    #[should_panic]
    fn should_get_an_expectation_failing_to_assert_an_expression_to_be_true() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(1 != 1);
    }

    #[test]
    #[should_panic]
    fn should_get_an_expectation_failing_to_assert_an_expression_to_be_true_and_verify_manually() {
        let e = get_expectation_for!(1 != 1);
        e.verify()
    }

    #[test]
    fn should_get_multiple_expectations_failing_to_assert_an_expression_to_be_true() {
        let e1 = get_expectation_for!(1 != 1);
        let e2 = get_expectation_for!(1 != 1, otherwise "failed");
        assert_that!(e1.verify(), panics);
        assert_that!(e2.verify(), panics);
    }

    #[test]
    fn should_get_multiple_expectations_some_failing_to_assert_an_expression_to_be_true() {
        let e1 = get_expectation_for!(1 != 1);
        let e2 = get_expectation_for!(1 == 1);
        assert_that!(e1.verify(), panics);
        assert_that!(e2.verify(), does not panic);
    }
}

mod get_expectation_for_panic {
    use super::*;

    #[test]
    fn should_get_an_expectation_asserting_an_expression_to_panic() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(panic!("panic"), panics);
    }

    #[test]
    #[should_panic]
    fn should_get_an_expectation_failing_to_assert_an_expression_to_panic() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(1 == 1, panics);
    }
}

mod get_expectation_for_no_panic {
    use super::*;

    #[test]
    fn should_get_an_expectation_asserting_an_expression_to_not_panic() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(1 == 1, does not panic);
    }

    #[test]
    #[should_panic]
    fn should_get_an_expectation_failing_to_assert_an_expression_to_panic() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(panic!("panic"), does not panic);
    }
}

mod get_expectation_for_matcher {
    use super::*;

    #[test]
    fn should_get_an_expectation_asserting_a_matcher_to_succeed() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(1, assertion_always_succeeds());
    }

    #[test]
    #[should_panic]
    fn should_get_an_expectation_asserting_a_matcher_to_fail() {
        #[allow(unused_variables)]
        let e = get_expectation_for!(1, assertion_always_fails());
    }
}
<commit_msg>add test cases for expect_that! and get_expectation_for!<commit_after>#[macro_use]
extern crate galvanic_assert;

use galvanic_assert::*;
use galvanic_assert::matchers::*;

mod get_expectation_for {
    use super::*;

    mod use_expression {
        use super::*;

        #[test]
        fn should_get_an_expectation_asserting_an_expression_to_be_true() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(1 == 1);
        }

        #[test]
        fn should_get_an_expectation_asserting_an_expression_to_be_true_and_verify_manually() {
            let e = get_expectation_for!(1 == 1);
            e.verify()
        }

        #[test]
        fn should_get_multiple_expectations_asserting_an_expression_to_be_true() {
            #[allow(unused_variables)]
            let e1 = get_expectation_for!(1 == 1);
            #[allow(unused_variables)]
            let e2 = get_expectation_for!(1 == 1, otherwise "failed");
        }

        #[test]
        #[should_panic]
        fn should_get_an_expectation_failing_to_assert_an_expression_to_be_true() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(1 != 1);
        }

        #[test]
        #[should_panic]
        fn should_get_an_expectation_failing_to_assert_an_expression_to_be_true_and_verify_manually() {
            let e = get_expectation_for!(1 != 1);
            e.verify()
        }

        #[test]
        fn should_get_multiple_expectations_failing_to_assert_an_expression_to_be_true() {
            let e1 = get_expectation_for!(1 != 1);
            let e2 = get_expectation_for!(1 != 1, otherwise "failed");
            assert_that!(e1.verify(), panics);
            assert_that!(e2.verify(), panics);
        }

        #[test]
        fn should_get_multiple_expectations_some_failing_to_assert_an_expression_to_be_true() {
            let e1 = get_expectation_for!(1 != 1);
            let e2 = get_expectation_for!(1 == 1);
            assert_that!(e1.verify(), panics);
            assert_that!(e2.verify(), does not panic);
        }
    }

    mod use_panic {
        use super::*;

        #[test]
        fn should_get_an_expectation_asserting_an_expression_to_panic() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(panic!("panic"), panics);
        }

        #[test]
        #[should_panic]
        fn should_get_an_expectation_failing_to_assert_an_expression_to_panic() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(1 == 1, panics);
        }
    }

    mod use_no_panic {
        use super::*;

        #[test]
        fn should_get_an_expectation_asserting_an_expression_to_not_panic() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(1 == 1, does not panic);
        }

        #[test]
        #[should_panic]
        fn should_get_an_expectation_failing_to_assert_an_expression_to_panic() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(panic!("panic"), does not panic);
        }
    }

    mod use_matcher {
        use super::*;

        #[test]
        fn should_get_an_expectation_asserting_a_matcher_to_succeed() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(1, assertion_always_succeeds());
        }

        #[test]
        #[should_panic]
        fn should_get_an_expectation_asserting_a_matcher_to_fail() {
            #[allow(unused_variables)]
            let e = get_expectation_for!(1, assertion_always_fails());
        }
    }
}

mod expect_that {
    use super::*;

    mod use_expression {
        use super::*;

        #[test]
        fn should_expect_an_expression_to_be_true() {
            expect_that!(1 == 1);
        }

        #[test]
        fn should_get_multiple_expectations_asserting_an_expression_to_be_true() {
            expect_that!(1 == 1);
            expect_that!(1 == 1, otherwise "failed");
        }

        #[test]
        #[should_panic]
        fn should_be_failing_to_expect_an_expression_to_be_true() {
            #[allow(unused_variables)]
            expect_that!(1 != 1);
        }

        #[test]
        #[should_panic]
        fn should_get_multiple_expectations_failing_to_assert_an_expression_to_be_true() {
            expect_that!(1 != 1);
            expect_that!(1 != 1, otherwise "failed");
        }

        #[test]
        #[should_panic]
        fn should_get_multiple_expectations_some_failing_to_assert_an_expression_to_be_true() {
            expect_that!(1 != 1);
            expect_that!(1 == 1);
        }
    }

    mod use_panic {
        use super::*;

        #[test]
        fn should_expect_an_expression_to_panic() {
            expect_that!(panic!("panic"), panics);
        }

        #[test]
        #[should_panic]
        fn should_be_failing_to_expect_an_expression_to_panic() {
            expect_that!(1 == 1, panics);
        }
    }

    mod use_no_panic {
        use super::*;

        #[test]
        fn should_expect_an_expression_to_not_panic() {
            expect_that!(1 == 1, does not panic);
        }

        #[test]
        #[should_panic]
        fn should_be_failing_to_expect_an_expression_to_panic() {
            expect_that!(panic!("panic"), does not panic);
        }
    }

    mod use_matcher {
        use super::*;

        #[test]
        fn should_expect_a_matcher_to_succeed() {
            expect_that!(1, assertion_always_succeeds());
        }

        #[test]
        #[should_panic]
        fn should_expect_a_matcher_to_fail() {
            expect_that!(1, assertion_always_fails());
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::net::SocketAddr;
use std::time::Duration;

use futures::{self, Future};

use tokio_service::Service;
use tokio_core::io::Io;
use tokio_core::reactor::Handle;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tokio_proto::TcpClient;
use tokio_proto::multiplex::{ClientProto, ClientService};
use tokio_middleware::Timeout;
use tokio_timer::Timer;

use tcp::proto::BytesClientProto;

struct ClientCore {
    inner: ClientService<TcpStream, BytesClientProto>,
}

pub struct Client {
    client: Timeout<ClientCore>,
    core: Box<Core>,
}

impl Service for ClientCore {

    type Request = Vec<u8>;
    type Response = Vec<u8>;
    type Error = io::Error;
    // Again for simplicity, we are just going to box a future
    type Future = Box<Future<Item = Self::Response, Error = io::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        self.inner.call(req).boxed()
    }
}

impl Client {
    pub fn connect_with_timeout (address: &String, timeout: Duration) -> io::Result<Client> {
        let mut core = Core::new()?;
        let address = address.parse().unwrap();
        let future = Box::new(TcpClient::new(BytesClientProto)
                                  .connect(&address, &core.handle())
                                  .map(|c| Timeout::new(
                                      ClientCore {
                                          inner: c,
                                      },
                                      Timer::default(),
                                      timeout)));
        let client = core.run(future)?;
        Ok(Client {
            client: client,
            core: Box::new(core),
        })
    }
    pub fn connect (address: &String) -> io::Result<Client> {
        Client::connect_with_timeout(address, Duration::from_secs(5))
    }
    pub fn send(&mut self, msg: Vec<u8>) -> io::Result<Vec<u8>> {
        let resq = self.client.call(msg);
        self.core.run(resq)
    }
}

unsafe impl Send for Client {}<commit_msg>leave async client inferface<commit_after>use std::io;
use std::net::SocketAddr;
use std::time::Duration;

use futures::{self, Future};

use tokio_service::Service;
use tokio_core::io::Io;
use tokio_core::reactor::Handle;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tokio_proto::TcpClient;
use tokio_proto::multiplex::{ClientProto, ClientService};
use tokio_middleware::Timeout;
use tokio_timer::Timer;

use tcp::proto::BytesClientProto;

pub struct ClientCore {
    inner: ClientService<TcpStream, BytesClientProto>,
}

pub struct Client {
    client: Timeout<ClientCore>,
    core: Box<Core>,
}

impl Service for ClientCore {

    type Request = Vec<u8>;
    type Response = Vec<u8>;
    type Error = io::Error;
    // Again for simplicity, we are just going to box a future
    type Future = Box<Future<Item = Self::Response, Error = io::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        self.inner.call(req).boxed()
    }
}

impl Client {
    pub fn connect_with_timeout (address: &String, timeout: Duration) -> io::Result<Client> {
        let mut core = Core::new()?;
        let address = address.parse().unwrap();
        let future = Box::new(TcpClient::new(BytesClientProto)
                                  .connect(&address, &core.handle())
                                  .map(|c| Timeout::new(
                                      ClientCore {
                                          inner: c,
                                      },
                                      Timer::default(),
                                      timeout)));
        let client = core.run(future)?;
        Ok(Client {
            client: client,
            core: Box::new(core),
        })
    }
    pub fn connect (address: &String) -> io::Result<Client> {
        Client::connect_with_timeout(address, Duration::from_secs(5))
    }
    pub fn send(&mut self, msg: Vec<u8>) -> io::Result<Vec<u8>> {
        let future = self.send_async(msg);
        self.core.run(future)
    }
    pub fn send_async(&mut self, msg: Vec<u8>) -> ::tokio_timer::Timeout<<ClientCore as Service>::Future> {
        self.client.call(msg)
    }
}

unsafe impl Send for Client {}<|endoftext|>