<commit_before>use std::fmt::{Debug, Formatter};
use std::fmt::Error as FormatError;
use std::default::Default;
use std::collections::BTreeMap;

use quire::validate as V;
use serialize::json;

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct DebianRepo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct AptKey {
    pub key_server: String,
    pub keys: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct PacmanRepo {
    pub name: String,
    pub url: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub path: Path,
    pub subdir: Path,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInstallInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub subdir: Option<Path>,
    pub script: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct GitInfo {
    pub url: String,
    pub revision: Option<String>,
    pub branch: Option<String>,
    pub path: Path,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct GitInstallInfo {
    pub url: String,
    pub revision: Option<String>,
    pub branch: Option<String>,
    pub subdir: Path,
    pub script: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct FileInfo {
    pub name: Path,
    pub contents: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct UbuntuRepoInfo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Default, Clone, Decodable, Debug, Encodable)]
pub struct PipSettings {
    pub find_links: Vec<String>,
    pub index_urls: Vec<String>,
    pub trusted_hosts: Vec<String>,
    pub dependencies: bool,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct GitSource {
    pub url: String,
    pub revision: Option<String>,
    pub branch: Option<String>,
}

#[derive(Clone, Decodable, Encodable, Debug)]
pub enum Source {
    Git(GitSource),
    Container(String),
    Directory,
}

#[derive(Clone, Decodable, Encodable, Debug)]
pub struct SubConfigInfo {
    pub source: Source,
    pub path: Path,
    pub container: String,
    pub cache: Option<bool>,
}

#[derive(Encodable, Decodable, Clone, Debug)]
pub enum Builder {
    // -- Generic --
    Sh(String),
    Cmd(Vec<String>),
    Env(BTreeMap<String, String>),
    Depends(Path),
    Tar(TarInfo),
    TarInstall(TarInstallInfo),
    Git(GitInfo),
    GitInstall(GitInstallInfo),
    Text(BTreeMap<Path, String>),
    //AddFile(FileInfo),
    Remove(Path),
    EnsureDir(Path),
    EmptyDir(Path),
    CacheDirs(BTreeMap<Path, String>),
    //Busybox,

    // -- Generic --
    Install(Vec<String>),
    BuildDeps(Vec<String>),
    Container(String),
    SubConfig(SubConfigInfo),

    // -- Ubuntu --
    Ubuntu(String),
    UbuntuRepo(UbuntuRepoInfo),
    UbuntuUniverse,
    //AddUbuntuPPA(String),

    // -- Ubuntu/Debian --
    //AddDebianRepo(DebianRepo),
    //AddAptKey(AptKey),

    // -- Arch --
    //ArchBase,
    //PacmanInstall(Vec<String>),
    //PacmanRemove(Vec<String>),
    //PacmanBuild(Path),
    //AddPacmanRepo(PacmanRepo),

    // -- Alpine --
    Alpine(String),
    //AlpineRemove(Vec<String>),

    // -- Docker --
    //DockerImage(String),
    //DockerPrivate(String),
    //Dockerfile(Path),

    // -- Languages --
    NpmInstall(Vec<String>),
    //PipRequirement(Path),
    //GemInstall(Vec<String>),

    // -- Python --
    PipConfig(PipSettings),
    Py2Install(Vec<String>),
    Py2Requirements(Path),
    Py3Install(Vec<String>),
    Py3Requirements(Path),
}

pub fn builder_validator<'x>() -> Box<V::Validator + 'x> {
    return box V::Enum { options: vec!(
        ("Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("BuildDeps".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Container".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("SubConfig".to_string(), box V::Structure {
            members: vec!(
                ("source".to_string(), box V::Enum { options: vec!(
                    ("Directory".to_string(),
                        box V::Nothing as Box<V::Validator>),
                    ("Container".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                    ("Git".to_string(), box V::Structure { members: vec!(
                        ("url".to_string(), box V::Scalar {
                            .. Default::default() } as Box<V::Validator>),
                        ("revision".to_string(), box V::Scalar {
                            optional: true,
                            .. Default::default() } as Box<V::Validator>),
                        ("branch".to_string(), box V::Scalar {
                            optional: true,
                            .. Default::default() } as Box<V::Validator>),
                        ), .. Default::default() } as Box<V::Validator>),
                    ), optional: true,
                       default_tag: Some("Directory".to_string()),
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(false),
                    default: Some(Path::new("vagga.yaml")),
                    .. Default::default() } as Box<V::Validator>),
                ("container".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("cache".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Text".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

        ("Ubuntu".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuRepo".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("suite".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("components".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuUniverse".to_string(), box V::Nothing as Box<V::Validator>),
        ("Sh".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Cmd".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Remove".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EnsureDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EmptyDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("CacheDirs".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Env".to_string(), box V::Mapping {
            key_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Depends".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Git".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("revision".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("branch".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(true),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("GitInstall".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("revision".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("branch".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    default: Some(Path::new(".")),
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("script".to_string(), box V::Scalar {
                    default: Some("./configure --prefix=/usr\n\
                                   make\n\
                                   make install\n\
                                   ".to_string()),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Tar".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(true),
                    default: Some(Path::new("/")),
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    default: Some(Path::new("")),
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("TarInstall".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    optional: true,
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("script".to_string(), box V::Scalar {
                    default: Some("./configure --prefix=/usr\n\
                                   make\n\
                                   make install\n\
                                   ".to_string()),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),

        ("Alpine".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),

        // Python
        ("PipConfig".to_string(), box V::Structure {
            members: vec!(
                ("dependencies".to_string(), box V::Scalar {
                    default: Some("false".to_string()),
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("find_links".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
                ("index_urls".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
                ("trusted_hosts".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Py2Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py2Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),
        ("Py3Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py3Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),

        // Node.js
        ("NpmInstall".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

    ), .. Default::default() } as Box<V::Validator>;
}
<commit_msg>Add "SubConfig.change-dir" setting<commit_after>use std::fmt::{Debug, Formatter};
use std::fmt::Error as FormatError;
use std::default::Default;
use std::collections::BTreeMap;

use quire::validate as V;
use serialize::json;

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct DebianRepo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct AptKey {
    pub key_server: String,
    pub keys: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct PacmanRepo {
    pub name: String,
    pub url: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub path: Path,
    pub subdir: Path,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInstallInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub subdir: Option<Path>,
    pub script: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct GitInfo {
    pub url: String,
    pub revision: Option<String>,
    pub branch: Option<String>,
    pub path: Path,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct GitInstallInfo {
    pub url: String,
    pub revision: Option<String>,
    pub branch: Option<String>,
    pub subdir: Path,
    pub script: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct FileInfo {
    pub name: Path,
    pub contents: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct UbuntuRepoInfo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Default, Clone, Decodable, Debug, Encodable)]
pub struct PipSettings {
    pub find_links: Vec<String>,
    pub index_urls: Vec<String>,
    pub trusted_hosts: Vec<String>,
    pub dependencies: bool,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct GitSource {
    pub url: String,
    pub revision: Option<String>,
    pub branch: Option<String>,
}

#[derive(Clone, Decodable, Encodable, Debug)]
pub enum Source {
    Git(GitSource),
    Container(String),
    Directory,
}

#[derive(Clone, Decodable, Encodable, Debug)]
pub struct SubConfigInfo {
    pub source: Source,
    pub path: Path,
    pub container: String,
    pub cache: Option<bool>,
    pub change_dir: Option<bool>,
}

#[derive(Encodable, Decodable, Clone, Debug)]
pub enum Builder {
    // -- Generic --
    Sh(String),
    Cmd(Vec<String>),
    Env(BTreeMap<String, String>),
    Depends(Path),
    Tar(TarInfo),
    TarInstall(TarInstallInfo),
    Git(GitInfo),
    GitInstall(GitInstallInfo),
    Text(BTreeMap<Path, String>),
    //AddFile(FileInfo),
    Remove(Path),
    EnsureDir(Path),
    EmptyDir(Path),
    CacheDirs(BTreeMap<Path, String>),
    //Busybox,

    // -- Generic --
    Install(Vec<String>),
    BuildDeps(Vec<String>),
    Container(String),
    SubConfig(SubConfigInfo),

    // -- Ubuntu --
    Ubuntu(String),
    UbuntuRepo(UbuntuRepoInfo),
    UbuntuUniverse,
    //AddUbuntuPPA(String),

    // -- Ubuntu/Debian --
    //AddDebianRepo(DebianRepo),
    //AddAptKey(AptKey),

    // -- Arch --
    //ArchBase,
    //PacmanInstall(Vec<String>),
    //PacmanRemove(Vec<String>),
    //PacmanBuild(Path),
    //AddPacmanRepo(PacmanRepo),

    // -- Alpine --
    Alpine(String),
    //AlpineRemove(Vec<String>),

    // -- Docker --
    //DockerImage(String),
    //DockerPrivate(String),
    //Dockerfile(Path),

    // -- Languages --
    NpmInstall(Vec<String>),
    //PipRequirement(Path),
    //GemInstall(Vec<String>),

    // -- Python --
    PipConfig(PipSettings),
    Py2Install(Vec<String>),
    Py2Requirements(Path),
    Py3Install(Vec<String>),
    Py3Requirements(Path),
}

pub fn builder_validator<'x>() -> Box<V::Validator + 'x> {
    return box V::Enum { options: vec!(
        ("Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("BuildDeps".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Container".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("SubConfig".to_string(), box V::Structure {
            members: vec!(
                ("source".to_string(), box V::Enum { options: vec!(
                    ("Directory".to_string(),
                        box V::Nothing as Box<V::Validator>),
                    ("Container".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                    ("Git".to_string(), box V::Structure { members: vec!(
                        ("url".to_string(), box V::Scalar {
                            .. Default::default() } as Box<V::Validator>),
                        ("revision".to_string(), box V::Scalar {
                            optional: true,
                            .. Default::default() } as Box<V::Validator>),
                        ("branch".to_string(), box V::Scalar {
                            optional: true,
                            .. Default::default() } as Box<V::Validator>),
                        ), .. Default::default() } as Box<V::Validator>),
                    ), optional: true,
                       default_tag: Some("Directory".to_string()),
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(false),
                    default: Some(Path::new("vagga.yaml")),
                    .. Default::default() } as Box<V::Validator>),
                ("container".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("cache".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("change_dir".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Text".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

        ("Ubuntu".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuRepo".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("suite".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("components".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuUniverse".to_string(), box V::Nothing as Box<V::Validator>),
        ("Sh".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Cmd".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Remove".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EnsureDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EmptyDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("CacheDirs".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Env".to_string(), box V::Mapping {
            key_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Depends".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Git".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("revision".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("branch".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(true),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("GitInstall".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("revision".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("branch".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    default: Some(Path::new(".")),
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("script".to_string(), box V::Scalar {
                    default: Some("./configure --prefix=/usr\n\
                                   make\n\
                                   make install\n\
                                   ".to_string()),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Tar".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(true),
                    default: Some(Path::new("/")),
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    default: Some(Path::new("")),
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("TarInstall".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    optional: true,
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("script".to_string(), box V::Scalar {
                    default: Some("./configure --prefix=/usr\n\
                                   make\n\
                                   make install\n\
                                   ".to_string()),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),

        ("Alpine".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),

        // Python
        ("PipConfig".to_string(), box V::Structure {
            members: vec!(
                ("dependencies".to_string(), box V::Scalar {
                    default: Some("false".to_string()),
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("find_links".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
                ("index_urls".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
                ("trusted_hosts".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Py2Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py2Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),
        ("Py3Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py3Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),

        // Node.js
        ("NpmInstall".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

    ), .. Default::default() } as Box<V::Validator>;
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use syntax::ast;
use syntax::attr;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;

use aster;

use super::attr::FieldAttrs;

enum Rename<'a> {
    None,
    Global(&'a ast::Lit),
    Format(HashMap<P<ast::Expr>, &'a ast::Lit>)
}

fn rename<'a>(
    builder: &aster::AstBuilder,
    mi: &'a ast::MetaItem,
    ) -> Option<Rename<'a>>
{
    match mi.node {
        ast::MetaNameValue(ref n, ref lit) => {
            if n == &"rename" {
                Some(Rename::Global(lit))
            } else {
                None
            }
        },
        ast::MetaList(ref n, ref items) => {
            if n == &"rename" {
                let mut m = HashMap::new();
                m.extend(
                    items.iter()
                        .filter_map(
                            |item|
                            match item.node {
                                ast::MetaNameValue(ref n, ref lit) =>
                                    Some((builder.expr().str(n),
                                          lit)),
                                _ => None
                            }));
                Some(Rename::Format(m))
            } else {
                None
            }
        },
        _ => None
    }
}

fn default_value(mi: &ast::MetaItem) -> bool {
    if let ast::MetaItem_::MetaWord(ref n) = mi.node {
        n == &"default"
    } else {
        false
    }
}

fn field_attrs<'a>(
    builder: &aster::AstBuilder,
    field: &'a ast::StructField,
) -> (Rename<'a>, bool) {
    field.node.attrs.iter()
        .find(|sa| {
            if let ast::MetaList(ref n, _) = sa.node.value.node {
                n == &"serde"
            } else {
                false
            }
        })
        .and_then(|sa| {
            if let ast::MetaList(_, ref vals) = sa.node.value.node {
                attr::mark_used(&sa);
                Some((vals.iter()
                      .fold(None, |v, mi| v.or(rename(builder, mi)))
                      .unwrap_or(Rename::None),
                      vals.iter().any(|mi| default_value(mi))))
            } else {
                Some((Rename::None, false))
            }
        })
        .unwrap_or((Rename::None, false))
}

pub fn struct_field_attrs(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    struct_def: &ast::StructDef,
) -> Vec<FieldAttrs> {
    struct_def.fields.iter()
        .map(|field| {
            match field_attrs(builder, field) {
                (Rename::Global(rename), default_value) =>
                    FieldAttrs::new(
                        default_value,
                        builder.expr().build_lit(P(rename.clone()))),
                (Rename::Format(renames), default_value) => {
                    let mut res = HashMap::new();
                    res.extend(
                        renames.into_iter()
                            .map(|(k,v)|
                                 (k, builder.expr().build_lit(P(v.clone())))));
                    FieldAttrs::new_with_formats(
                        default_value,
                        default_field_name(cx, builder, field.node.kind),
                        res)
                },
                (Rename::None, default_value) => {
                    FieldAttrs::new(
                        default_value,
                        default_field_name(cx, builder, field.node.kind))
                }
            }
        })
        .collect()
}

fn default_field_name(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    kind: ast::StructFieldKind,
) -> P<ast::Expr> {
    match kind {
        ast::NamedField(name, _) => {
            builder.expr().str(name)
        }
        ast::UnnamedField(_) => {
            cx.bug("struct has named and unnamed fields")
        }
    }
}
<commit_msg>Minor cleanup<commit_after>use std::collections::HashMap;

use aster;

use syntax::ast;
use syntax::attr;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;

use super::attr::FieldAttrs;

enum Rename<'a> {
    None,
    Global(&'a ast::Lit),
    Format(HashMap<P<ast::Expr>, &'a ast::Lit>)
}

fn rename<'a>(
    builder: &aster::AstBuilder,
    mi: &'a ast::MetaItem,
    ) -> Option<Rename<'a>>
{
    match mi.node {
        ast::MetaNameValue(ref n, ref lit) => {
            if n == &"rename" {
                Some(Rename::Global(lit))
            } else {
                None
            }
        },
        ast::MetaList(ref n, ref items) => {
            if n == &"rename" {
                let mut m = HashMap::new();
                m.extend(
                    items.iter()
                        .filter_map(
                            |item|
                            match item.node {
                                ast::MetaNameValue(ref n, ref lit) =>
                                    Some((builder.expr().str(n),
                                          lit)),
                                _ => None
                            }));
                Some(Rename::Format(m))
            } else {
                None
            }
        },
        _ => None
    }
}

fn default_value(mi: &ast::MetaItem) -> bool {
    if let ast::MetaItem_::MetaWord(ref n) = mi.node {
        n == &"default"
    } else {
        false
    }
}

fn field_attrs<'a>(
    builder: &aster::AstBuilder,
    field: &'a ast::StructField,
) -> (Rename<'a>, bool) {
    field.node.attrs.iter()
        .find(|sa| {
            if let ast::MetaList(ref n, _) = sa.node.value.node {
                n == &"serde"
            } else {
                false
            }
        })
        .and_then(|sa| {
            if let ast::MetaList(_, ref vals) = sa.node.value.node {
                attr::mark_used(&sa);
                Some((vals.iter()
                      .fold(None, |v, mi| v.or(rename(builder, mi)))
                      .unwrap_or(Rename::None),
                      vals.iter().any(|mi| default_value(mi))))
            } else {
                Some((Rename::None, false))
            }
        })
        .unwrap_or((Rename::None, false))
}

pub fn struct_field_attrs(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    struct_def: &ast::StructDef,
) -> Vec<FieldAttrs> {
    struct_def.fields.iter()
        .map(|field| {
            match field_attrs(builder, field) {
                (Rename::Global(rename), default_value) =>
                    FieldAttrs::new(
                        default_value,
                        builder.expr().build_lit(P(rename.clone()))),
                (Rename::Format(renames), default_value) => {
                    let mut res = HashMap::new();
                    res.extend(
                        renames.into_iter()
                            .map(|(k,v)|
                                 (k, builder.expr().build_lit(P(v.clone())))));
                    FieldAttrs::new_with_formats(
                        default_value,
                        default_field_name(cx, builder, field.node.kind),
                        res)
                },
                (Rename::None, default_value) => {
                    FieldAttrs::new(
                        default_value,
                        default_field_name(cx, builder, field.node.kind))
                }
            }
        })
        .collect()
}

fn default_field_name(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    kind: ast::StructFieldKind,
) -> P<ast::Expr> {
    match kind {
        ast::NamedField(name, _) => {
            builder.expr().str(name)
        }
        ast::UnnamedField(_) => {
            cx.bug("struct has named and unnamed fields")
        }
    }
}
<|endoftext|>


<commit_before>mod hyphen;

fn main() -> anyhow::Result<()> {
    let port = 8001;
    let prefix = "/rouille";
    println!(
        "Starting the server at <http://127.0.0.1:{}{}/>.",
        port, prefix
    );
    rouille::start_server_with_pool(format!("127.0.0.1:{}", port), None, move |request| {
        hyphen::app(request)
    });
}
<commit_msg>rouille: allow multiple apps on the same tcp port<commit_after>mod hyphen;

fn main() -> anyhow::Result<()> {
    let port = 8001;
    let prefix = "/rouille";
    println!(
        "Starting the server at <http://127.0.0.1:{}{}/>.",
        port, prefix
    );
    rouille::start_server_with_pool(format!("127.0.0.1:{}", port), None, move |request| {
        if request.url().starts_with("/rouille/hyphen") {
            return hyphen::app(request);
        }

        rouille::Response::empty_404()
    });
}
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
#[macro_use] extern crate pact_v1_matching;
extern crate pact_v1_mock_server;
#[macro_use] extern crate rustful;
#[macro_use] extern crate p_macro;
#[macro_use] extern crate log;
#[macro_use] extern crate maplit;
extern crate simplelog;
extern crate uuid;
extern crate rustc_serialize;
#[macro_use] extern crate hyper;

use clap::{Arg, App, SubCommand, AppSettings, ErrorKind, ArgMatches};
use std::env;
use std::str::FromStr;
use std::fs::{self, File};
use std::io;
use log::{LogLevelFilter};
use simplelog::{CombinedLogger, TermLogger, FileLogger};
use std::path::PathBuf;
use std::fs::OpenOptions;

fn display_error(error: String, matches: &ArgMatches) -> ! {
    println!("ERROR: {}", error);
    println!("");
    println!("{}", matches.usage());
    panic!("{}", error)
}

mod server;
mod create_mock;
mod list;

static SPEC_VERSION: &'static str = "1.0.0";

fn print_version() {
    println!("\npact mock server version  : v{}", crate_version!());
    println!("pact specification version: v{}", SPEC_VERSION);
}

fn setup_loggers(level: &str, command: &str, output: Option<&str>) -> Result<(), io::Error> {
    let log_level = match level {
        "none" => LogLevelFilter::Off,
        _ => LogLevelFilter::from_str(level).unwrap()
    };
    if command == "start" {
        let log_file = match output {
            Some(p) => {
                try!(fs::create_dir_all(p));
                let mut path = PathBuf::from(p);
                path.push("pact_v1_mock_server.log");
                path
            },
            None => PathBuf::from("pact_v1_mock_server.log")
        };
        let file = try!(OpenOptions::new()
            .read(false)
            .write(true)
            .append(true)
            .create(true)
            .open(log_file));
        CombinedLogger::init(
            vec![
                TermLogger::new(log_level),
                FileLogger::new(log_level, file)
            ]
        ).unwrap();
    } else {
        TermLogger::init(log_level).unwrap();
    }
    Ok(())
}

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

    let version = format!("v{}", crate_version!());
    let app = App::new(program)
        .version(version.as_str())
        .about("Standalone Pact mock server")
        .version_short("v")
        .setting(AppSettings::ArgRequiredElseHelp)
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ColoredHelp)
        .arg(Arg::with_name("port")
            .short("p")
            .long("port")
            .takes_value(true)
            .use_delimiter(false)
            .help("port the master mock server runs on (defaults to 8080)"))
        .arg(Arg::with_name("host")
            .short("h")
            .long("host")
            .takes_value(true)
            .use_delimiter(false)
            .help("hostname the master mock server runs on (defaults to localhost)"))
        .arg(Arg::with_name("loglevel")
            .short("l")
            .long("loglevel")
            .takes_value(true)
            .use_delimiter(false)
            .possible_values(&["error", "warn", "info", "debug", "trace", "none"])
            .help("Log level for mock servers to write to the log file (defaults to info)"))
        .subcommand(SubCommand::with_name("start")
                .about("Starts the master mock server")
                .setting(AppSettings::ColoredHelp)
                .arg(Arg::with_name("output")
                      .short("o")
                      .long("output")
                      .takes_value(true)
                      .use_delimiter(false)
                      .help("the directory where to write files to (defaults to current directory)")))
        .subcommand(SubCommand::with_name("list")
                .about("Lists all the running mock servers")
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("create")
                .about("Creates a new mock server from a pact file")
                .arg(Arg::with_name("file")
                    .short("f")
                    .long("file")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required(true)
                    .help("the pact file to define the mock server"))
                .setting(AppSettings::ColoredHelp));

    let matches = app.get_matches_safe();
    match matches {
        Ok(ref matches) => {
            if let Err(err) = setup_loggers(matches.value_of("loglevel").unwrap_or("info"),
                matches.subcommand_name().unwrap(),
                matches.subcommand().1.unwrap().value_of("output")) {
                display_error(format!("Could not setup loggers: {}", err), matches);
            }
            let port = matches.value_of("port").unwrap_or("8080");
            let host = matches.value_of("host").unwrap_or("localhost");
            match port.parse::<u16>() {
                Ok(p) => {
                    match matches.subcommand() {
                        ("start", Some(_)) => server::start_server(p),
                        ("list", Some(sub_matches)) => list::list_mock_servers(host, p, sub_matches),
                        ("create", Some(sub_matches)) => create_mock::create_mock_server(host, p, sub_matches),
                        _ => ()
                    }
                },
                Err(_) => display_error(format!("{} is not a valid port number", port), matches)
            }
        },
        Err(ref err) => {
            match err.kind {
                ErrorKind::HelpDisplayed => (),
                ErrorKind::VersionDisplayed => print_version(),
                _ => err.exit()
            }
        }
    }
}
<commit_msg>handle global options in the CLI correctly<commit_after>#[macro_use] extern crate clap;
#[macro_use] extern crate pact_v1_matching;
extern crate pact_v1_mock_server;
#[macro_use] extern crate rustful;
#[macro_use] extern crate p_macro;
#[macro_use] extern crate log;
#[macro_use] extern crate maplit;
extern crate simplelog;
extern crate uuid;
extern crate rustc_serialize;
#[macro_use] extern crate hyper;

use clap::{Arg, App, SubCommand, AppSettings, ErrorKind, ArgMatches};
use std::env;
use std::str::FromStr;
use std::fs::{self, File};
use std::io;
use log::{LogLevelFilter};
use simplelog::{CombinedLogger, TermLogger, FileLogger};
use std::path::PathBuf;
use std::fs::OpenOptions;

fn display_error(error: String, matches: &ArgMatches) -> ! {
    println!("ERROR: {}", error);
    println!("");
    println!("{}", matches.usage());
    panic!("{}", error)
}

mod server;
mod create_mock;
mod list;

static SPEC_VERSION: &'static str = "1.0.0";

fn print_version() {
    println!("\npact mock server version  : v{}", crate_version!());
    println!("pact specification version: v{}", SPEC_VERSION);
}

fn setup_loggers(level: &str, command: &str, output: Option<&str>) -> Result<(), io::Error> {
    let log_level = match level {
        "none" => LogLevelFilter::Off,
        _ => LogLevelFilter::from_str(level).unwrap()
    };
    if command == "start" {
        let log_file = match output {
            Some(p) => {
                try!(fs::create_dir_all(p));
                let mut path = PathBuf::from(p);
                path.push("pact_v1_mock_server.log");
                path
            },
            None => PathBuf::from("pact_v1_mock_server.log")
        };
        let file = try!(OpenOptions::new()
            .read(false)
            .write(true)
            .append(true)
            .create(true)
            .open(log_file));
        CombinedLogger::init(
            vec![
                TermLogger::new(log_level),
                FileLogger::new(log_level, file)
            ]
        ).unwrap();
    } else {
        TermLogger::init(log_level).unwrap();
    }
    Ok(())
}

fn lookup_global_option<'a>(option: &str, matches: &'a ArgMatches<'a>) -> Option<&'a str> {
    let global_value = matches.value_of(option);
    let command_value = matches.subcommand().1.unwrap().value_of(option);
    match (global_value, command_value) {
        (Some(v), None) => Some(v),
        (None, Some(v)) => Some(v),
        (Some(_), Some(v)) => {
            println!("WARNING: The value for '{}' has been provided twice, defaulting to the second value '{}'",
                option, v);
            Some(v)
        },
        (None, None) => None
    }
}

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

    let version = format!("v{}", crate_version!());
    let app = App::new(program)
        .version(version.as_str())
        .about("Standalone Pact mock server")
        .version_short("v")
        .setting(AppSettings::ArgRequiredElseHelp)
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ColoredHelp)
        .arg(Arg::with_name("port")
            .short("p")
            .long("port")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .help("port the master mock server runs on (defaults to 8080)"))
        .arg(Arg::with_name("host")
            .short("h")
            .long("host")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .help("hostname the master mock server runs on (defaults to localhost)"))
        .arg(Arg::with_name("loglevel")
            .short("l")
            .long("loglevel")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .possible_values(&["error", "warn", "info", "debug", "trace", "none"])
            .help("Log level for mock servers to write to the log file (defaults to info)"))
        .subcommand(SubCommand::with_name("start")
                .about("Starts the master mock server")
                .setting(AppSettings::ColoredHelp)
                .arg(Arg::with_name("output")
                      .short("o")
                      .long("output")
                      .takes_value(true)
                      .use_delimiter(false)
                      .help("the directory where to write files to (defaults to current directory)")))
        .subcommand(SubCommand::with_name("list")
                .about("Lists all the running mock servers")
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("create")
                .about("Creates a new mock server from a pact file")
                .arg(Arg::with_name("file")
                    .short("f")
                    .long("file")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required(true)
                    .help("the pact file to define the mock server"))
                .setting(AppSettings::ColoredHelp));

    let matches = app.get_matches_safe();
    match matches {
        Ok(ref matches) => {
            let log_level = lookup_global_option("loglevel", matches);
            if let Err(err) = setup_loggers(log_level.unwrap_or("info"),
                matches.subcommand_name().unwrap(),
                matches.subcommand().1.unwrap().value_of("output")) {
                display_error(format!("Could not setup loggers: {}", err), matches);
            }
            let port = lookup_global_option("port", matches).unwrap_or("8080");
            let host = lookup_global_option("host", matches).unwrap_or("localhost");
            match port.parse::<u16>() {
                Ok(p) => {
                    match matches.subcommand() {
                        ("start", Some(_)) => server::start_server(p),
                        ("list", Some(sub_matches)) => list::list_mock_servers(host, p, sub_matches),
                        ("create", Some(sub_matches)) => create_mock::create_mock_server(host, p, sub_matches),
                        _ => ()
                    }
                },
                Err(_) => display_error(format!("{} is not a valid port number", port), matches)
            }
        },
        Err(ref err) => {
            match err.kind {
                ErrorKind::HelpDisplayed => (),
                ErrorKind::VersionDisplayed => print_version(),
                _ => err.exit()
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::net::SocketAddr;
use std::pin::Pin;
use std::str;
use std::task::{Context, Poll};

use futures::prelude::*;
use futures::Future;

use hyper::Client;

use tokio::net::UdpSocket;
use tokio::time::timeout;

use hyper::body::Bytes;

use crate::aio::Gateway;
use crate::common::{messages, parsing, SearchOptions};
use crate::errors::SearchError;

const MAX_RESPONSE_SIZE: usize = 1500;

/// Search for a gateway with the provided options
pub async fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
    // Create socket for future calls
    let socket = UdpSocket::bind(&options.bind_addr).await?;

    let search_future = SearchFuture::search(socket, options.broadcast_address).await?;
    // Create future and issue request
    match options.timeout {
        Some(t) => timeout(t, search_future).await?,
        None => search_future.await,
    }
}

pub struct SearchFuture {
    socket: UdpSocket,
    pending: HashMap<SocketAddr, SearchState>,
}

enum SearchState {
    Connecting(String, Pin<Box<dyn Future<Output = Result<Bytes, SearchError>> + Send>>),
    Done(String),
    Error,
}

impl SearchFuture {
    // Create a new search
    async fn search(mut socket: UdpSocket, addr: SocketAddr) -> Result<SearchFuture, SearchError> {
        debug!(
            "sending broadcast request to: {} on interface: {:?}",
            addr,
            socket.local_addr()
        );
        socket.send_to(messages::SEARCH_REQUEST.as_bytes(), &addr).await?;
        Ok(SearchFuture {
            socket,
            pending: HashMap::new(),
        })
    }

    // Handle a UDP response message
    fn handle_broadcast_resp(from: SocketAddr, data: &[u8]) -> Result<(SocketAddr, String), SearchError> {
        debug!("handling broadcast response from: {}", from);

        // Convert response to text
        let text = str::from_utf8(&data).map_err(|e| SearchError::from(e))?;

        // Parse socket address and path
        let (addr, path) = parsing::parse_search_result(text)?;

        Ok((SocketAddr::V4(addr), path))
    }

    // Issue a control URL request over HTTP using the provided
    fn request_control_url(
        addr: SocketAddr,
        path: String,
    ) -> Result<Pin<Box<dyn Future<Output = Result<Bytes, SearchError>> + Send>>, SearchError> {
        let client = Client::new();

        let uri = match format!("http://{}{}", addr, path).parse() {
            Ok(uri) => uri,
            Err(err) => return Err(SearchError::from(err)),
        };

        debug!("requesting control url from: {}", uri);

        Ok(Box::pin(
            client
                .get(uri)
                .and_then(|resp| hyper::body::to_bytes(resp.into_body()))
                .map_err(|e| SearchError::from(e)),
        ))
    }

    // Process a control response to extract the control URL
    fn handle_control_resp(addr: SocketAddr, resp: Bytes) -> Result<String, SearchError> {
        debug!("handling control response from: {}", addr);

        // Create a cursor over the response data
        let c = std::io::Cursor::new(&resp);

        // Parse control URL out of body
        let url = parsing::parse_control_url(c)?;

        Ok(url)
    }
}

impl Future for SearchFuture {
    type Output = Result<Gateway, SearchError>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<Gateway, SearchError>> {
        // Poll for (and handle) incoming messages
        let mut buff = [0u8; MAX_RESPONSE_SIZE];
        let resp = self.socket.poll_recv_from(cx, &mut buff);
        if let Poll::Ready(Ok((n, from))) = resp {
            // Try handle response messages
            if let Ok((addr, path)) = Self::handle_broadcast_resp(from, &buff[0..n]) {
                if !self.pending.contains_key(&addr) {
                    debug!("received broadcast response from: {}", from);

                    // Issue control request
                    match Self::request_control_url(addr, path.clone()) {
                        // Store pending requests
                        Ok(f) => {
                            self.pending.insert(addr, SearchState::Connecting(path, f));
                        }
                        Err(e) => return Poll::Ready(Err(e)),
                    }
                } else {
                    debug!("received duplicate broadcast response from: {}, dropping", from);
                }
            }
        }
        if let Poll::Ready(Err(err)) = resp {
            return Poll::Ready(Err(err.into()));
        }

        // Poll on any outstanding control requests
        for (addr, state) in &mut self.pending {
            // Poll if we're in the connecting state
            let (root_url, resp) = {
                let (root_url, c) = match state {
                    SearchState::Connecting(root_url, c) => (root_url, c),
                    _ => continue,
                };

                match c.as_mut().poll(cx)? {
                    Poll::Ready(resp) => (root_url.clone(), resp),
                    _ => continue,
                }
            };

            // Handle any responses
            if let Ok(control_url) = Self::handle_control_resp(*addr, resp) {
                debug!("received control url from: {} (url: {})", addr, control_url);
                *state = SearchState::Done(url.clone());

                match addr {
                    SocketAddr::V4(a) => {
                        let g = Gateway {
                            addr: *a,
                            root_url,
                            control_url,
                        };
                        return Poll::Ready(Ok(g));
                    }
                    _ => warn!("unsupported IPv6 gateway response from addr: {}", addr),
                }
            } else {
                *state = SearchState::Error;
            }
        }

        Poll::Pending
    }
}
<commit_msg>Rename SearchState to RequestState<commit_after>use std::collections::HashMap;
use std::net::SocketAddr;
use std::pin::Pin;
use std::str;
use std::task::{Context, Poll};

use futures::prelude::*;
use futures::Future;

use hyper::Client;

use tokio::net::UdpSocket;
use tokio::time::timeout;

use hyper::body::Bytes;

use crate::aio::Gateway;
use crate::common::{messages, parsing, SearchOptions};
use crate::errors::SearchError;

const MAX_RESPONSE_SIZE: usize = 1500;

/// Search for a gateway with the provided options
pub async fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
    // Create socket for future calls
    let socket = UdpSocket::bind(&options.bind_addr).await?;

    let search_future = SearchFuture::search(socket, options.broadcast_address).await?;
    // Create future and issue request
    match options.timeout {
        Some(t) => timeout(t, search_future).await?,
        None => search_future.await,
    }
}

pub struct SearchFuture {
    socket: UdpSocket,
    pending: HashMap<SocketAddr, RequestState>,
}

enum RequestState {
    Connecting(String, Pin<Box<dyn Future<Output = Result<Bytes, SearchError>> + Send>>),
    Done(String),
    Error,
}

impl SearchFuture {
    // Create a new search
    async fn search(mut socket: UdpSocket, addr: SocketAddr) -> Result<SearchFuture, SearchError> {
        debug!(
            "sending broadcast request to: {} on interface: {:?}",
            addr,
            socket.local_addr()
        );
        socket.send_to(messages::SEARCH_REQUEST.as_bytes(), &addr).await?;
        Ok(SearchFuture {
            socket,
            pending: HashMap::new(),
        })
    }

    // Handle a UDP response message
    fn handle_broadcast_resp(from: SocketAddr, data: &[u8]) -> Result<(SocketAddr, String), SearchError> {
        debug!("handling broadcast response from: {}", from);

        // Convert response to text
        let text = str::from_utf8(&data).map_err(|e| SearchError::from(e))?;

        // Parse socket address and path
        let (addr, path) = parsing::parse_search_result(text)?;

        Ok((SocketAddr::V4(addr), path))
    }

    // Issue a control URL request over HTTP using the provided
    fn request_control_url(
        addr: SocketAddr,
        path: String,
    ) -> Result<Pin<Box<dyn Future<Output = Result<Bytes, SearchError>> + Send>>, SearchError> {
        let client = Client::new();

        let uri = match format!("http://{}{}", addr, path).parse() {
            Ok(uri) => uri,
            Err(err) => return Err(SearchError::from(err)),
        };

        debug!("requesting control url from: {}", uri);

        Ok(Box::pin(
            client
                .get(uri)
                .and_then(|resp| hyper::body::to_bytes(resp.into_body()))
                .map_err(|e| SearchError::from(e)),
        ))
    }

    // Process a control response to extract the control URL
    fn handle_control_resp(addr: SocketAddr, resp: Bytes) -> Result<String, SearchError> {
        debug!("handling control response from: {}", addr);

        // Create a cursor over the response data
        let c = std::io::Cursor::new(&resp);

        // Parse control URL out of body
        let url = parsing::parse_control_url(c)?;

        Ok(url)
    }
}

impl Future for SearchFuture {
    type Output = Result<Gateway, SearchError>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<Gateway, SearchError>> {
        // Poll for (and handle) incoming messages
        let mut buff = [0u8; MAX_RESPONSE_SIZE];
        let resp = self.socket.poll_recv_from(cx, &mut buff);
        if let Poll::Ready(Ok((n, from))) = resp {
            // Try handle response messages
            if let Ok((addr, path)) = Self::handle_broadcast_resp(from, &buff[0..n]) {
                if !self.pending.contains_key(&addr) {
                    debug!("received broadcast response from: {}", from);

                    // Issue control request
                    match Self::request_control_url(addr, path.clone()) {
                        // Store pending requests
                        Ok(f) => {
                            self.pending.insert(addr, RequestState::Connecting(path, f));
                        }
                        Err(e) => return Poll::Ready(Err(e)),
                    }
                } else {
                    debug!("received duplicate broadcast response from: {}, dropping", from);
                }
            }
        }
        if let Poll::Ready(Err(err)) = resp {
            return Poll::Ready(Err(err.into()));
        }

        // Poll on any outstanding control requests
        for (addr, state) in &mut self.pending {
            // Poll if we're in the connecting state
            let (root_url, resp) = {
                let (root_url, c) = match state {
                    RequestState::Connecting(root_url, c) => (root_url, c),
                    _ => continue,
                };

                match c.as_mut().poll(cx)? {
                    Poll::Ready(resp) => (root_url.clone(), resp),
                    _ => continue,
                }
            };

            // Handle any responses
            if let Ok(control_url) = Self::handle_control_resp(*addr, resp) {
                debug!("received control url from: {} (url: {})", addr, control_url);
                *state = RequestState::Done(url.clone());

                match addr {
                    SocketAddr::V4(a) => {
                        let g = Gateway {
                            addr: *a,
                            root_url,
                            control_url,
                        };
                        return Poll::Ready(Ok(g));
                    }
                    _ => warn!("unsupported IPv6 gateway response from addr: {}", addr),
                }
            } else {
                *state = RequestState::Error;
            }
        }

        Poll::Pending
    }
}
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#![feature(test)]

extern crate test;
extern crate cita_crypto as crypto;
extern crate libproto;
extern crate protobuf;
extern crate util;
extern crate rustc_serialize;
extern crate serde_json;
extern crate proof;
extern crate clap;
extern crate core;
extern crate dotenv;
extern crate bincode;
extern crate cpuprofiler;
extern crate common_types;

#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
extern crate cita_log;

mod generate_block;
mod call_chain;

use call_chain::Callchain;
use clap::App;
use core::db;
use core::libchain::Genesis;
use cpuprofiler::PROFILER;
use generate_block::Generateblock;
use log::LogLevelFilter;
use std::{env, time, thread};
use std::sync::Arc;
use std::sync::mpsc::channel;
use util::H256;
use util::kvdb::{Database, DatabaseConfig};

pub const DATA_PATH: &'static str = "DATA_PATH";

//创建合约交易性能
fn create_contract(block_tx_num: i32, call: Callchain, pre_hash: H256, flag_prof_start: u64, flag_prof_duration: u64, flag: i32) {
    let code = "60606040523415600e57600080fd5b5b5b5b60948061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635524107714603d575b600080fd5b3415604757600080fd5b605b6004808035906020019091905050605d565b005b806000819055505b505600a165627a7a72305820c471b4376626da2540b2374e8b4110501051c426ff46814a6170ce9e219e49a80029";
    let mut contract_address = "".to_string();
    if flag != 0 {
        contract_address = "ffffffffffffffffffff".to_string();
    }
    let mut txs = Vec::new();
    for _ in 0..block_tx_num - 1 {
        let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
        txs.push(tx);
    }
    let tx = Generateblock::generate_tx(code, contract_address.clone());
    let hash = tx.hash();
    txs.push(tx);

    //构造block
    let h = call.get_height() + 1;
    let (send_data, block) = Generateblock::build_block(txs, pre_hash, h as u64);

    info!("===============start add block===============");
    profifer(flag_prof_start, flag_prof_duration);
    let sys_time = time::SystemTime::now();
    call.add_block(block.clone());
    info!("===============end add block===============");
    let duration = sys_time.elapsed().unwrap();
    let secs = duration.as_secs() * 1000000 + (duration.subsec_nanos() / 1000) as u64;
    info!("tx_num = {}, size = {} , use time = {} μs", block_tx_num, send_data.len(), secs);
    info!("receipt = {:?}", call.get_receipt(hash));
}

//发送合约交易性能
#[allow(unused_assignments)]
fn send_contract_tx(block_tx_num: i32, call: Callchain, pre_hash: H256, flag_prof_start: u64, flag_prof_duration: u64) {
    //构造创建合约的交易交易
    let mut code = "60606040523415600e57600080fd5b5b5b5b60948061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635524107714603d575b600080fd5b3415604757600080fd5b605b6004808035906020019091905050605d565b005b806000819055505b505600a165627a7a72305820c471b4376626da2540b2374e8b4110501051c426ff46814a6170ce9e219e49a80029";
    let mut contract_address = "".to_string();
    let mut txs = Vec::new();
    let mut hash = H256::default();
    let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
    hash = tx.hash();
    txs.push(tx);

    //构造block
    {
        let h = call.get_height() + 1;
        let (_, block) = Generateblock::build_block(txs.clone(), pre_hash, h as u64);
        call.add_block(block.clone());
    }

    let addr = call.get_contract_address(hash);
    contract_address = format!("{:?}", addr);
    code = "552410770000000000000000000000000000000000000000000000000000000012345678";
    txs.clear();
    for _ in 0..block_tx_num - 1 {
        let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
        txs.push(tx);
    }
    let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
    hash = tx.hash();
    txs.push(tx);

    let h = call.get_height() + 1;
    let pre_hash = call.get_pre_hash();
    let (send_data, block) = Generateblock::build_block(txs.clone(), pre_hash, h as u64);
    info!("===============start add block===============");
    profifer(flag_prof_start, flag_prof_duration);
    let sys_time = time::SystemTime::now();
    call.add_block(block.clone());
    let duration = sys_time.elapsed().unwrap();
    info!("===============end add block===============");
    let secs = duration.as_secs() * 1000000 + (duration.subsec_nanos() / 1000) as u64;
    info!("tx_num = {}, size = {} , use time = {} μs", txs.len(), send_data.len(), secs);
    info!("receipt = {:?}", call.get_receipt(hash));
}

fn profifer(flag_prof_start: u64, flag_prof_duration: u64) {
    //start profiling
    let start = flag_prof_start;
    let duration = flag_prof_duration;
    thread::spawn(move || {
                      thread::sleep(time::Duration::new(start, 0));
                      PROFILER.lock().unwrap().start("./chain_performance.profile").expect("Couldn't start");
                      thread::sleep(time::Duration::new(duration, 0));
                      PROFILER.lock().unwrap().stop().unwrap();
                  });

}

fn main() {
    dotenv::dotenv().ok();
    cita_log::format(LogLevelFilter::Info);
    info!("CITA:chain_performance");
    let matches = App::new("generate_block")
        .version("0.1")
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .args_from_usage("--tx_num=[4000] 'transation num in block'")
        .args_from_usage("-c, --config=[FILE] 'Sets a custom config file'")
        .args_from_usage("-m, --method=[method] 'create | call | store'")
        .args_from_usage("--flag_prof_start=[1] 'prof start time'")
        .args_from_usage("--flag_prof_duration=[1] 'prof run time'")
        .get_matches();

    let block_tx_num = matches.value_of("tx_num").unwrap_or("4000").parse::<i32>().unwrap();
    let config_path = matches.value_of("config").unwrap_or("genesis.json");
    let method = matches.value_of("method").unwrap_or("create");
    let flag_prof_start = matches.value_of("flag_prof_start").unwrap_or("0").parse::<u64>().unwrap();
    let flag_prof_duration = matches.value_of("flag_prof_duration").unwrap_or("0").parse::<u64>().unwrap();

    //数据库配置
    let nosql_path = env::var(DATA_PATH).expect(format!("{} must be set", DATA_PATH).as_str()) + "/nosql";
    let config = DatabaseConfig::with_columns(db::NUM_COLUMNS);
    let db = Database::open(&config, &nosql_path).unwrap();
    let genesis = Genesis::init(config_path);

    //chain初始化
    let (sync_tx, _) = channel();
    let call = Callchain::new(Arc::new(db), genesis, sync_tx);
    let pre_hash = call.get_pre_hash();
    match method {
        "create" => create_contract(block_tx_num, call.clone(), pre_hash, flag_prof_start, flag_prof_duration, 0),
        "store" => create_contract(block_tx_num, call.clone(), pre_hash, flag_prof_start, flag_prof_duration, 1),
        "call" | _ => send_contract_tx(block_tx_num, call.clone(), pre_hash, flag_prof_start, flag_prof_duration),
    }
}
<commit_msg>fix ci error: STORE_ADDRESS changed in chain but not in chain_performance<commit_after>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#![feature(test)]

extern crate test;
extern crate cita_crypto as crypto;
extern crate libproto;
extern crate protobuf;
extern crate util;
extern crate rustc_serialize;
extern crate serde_json;
extern crate proof;
extern crate clap;
extern crate core;
extern crate dotenv;
extern crate bincode;
extern crate cpuprofiler;
extern crate common_types;

#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
extern crate cita_log;

mod generate_block;
mod call_chain;

use call_chain::Callchain;
use clap::App;
use core::db;
use core::libchain::Genesis;
use cpuprofiler::PROFILER;
use generate_block::Generateblock;
use log::LogLevelFilter;
use std::{env, time, thread};
use std::sync::Arc;
use std::sync::mpsc::channel;
use util::H256;
use util::kvdb::{Database, DatabaseConfig};

pub const DATA_PATH: &'static str = "DATA_PATH";

//创建合约交易性能
fn create_contract(block_tx_num: i32, call: Callchain, pre_hash: H256, flag_prof_start: u64, flag_prof_duration: u64, flag: i32) {
    let code = "60606040523415600e57600080fd5b5b5b5b60948061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635524107714603d575b600080fd5b3415604757600080fd5b605b6004808035906020019091905050605d565b005b806000819055505b505600a165627a7a72305820c471b4376626da2540b2374e8b4110501051c426ff46814a6170ce9e219e49a80029";
    let mut contract_address = "".to_string();
    if flag != 0 {
        contract_address = "ffffffffffffffffffffffffffffffffffffffff".to_string();
    }
    let mut txs = Vec::new();
    for _ in 0..block_tx_num - 1 {
        let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
        txs.push(tx);
    }
    let tx = Generateblock::generate_tx(code, contract_address.clone());
    let hash = tx.hash();
    txs.push(tx);

    //构造block
    let h = call.get_height() + 1;
    let (send_data, block) = Generateblock::build_block(txs, pre_hash, h as u64);

    info!("===============start add block===============");
    profifer(flag_prof_start, flag_prof_duration);
    let sys_time = time::SystemTime::now();
    call.add_block(block.clone());
    info!("===============end add block===============");
    let duration = sys_time.elapsed().unwrap();
    let secs = duration.as_secs() * 1000000 + (duration.subsec_nanos() / 1000) as u64;
    info!("tx_num = {}, size = {} , use time = {} μs", block_tx_num, send_data.len(), secs);
    info!("receipt = {:?}", call.get_receipt(hash));
}

//发送合约交易性能
#[allow(unused_assignments)]
fn send_contract_tx(block_tx_num: i32, call: Callchain, pre_hash: H256, flag_prof_start: u64, flag_prof_duration: u64) {
    //构造创建合约的交易交易
    let mut code = "60606040523415600e57600080fd5b5b5b5b60948061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635524107714603d575b600080fd5b3415604757600080fd5b605b6004808035906020019091905050605d565b005b806000819055505b505600a165627a7a72305820c471b4376626da2540b2374e8b4110501051c426ff46814a6170ce9e219e49a80029";
    let mut contract_address = "".to_string();
    let mut txs = Vec::new();
    let mut hash = H256::default();
    let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
    hash = tx.hash();
    txs.push(tx);

    //构造block
    {
        let h = call.get_height() + 1;
        let (_, block) = Generateblock::build_block(txs.clone(), pre_hash, h as u64);
        call.add_block(block.clone());
    }

    let addr = call.get_contract_address(hash);
    contract_address = format!("{:?}", addr);
    code = "552410770000000000000000000000000000000000000000000000000000000012345678";
    txs.clear();
    for _ in 0..block_tx_num - 1 {
        let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
        txs.push(tx);
    }
    let tx = Generateblock::generate_tx(code.clone(), contract_address.clone());
    hash = tx.hash();
    txs.push(tx);

    let h = call.get_height() + 1;
    let pre_hash = call.get_pre_hash();
    let (send_data, block) = Generateblock::build_block(txs.clone(), pre_hash, h as u64);
    info!("===============start add block===============");
    profifer(flag_prof_start, flag_prof_duration);
    let sys_time = time::SystemTime::now();
    call.add_block(block.clone());
    let duration = sys_time.elapsed().unwrap();
    info!("===============end add block===============");
    let secs = duration.as_secs() * 1000000 + (duration.subsec_nanos() / 1000) as u64;
    info!("tx_num = {}, size = {} , use time = {} μs", txs.len(), send_data.len(), secs);
    info!("receipt = {:?}", call.get_receipt(hash));
}

fn profifer(flag_prof_start: u64, flag_prof_duration: u64) {
    //start profiling
    let start = flag_prof_start;
    let duration = flag_prof_duration;
    thread::spawn(move || {
                      thread::sleep(time::Duration::new(start, 0));
                      PROFILER.lock().unwrap().start("./chain_performance.profile").expect("Couldn't start");
                      thread::sleep(time::Duration::new(duration, 0));
                      PROFILER.lock().unwrap().stop().unwrap();
                  });

}

fn main() {
    dotenv::dotenv().ok();
    cita_log::format(LogLevelFilter::Info);
    info!("CITA:chain_performance");
    let matches = App::new("generate_block")
        .version("0.1")
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .args_from_usage("--tx_num=[4000] 'transation num in block'")
        .args_from_usage("-c, --config=[FILE] 'Sets a custom config file'")
        .args_from_usage("-m, --method=[method] 'create | call | store'")
        .args_from_usage("--flag_prof_start=[1] 'prof start time'")
        .args_from_usage("--flag_prof_duration=[1] 'prof run time'")
        .get_matches();

    let block_tx_num = matches.value_of("tx_num").unwrap_or("4000").parse::<i32>().unwrap();
    let config_path = matches.value_of("config").unwrap_or("genesis.json");
    let method = matches.value_of("method").unwrap_or("create");
    let flag_prof_start = matches.value_of("flag_prof_start").unwrap_or("0").parse::<u64>().unwrap();
    let flag_prof_duration = matches.value_of("flag_prof_duration").unwrap_or("0").parse::<u64>().unwrap();

    //数据库配置
    let nosql_path = env::var(DATA_PATH).expect(format!("{} must be set", DATA_PATH).as_str()) + "/nosql";
    let config = DatabaseConfig::with_columns(db::NUM_COLUMNS);
    let db = Database::open(&config, &nosql_path).unwrap();
    let genesis = Genesis::init(config_path);

    //chain初始化
    let (sync_tx, _) = channel();
    let call = Callchain::new(Arc::new(db), genesis, sync_tx);
    let pre_hash = call.get_pre_hash();
    match method {
        "create" => create_contract(block_tx_num, call.clone(), pre_hash, flag_prof_start, flag_prof_duration, 0),
        "store" => create_contract(block_tx_num, call.clone(), pre_hash, flag_prof_start, flag_prof_duration, 1),
        "call" | _ => send_contract_tx(block_tx_num, call.clone(), pre_hash, flag_prof_start, flag_prof_duration),
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate flac;
extern crate rustc_serialize;

use docopt::Docopt;
use flac::{ReadStream, Stream, StreamProducer};
use flac::metadata;
use flac::metadata::VorbisComment;

use std::env;
use std::fs::File;

const USAGE: &'static str = "
Usage: metadata streaminfo [options] <filename>
       metadata comments [options] <filename>
       metadata seektable <filename>
       metadata --help

Options:
  --block-size       Show both the max and min block size from StreamInfo.
  --frame-size       Show both the max and min frame size from StreamInfo.
  --sample-rate      Show the sample rate from StreamInfo.
  --channels         Show the number of channels from StreamInfo.
  --bits-per-sample  Show the size in bits for each sample from StreamInfo.
  --total-samples    Show total number of samples from StreamInfo.
  --md5              Show the MD5 signature from StreamInfo.
  --vendor           Show the vendor string from VorbisComment.
  --name=NAME        Show the comments matching the `NAME` from VorbisComment.
  -h, --help         Show this message.
";

#[derive(Debug, RustcDecodable)]
struct Arguments {
  arg_filename: String,
  cmd_streaminfo: bool,
  cmd_comments: bool,
  cmd_seektable: bool,
  flag_block_size: bool,
  flag_frame_size: bool,
  flag_sample_rate: bool,
  flag_channels: bool,
  flag_bits_per_sample: bool,
  flag_total_samples: bool,
  flag_md5: bool,
  flag_vendor: bool,
  flag_name: Option<String>,
}

macro_rules! format_print (
  ($format_str: expr, $opt_str: expr, $data: expr, $no_flag: expr) => (
    {
      println!($format_str, if $no_flag {
        $opt_str
      } else {
        ""
      }, $data);
    }
  );
);

fn print_stream_info<P>(stream: &Stream<P>, args: &Arguments)
 where P: StreamProducer {
  let info     = stream.info();
  let no_flags = (args.flag_block_size      || args.flag_frame_size    ||
                  args.flag_sample_rate     || args.flag_channels      ||
                  args.flag_bits_per_sample || args.flag_total_samples ||
                  args.flag_md5) == false;

  if no_flags || args.flag_block_size {
    println!("Minimum block size: {} samples", info.min_block_size);
    println!("Maximum block size: {} samples", info.max_block_size);
  }

  if no_flags || args.flag_frame_size {
    println!("Minimum frame size: {} bytes", info.min_frame_size);
    println!("Maximum frame size: {} bytes", info.max_frame_size);
  }

  if no_flags || args.flag_sample_rate {
    format_print!("{}{} Hz", "Sample rate: ", info.sample_rate, no_flags);
  }

  if no_flags || args.flag_channels {
    format_print!("{}{}", "Number of channels: ", info.channels, no_flags);
  }

  if no_flags || args.flag_bits_per_sample {
    format_print!("{}{}", "Bits per samples: ", info.bits_per_sample,
                                                no_flags);
  }

  if no_flags || args.flag_total_samples {
    format_print!("{}{}", "Total samples: ", info.total_samples, no_flags);
  }

  if no_flags || args.flag_md5 {
    let mut md5  = String::with_capacity(32);

    for byte in &info.md5_sum {
      let hex = format!("{:02x}", byte);

      md5.push_str(&hex);
    }

    format_print!("{}{}", "MD5 sum: ", md5, no_flags);
  }
}

fn print_vorbis_comments(vorbis_comment: &VorbisComment, args: &Arguments) {
  let no_flags  = (args.flag_vendor || args.flag_name.is_some()) == false;

  if no_flags || args.flag_vendor {
    format_print!("{}{}", "Vendor string: ", vorbis_comment.vendor_string,
                                             no_flags);
  }

  if no_flags {
    let mut index = 1;

    println!("Number of Comments: {}", vorbis_comment.comments.len());

    for (name, value) in &vorbis_comment.comments {
      println!("  {}: \"{}\" = {}", index, name, value);

      index += 1;
    }
  } else {
    if let Some(ref name) = args.flag_name {
      vorbis_comment.comments.get(name)
                             .map(|value| println!("{}", value));
    }
  }
}

fn main() {
  let args: Arguments = Docopt::new(USAGE)
    .and_then(|d| d.argv(env::args()).decode())
    .unwrap_or_else(|e| e.exit());

  let stream = Stream::<ReadStream<File>>::from_file(&args.arg_filename)
                 .expect("Couldn't parse file");

  if args.cmd_streaminfo {
    print_stream_info(&stream, &args);
  }

  for meta in stream.metadata() {
    match meta.data {
      metadata::Data::VorbisComment(ref v) => {
        if args.cmd_comments {
          print_vorbis_comments(v, &args)
        }
      }
      _                                    => continue,
    }
  }
}
<commit_msg>Add implemention for SeekTable printing<commit_after>extern crate docopt;
extern crate flac;
extern crate rustc_serialize;

use docopt::Docopt;
use flac::{ReadStream, Stream, StreamProducer};
use flac::metadata;
use flac::metadata::{SeekPoint, VorbisComment};

use std::env;
use std::fs::File;

const USAGE: &'static str = "
Usage: metadata streaminfo [options] <filename>
       metadata comments [options] <filename>
       metadata seektable <filename>
       metadata --help

Options:
  --block-size       Show both the max and min block size from StreamInfo.
  --frame-size       Show both the max and min frame size from StreamInfo.
  --sample-rate      Show the sample rate from StreamInfo.
  --channels         Show the number of channels from StreamInfo.
  --bits-per-sample  Show the size in bits for each sample from StreamInfo.
  --total-samples    Show total number of samples from StreamInfo.
  --md5              Show the MD5 signature from StreamInfo.
  --vendor           Show the vendor string from VorbisComment.
  --name=NAME        Show the comments matching the `NAME` from VorbisComment.
  -h, --help         Show this message.
";

#[derive(Debug, RustcDecodable)]
struct Arguments {
  arg_filename: String,
  cmd_streaminfo: bool,
  cmd_comments: bool,
  cmd_seektable: bool,
  flag_block_size: bool,
  flag_frame_size: bool,
  flag_sample_rate: bool,
  flag_channels: bool,
  flag_bits_per_sample: bool,
  flag_total_samples: bool,
  flag_md5: bool,
  flag_vendor: bool,
  flag_name: Option<String>,
}

macro_rules! format_print (
  ($format_str: expr, $opt_str: expr, $data: expr, $no_flag: expr) => (
    {
      println!($format_str, if $no_flag {
        $opt_str
      } else {
        ""
      }, $data);
    }
  );
);

fn print_stream_info<P>(stream: &Stream<P>, args: &Arguments)
 where P: StreamProducer {
  let info     = stream.info();
  let no_flags = (args.flag_block_size      || args.flag_frame_size    ||
                  args.flag_sample_rate     || args.flag_channels      ||
                  args.flag_bits_per_sample || args.flag_total_samples ||
                  args.flag_md5) == false;

  if no_flags || args.flag_block_size {
    println!("Minimum block size: {} samples", info.min_block_size);
    println!("Maximum block size: {} samples", info.max_block_size);
  }

  if no_flags || args.flag_frame_size {
    println!("Minimum frame size: {} bytes", info.min_frame_size);
    println!("Maximum frame size: {} bytes", info.max_frame_size);
  }

  if no_flags || args.flag_sample_rate {
    format_print!("{}{} Hz", "Sample rate: ", info.sample_rate, no_flags);
  }

  if no_flags || args.flag_channels {
    format_print!("{}{}", "Number of channels: ", info.channels, no_flags);
  }

  if no_flags || args.flag_bits_per_sample {
    format_print!("{}{}", "Bits per samples: ", info.bits_per_sample,
                                                no_flags);
  }

  if no_flags || args.flag_total_samples {
    format_print!("{}{}", "Total samples: ", info.total_samples, no_flags);
  }

  if no_flags || args.flag_md5 {
    let mut md5  = String::with_capacity(32);

    for byte in &info.md5_sum {
      let hex = format!("{:02x}", byte);

      md5.push_str(&hex);
    }

    format_print!("{}{}", "MD5 sum: ", md5, no_flags);
  }
}

fn print_vorbis_comments(vorbis_comment: &VorbisComment, args: &Arguments) {
  let no_flags  = (args.flag_vendor || args.flag_name.is_some()) == false;

  if no_flags || args.flag_vendor {
    format_print!("{}{}", "Vendor string: ", vorbis_comment.vendor_string,
                                             no_flags);
  }

  if no_flags {
    let mut index = 1;

    println!("Number of Comments: {}", vorbis_comment.comments.len());

    for (name, value) in &vorbis_comment.comments {
      println!("  {}: \"{}\" = {}", index, name, value);

      index += 1;
    }
  } else {
    if let Some(ref name) = args.flag_name {
      vorbis_comment.comments.get(name)
                             .map(|value| println!("{}", value));
    }
  }
}

fn print_seek_table(seek_points: &[SeekPoint]) {
  let mut count = 0;

  println!("Number of Seek Points: {}", seek_points.len());

  for seek_point in seek_points {
    println!("Seek Point #{}", count);
    println!("  Sample number: {}", seek_point.sample_number);
    println!("  Stream offset: {}", seek_point.stream_offset);
    println!("  Frame samples: {}", seek_point.frame_samples);
    count += 1;
  }
}

fn main() {
  let args: Arguments = Docopt::new(USAGE)
    .and_then(|d| d.argv(env::args()).decode())
    .unwrap_or_else(|e| e.exit());

  let stream = Stream::<ReadStream<File>>::from_file(&args.arg_filename)
                 .expect("Couldn't parse file");

  if args.cmd_streaminfo {
    print_stream_info(&stream, &args);
  }

  for meta in stream.metadata() {
    match meta.data {
      metadata::Data::VorbisComment(ref v) => {
        if args.cmd_comments {
          print_vorbis_comments(v, &args)
        }
      }
      _                                    => continue,
    }
  }
}
<|endoftext|>