<commit_before>use std::{
    io::{stdin, stdout, BufRead, Write},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use semver::{Identifier, Version};
use serde::Deserialize;
use serde_json::json;

use crate::{cargo::Package, cmd, util::ask_yes_no, GithubConfig, Metadata, Result};

const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The metadata of the cargo workspace.
    metadata: Metadata,

    /// The crate to release.
    package: Package,

    /// The new version of the crate.
    version: Version,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `version`.
    pub(crate) fn new(name: String, version: Version) -> Result<Self> {
        let metadata = Metadata::load()?;

        let package = metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == name)
            .ok_or(format!("Package {} not found in cargo metadata", name))?;

        let config = crate::Config::load()?.github;

        let http_client = HttpClient::new()?;

        Ok(Self { metadata, package, version, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(&mut self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.version.is_prerelease();
        let publish_only = self.package.name == "ruma-identifiers-validation";

        if let Some(name) = self.package.name.strip_suffix("-macros") {
            return Err(format!(
                "Macro crates are always released together with their parent crate.\n\
                 To release both {main_cr} and {macro_cr}, simply run\n\
                 \n\
                 cargo xtask release {main_cr}",
                main_cr = name,
                macro_cr = self.package.name,
            )
            .into());
        }

        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains uncommitted changes. Continue?")?
        {
            return Ok(());
        }

        if self.package.version != self.version
            && !self.package.version.is_next(&self.version)
            && !ask_yes_no(&format!(
                "Version {} should not follow version {}. Do you really want to continue?",
                self.version, self.package.version,
            ))?
        {
            return Ok(());
        }

        let mut macros = self.macros();

        let create_commit = if self.package.version != self.version {
            if let Some(m) = macros.as_mut() {
                println!("Found macros crate {}.", m.name);

                m.update_version(&self.version)?;
                m.update_dependants(&self.metadata)?;

                println!("Resuming release of {}…", self.title());
            }

            self.package.update_version(&self.version)?;
            self.package.update_dependants(&self.metadata)?;
            true
        } else if !ask_yes_no(&format!(
            "Package is already version {}. Skip creating a commit and continue?",
            &self.version
        ))? {
            return Ok(());
        } else {
            false
        };

        let changes = &self.package.changes(!prerelease)?;

        if create_commit {
            self.commit()?;
        }

        if let Some(m) = macros {
            let published = m.publish(&self.http_client)?;

            if published {
                // Crate was published, instead of publishing skipped (because release already
                // existed).
                println!("Waiting 20 seconds for the release to make it into the crates.io index…");
                sleep(Duration::from_secs(20));
            }
        }

        self.package.publish(&self.http_client)?;

        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        if publish_only {
            println!("Pushing to remote repository…");
            cmd!("git push {remote} {branch}").run()?;

            println!("Crate published successfully!");
            return Ok(());
        }

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {branch} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<Package> {
        self.metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == format!("{}-macros", self.package.name))
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.package.name, self.version)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.package.name, self.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Commit and push all the changes in the git repository.
    fn commit(&self) -> Result<()> {
        let stdin = stdin();

        let instructions = "Ready to commit the changes. [continue/abort/diff]: ";
        print!("{}", instructions);
        stdout().flush()?;

        let mut handle = stdin.lock();

        let mut input = String::new();
        loop {
            let eof = handle.read_line(&mut input)? == 0;
            if eof {
                return Err("User aborted commit".into());
            }

            match input.trim().to_ascii_lowercase().as_str() {
                "c" | "con" | "continue" => {
                    break;
                }
                "a" | "abort" => {
                    return Err("User aborted commit".into());
                }
                "d" | "diff" => {
                    cmd!("git diff").run()?;
                }
                _ => {
                    println!("Unknown command.");
                }
            }
            print!("{}", instructions);
            stdout().flush()?;

            input.clear();
        }

        let message = format!("Release {}", self.title());

        println!("Creating commit…");
        cmd!("git commit -a -m {message}").read()?;

        Ok(())
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}

/// Extra Version increment methods for crate release.
trait VersionExt {
    /// Adds a pre-release label and number if there is none.
    fn add_pre_release(&mut self);

    /// Increments the pre-release number, if this is a pre-release.
    fn increment_pre_number(&mut self);

    /// Increments the pre-release label from `alpha` to `beta` if this is a pre-release and it is
    /// possible, otherwise does nothing.
    fn increment_pre_label(&mut self);

    /// If the given version can be the next after this one.
    ///
    /// This checks all the version bumps of the format MAJOR.MINOR.PATCH-PRE_LABEL.PRE_NUMBER, with
    /// PRE_LABEL = alpha or beta.
    fn is_next(&self, version: &Version) -> bool;
}

impl VersionExt for Version {
    fn add_pre_release(&mut self) {
        if !self.is_prerelease() {
            self.pre = vec![Identifier::AlphaNumeric("alpha".into()), Identifier::Numeric(1)];
        }
    }

    fn increment_pre_number(&mut self) {
        if self.is_prerelease() {
            if let Identifier::Numeric(n) = self.pre[1] {
                self.pre[1] = Identifier::Numeric(n + 1);
            }
        }
    }

    fn increment_pre_label(&mut self) {
        if self.is_prerelease() {
            match &self.pre[0] {
                Identifier::AlphaNumeric(n) if n == "alpha" => {
                    self.pre =
                        vec![Identifier::AlphaNumeric("beta".into()), Identifier::Numeric(1)];
                }
                _ => {}
            }
        }
    }

    fn is_next(&self, version: &Version) -> bool {
        let mut next = self.clone();

        if self.is_prerelease() {
            next.increment_pre_number();
            if next == *version {
                return true;
            }

            next.increment_pre_label();
            if next == *version {
                return true;
            }

            next.pre = vec![];
        } else {
            next.increment_patch();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_minor();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_major();
            if next == *version {
                return true;
            }

            next.add_pre_release();
        }
        next == *version
    }
}
<commit_msg>xtask: Remove dead code<commit_after>use std::{
    io::{stdin, stdout, BufRead, Write},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use semver::{Identifier, Version};
use serde_json::json;

use crate::{cargo::Package, cmd, util::ask_yes_no, GithubConfig, Metadata, Result};

const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The metadata of the cargo workspace.
    metadata: Metadata,

    /// The crate to release.
    package: Package,

    /// The new version of the crate.
    version: Version,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `version`.
    pub(crate) fn new(name: String, version: Version) -> Result<Self> {
        let metadata = Metadata::load()?;

        let package = metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == name)
            .ok_or(format!("Package {} not found in cargo metadata", name))?;

        let config = crate::Config::load()?.github;

        let http_client = HttpClient::new()?;

        Ok(Self { metadata, package, version, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(&mut self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.version.is_prerelease();
        let publish_only = self.package.name == "ruma-identifiers-validation";

        if let Some(name) = self.package.name.strip_suffix("-macros") {
            return Err(format!(
                "Macro crates are always released together with their parent crate.\n\
                 To release both {main_cr} and {macro_cr}, simply run\n\
                 \n\
                 cargo xtask release {main_cr}",
                main_cr = name,
                macro_cr = self.package.name,
            )
            .into());
        }

        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains uncommitted changes. Continue?")?
        {
            return Ok(());
        }

        if self.package.version != self.version
            && !self.package.version.is_next(&self.version)
            && !ask_yes_no(&format!(
                "Version {} should not follow version {}. Do you really want to continue?",
                self.version, self.package.version,
            ))?
        {
            return Ok(());
        }

        let mut macros = self.macros();

        let create_commit = if self.package.version != self.version {
            if let Some(m) = macros.as_mut() {
                println!("Found macros crate {}.", m.name);

                m.update_version(&self.version)?;
                m.update_dependants(&self.metadata)?;

                println!("Resuming release of {}…", self.title());
            }

            self.package.update_version(&self.version)?;
            self.package.update_dependants(&self.metadata)?;
            true
        } else if !ask_yes_no(&format!(
            "Package is already version {}. Skip creating a commit and continue?",
            &self.version
        ))? {
            return Ok(());
        } else {
            false
        };

        let changes = &self.package.changes(!prerelease)?;

        if create_commit {
            self.commit()?;
        }

        if let Some(m) = macros {
            let published = m.publish(&self.http_client)?;

            if published {
                // Crate was published, instead of publishing skipped (because release already
                // existed).
                println!("Waiting 20 seconds for the release to make it into the crates.io index…");
                sleep(Duration::from_secs(20));
            }
        }

        self.package.publish(&self.http_client)?;

        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        if publish_only {
            println!("Pushing to remote repository…");
            cmd!("git push {remote} {branch}").run()?;

            println!("Crate published successfully!");
            return Ok(());
        }

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {branch} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<Package> {
        self.metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == format!("{}-macros", self.package.name))
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.package.name, self.version)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.package.name, self.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Commit and push all the changes in the git repository.
    fn commit(&self) -> Result<()> {
        let stdin = stdin();

        let instructions = "Ready to commit the changes. [continue/abort/diff]: ";
        print!("{}", instructions);
        stdout().flush()?;

        let mut handle = stdin.lock();

        let mut input = String::new();
        loop {
            let eof = handle.read_line(&mut input)? == 0;
            if eof {
                return Err("User aborted commit".into());
            }

            match input.trim().to_ascii_lowercase().as_str() {
                "c" | "con" | "continue" => {
                    break;
                }
                "a" | "abort" => {
                    return Err("User aborted commit".into());
                }
                "d" | "diff" => {
                    cmd!("git diff").run()?;
                }
                _ => {
                    println!("Unknown command.");
                }
            }
            print!("{}", instructions);
            stdout().flush()?;

            input.clear();
        }

        let message = format!("Release {}", self.title());

        println!("Creating commit…");
        cmd!("git commit -a -m {message}").read()?;

        Ok(())
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}

/// Extra Version increment methods for crate release.
trait VersionExt {
    /// Adds a pre-release label and number if there is none.
    fn add_pre_release(&mut self);

    /// Increments the pre-release number, if this is a pre-release.
    fn increment_pre_number(&mut self);

    /// Increments the pre-release label from `alpha` to `beta` if this is a pre-release and it is
    /// possible, otherwise does nothing.
    fn increment_pre_label(&mut self);

    /// If the given version can be the next after this one.
    ///
    /// This checks all the version bumps of the format MAJOR.MINOR.PATCH-PRE_LABEL.PRE_NUMBER, with
    /// PRE_LABEL = alpha or beta.
    fn is_next(&self, version: &Version) -> bool;
}

impl VersionExt for Version {
    fn add_pre_release(&mut self) {
        if !self.is_prerelease() {
            self.pre = vec![Identifier::AlphaNumeric("alpha".into()), Identifier::Numeric(1)];
        }
    }

    fn increment_pre_number(&mut self) {
        if self.is_prerelease() {
            if let Identifier::Numeric(n) = self.pre[1] {
                self.pre[1] = Identifier::Numeric(n + 1);
            }
        }
    }

    fn increment_pre_label(&mut self) {
        if self.is_prerelease() {
            match &self.pre[0] {
                Identifier::AlphaNumeric(n) if n == "alpha" => {
                    self.pre =
                        vec![Identifier::AlphaNumeric("beta".into()), Identifier::Numeric(1)];
                }
                _ => {}
            }
        }
    }

    fn is_next(&self, version: &Version) -> bool {
        let mut next = self.clone();

        if self.is_prerelease() {
            next.increment_pre_number();
            if next == *version {
                return true;
            }

            next.increment_pre_label();
            if next == *version {
                return true;
            }

            next.pre = vec![];
        } else {
            next.increment_patch();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_minor();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_major();
            if next == *version {
                return true;
            }

            next.add_pre_release();
        }
        next == *version
    }
}
<|endoftext|>


<commit_before>use grid::Grid;

mod sequential;
use self::sequential::Sequential;

pub trait Engine {
    type Grid: Grid;

    fn new(grid: Self::Grid) -> Self;

    fn run_times(&self, times: i64);
}<commit_msg>New trait for parallel computation.<commit_after>use grid::Grid;

mod sequential;
use self::sequential::Sequential;

pub trait Engine {
    type Grid: Grid;

    fn new(grid: Self::Grid) -> Self;
    fn run_times(&self, times: i64);
}

pub trait Parallel: Sized {
    fn into_parts(self) -> Vec<Self>;
}<|endoftext|>


<commit_before>//! Manage SBD messages stored on the fileystem.
//!
//! Messages are stored in a directory hierarchy under a single root directory. Message storage and
//! retrieval are managed by a `Storage` object, which is configured for a single root directory.

use std::fs;
use std::path::{Path, PathBuf};

use glob::glob;

use super::{Message, Result};

const SBD_EXTENSION: &'static str = ".sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
pub struct Storage {
    root: PathBuf,
}

/// An interator over the messages in a `Storage`.
pub struct StorageIterator;

/// The object yielded by a `StorageIterator`.
pub struct StorageEntry;

impl Iterator for StorageIterator {
    type Item = StorageEntry;

    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

impl<'a> IntoIterator for &'a Storage {
    type Item = StorageEntry;
    type IntoIter = StorageIterator;

    fn into_iter(self) -> Self::IntoIter {
        StorageIterator
    }
}

impl Storage {
    /// Creates a new storage manager for a given root directory.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// let storage = Storage::new("/var/iridium");
    /// ```
    pub fn new<P: AsRef<Path>>(root: P) -> Storage {
        Storage {
            root: root.as_ref().to_path_buf(),
        }
    }

    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// use sbd::Message;
    /// let message: Message = Default::default();
    /// let storage = Storage::new("/var/iridium");
    /// storage.store(&message);
    /// ```
    pub fn store(&self, message: &Message) -> Result<PathBuf> {
        let mut path_buf = self.root.clone();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session().format(&format!("%y%m%d_%H%M%S{}", SBD_EXTENSION)).to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(path_buf)
    }

    pub fn retrieve_all(&self) -> Result<Vec<Message>> {
        let mut messages: Vec<Message> = Vec::new();
        let mut path_buf = self.root.clone();
        path_buf.push("**");
        path_buf.push(format!("*{}", SBD_EXTENSION));
        for entry in try!(glob(&path_buf.to_str().unwrap())) {
            messages.push(try!(Message::from_path(try!(entry))));
        }
        messages.sort();
        Ok(messages)
    }
}

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

    use tempdir::TempDir;

    use super::super::Message;

    #[test]
    fn new_storage() {
        Storage::new(TempDir::new("").unwrap().path());
    }

    #[test]
    fn store_message() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        let pathbuf = storage.store(&message).unwrap();
        Message::from_path(pathbuf).unwrap();
    }

    #[test]
    fn iterate_over_messages() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        storage.store(&message).unwrap();
        let messages = storage.into_iter().collect::<Vec<StorageEntry>>();
        assert_eq!(1, messages.len());
    }
}
<commit_msg>Additional stash while working on iter<commit_after>//! Manage SBD messages stored on the fileystem.
//!
//! Messages are stored in a directory hierarchy under a single root directory. Message storage and
//! retrieval are managed by a `Storage` object, which is configured for a single root directory.

use std::fs;
use std::path::{Path, PathBuf};
use std::result;

use glob::{glob, Paths, PatternError};

use super::{Message, Result};

const SBD_EXTENSION: &'static str = ".sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
pub struct Storage {
    root: PathBuf,
}

/// An interator over the messages in a `Storage`.
pub struct StorageIterator {
    paths: Option<Paths>,
}

/// The object yielded by a `StorageIterator`.
pub struct StorageEntry;

impl Iterator for StorageIterator {
    type Item = StorageEntry;

    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

impl StorageIterator {
    fn new(storage: &Storage) -> StorageIterator {
        let paths = match storage.glob() {
            Ok(paths) => Some(paths),
            Err(_) => None,
        };
        StorageIterator {
            paths: paths,
        }
    }
}

impl<'a> IntoIterator for &'a Storage {
    type Item = StorageEntry;
    type IntoIter = StorageIterator;

    fn into_iter(self) -> Self::IntoIter {
        StorageIterator::new(self)
    }
}

impl Storage {
    /// Creates a new storage manager for a given root directory.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// let storage = Storage::new("/var/iridium");
    /// ```
    pub fn new<P: AsRef<Path>>(root: P) -> Storage {
        Storage {
            root: root.as_ref().to_path_buf(),
        }
    }

    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// use sbd::Message;
    /// let message: Message = Default::default();
    /// let storage = Storage::new("/var/iridium");
    /// storage.store(&message);
    /// ```
    pub fn store(&self, message: &Message) -> Result<PathBuf> {
        let mut path_buf = self.root.clone();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session().format(&format!("%y%m%d_%H%M%S{}", SBD_EXTENSION)).to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(path_buf)
    }

    fn glob(&self) -> result::Result<Paths, PatternError> {
        let mut path_buf = self.root.clone();
        path_buf.push("**");
        path_buf.push(format!("*{}", SBD_EXTENSION));
        glob(&path_buf.to_str().unwrap())
    }
}

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

    use tempdir::TempDir;

    use super::super::Message;

    #[test]
    fn new_storage() {
        Storage::new(TempDir::new("").unwrap().path());
    }

    #[test]
    fn store_message() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        let pathbuf = storage.store(&message).unwrap();
        Message::from_path(pathbuf).unwrap();
    }

    #[test]
    fn iterate_over_messages() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        storage.store(&message).unwrap();
        let messages = storage.into_iter().collect::<Vec<StorageEntry>>();
        assert_eq!(1, messages.len());
    }
}
<|endoftext|>


<commit_before>const BOARD_WIDTH : usize = 7;
const BOARD_HEIGHT : usize = 6;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct GameState{
    board : [[Color; BOARD_HEIGHT]; BOARD_WIDTH],
    pub player : Color
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Color {
    Empty,
    White,
    Black,
}

#[derive(Debug, Copy, Clone)]
pub struct Move {
    pub color : Color,
    pub x: usize
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum End{
    Ongoing,
    Victory(Color),
    Tie
}

impl Move{
    pub fn new(nx : usize, ncolor : Color) -> Self{
        Move{
            color : ncolor,
            x: nx
        }
    }

    pub fn white_new(nx : usize) -> Self{
        Move::new(nx, Color::White)
    }

    pub fn black_new(nx : usize) -> Self{
        Move::new(nx, Color::Black)
    }

    fn in_bounds(&self) -> bool{
        (self.x <= BOARD_WIDTH && self.x  >= 0)
    }
}

fn push_back(item : Color, column : &mut [Color; 6]) -> usize{
    for i in (0..column.len()).rev(){
        if (column[i] == Color::Empty){
            return i;
        }
    }
    return 0;
}

impl GameState{
    pub fn new() -> Self{
        GameState{
            board: [[Color::Empty; BOARD_HEIGHT]; BOARD_WIDTH],
            player : Color::White
        }
    }

    pub fn place(&self, game_move : &Move) -> Self{
        let mut copy = self.clone();
        if !self.legal(&game_move){
            return copy;
        }

        let mut column = copy.board[game_move.x];
        let y = push_back(game_move.color, &mut column);
        copy.board[game_move.x][y] = game_move.color;
        //copy.board[game_move.y][game_move.x] = game_move.color;
        let next_player =
        match game_move.color{
            Color::White => Color::Black,
            Color::Black => Color::White,
            _ => Color::White
        };

        copy.player = next_player;
        return copy;
    }

    pub fn legal(&self, game_move: &Move) -> bool{
        if !game_move.in_bounds(){
            return false;
        }

        let column = self.board[game_move.x];
        column.iter().fold(0, |sum, next| if next == &Color::Empty { sum }  else { sum + 1 } ) < BOARD_HEIGHT
    }

    pub fn legal_moves(&self, color : Color) -> Vec<Move>{
        let mut moves = Vec::new();
        for x in 0..BOARD_WIDTH{
            let potential_move = Move::new(x, color);
            if self.legal(&potential_move){
                moves.push(potential_move);
            }
        }
        return moves;
    }

    pub fn linear_match(&self, start_x : i32, start_y : i32, stepX : i32, stepY : i32, color : Color) -> bool{
        for i in 0..4{
            let x = (start_x + i * stepX) as usize;
            let y = (start_y + i * stepY) as usize;
            //bound checking
            if x < 0 || x >= BOARD_HEIGHT || y < 0 || y >= BOARD_WIDTH{
                return false;
            }
            let value = self.board[y][x];
            if value != color{
                return false;
            }
        }
        return true;
    }

    pub fn is_line_start(&self, x : i32, y : i32, color : Color) -> bool{
        self.linear_match(x, y, 1, 0, color)
        || self.linear_match(x, y, 0, 1, color)
        || self.linear_match(x, y, 1, 1, color)
        || self.linear_match(x, y, 1, -1, color)
    }

    fn color_win(&self, color : Color) -> bool{
        for x in 0..BOARD_HEIGHT{
            for y in 0..BOARD_WIDTH{
                if self.is_line_start(y as i32, x as i32, color){
                    return true;
                }
            }
        }
        return false;
    }

    pub fn win(&self) -> End{
        if self.color_win(Color::White) {
            return End::Victory(Color::White);
        }
        if self.color_win(Color::Black) {
            return End::Victory(Color::Black);
        }
        let mut no_empty = true;
        for column in self.board.iter(){
            for &tile in column{
                if tile == Color::Empty{
                    no_empty = false;
                }
            }
        }
        if no_empty {
            return End::Tie;
        }
        return End::Ongoing;
    }

    pub fn print(&self) -> String{
        let mut string = String::from("\n");
        for x in 0..BOARD_HEIGHT{
            for y in 0..BOARD_WIDTH{
                string.push_str("|");
                let tile = self.board[y][x];
                let tile_str =
                    match tile {
                        Color::Empty => " ",
                        Color::White => "X",
                        Color::Black => "O"
                    };
                string.push_str(tile_str);
                string.push_str("|")
            }
            string.push_str("\n");
        }
        string.push_str("|1||2||3||4||5||6||7|");

        return string;
    }
}

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

}
<commit_msg>fixed erroneous win checking<commit_after>const BOARD_WIDTH : usize = 7;
const BOARD_HEIGHT : usize = 6;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct GameState{
    board : [[Color; BOARD_HEIGHT]; BOARD_WIDTH],
    pub player : Color
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Color {
    Empty,
    White,
    Black,
}

#[derive(Debug, Copy, Clone)]
pub struct Move {
    pub color : Color,
    pub x: usize
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum End{
    Ongoing,
    Victory(Color),
    Tie
}

impl Move{
    pub fn new(nx : usize, ncolor : Color) -> Self{
        Move{
            color : ncolor,
            x: nx
        }
    }

    pub fn white_new(nx : usize) -> Self{
        Move::new(nx, Color::White)
    }

    pub fn black_new(nx : usize) -> Self{
        Move::new(nx, Color::Black)
    }

    fn in_bounds(&self) -> bool{
        (self.x <= BOARD_WIDTH && self.x  >= 0)
    }
}

fn push_back(item : Color, column : &mut [Color; 6]) -> usize{
    for i in (0..column.len()).rev(){
        if (column[i] == Color::Empty){
            return i;
        }
    }
    return 0;
}

impl GameState{
    pub fn new() -> Self{
        GameState{
            board: [[Color::Empty; BOARD_HEIGHT]; BOARD_WIDTH],
            player : Color::White
        }
    }

    pub fn place(&self, game_move : &Move) -> Self{
        let mut copy = self.clone();
        if !self.legal(&game_move){
            return copy;
        }

        let mut column = copy.board[game_move.x];
        let y = push_back(game_move.color, &mut column);
        copy.board[game_move.x][y] = game_move.color;
        //copy.board[game_move.y][game_move.x] = game_move.color;
        let next_player =
        match game_move.color{
            Color::White => Color::Black,
            Color::Black => Color::White,
            _ => Color::White
        };

        copy.player = next_player;
        return copy;
    }

    pub fn legal(&self, game_move: &Move) -> bool{
        if !game_move.in_bounds(){
            return false;
        }

        let column = self.board[game_move.x];
        column.iter().fold(0, |sum, next| if next == &Color::Empty { sum }  else { sum + 1 } ) < BOARD_HEIGHT
    }

    pub fn legal_moves(&self, color : Color) -> Vec<Move>{
        let mut moves = Vec::new();
        for x in 0..BOARD_WIDTH{
            let potential_move = Move::new(x, color);
            if self.legal(&potential_move){
                moves.push(potential_move);
            }
        }
        return moves;
    }

    pub fn linear_match(&self, start_x : i32, start_y : i32, stepX : i32, stepY : i32, color : Color) -> bool{
        for i in 0..4{
            let x = (start_x + i * stepX) as usize;
            let y = (start_y + i * stepY) as usize;
            //bound checking
            if x < 0 || x >= BOARD_HEIGHT || y < 0 || y >= BOARD_WIDTH{
                return false;
            }
            let value = self.board[y][x];
            if value != color{
                return false;
            }
        }
        return true;
    }

    pub fn is_line_start(&self, x : i32, y : i32, color : Color) -> bool{
        self.linear_match(x, y, 1, 0, color)
        || self.linear_match(x, y, 0, 1, color)
        || self.linear_match(x, y, 1, 1, color)
        || self.linear_match(x, y, 1, -1, color)
    }

    fn color_win(&self, color : Color) -> bool{
        for x in 0..BOARD_HEIGHT + 1{
            for y in 0..BOARD_WIDTH + 1{
                if self.is_line_start(y as i32, x as i32, color){
                    return true;
                }
            }
        }
        return false;
    }

    pub fn win(&self) -> End{
        if self.color_win(Color::White) {
            return End::Victory(Color::White);
        }
        if self.color_win(Color::Black) {
            return End::Victory(Color::Black);
        }
        let mut no_empty = true;
        for column in self.board.iter(){
            for &tile in column{
                if tile == Color::Empty{
                    no_empty = false;
                }
            }
        }
        if no_empty {
            return End::Tie;
        }
        return End::Ongoing;
    }

    pub fn print(&self) -> String{
        let mut string = String::from("\n");
        for x in 0..BOARD_HEIGHT{
            for y in 0..BOARD_WIDTH{
                string.push_str("|");
                let tile = self.board[y][x];
                let tile_str =
                    match tile {
                        Color::Empty => " ",
                        Color::White => "X",
                        Color::Black => "O"
                    };
                string.push_str(tile_str);
                string.push_str("|")
            }
            string.push_str("\n");
        }
        string.push_str("|1||2||3||4||5||6||7|");

        return string;
    }
}

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

}
<|endoftext|>


<commit_before>use super::point::Point;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LineIntersection {
   Point(Point),
   Overlapping,
   None,
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Line {
   pub a: f64,
   pub b: f64,
   pub c: f64
}

impl Line {
   pub fn new(x1: i64, y1: i64, x2: i64, y2: i64) -> Self {
      let (x1, y1, x2, y2) = (x1 as f64, y1 as f64, x2 as f64, y2 as f64);
      Line {
         a: y2 - y1,
         b: x1 - x2,
         c: x2 * y1 - x1 * y2
      }
   }

   pub fn intersect(&self, line: &Self) -> LineIntersection {
      let denominator = self.a * line.b - line.a * self.b;

      if denominator == 0. {
         if self == line {
            return LineIntersection::Overlapping;
         } else {
            return LineIntersection::None;
         }
      }

      let x = (line.c * self.b - self.c * line.b) / denominator;
      let y = (line.a * self.c - self.a * line.c) / denominator;

      LineIntersection::Point(Point::new(x.round() as i64, y.round() as i64))
   }
}

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

   use super::*;

   #[test]
   fn test_line_new() {
      let ln = Line::new(
         10_000, 20_000,
         -100_000, 0
      );

      assert_eq!(ln.a, -20_000.);
      assert_eq!(ln.b, 110_000.);
      assert_eq!(ln.c, -2_000_000_000.);
   }

   #[test]
   fn test_line_intersect() {
      let l1 = Line::new(
         1, 1,
         7, 5
      );

      let l2 = Line::new(
         4, 5,
         7, 0
      );

      let intersection = l1.intersect(&l2);

      assert_eq!(intersection, LineIntersection::Point(Point::new(5, 4)));
   }

   #[test]
   fn test_line_intersect_mirror() {
      let l1 = Line::new(
         1, -1,
         7, -5
      );

      let l2 = Line::new(
         4, -5,
         7, 0
      );

      let intersection = l1.intersect(&l2);

      assert_eq!(intersection, LineIntersection::Point(Point::new(5, -4)));
   }
}
<commit_msg>Transferring the LineSegment implementation to the new geom module<commit_after>use super::point::Point;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LineIntersection {
   Point(Point),
   Overlapping,
   None,
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Line {
   pub a: f64,
   pub b: f64,
   pub c: f64
}

impl Line {
   pub fn new(x1: i64, y1: i64, x2: i64, y2: i64) -> Self {
      let (x1, y1, x2, y2) = (x1 as f64, y1 as f64, x2 as f64, y2 as f64);
      Line {
         a: y2 - y1,
         b: x1 - x2,
         c: x2 * y1 - x1 * y2
      }
   }

   pub fn update(&mut self, x1: i64, y1: i64, x2: i64, y2: i64) {
      let (x1, y1, x2, y2) = (x1 as f64, y1 as f64, x2 as f64, y2 as f64);

      self.a = y2 - y1;
      self.b = x1 - x2;
      self.c = x2 * y1 - x1 * y2;
   }

   pub fn intersect(&self, line: &Self) -> LineIntersection {
      let denominator = self.a * line.b - line.a * self.b;

      if denominator == 0. {
         if self == line {
            return LineIntersection::Overlapping;
         } else {
            return LineIntersection::None;
         }
      }

      let x = (line.c * self.b - self.c * line.b) / denominator;
      let y = (line.a * self.c - self.a * line.c) / denominator;

      LineIntersection::Point(Point::new(x.round() as i64, y.round() as i64))
   }

   #[inline]
   pub fn is_horizontal(&self) -> bool {
      self.a == 0.
   }

   #[inline]
   pub fn is_vertical(&self) -> bool {
      self.b == 0.
   }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LineSegmentIntersection {
   Point(Point),
   Overlapping(Point, Point),
   None,
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct LineSegment {
   p1: Point,
   p2: Point,
   line: Line
}

impl LineSegment {
   pub fn new(x1: i64, y1: i64, x2: i64, y2: i64) -> Self {
      let p1 = Point::new(x1, y1);
      let p2 = Point::new(x2, y2);
      let line = Line::new(x1, y1, x2, y2);

      LineSegment {
         p1: p1,
         p2: p2,
         line: line
      }
   }

   pub fn update(&mut self, x1: i64, y1: i64, x2: i64, y2: i64) {
      self.p1.x = x1;
      self.p1.y = y1;
      self.p2.x = x2;
      self.p2.y = y2;

      self.line.update(x1, y1, x2, y2);
   }

   #[inline]
   pub fn p1(&self) -> Point {
      self.p1
   }

   #[inline]
   pub fn p2(&self) -> Point {
      self.p2
   }

   #[inline]
   pub fn x1(&self) -> i64 {
      self.p1.x
   }

   #[inline]
   pub fn y1(&self) -> i64 {
      self.p1.y
   }

   #[inline]
   pub fn x2(&self) -> i64 {
      self.p2.x
   }

   #[inline]
   pub fn y2(&self) -> i64 {
      self.p2.y
   }

   #[inline]
   pub fn line(&self) -> &Line {
      &self.line
   }

   #[inline]
   fn is_inside(&self, p: &Point) -> bool {
      if self.line.is_vertical() {
         let (ls1_min, ls1_max) = if self.y2() > self.y1() {
            (self.p1(), self.p2())
         } else {
            (self.p2(), self.p1())
         };

         p.y >= ls1_min.y && p.y <= ls1_max.y
      } else {
         let (ls1_min, ls1_max) = if self.x2() > self.x1() {
            (self.p1(), self.p2())
         } else {
            (self.p2(), self.p1())
         };

         p.x >= ls1_min.x && p.x <= ls1_max.x
      }
   }

   pub fn intersect(&self, ls: &Self) -> LineSegmentIntersection {
      match self.line.intersect(ls.line()) {
         LineIntersection::Point(p) => {
            if self.is_inside(&p) && ls.is_inside(&p) {
               LineSegmentIntersection::Point(p)
            } else {
               LineSegmentIntersection::None
            }
         },

         LineIntersection::Overlapping => {
            let (p1, p2) = if self.line.is_vertical() {
               let (ls1_min, ls1_max) = if self.y2() > self.y1() {
                  (self.p1(), self.p2())
               } else {
                  (self.p2(), self.p1())
               };

               let (ls2_min, ls2_max) = if ls.y2() > ls.y1() {
                  (ls.p1(), ls.p2())
               } else {
                  (ls.p2(), ls.p1())
               };

               if ls1_min.y < ls2_min.y {
                  if ls1_max.y < ls2_min.y {
                     return LineSegmentIntersection::None;
                  } else if ls1_max.y < ls2_max.y {
                     (ls2_min, ls1_max)
                  } else {
                     (ls2_min, ls2_max)
                  }
               } else {
                  if ls2_max.y < ls2_min.y {
                     return LineSegmentIntersection::None;
                  } else if ls2_max.y < ls1_max.y {
                     (ls1_min, ls2_max)
                  } else {
                     (ls1_min, ls1_max)
                  }
               }
            } else {
               let (ls1_min, ls1_max) = if self.x2() > self.x1() {
                  (self.p1(), self.p2())
               } else {
                  (self.p2(), self.p1())
               };

               let (ls2_min, ls2_max) = if ls.x2() > ls.x1() {
                  (ls.p1(), ls.p2())
               } else {
                  (ls.p2(), ls.p1())
               };

               if ls1_min.x < ls2_min.x {
                  if ls1_max.x < ls2_min.x {
                     return LineSegmentIntersection::None;
                  } else if ls1_max.x < ls2_max.x {
                     (ls2_min, ls1_max)
                  } else {
                     (ls2_min, ls2_max)
                  }
               } else {
                  if ls2_max.x < ls2_min.x {
                     return LineSegmentIntersection::None;
                  } else if ls2_max.x < ls1_max.x {
                     (ls1_min, ls2_max)
                  } else {
                     (ls1_min, ls1_max)
                  }
               }
            };

            if p1 == p2 {
               LineSegmentIntersection::Point(p1)
            } else {
               LineSegmentIntersection::Overlapping(p1, p2)
            }
         }

         LineIntersection::None => LineSegmentIntersection::None,
      }
   }
}

impl Default for LineSegment {
   fn default() -> LineSegment {
      LineSegment::new(0, 0, 0, 0)
   }
}

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

   use super::*;

   #[test]
   fn test_line_new() {
      let ln = Line::new(
         10_000, 20_000,
         -100_000, 0
      );

      assert_eq!(ln.a, -20_000.);
      assert_eq!(ln.b, 110_000.);
      assert_eq!(ln.c, -2_000_000_000.);
   }

   #[test]
   fn test_line_intersect() {
      let l1 = Line::new(
         1, 1,
         7, 5
      );

      let l2 = Line::new(
         4, 5,
         7, 0
      );

      let intersection = l1.intersect(&l2);

      assert_eq!(intersection, LineIntersection::Point(Point::new(5, 4)));
   }

   #[test]
   fn test_line_intersect_mirror() {
      let l1 = Line::new(
         1, -1,
         7, -5
      );

      let l2 = Line::new(
         4, -5,
         7, 0
      );

      let intersection = l1.intersect(&l2);

      assert_eq!(intersection, LineIntersection::Point(Point::new(5, -4)));
   }
}
<|endoftext|>


<commit_before>use mio::*;
use mio::unix::*;
use mio::util::Slab;
use std::path::PathBuf;
use std::io::{self,Read,Write,ErrorKind};
use std::iter::repeat;
use std::thread;
use std::str::from_utf8;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::collections::HashMap;
use std::sync::mpsc;
use log;
use rustc_serialize::json::{decode,encode};
use rustc_serialize::{Encodable,Decodable,Encoder,Decoder};
use nom::{IResult,HexDisplay};

use yxorp::network::{ProxyOrder,ServerMessage};
use yxorp::network::buffer::Buffer;
use yxorp::messages::Command;

use state::{HttpProxy,TlsProxy,ConfigState};

const SERVER: Token = Token(0);

#[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)]
pub enum ListenerType {
  HTTP,
  HTTPS,
  TCP
}

impl Encodable for ListenerType {
  fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
    match *self {
      ListenerType::HTTP  => e.emit_str("HTTP"),
      ListenerType::HTTPS => e.emit_str("HTTPS"),
      ListenerType::TCP   => e.emit_str("TCP"),
    }
  }
}

impl Decodable for ListenerType {
  fn decode<D: Decoder>(decoder: &mut D) -> Result<ListenerType, D::Error> {
    let tag = try!(decoder.read_str());
    match &tag[..] {
      "HTTP"  => Ok(ListenerType::HTTP),
      "HTTPS" => Ok(ListenerType::HTTPS),
      "TCP"   => Ok(ListenerType::TCP),
      _       => Err(decoder.error("unrecognized listener type"))
    }
  }
}

pub struct Listener {
  pub tag:           String,
  pub listener_type: ListenerType,
  pub sender:        Sender<ProxyOrder>,
  pub receiver:      mpsc::Receiver<ServerMessage>,
  pub state:         ConfigState,
}

impl Listener {
  pub fn new(tag: String, listener_type: ListenerType, ip_address: String, port: u16, sender: Sender<ProxyOrder>, receiver: mpsc::Receiver<ServerMessage>) -> Listener {
    let state = match listener_type {
      ListenerType::HTTP  => ConfigState::Http(HttpProxy::new(ip_address, port)),
      ListenerType::HTTPS => ConfigState::Tls(TlsProxy::new(ip_address, port)),
      _                   => unimplemented!(),
    };

    Listener {
      tag:           tag,
      listener_type: listener_type,
      sender:        sender,
      receiver:      receiver,
      state:         state,
    }
  }
}

impl Encodable for Listener {
  fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
    e.emit_map(3, |e| {
      try!(e.emit_map_elt_key(0, |e| "tag".encode(e)));
      try!(e.emit_map_elt_val(0, |e| self.tag.encode(e)));
      try!(e.emit_map_elt_key(1, |e| "listener_type".encode(e)));
      try!(e.emit_map_elt_val(1, |e| self.listener_type.encode(e)));
      try!(e.emit_map_elt_key(2, |e| "state".encode(e)));
      try!(e.emit_map_elt_val(2, |e| self.state.encode(e)));
      Ok(())
    })
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct ConfigMessage {
    pub id:       String,
    pub listener: String,
    pub command:  Command
}

struct CommandClient {
  sock:        UnixStream,
  buf:         Buffer,
  back_buf:    Buffer,
  token:       Option<Token>,
  message_ids: Vec<String>,
}

impl CommandClient {
  fn new(sock: UnixStream) -> CommandClient {
    CommandClient {
      sock:        sock,
      buf:         Buffer::with_capacity(1024),
      back_buf:    Buffer::with_capacity(10000),
      token:       None,
      message_ids: Vec::new(),
    }
  }

  fn add_message_id(&mut self, id: String) {
    self.message_ids.push(id);
    self.message_ids.sort();
  }

  fn has_message_id(&self, id: &String) ->Option<usize> {
    self.message_ids.binary_search(&id).ok()
  }

  fn remove_message_id(&mut self, index: usize) {
    self.message_ids.remove(index);
  }

  fn reregister(&self,  event_loop: &mut EventLoop<CommandServer>, tok: Token) {
    let mut interest = EventSet::hup();
    interest.insert(EventSet::readable());
    interest.insert(EventSet::writable());
    event_loop.register(&self.sock, tok, interest, PollOpt::level() | PollOpt::oneshot());
  }

  fn conn_readable(&mut self, event_loop: &mut EventLoop<CommandServer>, tok: Token) -> Option<Vec<ConfigMessage>>{
    trace!("server conn readable; tok={:?}", tok);
    match self.sock.read(self.buf.space()) {
      Ok(0) => {
        self.reregister(event_loop, tok);
        return None;
      },
      Err(e) => {
        log!(log::LogLevel::Error, "UNIX CLIENT[{}] read error: {:?}", tok.as_usize(), e);
        return None;
      },
      Ok(r) => {
        self.buf.fill(r);
        debug!("UNIX CLIENT[{}] sent {} bytes: {:?}", tok.as_usize(), r, from_utf8(self.buf.data()));
      },
    };

    let mut res: Option<Vec<ConfigMessage>> = None;
    let mut offset = 0usize;
    match parse(self.buf.data()) {
      IResult::Incomplete(_) => {
        return Some(Vec::new());
      },
      IResult::Error(e)      => {
        log!(log::LogLevel::Error, "UNIX CLIENT[{}] read error: {:?}", tok.as_usize(), e);
        return None;
      },
      IResult::Done(i, v)    => {
        offset = self.buf.data().offset(i);
        res = Some(v);
      }
    }
    debug!("parsed {} bytes, result: {:?}", offset, res);
    self.buf.consume(offset);
    return res;
  }

  fn conn_writable(&mut self, event_loop: &mut EventLoop<CommandServer>, tok: Token) {
    trace!("server conn writable; tok={:?}", tok);
    match self.sock.write(self.back_buf.data()) {
      Ok(0) => {},
      Ok(r) => {
        self.back_buf.consume(r);
      },
      Err(e) => log!(log::LogLevel::Error,"UNIX CLIENT[{}] read error: {:?}", tok.as_usize(), e),
    }
    let mut interest = EventSet::hup();
    interest.insert(EventSet::readable());
    interest.insert(EventSet::writable());
    event_loop.register(&self.sock, tok, interest, PollOpt::level() | PollOpt::oneshot());
  }
}

fn parse(input: &[u8]) -> IResult<&[u8], Vec<ConfigMessage>> {
  many0!(input,
    complete!(terminated!(map_res!(map_res!(is_not!("\0"), from_utf8), decode), char!('\0')))
  )
}

struct CommandServer {
  sock:  UnixListener,
  conns: Slab<CommandClient>,
  listeners: HashMap<String, Listener>,
}

impl CommandServer {
  fn accept(&mut self, event_loop: &mut EventLoop<CommandServer>) -> io::Result<()> {
    debug!("server accepting socket");

    let sock = self.sock.accept().unwrap().unwrap();
    let conn = CommandClient::new(sock);
    let tok = self.conns.insert(conn)
      .ok().expect("could not add connection to slab");

    // Register the connection
    self.conns[tok].token = Some(tok);
    let mut interest = EventSet::hup();
    interest.insert(EventSet::readable());
    interest.insert(EventSet::writable());
    event_loop.register(&self.conns[tok].sock, tok, interest, PollOpt::level())
      .ok().expect("could not register socket with event loop");

    let accept_interest = EventSet::readable();
    event_loop.reregister(&self.sock, SERVER, accept_interest, PollOpt::level());
    Ok(())
  }

  fn conn<'a>(&'a mut self, tok: Token) -> &'a mut CommandClient {
    &mut self.conns[tok]
  }

  fn dispatch(&mut self, token: Token, v: Vec<ConfigMessage>) {
    for message in &v {
      let command = message.command.clone();
      if let Some(ref mut listener) = self.listeners.get_mut (&message.listener) {
        if message.command == Command::DumpConfiguration {
          self.conns[token].back_buf.write(&encode(*listener).unwrap().into_bytes());
          self.conns[token].back_buf.write(&b"\0"[..]);
        } else {
          self.conns[token].add_message_id(message.id.clone());
          listener.state.handle_command(&command);
          listener.sender.send(ProxyOrder::Command(message.id.clone(), command));
        }
      } else {
        log!(log::LogLevel::Error, "no listener found for tag: {}", message.listener);
      }
    }
  }

  fn new(srv: UnixListener, listeners: HashMap<String, Listener>) -> CommandServer {
    CommandServer {
      sock:  srv,
      conns: Slab::new_starting_at(Token(1), 128),
      listeners: listeners,
    }
  }
}

impl Handler for CommandServer {
	type Timeout = usize;
	type Message = ();

	fn ready(&mut self, event_loop: &mut EventLoop<CommandServer>, token: Token, events: EventSet) {
    //trace!("ready: {:?} -> {:?}", token, events);
    if events.is_readable() {
      match token {
        SERVER => self.accept(event_loop).unwrap(),
        _      => {
          if self.conns.contains(token) {
            let opt_v = self.conns[token].conn_readable(event_loop, token);
            if let Some(v) = opt_v {
              self.dispatch(token, v);
            }
          }
        }
      };
    }

    if events.is_writable() {
      match token {
        SERVER => panic!("received writable for token 0"),
        _      => {
          if self.conns.contains(token) {
            self.conns[token].conn_writable(event_loop, token)
          }
        }
      };
    }

    if events.is_hup() {
      if self.conns.contains(token) {
        event_loop.deregister(&self.conns[token].sock);
        self.conns.remove(token);
        trace!("closed client [{}]", token.as_usize());
      }
    }
  }

  fn notify(&mut self, event_loop: &mut EventLoop<Self>, message: Self::Message) {
    // TODO: dispatch here to clients the list of messages you want to display
    info!("notify");
  }

  fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
    for listener in self.listeners.values() {
      while let Ok(msg) = listener.receiver.try_recv() {
        println!("got msg: {:?}", msg);
        for client in self.conns.iter_mut() {
          if let Some(index) = client.has_message_id(&msg.id) {
            client.back_buf.write(&encode(&msg).unwrap().into_bytes());
            client.back_buf.write(&b"\0"[..]);
            client.remove_message_id(index);
          }
        }
      }
    }
    event_loop.timeout_ms(0, 700);
  }

}

pub fn start(path: String, listeners: HashMap<String, Listener>) {
  thread::spawn(move || {
    let mut event_loop = EventLoop::new().unwrap();
    let addr = PathBuf::from(path);
    if let Err(e) = fs::remove_file(&addr) {
      match e.kind() {
        ErrorKind::NotFound => {},
        _ => {
          log!(log::LogLevel::Error, "could not delete previous socket at {:?}: {:?}", addr, e);
          return;
        }
      }
    }
    match UnixListener::bind(&addr) {
      Ok(srv) => {
        if let Err(e) =  fs::set_permissions(&addr, fs::Permissions::from_mode(0o600)) {
          log!( log::LogLevel::Error, "could not set the unix socket permissions: {:?}", e);
          fs::remove_file(&addr);
          return;
        }
        info!("listen for connections");
        event_loop.register(&srv, SERVER, EventSet::readable(), PollOpt::level() | PollOpt::oneshot()).unwrap();
        event_loop.timeout_ms(0, 700);

        event_loop.run(&mut CommandServer::new(srv, listeners)).unwrap()
      },
      Err(e) => {
        log!(log::LogLevel::Error, "could not create unix socket: {:?}", e);
      }
    }
  });
}

#[cfg(test)]
mod tests {
  use super::*;
  use rustc_serialize::json;
  use yxorp::messages::{Command,HttpFront};

  #[test]
  fn config_message_test() {
    let raw_json = r#"{ "listener": "HTTP", "command":{"type": "ADD_HTTP_FRONT", "data": {"app_id": "xxx", "hostname": "yyy", "path_begin": "xxx", "port": 4242}} }"#;
    let config: ConfigMessage = json::decode(raw_json).unwrap();
    println!("{:?}", config);
    assert_eq!(config.listener, "HTTP");
    assert_eq!(config.command, Command::AddHttpFront(HttpFront{
      app_id: String::from("xxx"),
      hostname: String::from("yyy"),
      path_begin: String::from("xxx"),
      port: 4242
    }));
  }
}
<commit_msg>fix the tests<commit_after>use mio::*;
use mio::unix::*;
use mio::util::Slab;
use std::path::PathBuf;
use std::io::{self,Read,Write,ErrorKind};
use std::iter::repeat;
use std::thread;
use std::str::from_utf8;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::collections::HashMap;
use std::sync::mpsc;
use log;
use rustc_serialize::json::{decode,encode};
use rustc_serialize::{Encodable,Decodable,Encoder,Decoder};
use nom::{IResult,HexDisplay};

use yxorp::network::{ProxyOrder,ServerMessage};
use yxorp::network::buffer::Buffer;
use yxorp::messages::Command;

use state::{HttpProxy,TlsProxy,ConfigState};

const SERVER: Token = Token(0);

#[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)]
pub enum ListenerType {
  HTTP,
  HTTPS,
  TCP
}

impl Encodable for ListenerType {
  fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
    match *self {
      ListenerType::HTTP  => e.emit_str("HTTP"),
      ListenerType::HTTPS => e.emit_str("HTTPS"),
      ListenerType::TCP   => e.emit_str("TCP"),
    }
  }
}

impl Decodable for ListenerType {
  fn decode<D: Decoder>(decoder: &mut D) -> Result<ListenerType, D::Error> {
    let tag = try!(decoder.read_str());
    match &tag[..] {
      "HTTP"  => Ok(ListenerType::HTTP),
      "HTTPS" => Ok(ListenerType::HTTPS),
      "TCP"   => Ok(ListenerType::TCP),
      _       => Err(decoder.error("unrecognized listener type"))
    }
  }
}

pub struct Listener {
  pub tag:           String,
  pub listener_type: ListenerType,
  pub sender:        Sender<ProxyOrder>,
  pub receiver:      mpsc::Receiver<ServerMessage>,
  pub state:         ConfigState,
}

impl Listener {
  pub fn new(tag: String, listener_type: ListenerType, ip_address: String, port: u16, sender: Sender<ProxyOrder>, receiver: mpsc::Receiver<ServerMessage>) -> Listener {
    let state = match listener_type {
      ListenerType::HTTP  => ConfigState::Http(HttpProxy::new(ip_address, port)),
      ListenerType::HTTPS => ConfigState::Tls(TlsProxy::new(ip_address, port)),
      _                   => unimplemented!(),
    };

    Listener {
      tag:           tag,
      listener_type: listener_type,
      sender:        sender,
      receiver:      receiver,
      state:         state,
    }
  }
}

impl Encodable for Listener {
  fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
    e.emit_map(3, |e| {
      try!(e.emit_map_elt_key(0, |e| "tag".encode(e)));
      try!(e.emit_map_elt_val(0, |e| self.tag.encode(e)));
      try!(e.emit_map_elt_key(1, |e| "listener_type".encode(e)));
      try!(e.emit_map_elt_val(1, |e| self.listener_type.encode(e)));
      try!(e.emit_map_elt_key(2, |e| "state".encode(e)));
      try!(e.emit_map_elt_val(2, |e| self.state.encode(e)));
      Ok(())
    })
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct ConfigMessage {
    pub id:       String,
    pub listener: String,
    pub command:  Command
}

struct CommandClient {
  sock:        UnixStream,
  buf:         Buffer,
  back_buf:    Buffer,
  token:       Option<Token>,
  message_ids: Vec<String>,
}

impl CommandClient {
  fn new(sock: UnixStream) -> CommandClient {
    CommandClient {
      sock:        sock,
      buf:         Buffer::with_capacity(1024),
      back_buf:    Buffer::with_capacity(10000),
      token:       None,
      message_ids: Vec::new(),
    }
  }

  fn add_message_id(&mut self, id: String) {
    self.message_ids.push(id);
    self.message_ids.sort();
  }

  fn has_message_id(&self, id: &String) ->Option<usize> {
    self.message_ids.binary_search(&id).ok()
  }

  fn remove_message_id(&mut self, index: usize) {
    self.message_ids.remove(index);
  }

  fn reregister(&self,  event_loop: &mut EventLoop<CommandServer>, tok: Token) {
    let mut interest = EventSet::hup();
    interest.insert(EventSet::readable());
    interest.insert(EventSet::writable());
    event_loop.register(&self.sock, tok, interest, PollOpt::level() | PollOpt::oneshot());
  }

  fn conn_readable(&mut self, event_loop: &mut EventLoop<CommandServer>, tok: Token) -> Option<Vec<ConfigMessage>>{
    trace!("server conn readable; tok={:?}", tok);
    match self.sock.read(self.buf.space()) {
      Ok(0) => {
        self.reregister(event_loop, tok);
        return None;
      },
      Err(e) => {
        log!(log::LogLevel::Error, "UNIX CLIENT[{}] read error: {:?}", tok.as_usize(), e);
        return None;
      },
      Ok(r) => {
        self.buf.fill(r);
        debug!("UNIX CLIENT[{}] sent {} bytes: {:?}", tok.as_usize(), r, from_utf8(self.buf.data()));
      },
    };

    let mut res: Option<Vec<ConfigMessage>> = None;
    let mut offset = 0usize;
    match parse(self.buf.data()) {
      IResult::Incomplete(_) => {
        return Some(Vec::new());
      },
      IResult::Error(e)      => {
        log!(log::LogLevel::Error, "UNIX CLIENT[{}] read error: {:?}", tok.as_usize(), e);
        return None;
      },
      IResult::Done(i, v)    => {
        offset = self.buf.data().offset(i);
        res = Some(v);
      }
    }
    debug!("parsed {} bytes, result: {:?}", offset, res);
    self.buf.consume(offset);
    return res;
  }

  fn conn_writable(&mut self, event_loop: &mut EventLoop<CommandServer>, tok: Token) {
    trace!("server conn writable; tok={:?}", tok);
    match self.sock.write(self.back_buf.data()) {
      Ok(0) => {},
      Ok(r) => {
        self.back_buf.consume(r);
      },
      Err(e) => log!(log::LogLevel::Error,"UNIX CLIENT[{}] read error: {:?}", tok.as_usize(), e),
    }
    let mut interest = EventSet::hup();
    interest.insert(EventSet::readable());
    interest.insert(EventSet::writable());
    event_loop.register(&self.sock, tok, interest, PollOpt::level() | PollOpt::oneshot());
  }
}

fn parse(input: &[u8]) -> IResult<&[u8], Vec<ConfigMessage>> {
  many0!(input,
    complete!(terminated!(map_res!(map_res!(is_not!("\0"), from_utf8), decode), char!('\0')))
  )
}

struct CommandServer {
  sock:  UnixListener,
  conns: Slab<CommandClient>,
  listeners: HashMap<String, Listener>,
}

impl CommandServer {
  fn accept(&mut self, event_loop: &mut EventLoop<CommandServer>) -> io::Result<()> {
    debug!("server accepting socket");

    let sock = self.sock.accept().unwrap().unwrap();
    let conn = CommandClient::new(sock);
    let tok = self.conns.insert(conn)
      .ok().expect("could not add connection to slab");

    // Register the connection
    self.conns[tok].token = Some(tok);
    let mut interest = EventSet::hup();
    interest.insert(EventSet::readable());
    interest.insert(EventSet::writable());
    event_loop.register(&self.conns[tok].sock, tok, interest, PollOpt::level())
      .ok().expect("could not register socket with event loop");

    let accept_interest = EventSet::readable();
    event_loop.reregister(&self.sock, SERVER, accept_interest, PollOpt::level());
    Ok(())
  }

  fn conn<'a>(&'a mut self, tok: Token) -> &'a mut CommandClient {
    &mut self.conns[tok]
  }

  fn dispatch(&mut self, token: Token, v: Vec<ConfigMessage>) {
    for message in &v {
      let command = message.command.clone();
      if let Some(ref mut listener) = self.listeners.get_mut (&message.listener) {
        if message.command == Command::DumpConfiguration {
          self.conns[token].back_buf.write(&encode(*listener).unwrap().into_bytes());
          self.conns[token].back_buf.write(&b"\0"[..]);
        } else {
          self.conns[token].add_message_id(message.id.clone());
          listener.state.handle_command(&command);
          listener.sender.send(ProxyOrder::Command(message.id.clone(), command));
        }
      } else {
        log!(log::LogLevel::Error, "no listener found for tag: {}", message.listener);
      }
    }
  }

  fn new(srv: UnixListener, listeners: HashMap<String, Listener>) -> CommandServer {
    CommandServer {
      sock:  srv,
      conns: Slab::new_starting_at(Token(1), 128),
      listeners: listeners,
    }
  }
}

impl Handler for CommandServer {
	type Timeout = usize;
	type Message = ();

	fn ready(&mut self, event_loop: &mut EventLoop<CommandServer>, token: Token, events: EventSet) {
    //trace!("ready: {:?} -> {:?}", token, events);
    if events.is_readable() {
      match token {
        SERVER => self.accept(event_loop).unwrap(),
        _      => {
          if self.conns.contains(token) {
            let opt_v = self.conns[token].conn_readable(event_loop, token);
            if let Some(v) = opt_v {
              self.dispatch(token, v);
            }
          }
        }
      };
    }

    if events.is_writable() {
      match token {
        SERVER => panic!("received writable for token 0"),
        _      => {
          if self.conns.contains(token) {
            self.conns[token].conn_writable(event_loop, token)
          }
        }
      };
    }

    if events.is_hup() {
      if self.conns.contains(token) {
        event_loop.deregister(&self.conns[token].sock);
        self.conns.remove(token);
        trace!("closed client [{}]", token.as_usize());
      }
    }
  }

  fn notify(&mut self, event_loop: &mut EventLoop<Self>, message: Self::Message) {
    // TODO: dispatch here to clients the list of messages you want to display
    info!("notify");
  }

  fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
    for listener in self.listeners.values() {
      while let Ok(msg) = listener.receiver.try_recv() {
        println!("got msg: {:?}", msg);
        for client in self.conns.iter_mut() {
          if let Some(index) = client.has_message_id(&msg.id) {
            client.back_buf.write(&encode(&msg).unwrap().into_bytes());
            client.back_buf.write(&b"\0"[..]);
            client.remove_message_id(index);
          }
        }
      }
    }
    event_loop.timeout_ms(0, 700);
  }

}

pub fn start(path: String, listeners: HashMap<String, Listener>) {
  thread::spawn(move || {
    let mut event_loop = EventLoop::new().unwrap();
    let addr = PathBuf::from(path);
    if let Err(e) = fs::remove_file(&addr) {
      match e.kind() {
        ErrorKind::NotFound => {},
        _ => {
          log!(log::LogLevel::Error, "could not delete previous socket at {:?}: {:?}", addr, e);
          return;
        }
      }
    }
    match UnixListener::bind(&addr) {
      Ok(srv) => {
        if let Err(e) =  fs::set_permissions(&addr, fs::Permissions::from_mode(0o600)) {
          log!( log::LogLevel::Error, "could not set the unix socket permissions: {:?}", e);
          fs::remove_file(&addr);
          return;
        }
        info!("listen for connections");
        event_loop.register(&srv, SERVER, EventSet::readable(), PollOpt::level() | PollOpt::oneshot()).unwrap();
        event_loop.timeout_ms(0, 700);

        event_loop.run(&mut CommandServer::new(srv, listeners)).unwrap()
      },
      Err(e) => {
        log!(log::LogLevel::Error, "could not create unix socket: {:?}", e);
      }
    }
  });
}

#[cfg(test)]
mod tests {
  use super::*;
  use rustc_serialize::json;
  use yxorp::messages::{Command,HttpFront};

  #[test]
  fn config_message_test() {
    let raw_json = r#"{ "id": "ID_TEST", "listener": "HTTP", "command":{"type": "ADD_HTTP_FRONT", "data": {"app_id": "xxx", "hostname": "yyy", "path_begin": "xxx", "port": 4242}} }"#;
    let config: ConfigMessage = json::decode(raw_json).unwrap();
    println!("{:?}", config);
    assert_eq!(config.listener, "HTTP");
    assert_eq!(config.command, Command::AddHttpFront(HttpFront{
      app_id: String::from("xxx"),
      hostname: String::from("yyy"),
      path_begin: String::from("xxx"),
      port: 4242
    }));
  }
}
<|endoftext|>