<commit_before>use crate::{build::BuildRoot,
            error::{Error,
                    Result},
            naming::Naming,
            util,
            Credentials};
use failure::SyncFailure;
use habitat_common::ui::{Status,
                         UIWriter,
                         UI};
use habitat_core::package::PackageIdent;
use handlebars::Handlebars;
use serde_json;
use std::{fs,
          path::{Path,
                 PathBuf},
          str::FromStr};

// This code makes heavy use of `#[cfg(unix)]` and `#[cfg(windows)]`. This should potentially be
// changed to use the various target feature flags.

/// The `Dockerfile` template.
#[cfg(unix)]
const DOCKERFILE: &str = include_str!("../defaults/Dockerfile.hbs");
#[cfg(windows)]
const DOCKERFILE: &str = include_str!("../defaults/Dockerfile_win.hbs");
/// The build report template.
const BUILD_REPORT: &str = include_str!("../defaults/last_docker_export.env.hbs");

// TODO (CM): public temporarily
pub(crate) trait Identified {
    /// The base name of an image.
    fn name(&self) -> String;

    /// The possibly-empty list of tags for an image.
    fn tags(&self) -> Vec<String>;

    /// Returns a non-empty collection of names this image is known
    /// by.
    ///
    /// If an image has no tags, it includes just the name. If it
    /// *does* have tags, it includes the tags prepended with the
    /// name.
    ///
    /// Thus, you could get as little as:
    ///
    /// core/redis
    ///
    /// or as much as:
    ///
    /// core/redis:latest
    /// core/redis:4.0.14
    /// core/redis:4.0.14-20190319155852
    /// core/redis:latest
    /// core/redis:my-custom-tag
    fn expanded_identifiers(&self) -> Vec<String> {
        let mut ids = vec![];

        let tags = self.tags();
        let name = self.name();

        if tags.is_empty() {
            ids.push(name);
        } else {
            for tag in tags {
                ids.push(format!("{}:{}", name, tag));
            }
        }

        ids
    }
}

/// A builder used to create a Docker image.
pub struct DockerBuilder {
    /// The base workdir which hosts the root file system.
    workdir: PathBuf,
    /// The name for the image.
    name:    String,
    /// A list of tags for the image.
    tags:    Vec<String>,
    /// Optional memory limit to pass to pass to the docker build
    memory:  Option<String>,
}

impl Identified for DockerBuilder {
    fn name(&self) -> String { self.name.clone() }

    fn tags(&self) -> Vec<String> { self.tags.clone() }
}

impl DockerBuilder {
    fn new(workdir: &Path, name: &str) -> Self {
        DockerBuilder { workdir: workdir.to_path_buf(),
                        name:    name.to_string(),
                        tags:    Vec::new(),
                        memory:  None, }
    }

    /// Adds a tag for the Docker image.
    pub fn tag(mut self, tag: String) -> Self {
        self.tags.push(tag);
        self
    }

    /// Specifies an amount of memory to allocate to build
    pub fn memory(mut self, memory: &str) -> Self {
        self.memory = Some(memory.to_string());
        self
    }

    /// Builds the Docker image locally and returns the corresponding `DockerImage`.
    ///
    /// # Errors
    ///
    /// * If building the Docker image fails
    pub fn build(self) -> Result<DockerImage> {
        let mut cmd = util::docker_cmd();
        cmd.current_dir(&self.workdir)
           .arg("build")
           .arg("--force-rm");
        if let Some(ref mem) = self.memory {
            cmd.arg("--memory").arg(mem);
        }
        for identifier in &self.expanded_identifiers() {
            cmd.arg("--tag").arg(identifier);
        }
        cmd.arg(".");
        debug!("Running: {:?}", &cmd);
        let exit_status = cmd.spawn()?.wait()?;
        if !exit_status.success() {
            return Err(Error::BuildFailed(exit_status).into());
        }

        let id = match self.tags.first() {
            Some(tag) => self.image_id(&format!("{}:{}", &self.name, tag))?,
            None => self.image_id(&self.name)?,
        };

        Ok(DockerImage { id,
                         name: self.name,
                         tags: self.tags,
                         workdir: self.workdir.to_owned() })
    }

    fn image_id(&self, image_tag: &str) -> Result<String> {
        let mut cmd = util::docker_cmd();
        cmd.arg("images").arg("-q").arg(image_tag);
        debug!("Running: {:?}", &cmd);
        let output = cmd.output()?;
        let stdout = String::from_utf8_lossy(&output.stdout);

        match stdout.lines().next() {
            Some(id) => Ok(id.to_string()),
            None => Err(Error::DockerImageIdNotFound(image_tag.to_string()).into()),
        }
    }
}

/// A built Docker image which exists locally.
pub struct DockerImage {
    /// The image ID for this image.
    id:      String,
    /// The name of this image.
    name:    String,
    /// The list of tags for this image.
    tags:    Vec<String>,
    /// The base workdir which hosts the root file system.
    workdir: PathBuf,
}

impl Identified for DockerImage {
    fn name(&self) -> String { self.name.clone() }

    fn tags(&self) -> Vec<String> { self.tags.clone() }
}

impl DockerImage {
    /// Pushes the Docker image, with all tags, to a remote registry using the provided
    /// `Credentials`.
    ///
    /// # Errors
    ///
    /// * If a registry login is not successful
    /// * If a pushing one or more of the image tags fails
    /// * If a registry logout is not successful
    pub fn push(&self,
                ui: &mut UI,
                credentials: &Credentials,
                registry_url: Option<&str>)
                -> Result<()> {
        ui.begin(format!("Pushing Docker image '{}' with all tags to remote registry",
                         self.name))?;
        self.create_docker_config_file(credentials, registry_url)
            .unwrap();

        for identifier in self.expanded_identifiers() {
            self.push_image(ui, &identifier)?;
        }

        ui.end(format!("Docker image '{}' published with tags: {}",
                       self.name,
                       self.tags.join(", "),))?;

        Ok(())
    }

    /// Removes the image from the local Docker engine along with all tags.
    ///
    /// # Errors
    ///
    /// * If one or more of the image tags cannot be removed
    pub fn rm(self, ui: &mut UI) -> Result<()> {
        ui.begin(format!("Cleaning up local Docker image '{}' with all tags",
                         self.name))?;

        for identifier in self.expanded_identifiers() {
            self.rm_image(ui, &identifier)?;
        }

        ui.end(format!("Local Docker image '{}' with tags: {} cleaned up",
                       self.name,
                       self.tags.join(", "),))?;
        Ok(())
    }

    /// Create a build report with image metadata in the given path.
    ///
    /// # Errors
    ///
    /// * If the destination directory cannot be created
    /// * If the report file cannot be written
    pub fn create_report<P: AsRef<Path>>(&self, ui: &mut UI, dst: P) -> Result<()> {
        let report = dst.as_ref().join("last_docker_export.env");
        ui.status(Status::Creating,
                  format!("build report {}", report.display()))?;
        fs::create_dir_all(&dst)?;
        let name_tags: Vec<_> = self.tags
                                    .iter()
                                    .map(|t| format!("{}:{}", &self.name, t))
                                    .collect();
        let json = json!({
            "id": &self.id,
            "name": &self.name,
            "tags": self.tags.join(","),
            "name_tags": name_tags.join(","),
        });
        util::write_file(&report,
                         &Handlebars::new().template_render(BUILD_REPORT, &json)
                                           .map_err(SyncFailure::new)?)?;
        Ok(())
    }

    pub fn create_docker_config_file(&self,
                                     credentials: &Credentials,
                                     registry_url: Option<&str>)
                                     -> Result<()> {
        let config = self.workdir.join("config.json");
        fs::create_dir_all(&self.workdir)?;
        let registry = match registry_url {
            Some(url) => url,
            None => "https://index.docker.io/v1/",
        };
        debug!("Using registry: {:?}", registry);
        let json = json!({
            "auths": {
                registry: {
                    "auth": credentials.token
                }
            }
        });
        util::write_file(&config, &serde_json::to_string(&json).unwrap())?;
        Ok(())
    }

    fn push_image(&self, ui: &mut UI, image_tag: &str) -> Result<()> {
        ui.status(Status::Uploading,
                  format!("image '{}' to remote registry", image_tag))?;
        let mut cmd = util::docker_cmd();
        cmd.arg("--config");
        cmd.arg(self.workdir.to_str().unwrap());
        cmd.arg("push").arg(image_tag);
        debug!("Running: {:?}", &cmd);
        let exit_status = cmd.spawn()?.wait()?;
        if !exit_status.success() {
            return Err(Error::PushImageFailed(exit_status).into());
        }
        ui.status(Status::Uploaded, format!("image '{}'", &image_tag))?;

        Ok(())
    }

    fn rm_image(&self, ui: &mut UI, image_tag: &str) -> Result<()> {
        ui.status(Status::Deleting, format!("local image '{}'", image_tag))?;
        let mut cmd = util::docker_cmd();
        cmd.arg("rmi").arg(image_tag);
        debug!("Running: {:?}", &cmd);
        let exit_status = cmd.spawn()?.wait()?;
        if !exit_status.success() {
            return Err(Error::RemoveImageFailed(exit_status).into());
        }

        Ok(())
    }
}

/// A temporary file system build root for building a Docker image, based on Habitat packages.
pub struct DockerBuildRoot(BuildRoot);

impl DockerBuildRoot {
    /// Builds a completed Docker build root from a `BuildRoot`, performing any final tasks on the
    /// root file system.
    ///
    /// # Errors
    ///
    /// * If any remaining tasks cannot be performed in the build root
    #[cfg(unix)]
    pub fn from_build_root(build_root: BuildRoot, ui: &mut UI) -> Result<Self> {
        let root = DockerBuildRoot(build_root);
        root.add_users_and_groups(ui)?;
        root.create_entrypoint(ui)?;
        root.create_dockerfile(ui)?;

        Ok(root)
    }

    #[cfg(windows)]
    pub fn from_build_root(build_root: BuildRoot, ui: &mut UI) -> Result<Self> {
        let root = DockerBuildRoot(build_root);
        root.create_dockerfile(ui)?;

        Ok(root)
    }

    /// Destroys the temporary build root.
    ///
    /// Note that the build root will automatically destroy itself when it falls out of scope, so
    /// a call to this method is not required, but calling this will provide more user-facing
    /// progress and error reporting.
    ///
    /// # Errors
    ///
    /// * If the temporary work directory cannot be removed
    pub fn destroy(self, ui: &mut UI) -> Result<()> { self.0.destroy(ui) }

    #[cfg(unix)]
    fn add_users_and_groups(&self, ui: &mut UI) -> Result<()> {
        use std::{fs::OpenOptions,
                  io::Write};

        let ctx = self.0.ctx();
        let (users, groups) = ctx.svc_users_and_groups()?;
        {
            let file = "etc/passwd";
            let mut f = OpenOptions::new().append(true)
                                          .open(ctx.rootfs().join(&file))?;
            for user in users {
                ui.status(Status::Creating,
                          format!("user '{}' in /{}", user.name, &file))?;
                writeln!(f, "{}", user)?;
            }
        }
        {
            let file = "etc/group";
            let mut f = OpenOptions::new().append(true)
                                          .open(ctx.rootfs().join(&file))?;
            for group in groups {
                ui.status(Status::Creating,
                          format!("group '{}' in /{}", group.name, &file))?;
                writeln!(f, "{}", group)?;
            }
        }
        Ok(())
    }

    #[cfg(unix)]
    fn create_entrypoint(&self, ui: &mut UI) -> Result<()> {
        use habitat_core::util::posix_perm;

        /// The entrypoint script template.
        const INIT_SH: &str = include_str!("../defaults/init.sh.hbs");

        ui.status(Status::Creating, "entrypoint script")?;
        let ctx = self.0.ctx();
        let busybox_shell =
            util::pkg_path_for(&util::busybox_ident()?, ctx.rootfs())?.join("bin/sh");
        let json = json!({
            "busybox_shell": busybox_shell,
            "path": ctx.env_path(),
            "sup_bin": format!("{} sup", ctx.bin_path().join("hab").display()),
            "primary_svc_ident": ctx.primary_svc_ident().to_string(),
        });
        let init = ctx.rootfs().join("init.sh");
        util::write_file(&init,
                         &Handlebars::new().template_render(INIT_SH, &json)
                                           .map_err(SyncFailure::new)?)?;
        posix_perm::set_permissions(init.to_string_lossy().as_ref(), 0o0755)?;
        Ok(())
    }

    fn create_dockerfile(&self, ui: &mut UI) -> Result<()> {
        ui.status(Status::Creating, "image Dockerfile")?;
        let ctx = self.0.ctx();
        let json = json!({
            "base_image": ctx.base_image(),
            "rootfs": ctx.rootfs().file_name().expect("file_name exists")
                .to_string_lossy()
                .as_ref(),
            "path": ctx.env_path(),
            "hab_path": util::pkg_path_for(
                &PackageIdent::from_str("core/hab")?,
                ctx.rootfs())?.join("bin/hab")
                .to_string_lossy()
                .replace("\\", "/"),
            "exposes": ctx.svc_exposes().join(" "),
            "multi_layer": ctx.multi_layer(),
            "primary_svc_ident": ctx.primary_svc_ident().to_string(),
            "installed_primary_svc_ident": ctx.installed_primary_svc_ident()?.to_string(),
            "environment": ctx.environment,
            "packages": self.0.graph().reverse_topological_sort().iter().map(ToString::to_string).collect::<Vec<_>>(),
        });
        util::write_file(self.0.workdir().join("Dockerfile"),
                         &Handlebars::new().template_render(DOCKERFILE, &json)
                                           .map_err(SyncFailure::new)?)?;
        Ok(())
    }

    /// Build the Docker image locally using the provided naming policy.
    pub fn export(&self,
                  ui: &mut UI,
                  naming: &Naming,
                  memory: Option<&str>)
                  -> Result<DockerImage> {
        ui.status(Status::Creating, "Docker image")?;
        let ident = self.0.ctx().installed_primary_svc_ident()?;
        let channel = self.0.ctx().channel();

        // TODO (CM): Ideally, we'd toss this error much earlier,
        // since this error would be based on user input errors
        let (image_name, tags) = naming.image_identifiers(&ident, &channel)?;

        let mut builder = DockerBuilder::new(self.0.workdir(), &image_name);
        for tag in tags {
            builder = builder.tag(tag);
        }
        if let Some(memory) = memory {
            builder = builder.memory(memory);
        }
        builder.build()
    }
}
<commit_msg>Inline the rm_image and push_image methods<commit_after>use crate::{build::BuildRoot,
            error::{Error,
                    Result},
            naming::Naming,
            util,
            Credentials};
use failure::SyncFailure;
use habitat_common::ui::{Status,
                         UIWriter,
                         UI};
use habitat_core::package::PackageIdent;
use handlebars::Handlebars;
use serde_json;
use std::{fs,
          path::{Path,
                 PathBuf},
          str::FromStr};

// This code makes heavy use of `#[cfg(unix)]` and `#[cfg(windows)]`. This should potentially be
// changed to use the various target feature flags.

/// The `Dockerfile` template.
#[cfg(unix)]
const DOCKERFILE: &str = include_str!("../defaults/Dockerfile.hbs");
#[cfg(windows)]
const DOCKERFILE: &str = include_str!("../defaults/Dockerfile_win.hbs");
/// The build report template.
const BUILD_REPORT: &str = include_str!("../defaults/last_docker_export.env.hbs");

// TODO (CM): public temporarily
pub(crate) trait Identified {
    /// The base name of an image.
    fn name(&self) -> String;

    /// The possibly-empty list of tags for an image.
    fn tags(&self) -> Vec<String>;

    /// Returns a non-empty collection of names this image is known
    /// by.
    ///
    /// If an image has no tags, it includes just the name. If it
    /// *does* have tags, it includes the tags prepended with the
    /// name.
    ///
    /// Thus, you could get as little as:
    ///
    /// core/redis
    ///
    /// or as much as:
    ///
    /// core/redis:latest
    /// core/redis:4.0.14
    /// core/redis:4.0.14-20190319155852
    /// core/redis:latest
    /// core/redis:my-custom-tag
    fn expanded_identifiers(&self) -> Vec<String> {
        let mut ids = vec![];

        let tags = self.tags();
        let name = self.name();

        if tags.is_empty() {
            ids.push(name);
        } else {
            for tag in tags {
                ids.push(format!("{}:{}", name, tag));
            }
        }

        ids
    }
}

/// A builder used to create a Docker image.
pub struct DockerBuilder {
    /// The base workdir which hosts the root file system.
    workdir: PathBuf,
    /// The name for the image.
    name:    String,
    /// A list of tags for the image.
    tags:    Vec<String>,
    /// Optional memory limit to pass to pass to the docker build
    memory:  Option<String>,
}

impl Identified for DockerBuilder {
    fn name(&self) -> String { self.name.clone() }

    fn tags(&self) -> Vec<String> { self.tags.clone() }
}

impl DockerBuilder {
    fn new(workdir: &Path, name: &str) -> Self {
        DockerBuilder { workdir: workdir.to_path_buf(),
                        name:    name.to_string(),
                        tags:    Vec::new(),
                        memory:  None, }
    }

    /// Adds a tag for the Docker image.
    pub fn tag(mut self, tag: String) -> Self {
        self.tags.push(tag);
        self
    }

    /// Specifies an amount of memory to allocate to build
    pub fn memory(mut self, memory: &str) -> Self {
        self.memory = Some(memory.to_string());
        self
    }

    /// Builds the Docker image locally and returns the corresponding `DockerImage`.
    ///
    /// # Errors
    ///
    /// * If building the Docker image fails
    pub fn build(self) -> Result<DockerImage> {
        let mut cmd = util::docker_cmd();
        cmd.current_dir(&self.workdir)
           .arg("build")
           .arg("--force-rm");
        if let Some(ref mem) = self.memory {
            cmd.arg("--memory").arg(mem);
        }
        for identifier in &self.expanded_identifiers() {
            cmd.arg("--tag").arg(identifier);
        }
        cmd.arg(".");
        debug!("Running: {:?}", &cmd);
        let exit_status = cmd.spawn()?.wait()?;
        if !exit_status.success() {
            return Err(Error::BuildFailed(exit_status).into());
        }

        let id = match self.tags.first() {
            Some(tag) => self.image_id(&format!("{}:{}", &self.name, tag))?,
            None => self.image_id(&self.name)?,
        };

        Ok(DockerImage { id,
                         name: self.name,
                         tags: self.tags,
                         workdir: self.workdir.to_owned() })
    }

    fn image_id(&self, image_tag: &str) -> Result<String> {
        let mut cmd = util::docker_cmd();
        cmd.arg("images").arg("-q").arg(image_tag);
        debug!("Running: {:?}", &cmd);
        let output = cmd.output()?;
        let stdout = String::from_utf8_lossy(&output.stdout);

        match stdout.lines().next() {
            Some(id) => Ok(id.to_string()),
            None => Err(Error::DockerImageIdNotFound(image_tag.to_string()).into()),
        }
    }
}

/// A built Docker image which exists locally.
pub struct DockerImage {
    /// The image ID for this image.
    id:      String,
    /// The name of this image.
    name:    String,
    /// The list of tags for this image.
    tags:    Vec<String>,
    /// The base workdir which hosts the root file system.
    workdir: PathBuf,
}

impl Identified for DockerImage {
    fn name(&self) -> String { self.name.clone() }

    fn tags(&self) -> Vec<String> { self.tags.clone() }
}

impl DockerImage {
    /// Pushes the Docker image, with all tags, to a remote registry using the provided
    /// `Credentials`.
    ///
    /// # Errors
    ///
    /// * If a registry login is not successful
    /// * If a pushing one or more of the image tags fails
    /// * If a registry logout is not successful
    pub fn push(&self,
                ui: &mut UI,
                credentials: &Credentials,
                registry_url: Option<&str>)
                -> Result<()> {
        ui.begin(format!("Pushing Docker image '{}' with all tags to remote registry",
                         self.name))?;
        self.create_docker_config_file(credentials, registry_url)
            .unwrap();

        for image_tag in self.expanded_identifiers() {
            ui.status(Status::Uploading,
                      format!("image '{}' to remote registry", image_tag))?;
            let mut cmd = util::docker_cmd();
            cmd.arg("--config");
            cmd.arg(self.workdir.to_str().unwrap());
            cmd.arg("push").arg(&image_tag);
            debug!("Running: {:?}", &cmd);
            let exit_status = cmd.spawn()?.wait()?;
            if !exit_status.success() {
                return Err(Error::PushImageFailed(exit_status).into());
            }
            ui.status(Status::Uploaded, format!("image '{}'", &image_tag))?;
        }

        ui.end(format!("Docker image '{}' published with tags: {}",
                       self.name,
                       self.tags.join(", "),))?;

        Ok(())
    }

    /// Removes the image from the local Docker engine along with all tags.
    ///
    /// # Errors
    ///
    /// * If one or more of the image tags cannot be removed
    pub fn rm(self, ui: &mut UI) -> Result<()> {
        ui.begin(format!("Cleaning up local Docker image '{}' with all tags",
                         self.name))?;

        for image_tag in self.expanded_identifiers() {
            ui.status(Status::Deleting, format!("local image '{}'", image_tag))?;
            let mut cmd = util::docker_cmd();
            cmd.arg("rmi").arg(image_tag);
            debug!("Running: {:?}", &cmd);
            let exit_status = cmd.spawn()?.wait()?;
            if !exit_status.success() {
                return Err(Error::RemoveImageFailed(exit_status).into());
            }
        }

        ui.end(format!("Local Docker image '{}' with tags: {} cleaned up",
                       self.name,
                       self.tags.join(", "),))?;
        Ok(())
    }

    /// Create a build report with image metadata in the given path.
    ///
    /// # Errors
    ///
    /// * If the destination directory cannot be created
    /// * If the report file cannot be written
    pub fn create_report<P: AsRef<Path>>(&self, ui: &mut UI, dst: P) -> Result<()> {
        let report = dst.as_ref().join("last_docker_export.env");
        ui.status(Status::Creating,
                  format!("build report {}", report.display()))?;
        fs::create_dir_all(&dst)?;
        let name_tags: Vec<_> = self.tags
                                    .iter()
                                    .map(|t| format!("{}:{}", &self.name, t))
                                    .collect();
        let json = json!({
            "id": &self.id,
            "name": &self.name,
            "tags": self.tags.join(","),
            "name_tags": name_tags.join(","),
        });
        util::write_file(&report,
                         &Handlebars::new().template_render(BUILD_REPORT, &json)
                                           .map_err(SyncFailure::new)?)?;
        Ok(())
    }

    pub fn create_docker_config_file(&self,
                                     credentials: &Credentials,
                                     registry_url: Option<&str>)
                                     -> Result<()> {
        let config = self.workdir.join("config.json");
        fs::create_dir_all(&self.workdir)?;
        let registry = match registry_url {
            Some(url) => url,
            None => "https://index.docker.io/v1/",
        };
        debug!("Using registry: {:?}", registry);
        let json = json!({
            "auths": {
                registry: {
                    "auth": credentials.token
                }
            }
        });
        util::write_file(&config, &serde_json::to_string(&json).unwrap())?;
        Ok(())
    }
}

/// A temporary file system build root for building a Docker image, based on Habitat packages.
pub struct DockerBuildRoot(BuildRoot);

impl DockerBuildRoot {
    /// Builds a completed Docker build root from a `BuildRoot`, performing any final tasks on the
    /// root file system.
    ///
    /// # Errors
    ///
    /// * If any remaining tasks cannot be performed in the build root
    #[cfg(unix)]
    pub fn from_build_root(build_root: BuildRoot, ui: &mut UI) -> Result<Self> {
        let root = DockerBuildRoot(build_root);
        root.add_users_and_groups(ui)?;
        root.create_entrypoint(ui)?;
        root.create_dockerfile(ui)?;

        Ok(root)
    }

    #[cfg(windows)]
    pub fn from_build_root(build_root: BuildRoot, ui: &mut UI) -> Result<Self> {
        let root = DockerBuildRoot(build_root);
        root.create_dockerfile(ui)?;

        Ok(root)
    }

    /// Destroys the temporary build root.
    ///
    /// Note that the build root will automatically destroy itself when it falls out of scope, so
    /// a call to this method is not required, but calling this will provide more user-facing
    /// progress and error reporting.
    ///
    /// # Errors
    ///
    /// * If the temporary work directory cannot be removed
    pub fn destroy(self, ui: &mut UI) -> Result<()> { self.0.destroy(ui) }

    #[cfg(unix)]
    fn add_users_and_groups(&self, ui: &mut UI) -> Result<()> {
        use std::{fs::OpenOptions,
                  io::Write};

        let ctx = self.0.ctx();
        let (users, groups) = ctx.svc_users_and_groups()?;
        {
            let file = "etc/passwd";
            let mut f = OpenOptions::new().append(true)
                                          .open(ctx.rootfs().join(&file))?;
            for user in users {
                ui.status(Status::Creating,
                          format!("user '{}' in /{}", user.name, &file))?;
                writeln!(f, "{}", user)?;
            }
        }
        {
            let file = "etc/group";
            let mut f = OpenOptions::new().append(true)
                                          .open(ctx.rootfs().join(&file))?;
            for group in groups {
                ui.status(Status::Creating,
                          format!("group '{}' in /{}", group.name, &file))?;
                writeln!(f, "{}", group)?;
            }
        }
        Ok(())
    }

    #[cfg(unix)]
    fn create_entrypoint(&self, ui: &mut UI) -> Result<()> {
        use habitat_core::util::posix_perm;

        /// The entrypoint script template.
        const INIT_SH: &str = include_str!("../defaults/init.sh.hbs");

        ui.status(Status::Creating, "entrypoint script")?;
        let ctx = self.0.ctx();
        let busybox_shell =
            util::pkg_path_for(&util::busybox_ident()?, ctx.rootfs())?.join("bin/sh");
        let json = json!({
            "busybox_shell": busybox_shell,
            "path": ctx.env_path(),
            "sup_bin": format!("{} sup", ctx.bin_path().join("hab").display()),
            "primary_svc_ident": ctx.primary_svc_ident().to_string(),
        });
        let init = ctx.rootfs().join("init.sh");
        util::write_file(&init,
                         &Handlebars::new().template_render(INIT_SH, &json)
                                           .map_err(SyncFailure::new)?)?;
        posix_perm::set_permissions(init.to_string_lossy().as_ref(), 0o0755)?;
        Ok(())
    }

    fn create_dockerfile(&self, ui: &mut UI) -> Result<()> {
        ui.status(Status::Creating, "image Dockerfile")?;
        let ctx = self.0.ctx();
        let json = json!({
            "base_image": ctx.base_image(),
            "rootfs": ctx.rootfs().file_name().expect("file_name exists")
                .to_string_lossy()
                .as_ref(),
            "path": ctx.env_path(),
            "hab_path": util::pkg_path_for(
                &PackageIdent::from_str("core/hab")?,
                ctx.rootfs())?.join("bin/hab")
                .to_string_lossy()
                .replace("\\", "/"),
            "exposes": ctx.svc_exposes().join(" "),
            "multi_layer": ctx.multi_layer(),
            "primary_svc_ident": ctx.primary_svc_ident().to_string(),
            "installed_primary_svc_ident": ctx.installed_primary_svc_ident()?.to_string(),
            "environment": ctx.environment,
            "packages": self.0.graph().reverse_topological_sort().iter().map(ToString::to_string).collect::<Vec<_>>(),
        });
        util::write_file(self.0.workdir().join("Dockerfile"),
                         &Handlebars::new().template_render(DOCKERFILE, &json)
                                           .map_err(SyncFailure::new)?)?;
        Ok(())
    }

    /// Build the Docker image locally using the provided naming policy.
    pub fn export(&self,
                  ui: &mut UI,
                  naming: &Naming,
                  memory: Option<&str>)
                  -> Result<DockerImage> {
        ui.status(Status::Creating, "Docker image")?;
        let ident = self.0.ctx().installed_primary_svc_ident()?;
        let channel = self.0.ctx().channel();

        // TODO (CM): Ideally, we'd toss this error much earlier,
        // since this error would be based on user input errors
        let (image_name, tags) = naming.image_identifiers(&ident, &channel)?;

        let mut builder = DockerBuilder::new(self.0.workdir(), &image_name);
        for tag in tags {
            builder = builder.tag(tag);
        }
        if let Some(memory) = memory {
            builder = builder.memory(memory);
        }
        builder.build()
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]
#![warn(rust_2018_idioms)]
#![doc(html_root_url = "https://docs.rs/amq-protocol-tcp/6.0.0-beta4/")]

//! # AMQP URI TCP connection handling
//!
//! amq-protocol-tcp is a library aiming at providing tools to help
//! connecting to an AMQP URI

use amq_protocol_uri::{AMQPScheme, AMQPUri};
use log::trace;
use std::ops::{Deref, DerefMut};

/// Re-export TcpStream
pub use tcp_stream::{HandshakeError, HandshakeResult, Identity, MidHandshakeTlsStream, TcpStream};

#[cfg(feature = "native-tls")]
pub use tcp_stream::NativeTlsConnector;

#[cfg(feature = "openssl")]
pub use tcp_stream::OpenSslConnector;

#[cfg(feature = "rustls-connector")]
pub use tcp_stream::RustlsConnector;

pub use crate::sys::TcpStreamWrapper;

/// Trait providing a method to connect to a TcpStream
pub trait AMQPUriTcpExt {
    /// connect to a TcpStream
    fn connect(&self) -> HandshakeResult
    where
        Self: Sized,
    {
        self.connect_with_identity(None)
    }

    /// connect to a TcpStream with the given identity
    fn connect_with_identity(&self, identity: Option<Identity<'_, '_>>) -> HandshakeResult;
}

impl AMQPUriTcpExt for AMQPUri {
    fn connect_with_identity(&self, identity: Option<Identity<'_, '_>>) -> HandshakeResult {
        let uri = format!("{}:{}", self.authority.host, self.authority.port);
        trace!("Connecting to {}", uri);
        let stream = TcpStream::connect(uri)?;

        match self.scheme {
            AMQPScheme::AMQP => Ok(stream),
            AMQPScheme::AMQPS => stream.into_tls(&self.authority.host, identity),
        }
    }
}

impl Deref for TcpStreamWrapper {
    type Target = TcpStream;

    fn deref(&self) -> &Self::Target {
        &*self.inner
    }
}

impl DerefMut for TcpStreamWrapper {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.inner
    }
}

#[cfg(unix)]
mod sys {
    use crate::TcpStream;
    use std::{
        mem::ManuallyDrop,
        os::unix::io::{AsRawFd, FromRawFd, RawFd},
    };

    /// Unsafe wrapper "Cloning" the TcpStream but not closing it on drop.
    pub struct TcpStreamWrapper {
        fd: RawFd,
        pub(crate) inner: ManuallyDrop<TcpStream>,
    }

    impl TcpStreamWrapper {
        /// Clone the TcpStream. Original one needs to last at least for the same lifetime.
        pub unsafe fn new(socket: &TcpStream) -> Self {
            Self {
                fd: socket.as_raw_fd(),
                inner: ManuallyDrop::new(TcpStream::from_raw_fd(socket.as_raw_fd())),
            }
        }
    }

    impl AsRawFd for TcpStreamWrapper {
        fn as_raw_fd(&self) -> RawFd {
            self.fd
        }
    }
}

#[cfg(windows)]
mod sys {
    use crate::TcpStream;
    use std::{
        mem::ManuallyDrop,
        os::windows::io::{AsRawSocket, FromRawSocket, RawSocket},
    };

    /// Unsafe wrapper "Cloning" the TcpStream but not closing it on drop.
    pub struct TcpStreamWrapper {
        socket: RawSocket,
        pub(crate) inner: ManuallyDrop<TcpStream>,
    }

    impl TcpStreamWrapper {
        /// Clone the TcpStream. Original one needs to last at least for the same lifetime.
        pub unsafe fn new(socket: &TcpStream) -> Self {
            Self {
                socket: socket.as_raw_socket(),
                inner: ManuallyDrop::new(TcpStream::from_raw_socket(socket.as_raw_socket())),
            }
        }
    }

    impl AsRawSocket for TcpStreamWrapper {
        fn as_raw_socket(&self) -> RawSocket {
            self.socket
        }
    }
}
<commit_msg>simplify TcpStreamWrapper<commit_after>#![deny(missing_docs)]
#![warn(rust_2018_idioms)]
#![doc(html_root_url = "https://docs.rs/amq-protocol-tcp/6.0.0-beta4/")]

//! # AMQP URI TCP connection handling
//!
//! amq-protocol-tcp is a library aiming at providing tools to help
//! connecting to an AMQP URI

use amq_protocol_uri::{AMQPScheme, AMQPUri};
use log::trace;
use std::{
    mem::ManuallyDrop,
    ops::{Deref, DerefMut},
};

/// Re-export TcpStream
pub use tcp_stream::{HandshakeError, HandshakeResult, Identity, MidHandshakeTlsStream, TcpStream};

#[cfg(feature = "native-tls")]
pub use tcp_stream::NativeTlsConnector;

#[cfg(feature = "openssl")]
pub use tcp_stream::OpenSslConnector;

#[cfg(feature = "rustls-connector")]
pub use tcp_stream::RustlsConnector;

/// Trait providing a method to connect to a TcpStream
pub trait AMQPUriTcpExt {
    /// connect to a TcpStream
    fn connect(&self) -> HandshakeResult
    where
        Self: Sized,
    {
        self.connect_with_identity(None)
    }

    /// connect to a TcpStream with the given identity
    fn connect_with_identity(&self, identity: Option<Identity<'_, '_>>) -> HandshakeResult;
}

impl AMQPUriTcpExt for AMQPUri {
    fn connect_with_identity(&self, identity: Option<Identity<'_, '_>>) -> HandshakeResult {
        let uri = format!("{}:{}", self.authority.host, self.authority.port);
        trace!("Connecting to {}", uri);
        let stream = TcpStream::connect(uri)?;

        match self.scheme {
            AMQPScheme::AMQP => Ok(stream),
            AMQPScheme::AMQPS => stream.into_tls(&self.authority.host, identity),
        }
    }
}

/// Unsafe wrapper "Cloning" the TcpStream but not closing it on drop.
pub struct TcpStreamWrapper(ManuallyDrop<TcpStream>);

impl Deref for TcpStreamWrapper {
    type Target = TcpStream;

    fn deref(&self) -> &Self::Target {
        &*self.0
    }
}

impl DerefMut for TcpStreamWrapper {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.0
    }
}

#[cfg(unix)]
mod sys {
    use crate::{TcpStream, TcpStreamWrapper};
    use std::{
        mem::ManuallyDrop,
        os::unix::io::{AsRawFd, FromRawFd},
    };

    impl TcpStreamWrapper {
        /// Clone the TcpStream. Original one needs to last at least for the same lifetime.
        pub unsafe fn new(socket: &TcpStream) -> Self {
            Self(ManuallyDrop::new(TcpStream::from_raw_fd(
                socket.as_raw_fd(),
            )))
        }
    }
}

#[cfg(windows)]
mod sys {
    use crate::{TcpStream, TcpStreamWrapper};
    use std::{
        mem::ManuallyDrop,
        os::windows::io::{AsRawSocket, FromRawSocket},
    };

    impl TcpStreamWrapper {
        /// Clone the TcpStream. Original one needs to last at least for the same lifetime.
        pub unsafe fn new(socket: &TcpStream) -> Self {
            Self(ManuallyDrop::new(TcpStream::from_raw_socket(
                socket.as_raw_socket(),
            )))
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(thread_local)]
#![feature(box_syntax)]
#![feature(rustc_private)]
// For FFI
#![allow(non_snake_case, dead_code)]

#[macro_use]
extern crate log;

extern crate compositing;
extern crate devtools;
extern crate net;
extern crate net_traits;
extern crate msg;
#[macro_use]
extern crate util;
extern crate script;
extern crate layout;
extern crate gfx;
extern crate libc;
extern crate profile;
extern crate url;

use compositing::CompositorEventListener;
use compositing::windowing::{WindowEvent, WindowMethods};

#[cfg(not(test))]
use compositing::{CompositorProxy, CompositorTask, Constellation};
#[cfg(not(test))]
use msg::constellation_msg::Msg as ConstellationMsg;
#[cfg(not(test))]
use msg::constellation_msg::ConstellationChan;
#[cfg(not(test))]
use script::dom::bindings::codegen::RegisterBindings;

#[cfg(not(test))]
use net::image_cache_task::new_image_cache_task;
#[cfg(not(test))]
use net::storage_task::StorageTaskFactory;
#[cfg(not(test))]
use net::resource_task::new_resource_task;
#[cfg(not(test))]
use gfx::font_cache_task::FontCacheTask;
#[cfg(not(test))]
use profile::mem;
#[cfg(not(test))]
use profile::time;
#[cfg(not(test))]
use util::opts;
#[cfg(not(test))]
use util::taskpool::TaskPool;

#[cfg(not(test))]
use std::env;
#[cfg(not(test))]
use std::rc::Rc;

pub struct Browser {
    compositor: Box<CompositorEventListener + 'static>,
}

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
impl Browser {
    #[cfg(not(test))]
    pub fn new<Window>(window: Option<Rc<Window>>) -> Browser
        where Window: WindowMethods + 'static {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        // Create the global vtables used by the (generated) DOM
        // bindings to implement JS proxies.
        RegisterBindings::RegisterProxyHandlers();

        // Use this thread pool to load-balance simple tasks, such as
        // image decoding.
        let shared_task_pool = TaskPool::new(8);

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            WindowMethods::create_compositor_channel(&window);
        let time_profiler_chan = time::Profiler::create(opts.time_profiler_period);
        let mem_profiler_chan = mem::Profiler::create(opts.mem_profiler_period);
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        // Create a Servo instance.
        let resource_task = new_resource_task(opts.user_agent.clone(), devtools_chan.clone());

        let image_cache_task = new_image_cache_task(resource_task.clone());
        let font_cache_task = FontCacheTask::new(resource_task.clone());
        let storage_task = StorageTaskFactory::new();

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let constellation_chan = Constellation::<layout::layout_task::LayoutTask,
                                                 script::script_task::ScriptTask>::start(
                                                      compositor_proxy.clone_compositor_proxy(),
                                                      resource_task,
                                                      image_cache_task,
                                                      font_cache_task,
                                                      time_profiler_chan.clone(),
                                                      mem_profiler_chan.clone(),
                                                      devtools_chan,
                                                      storage_task);

        let ConstellationChan(ref chan) = constellation_chan;
        chan.send(ConstellationMsg::InitLoadUrl(opts.url.clone())).unwrap();

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = CompositorTask::create(window,
                                                compositor_proxy,
                                                compositor_receiver,
                                                constellation_chan.clone(),
                                                time_profiler_chan,
                                                mem_profiler_chan);

        Browser {
            compositor: compositor,
        }
    }

    pub fn handle_event(&mut self, event: WindowEvent) -> bool {
        self.compositor.handle_event(event)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn get_title_for_main_frame(&self) {
        self.compositor.get_title_for_main_frame()
    }

    pub fn shutdown(mut self) {
        self.compositor.shutdown();
    }
}
<commit_msg>Remove lib.rs from gonk port. Unused<commit_after><|endoftext|>


<commit_before>use super::*;
use super::super::packets::{self, Packet};
use std::net;

#[derive(Debug, Copy, Clone)]
pub enum ConnectionState {
    Establishing{listening: bool, compatible_version: bool, attempts: u32},
    Established,
    Closed,
}

#[derive(Debug)]
pub struct Connection {
    pub state: ConnectionState,
    pub local_id: u64,
    pub remote_id: u64,
    pub address: net::SocketAddr,
    pub received_packets: u64,
    pub sent_packets: u64,
    pub heartbeat_counter: u64,
}

const MAX_STATUS_ATTEMPTS: u32 = 10;
const MAX_CONNECTION_ATTEMPTS:u32 = 25; //5000 ms divided by 200 ms per attempt, see spec.

impl Connection {

    pub fn new(address: net::SocketAddr, local_id: u64)->Connection {
        Connection {
            state: ConnectionState::Closed,
            local_id: local_id,
            remote_id: 0,
            address: address,
            sent_packets: 0,
            received_packets: 0,
            heartbeat_counter: 0,
        }
    }

    pub fn send<T: PacketSender>(&mut self, packet: &packets::Packet, sender: &mut T)->bool {
        self.sent_packets += 1;
        sender.send(packet, self.address)
    }

    pub fn handle_incoming_packet<T: PacketSender>(&mut self, packet: &packets::Packet, sender: &mut T)->bool {
        self.received_packets += 1; //Always.
        match *packet {
            Packet::Echo(id) => {
                self.send(packet, sender);
                true
            },
            Packet::Heartbeat{counter: c, sent: s, received: r} => {
                true
            },
            _ => false
        }
    }

    pub fn tick1000<T: PacketSender>(&mut self, sender: &mut T) {
        let heartbeat = packets::Packet::Heartbeat{counter: self.heartbeat_counter, sent: self.sent_packets, received: self.received_packets};
        self.send(&heartbeat, sender);
    }

    pub fn tick200<T: PacketSender>(&mut self, sender: &mut T) {
        match self.state {
            ConnectionState::Establishing{mut attempts, listening, compatible_version} => {
                attempts += 1;
                if listening == false {
                    if attempts > MAX_STATUS_ATTEMPTS {
                        self.state = ConnectionState::Closed;
                        return;
                    }
                    sender.send(&packets::Packet::StatusRequest(packets::StatusRequest::FastnetQuery), self.address);
                }
                else if compatible_version == false {
                    if attempts > MAX_STATUS_ATTEMPTS {
                        self.state = ConnectionState::Closed;
                        return;
                    }
                    sender.send(&packets::Packet::StatusRequest(packets::StatusRequest::VersionQuery), self.address);
                }
                else {
                    if attempts > MAX_CONNECTION_ATTEMPTS {
                        self.state = ConnectionState::Closed;
                        return;
                    }
                    sender.send(&packets::Packet::Connect, self.address);
                }
            },
            _ => {},
        }
    }
}
<commit_msg>We actually have to increment the heartbeat  counter.<commit_after>use super::*;
use super::super::packets::{self, Packet};
use std::net;

#[derive(Debug, Copy, Clone)]
pub enum ConnectionState {
    Establishing{listening: bool, compatible_version: bool, attempts: u32},
    Established,
    Closed,
}

#[derive(Debug)]
pub struct Connection {
    pub state: ConnectionState,
    pub local_id: u64,
    pub remote_id: u64,
    pub address: net::SocketAddr,
    pub received_packets: u64,
    pub sent_packets: u64,
    pub heartbeat_counter: u64,
}

const MAX_STATUS_ATTEMPTS: u32 = 10;
const MAX_CONNECTION_ATTEMPTS:u32 = 25; //5000 ms divided by 200 ms per attempt, see spec.

impl Connection {

    pub fn new(address: net::SocketAddr, local_id: u64)->Connection {
        Connection {
            state: ConnectionState::Closed,
            local_id: local_id,
            remote_id: 0,
            address: address,
            sent_packets: 0,
            received_packets: 0,
            heartbeat_counter: 0,
        }
    }

    pub fn send<T: PacketSender>(&mut self, packet: &packets::Packet, sender: &mut T)->bool {
        self.sent_packets += 1;
        sender.send(packet, self.address)
    }

    pub fn handle_incoming_packet<T: PacketSender>(&mut self, packet: &packets::Packet, sender: &mut T)->bool {
        self.received_packets += 1; //Always.
        match *packet {
            Packet::Echo(id) => {
                self.send(packet, sender);
                true
            },
            Packet::Heartbeat{counter: c, sent: s, received: r} => {
                true
            },
            _ => false
        }
    }

    pub fn tick1000<T: PacketSender>(&mut self, sender: &mut T) {
        let heartbeat = packets::Packet::Heartbeat{counter: self.heartbeat_counter, sent: self.sent_packets, received: self.received_packets};
        self.heartbeat_counter += 1;
        self.send(&heartbeat, sender);
    }

    pub fn tick200<T: PacketSender>(&mut self, sender: &mut T) {
        match self.state {
            ConnectionState::Establishing{mut attempts, listening, compatible_version} => {
                attempts += 1;
                if listening == false {
                    if attempts > MAX_STATUS_ATTEMPTS {
                        self.state = ConnectionState::Closed;
                        return;
                    }
                    sender.send(&packets::Packet::StatusRequest(packets::StatusRequest::FastnetQuery), self.address);
                }
                else if compatible_version == false {
                    if attempts > MAX_STATUS_ATTEMPTS {
                        self.state = ConnectionState::Closed;
                        return;
                    }
                    sender.send(&packets::Packet::StatusRequest(packets::StatusRequest::VersionQuery), self.address);
                }
                else {
                    if attempts > MAX_CONNECTION_ATTEMPTS {
                        self.state = ConnectionState::Closed;
                        return;
                    }
                    sender.send(&packets::Packet::Connect, self.address);
                }
            },
            _ => {},
        }
    }
}
<|endoftext|>


<commit_before>use crate::config::{Config, EnvConfig};
use crate::pull_request::{find_page, Page};
use crate::test::helper;
use std::fs;
use std::path::Path;

macro_rules! env {
    () => {{
        let mut e = helper::empty_env();
        e.github_token = skip_if_no_token!();
        e.https_proxy = helper::https_proxy();
        e
    }};
}

fn config(branch: Option<&str>, env: EnvConfig) -> Config {
    let mut dir = std::env::current_dir().unwrap();
    dir.push(Path::new(".git"));
    let dir = fs::canonicalize(dir).unwrap();
    Config {
        repo: "dummy url not used".to_string(),
        branch: branch.map(|s| s.to_string()),
        git_dir: Some(dir),
        args: vec![],        // Unused
        stdout: false,       // Unused
        pull_request: false, // Unused
        website: false,      // Unused
        blame: false,        // Unused
        env,
    }
}

#[test]
fn test_find_pr_within_orig_repo() {
    let cfg = config(Some("async-eventloop"), env!());
    let page = find_page("api.github.com", "rhysd", "vim.wasm", &cfg).unwrap();
    assert_eq!(
        page,
        Page::Existing {
            url: "https://github.com/rhysd/vim.wasm/pull/10".to_string(),
        },
    );
}

#[test]
fn test_find_pr_from_fork_repo_url() {
    let cfg = config(Some("async-contextual-keyword"), env!());
    let page = find_page("api.github.com", "rhysd", "rust.vim", &cfg).unwrap();
    assert_eq!(
        page,
        Page::Existing {
            url: "https://github.com/rust-lang/rust.vim/pull/290".to_string(),
        },
    );
}

#[test]
fn test_find_pr_from_original_repo_url() {
    let cfg = config(Some("async-contextual-keyword"), env!());
    let page = find_page("api.github.com", "rust-lang", "rust.vim", &cfg).unwrap();
    assert_eq!(
        page,
        Page::Existing {
            url: "https://github.com/rust-lang/rust.vim/pull/290".to_string(),
        },
    );
}

#[test]
fn test_no_pr_found_at_own_repo() {
    let cfg = config(Some("unknown-branch-which-does-not-exist-for-test"), env!());
    match find_page("api.github.com", "rhysd", "git-brws", &cfg).unwrap() {
        Page::New {
            author,
            repo,
            default_branch,
            branch,
        } => {
            assert_eq!(author, "rhysd");
            assert_eq!(repo, "git-brws");
            assert_eq!(default_branch, "master");
            assert_eq!(branch, "unknown-branch-which-does-not-exist-for-test")
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn test_no_pr_found_at_parent_repo() {
    let cfg = config(Some("unknown-branch-which-does-not-exist-for-test"), env!());
    match find_page("api.github.com", "rhysd", "rust.vim", &cfg).unwrap() {
        Page::New {
            author,
            repo,
            default_branch,
            branch,
        } => {
            assert_eq!(author, "rust-lang");
            assert_eq!(repo, "rust.vim");
            assert_eq!(default_branch, "master");
            assert_eq!(branch, "unknown-branch-which-does-not-exist-for-test")
        }
        p => assert!(false, "{:?}", p),
    }
}
<commit_msg>add test case for pull_request::Page::NewAtParent<commit_after>use crate::config::{Config, EnvConfig};
use crate::pull_request::{find_page, Page};
use crate::test::helper;
use std::fs;
use std::path::Path;

macro_rules! env {
    () => {{
        let mut e = helper::empty_env();
        e.github_token = skip_if_no_token!();
        e.https_proxy = helper::https_proxy();
        e
    }};
}

fn config(branch: Option<&str>, env: EnvConfig) -> Config {
    let mut dir = std::env::current_dir().unwrap();
    dir.push(Path::new(".git"));
    let dir = fs::canonicalize(dir).unwrap();
    Config {
        repo: "dummy url not used".to_string(),
        branch: branch.map(|s| s.to_string()),
        git_dir: Some(dir),
        args: vec![],        // Unused
        stdout: false,       // Unused
        pull_request: false, // Unused
        website: false,      // Unused
        blame: false,        // Unused
        env,
    }
}

#[test]
fn test_find_pr_within_orig_repo() {
    let cfg = config(Some("async-eventloop"), env!());
    let page = find_page("api.github.com", "rhysd", "vim.wasm", &cfg).unwrap();
    assert_eq!(
        page,
        Page::Existing {
            url: "https://github.com/rhysd/vim.wasm/pull/10".to_string(),
        },
    );
}

#[test]
fn test_find_pr_from_fork_repo_url() {
    let cfg = config(Some("async-contextual-keyword"), env!());
    let page = find_page("api.github.com", "rhysd", "rust.vim", &cfg).unwrap();
    assert_eq!(
        page,
        Page::Existing {
            url: "https://github.com/rust-lang/rust.vim/pull/290".to_string(),
        },
    );
}

#[test]
fn test_find_pr_from_original_repo_url() {
    let cfg = config(Some("async-contextual-keyword"), env!());
    let page = find_page("api.github.com", "rust-lang", "rust.vim", &cfg).unwrap();
    assert_eq!(
        page,
        Page::Existing {
            url: "https://github.com/rust-lang/rust.vim/pull/290".to_string(),
        },
    );
}

#[test]
fn test_no_pr_found_at_own_repo() {
    let cfg = config(Some("unknown-branch-which-does-not-exist-for-test"), env!());
    match find_page("api.github.com", "rhysd", "git-brws", &cfg).unwrap() {
        Page::New {
            author,
            repo,
            default_branch,
            branch,
        } => {
            assert_eq!(author, "rhysd");
            assert_eq!(repo, "git-brws");
            assert_eq!(default_branch, "master");
            assert_eq!(branch, "unknown-branch-which-does-not-exist-for-test")
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn test_no_pr_found_at_parent_repo() {
    let cfg = config(Some("unknown-branch-which-does-not-exist-for-test"), env!());
    match find_page("api.github.com", "rhysd", "rust.vim", &cfg).unwrap() {
        Page::New {
            author,
            repo,
            default_branch,
            branch,
        } => {
            assert_eq!(author, "rust-lang");
            assert_eq!(repo, "rust.vim");
            assert_eq!(default_branch, "master");
            assert_eq!(branch, "unknown-branch-which-does-not-exist-for-test")
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn test_find_pr_new_pr_from_fork() {
    let cfg = config(Some("unknown-branch-which-does-not-exist-for-test"), env!());
    match find_page("api.github.com", "rhysd", "rust.vim", &cfg).unwrap() {
        Page::NewAtParent {
            author,
            repo,
            fork_author,
            default_branch,
            branch,
        } => {
            assert_eq!(author, "rust-lang");
            assert_eq!(repo, "rust.vim");
            assert_eq!(default_branch, "master");
            assert_eq!(branch, "unknown-branch-which-does-not-exist-for-test");
            assert_eq!(fork_author, "rhysd");
        }
        p => assert!(false, "{:?}", p),
    }
}
<|endoftext|>


<commit_before>mod card {
    use std::fmt;
    use std::iter::AdditiveIterator;
    use std::ops::Add;
    use std::slice;

    pub fn score(hand: Vec<Card>, start_card: Card) -> i32 {
        let mut with_start = hand.clone();
        with_start.push(start_card);
        2 * count_15s(&with_start)
            + 2 * count_pairs(&with_start)
            + score_runs(&with_start)
    }

    fn count_15s(cards: &Vec<Card>) -> i32 {
        power_set(&mut cards.iter()).iter()
            .filter(|ref x| x.iter().map(|ref x| x.rank.value()).sum() == 15)
            .count() as i32
    }

    fn count_pairs(cards: &Vec<Card>) -> i32 {
        power_set(&mut cards.iter()).iter()
            .filter(|&x| x.len() == 2)
            .filter(|ref x| x[0].rank == x[1].rank)
            .count() as i32
    }

    fn score_runs(cards: &Vec<Card>) -> i32 {
        let mut cpy = cards.clone();
        cpy.sort();
        let mut runs = Vec::new();
        let mut run: Vec<Card> = Vec::new();
        for card in cpy.iter() {
            if run.len() == 0 || run[run.len()-1].clone().rank + Rank(1) == card.rank {
                run.push(card.clone())
            } else {
                runs.push(run);
                run = Vec::new();
                run.push(card.clone());
            }
        }
        runs.push(run);
        runs.iter()
            .map(|x| x.len() as i32)
            .sum()
    }

    fn power_set<'a, T: Clone + 'a>(items: &mut slice::Iter<'a,T>) -> Vec<Vec<T>> {
        let mut power = Vec::new();
        match items.next() {
            None       => power.push(Vec::new()),
            Some(item) => {
                for mut set in power_set(items).into_iter() {
                    power.push(set.clone());
                    set.push(item.clone());
                    power.push(set);
                }
            }
        }
        power
    }

    #[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
    struct Card {
        rank: Rank,
        suit: Suit,
    }

    pub fn new(rank: i32, suit: char) -> Card {
        Card{rank: Rank(rank), suit: from_char(suit)}
    }

    impl fmt::Show for Card {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}{}", self.rank, self.suit)
        }
    }

    #[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
    enum Suit {
        Spades,
        Hearts,
        Clubs,
        Diamonds,
    }

    impl Suit {
        pub fn to_char(&self) -> char {
            match *self {
                Suit::Spades   => 'S',
                Suit::Hearts   => 'H',
                Suit::Diamonds => 'D',
                Suit::Clubs    => 'C',
            }
        }
    }

    impl fmt::Show for Suit {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                    write!(f, "{}", self.to_char())
        }
    }

    pub fn from_char(a: char) -> Suit {
        match a {
            'S'=> Suit::Spades,
            'H'=> Suit::Hearts,
            'C'=> Suit::Clubs,
            'D'=> Suit::Diamonds,
            _  => panic!("Not a suit")
        }
    }

    #[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
    struct Rank(i32);

    impl fmt::Show for Rank {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let value = match *self {
                Rank(13) => "K".to_string(),
                Rank(12) => "Q".to_string(),
                Rank(11) => "J".to_string(),
                Rank(aa) => aa.to_string(),
            };
            write!(f, "{}", value)
        }
    }

    impl Rank {
        fn value(&self) -> i32 {
            let Rank(x) = *self;
            if x > 10 {
                10
            } else {
                x
            }
        }
    }

    impl Add for Rank {
        type Output = Rank;

        fn add(self, rhs: Rank) -> Rank {
            let (Rank(lh), Rank(rh)) = (self, rhs);
            Rank(lh + rh)
        }
    }

}

fn main() {
    let cards = vec![
        card::new(12, 'H'),
        card::new(11, 'S'),
        card::new(10, 'S'),
        card::new(5, 'C'),
        ];
    println!("{}", card::score(cards, card::new(4, 'D')));
}
<commit_msg>Fix run<commit_after>mod card {
    use std::fmt;
    use std::iter::AdditiveIterator;
    use std::ops::Add;
    use std::slice;

    pub fn score(hand: Vec<Card>, start_card: Card) -> i32 {
        let mut with_start = hand.clone();
        with_start.push(start_card);
        2 * count_15s(&with_start)
            + 2 * count_pairs(&with_start)
            + score_runs(&with_start)
    }

    fn count_15s(cards: &Vec<Card>) -> i32 {
        power_set(&mut cards.iter()).iter()
            .filter(|ref x| x.iter().map(|ref x| x.rank.value()).sum() == 15)
            .count() as i32
    }

    fn count_pairs(cards: &Vec<Card>) -> i32 {
        power_set(&mut cards.iter()).iter()
            .filter(|&x| x.len() == 2)
            .filter(|ref x| x[0].rank == x[1].rank)
            .count() as i32
    }

    fn score_runs(cards: &Vec<Card>) -> i32 {
        let mut cpy = cards.clone();
        cpy.sort();
        let mut runs = Vec::new();
        let mut run: Vec<Card> = Vec::new();
        for card in cpy.iter() {
            //println!("Looking at {} and {}", card, run);
            if run.len() == 0 || run[run.len()-1].clone().rank + Rank(1) == card.rank {
                run.push(card.clone())
            } else {
                if run.len() > 1 {
                    runs.push(run);
                }
                run = Vec::new();
                run.push(card.clone());
            }
        }
        if run.len() > 1 {
            runs.push(run);
        }
        runs.iter()
            .map(|x| x.len() as i32)
            .sum()
    }

    fn power_set<'a, T: Clone + 'a>(items: &mut slice::Iter<'a,T>) -> Vec<Vec<T>> {
        let mut power = Vec::new();
        match items.next() {
            None       => power.push(Vec::new()),
            Some(item) => {
                for mut set in power_set(items).into_iter() {
                    power.push(set.clone());
                    set.push(item.clone());
                    power.push(set);
                }
            }
        }
        power
    }

    #[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
    struct Card {
        rank: Rank,
        suit: Suit,
    }

    pub fn new(rank: i32, suit: char) -> Card {
        Card{rank: Rank(rank), suit: from_char(suit)}
    }

    impl fmt::Show for Card {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}{}", self.rank, self.suit)
        }
    }

    #[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
    enum Suit {
        Spades,
        Hearts,
        Clubs,
        Diamonds,
    }

    impl Suit {
        pub fn to_char(&self) -> char {
            match *self {
                Suit::Spades   => 'S',
                Suit::Hearts   => 'H',
                Suit::Diamonds => 'D',
                Suit::Clubs    => 'C',
            }
        }
    }

    impl fmt::Show for Suit {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                    write!(f, "{}", self.to_char())
        }
    }

    pub fn from_char(a: char) -> Suit {
        match a {
            'S'=> Suit::Spades,
            'H'=> Suit::Hearts,
            'C'=> Suit::Clubs,
            'D'=> Suit::Diamonds,
            _  => panic!("Not a suit")
        }
    }

    #[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
    struct Rank(i32);

    impl fmt::Show for Rank {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let value = match *self {
                Rank(13) => "K".to_string(),
                Rank(12) => "Q".to_string(),
                Rank(11) => "J".to_string(),
                Rank(aa) => aa.to_string(),
            };
            write!(f, "{}", value)
        }
    }

    impl Rank {
        fn value(&self) -> i32 {
            let Rank(x) = *self;
            if x > 10 {
                10
            } else {
                x
            }
        }
    }

    impl Add for Rank {
        type Output = Rank;

        fn add(self, rhs: Rank) -> Rank {
            let (Rank(lh), Rank(rh)) = (self, rhs);
            Rank(lh + rh)
        }
    }

}

fn main() {
    let cards = vec![
        card::new(12, 'H'),
        card::new(11, 'S'),
        card::new(10, 'S'),
        card::new(5, 'C'),
        ];
    println!("{}", card::score(cards, card::new(4, 'D')));
}
<|endoftext|>


<commit_before>pub mod ofints;
pub mod ofrects;
<commit_msg>Rust: more Iterator<Rect> examples.<commit_after>pub mod ofints;
pub mod ofrects;
pub mod linq;
<|endoftext|>


<commit_before>// Copyright 2017 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Read;
use std::sync::{Arc, RwLock};
use std::thread;

use iron::prelude::*;
use iron::Handler;
use iron::status;
use urlencoded::UrlEncodedQuery;
use serde::Serialize;
use serde_json;

use chain;
use core::core::Transaction;
use core::ser;
use pool;
use p2p;
use rest::*;
use util::secp::pedersen::Commitment;
use types::*;
use util;
use util::LOGGER;

// Supports retrieval of multiple outputs in a single request -
// GET /v1/chain/utxos?id=xxx,yyy,zzz
// GET /v1/chain/utxos?id=xxx&id=yyy&id=zzz
struct UtxoHandler {
	chain: Arc<chain::Chain>,
}

impl UtxoHandler {
	fn get_utxo(&self, id: &str) -> Result<Output, Error> {
		debug!(LOGGER, "getting utxo: {}", id);
		let c = util::from_hex(String::from(id)).map_err(|_| {
			Error::Argument(format!("Not a valid commitment: {}", id))
		})?;
		let commit = Commitment::from_vec(c);

		let out = self.chain
			.get_unspent(&commit)
			.map_err(|_| Error::NotFound)?;

		let header = self.chain
			.get_block_header_by_output_commit(&commit)
			.map_err(|_| Error::NotFound)?;

		Ok(Output::from_output(&out, &header, false))
	}
}

impl Handler for UtxoHandler {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let mut commitments: Vec<&str> = vec![];
		if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
			if let Some(ids) = params.get("id") {
				for id in ids {
					for id in id.split(",") {
						commitments.push(id.clone());
					}
				}
			}
		}

		let mut utxos: Vec<Output> = vec![];
		for commit in commitments {
			if let Ok(out) = self.get_utxo(commit) {
				utxos.push(out);
			}
		}
		json_response(&utxos)
	}
}

// Sum tree handler. Retrieve the roots:
// GET /v1/sumtrees/roots
//
// Last inserted nodes::
// GET /v1/sumtrees/lastutxos (gets last 10)
// GET /v1/sumtrees/lastutxos?n=5
// GET /v1/sumtrees/lastrangeproofs
// GET /v1/sumtrees/lastkernels
struct SumTreeHandler {
	chain: Arc<chain::Chain>,
}

impl SumTreeHandler {
	// gets roots
	fn get_roots(&self) -> SumTrees {
		SumTrees::from_head(self.chain.clone())
	}

	// gets last n utxos inserted in to the tree
	fn get_last_n_utxo(&self, distance: u64) -> Vec<SumTreeNode> {
		SumTreeNode::get_last_n_utxo(self.chain.clone(), distance)
	}

	// gets last n utxos inserted in to the tree
	fn get_last_n_rangeproof(&self, distance: u64) -> Vec<SumTreeNode> {
		SumTreeNode::get_last_n_rangeproof(self.chain.clone(), distance)
	}

	// gets last n utxos inserted in to the tree
	fn get_last_n_kernel(&self, distance: u64) -> Vec<SumTreeNode> {
		SumTreeNode::get_last_n_kernel(self.chain.clone(), distance)
	}
}

impl Handler for SumTreeHandler {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let url = req.url.clone();
		let mut path_elems = url.path();
		if *path_elems.last().unwrap() == "" {
			path_elems.pop();
		}
		// TODO: probably need to set a reasonable max limit here
		let mut last_n = 10;
		if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
			if let Some(nums) = params.get("n") {
				for num in nums {
					if let Ok(n) = str::parse(num) {
						last_n = n;
					}
				}
			}
		}
		match *path_elems.last().unwrap() {
			"roots" => json_response_pretty(&self.get_roots()),
			"lastutxos" => json_response_pretty(&self.get_last_n_utxo(last_n)),
			"lastrangeproofs" => json_response_pretty(&self.get_last_n_rangeproof(last_n)),
			"lastkernels" => json_response_pretty(&self.get_last_n_kernel(last_n)),
			_ => Ok(Response::with((status::BadRequest, ""))),
		}
	}
}

pub struct PeersAllHandler {
	pub peer_store: Arc<p2p::PeerStore>,
}

impl Handler for PeersAllHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		let peers = &self.peer_store.all_peers();
		json_response_pretty(&peers)
	}
}

pub struct PeersConnectedHandler {
	pub p2p_server: Arc<p2p::Server>,
}

impl Handler for PeersConnectedHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		let mut peers = vec![];
		for p in &self.p2p_server.all_peers() {
			let peer_info = p.info.clone();
			peers.push(peer_info);
		}
		json_response(&peers)
	}
}

// Chain handler. Get the head details.
// GET /v1/chain
pub struct ChainHandler {
	pub chain: Arc<chain::Chain>,
}

impl ChainHandler {
	fn get_tip(&self) -> Tip {
		Tip::from_tip(self.chain.head().unwrap())
	}
}

impl Handler for ChainHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		json_response(&self.get_tip())
	}
}

// Get basic information about the transaction pool.
struct PoolInfoHandler<T> {
	tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
}

impl<T> Handler for PoolInfoHandler<T>
where
	T: pool::BlockChain + Send + Sync + 'static,
{
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		let pool = self.tx_pool.read().unwrap();
		json_response(&PoolInfo {
			pool_size: pool.pool_size(),
			orphans_size: pool.orphans_size(),
			total_size: pool.total_size(),
		})
	}
}

/// Dummy wrapper for the hex-encoded serialized transaction.
#[derive(Serialize, Deserialize)]
struct TxWrapper {
	tx_hex: String,
}

// Push new transactions to our transaction pool, that should broadcast it
// to the network if valid.
struct PoolPushHandler<T> {
	tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
}

impl<T> Handler for PoolPushHandler<T>
where
	T: pool::BlockChain + Send + Sync + 'static,
{
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let wrapper: TxWrapper = serde_json::from_reader(req.body.by_ref())
			.map_err(|e| IronError::new(e, status::BadRequest))?;

		let tx_bin = util::from_hex(wrapper.tx_hex).map_err(|_| {
			Error::Argument(format!("Invalid hex in transaction wrapper."))
		})?;

		let tx: Transaction = ser::deserialize(&mut &tx_bin[..]).map_err(|_| {
			Error::Argument("Could not deserialize transaction, invalid format.".to_string())
		})?;

		let source = pool::TxSource {
			debug_name: "push-api".to_string(),
			identifier: "?.?.?.?".to_string(),
		};
		info!(
			LOGGER,
			"Pushing transaction with {} inputs and {} outputs to pool.",
			tx.inputs.len(),
			tx.outputs.len()
		);
		self.tx_pool
			.write()
			.unwrap()
			.add_to_memory_pool(source, tx)
			.map_err(|e| {
				Error::Internal(format!("Addition to transaction pool failed: {:?}", e))
			})?;

		Ok(Response::with(status::Ok))
	}
}

// Utility to serialize a struct into JSON and produce a sensible IronResult
// out of it.
fn json_response<T>(s: &T) -> IronResult<Response>
where
	T: Serialize,
{
	match serde_json::to_string(s) {
		Ok(json) => Ok(Response::with((status::Ok, json))),
		Err(_) => Ok(Response::with((status::InternalServerError, ""))),
	}
}

// pretty-printed version of above
fn json_response_pretty<T>(s: &T) -> IronResult<Response>
where
	T: Serialize,
{
	match serde_json::to_string_pretty(s) {
		Ok(json) => Ok(Response::with((status::Ok, json))),
		Err(_) => Ok(Response::with((status::InternalServerError, ""))),
	}
}
/// Start all server HTTP handlers. Register all of them with Iron
/// and runs the corresponding HTTP server.
pub fn start_rest_apis<T>(
	addr: String,
	chain: Arc<chain::Chain>,
	tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
	p2p_server: Arc<p2p::Server>,
	peer_store: Arc<p2p::PeerStore>,
) where
	T: pool::BlockChain + Send + Sync + 'static,
{
	thread::spawn(move || {
		// build handlers and register them under the appropriate endpoint
		let utxo_handler = UtxoHandler {
			chain: chain.clone(),
		};
		let chain_tip_handler = ChainHandler {
			chain: chain.clone(),
		};
		let sumtree_handler = SumTreeHandler {
			chain: chain.clone(),
		};
		let pool_info_handler = PoolInfoHandler {
			tx_pool: tx_pool.clone(),
		};
		let pool_push_handler = PoolPushHandler {
			tx_pool: tx_pool.clone(),
		};
		let peers_all_handler = PeersAllHandler {
			peer_store: peer_store.clone(),
		};
		let peers_connected_handler = PeersConnectedHandler {
			p2p_server: p2p_server.clone(),
		};

		let router = router!(
			chain_tip: get "/chain" => chain_tip_handler,
			chain_utxos: get "/chain/utxos" => utxo_handler,
			sumtree_roots: get "/sumtrees/*" => sumtree_handler,
			pool_info: get "/pool" => pool_info_handler,
			pool_push: post "/pool/push" => pool_push_handler,
			peers_all: get "/peers/all" => peers_all_handler,
			peers_connected: get "/peers/connected" => peers_connected_handler,
		);

		let mut apis = ApiServer::new("/v1".to_string());
		apis.register_handler(router);

		info!(LOGGER, "Starting HTTP API server at {}.", addr);
		apis.start(&addr[..]).unwrap_or_else(|e| {
			error!(LOGGER, "Failed to start API HTTP server: {}.", e);
		});
	});
}
<commit_msg>Quick placeholder api route index (#286) (#324)<commit_after>// Copyright 2017 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Read;
use std::sync::{Arc, RwLock};
use std::thread;

use iron::prelude::*;
use iron::Handler;
use iron::status;
use urlencoded::UrlEncodedQuery;
use serde::Serialize;
use serde_json;

use chain;
use core::core::Transaction;
use core::ser;
use pool;
use p2p;
use rest::*;
use util::secp::pedersen::Commitment;
use types::*;
use util;
use util::LOGGER;

// RESTful index of available api endpoints
// GET /v1/
struct IndexHandler {
	list: Vec<String>,
}

impl IndexHandler {
}

impl Handler for IndexHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		json_response_pretty(&self.list)
	}
}

// Supports retrieval of multiple outputs in a single request -
// GET /v1/chain/utxos?id=xxx,yyy,zzz
// GET /v1/chain/utxos?id=xxx&id=yyy&id=zzz
struct UtxoHandler {
	chain: Arc<chain::Chain>,
}

impl UtxoHandler {
	fn get_utxo(&self, id: &str) -> Result<Output, Error> {
		debug!(LOGGER, "getting utxo: {}", id);
		let c = util::from_hex(String::from(id)).map_err(|_| {
			Error::Argument(format!("Not a valid commitment: {}", id))
		})?;
		let commit = Commitment::from_vec(c);

		let out = self.chain
			.get_unspent(&commit)
			.map_err(|_| Error::NotFound)?;

		let header = self.chain
			.get_block_header_by_output_commit(&commit)
			.map_err(|_| Error::NotFound)?;

		Ok(Output::from_output(&out, &header, false))
	}
}

impl Handler for UtxoHandler {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let mut commitments: Vec<&str> = vec![];
		if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
			if let Some(ids) = params.get("id") {
				for id in ids {
					for id in id.split(",") {
						commitments.push(id.clone());
					}
				}
			}
		}

		let mut utxos: Vec<Output> = vec![];
		for commit in commitments {
			if let Ok(out) = self.get_utxo(commit) {
				utxos.push(out);
			}
		}
		json_response(&utxos)
	}
}

// Sum tree handler. Retrieve the roots:
// GET /v1/sumtrees/roots
//
// Last inserted nodes::
// GET /v1/sumtrees/lastutxos (gets last 10)
// GET /v1/sumtrees/lastutxos?n=5
// GET /v1/sumtrees/lastrangeproofs
// GET /v1/sumtrees/lastkernels
struct SumTreeHandler {
	chain: Arc<chain::Chain>,
}

impl SumTreeHandler {
	// gets roots
	fn get_roots(&self) -> SumTrees {
		SumTrees::from_head(self.chain.clone())
	}

	// gets last n utxos inserted in to the tree
	fn get_last_n_utxo(&self, distance: u64) -> Vec<SumTreeNode> {
		SumTreeNode::get_last_n_utxo(self.chain.clone(), distance)
	}

	// gets last n utxos inserted in to the tree
	fn get_last_n_rangeproof(&self, distance: u64) -> Vec<SumTreeNode> {
		SumTreeNode::get_last_n_rangeproof(self.chain.clone(), distance)
	}

	// gets last n utxos inserted in to the tree
	fn get_last_n_kernel(&self, distance: u64) -> Vec<SumTreeNode> {
		SumTreeNode::get_last_n_kernel(self.chain.clone(), distance)
	}
}

impl Handler for SumTreeHandler {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let url = req.url.clone();
		let mut path_elems = url.path();
		if *path_elems.last().unwrap() == "" {
			path_elems.pop();
		}
		// TODO: probably need to set a reasonable max limit here
		let mut last_n = 10;
		if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
			if let Some(nums) = params.get("n") {
				for num in nums {
					if let Ok(n) = str::parse(num) {
						last_n = n;
					}
				}
			}
		}
		match *path_elems.last().unwrap() {
			"roots" => json_response_pretty(&self.get_roots()),
			"lastutxos" => json_response_pretty(&self.get_last_n_utxo(last_n)),
			"lastrangeproofs" => json_response_pretty(&self.get_last_n_rangeproof(last_n)),
			"lastkernels" => json_response_pretty(&self.get_last_n_kernel(last_n)),
			_ => Ok(Response::with((status::BadRequest, ""))),
		}
	}
}

pub struct PeersAllHandler {
	pub peer_store: Arc<p2p::PeerStore>,
}

impl Handler for PeersAllHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		let peers = &self.peer_store.all_peers();
		json_response_pretty(&peers)
	}
}

pub struct PeersConnectedHandler {
	pub p2p_server: Arc<p2p::Server>,
}

impl Handler for PeersConnectedHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		let mut peers = vec![];
		for p in &self.p2p_server.all_peers() {
			let peer_info = p.info.clone();
			peers.push(peer_info);
		}
		json_response(&peers)
	}
}

// Chain handler. Get the head details.
// GET /v1/chain
pub struct ChainHandler {
	pub chain: Arc<chain::Chain>,
}

impl ChainHandler {
	fn get_tip(&self) -> Tip {
		Tip::from_tip(self.chain.head().unwrap())
	}
}

impl Handler for ChainHandler {
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		json_response(&self.get_tip())
	}
}

// Get basic information about the transaction pool.
struct PoolInfoHandler<T> {
	tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
}

impl<T> Handler for PoolInfoHandler<T>
where
	T: pool::BlockChain + Send + Sync + 'static,
{
	fn handle(&self, _req: &mut Request) -> IronResult<Response> {
		let pool = self.tx_pool.read().unwrap();
		json_response(&PoolInfo {
			pool_size: pool.pool_size(),
			orphans_size: pool.orphans_size(),
			total_size: pool.total_size(),
		})
	}
}

/// Dummy wrapper for the hex-encoded serialized transaction.
#[derive(Serialize, Deserialize)]
struct TxWrapper {
	tx_hex: String,
}

// Push new transactions to our transaction pool, that should broadcast it
// to the network if valid.
struct PoolPushHandler<T> {
	tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
}

impl<T> Handler for PoolPushHandler<T>
where
	T: pool::BlockChain + Send + Sync + 'static,
{
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let wrapper: TxWrapper = serde_json::from_reader(req.body.by_ref())
			.map_err(|e| IronError::new(e, status::BadRequest))?;

		let tx_bin = util::from_hex(wrapper.tx_hex).map_err(|_| {
			Error::Argument(format!("Invalid hex in transaction wrapper."))
		})?;

		let tx: Transaction = ser::deserialize(&mut &tx_bin[..]).map_err(|_| {
			Error::Argument("Could not deserialize transaction, invalid format.".to_string())
		})?;

		let source = pool::TxSource {
			debug_name: "push-api".to_string(),
			identifier: "?.?.?.?".to_string(),
		};
		info!(
			LOGGER,
			"Pushing transaction with {} inputs and {} outputs to pool.",
			tx.inputs.len(),
			tx.outputs.len()
		);
		self.tx_pool
			.write()
			.unwrap()
			.add_to_memory_pool(source, tx)
			.map_err(|e| {
				Error::Internal(format!("Addition to transaction pool failed: {:?}", e))
			})?;

		Ok(Response::with(status::Ok))
	}
}

// Utility to serialize a struct into JSON and produce a sensible IronResult
// out of it.
fn json_response<T>(s: &T) -> IronResult<Response>
where
	T: Serialize,
{
	match serde_json::to_string(s) {
		Ok(json) => Ok(Response::with((status::Ok, json))),
		Err(_) => Ok(Response::with((status::InternalServerError, ""))),
	}
}

// pretty-printed version of above
fn json_response_pretty<T>(s: &T) -> IronResult<Response>
where
	T: Serialize,
{
	match serde_json::to_string_pretty(s) {
		Ok(json) => Ok(Response::with((status::Ok, json))),
		Err(_) => Ok(Response::with((status::InternalServerError, ""))),
	}
}
/// Start all server HTTP handlers. Register all of them with Iron
/// and runs the corresponding HTTP server.
pub fn start_rest_apis<T>(
	addr: String,
	chain: Arc<chain::Chain>,
	tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
	p2p_server: Arc<p2p::Server>,
	peer_store: Arc<p2p::PeerStore>,
) where
	T: pool::BlockChain + Send + Sync + 'static,
{
	thread::spawn(move || {
		// build handlers and register them under the appropriate endpoint
		let utxo_handler = UtxoHandler {
			chain: chain.clone(),
		};
		let chain_tip_handler = ChainHandler {
			chain: chain.clone(),
		};
		let sumtree_handler = SumTreeHandler {
			chain: chain.clone(),
		};
		let pool_info_handler = PoolInfoHandler {
			tx_pool: tx_pool.clone(),
		};
		let pool_push_handler = PoolPushHandler {
			tx_pool: tx_pool.clone(),
		};
		let peers_all_handler = PeersAllHandler {
			peer_store: peer_store.clone(),
		};
		let peers_connected_handler = PeersConnectedHandler {
			p2p_server: p2p_server.clone(),
		};

		let route_list = vec!(
			"get /".to_string(),
			"get /chain".to_string(),
			"get /chain/utxos".to_string(),
			"get /sumtrees/roots".to_string(),
			"get /sumtrees/lastutxos?n=10".to_string(),
			"get /sumtrees/lastrangeproofs".to_string(),
			"get /sumtrees/lastkernels".to_string(),
			"get /pool".to_string(),
			"post /pool/push".to_string(),
			"get /peers/all".to_string(),
			"get /peers/connected".to_string(),
		);
		let index_handler = IndexHandler { list: route_list };
		let router = router!(
			index: get "/" => index_handler,
			chain_tip: get "/chain" => chain_tip_handler,
			chain_utxos: get "/chain/utxos" => utxo_handler,
			sumtree_roots: get "/sumtrees/*" => sumtree_handler,
			pool_info: get "/pool" => pool_info_handler,
			pool_push: post "/pool/push" => pool_push_handler,
			peers_all: get "/peers/all" => peers_all_handler,
			peers_connected: get "/peers/connected" => peers_connected_handler,
		);

		let mut apis = ApiServer::new("/v1".to_string());
		apis.register_handler(router);

		info!(LOGGER, "Starting HTTP API server at {}.", addr);
		apis.start(&addr[..]).unwrap_or_else(|e| {
			error!(LOGGER, "Failed to start API HTTP server: {}.", e);
		});
	});
}
<|endoftext|>


<commit_before>use super::test_ident_pb::*;

#[test]
fn test() {
    let _ = TestType::new();
}
<commit_msg>Better reflection test<commit_after>use super::test_ident_pb::*;
use protobuf::Message;

#[test]
fn test() {
    let _ = TestType::new();
}

#[test]
fn test_reflect() {
    message_Self::new();
    // instantiate reflection
    assert_eq!("Self", message_Self::descriptor_static().name());
}
<|endoftext|>


<commit_before>//! Insertion sort algorithm.
//!
//! The `insertion_sort` module contains the simple sorting algorithm "Insertion Sort".
//!
//! Source: https://en.wikipedia.org/wiki/Insertion_sort

use std::fmt::Debug;

/// Simple sort: insertion sort.
///
/// # Details
///
/// Insertion sort is one of the simplest sorting algorithm. Every elements of the list to sort
/// are picked one at a time and inserted into a the sorted list at the right location.
///
/// In the best case scenario, the list is already sorted and the work is simply to append the
/// element at the end of the list, hence a O(N) scaling.
///
/// In the worst case scenario, the list is in reverse order. Hence, every element is compared to
/// all previous elements already sorted until this new one gets inserted at the beginning of the
/// sorted list, thus making this case O(N^2).
///
/// Insertion sort is still useful as its simplicity (and thus small overhead) makes it ideal for
/// small vectors.
///
/// This function takes an immutable reference vector of any type and returns a vector of `usize`
/// of the same length containing the indices of the initial vector sorted.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// assert_eq!(vec![3, 1, 2, 0], sorting::simplesorts::insertion::sort(&data));
/// ```
///
pub fn sort<T: Debug + PartialOrd>(input: &Vec<T>) -> Vec<usize> {
    let n = input.len();

    let mut output_index: Vec<usize> = Vec::with_capacity(n);

    // Place the first element's index (0) as the first element of the output_index vector.
    output_index.push(0);

    println!("n = {:?}", n);
    println!("input:        {:?}", input);
    println!("output_index: {:?}", output_index);

    if n > 1 {
        // Loop over input vector, skipping the first element as it's already inserted as the first
        // element of output_index.
        for (i_start_at_0,elem) in input.iter().skip(1).enumerate() {
            let i = i_start_at_0 + 1;
            println!("  i={:?} Finding where to put element {:?} (i={:?}) in output_index: {:?}", i, elem, i, output_index);

            // Verify next element if it is smaller
            // If next element is larger than the last stored index, just place that element at
            // the end.
            if *elem >= input[*output_index.last().unwrap()] {
                // Next element "elem" is already larger than the previous stored index.
                // Just use its index.
                output_index.push(i);

                println!("    1. Pushing element '{:?}' at the end.", elem);
            } else {
                println!("    2. elem ?< input[output_index[0]]");
                println!("           {:?} ?< {:?}", elem, input[output_index[0]]);
                // The next element of the input vector is smaller than the last stored on.
                // Let's find where it should go by looping back on the stored indices.
                // NOTE: we must use a loop on indices as to prevent problems with the borrow-checker.
                if *elem < input[output_index[0]] {
                    println!("        2a. Yes: --> putting i in front");
                    output_index.insert(0, i);
                } else {
                    println!("        2b. No: --> Looping back");
                    for j in (0..i).rev() {
                        println!("            j={:?}   elem={:?}   input[output_index[j]]={:?}", j, elem, input[output_index[j]]);
                        if *elem > input[output_index[j]] {
                            output_index.insert(j+1, i);
                            break;
                        }
                    }
                }
                println!("        3. output_index: {:?}", output_index);
            }

            print!("           output: [");
            for j in output_index.iter() {
                    print!("{:?}, ", input[*j]);
            }
            println!("]");
        }
    }

    println!("");
    println!("END: input:        {:?}", input);
    println!("END: output_index: {:?}", output_index);
    print!("END: output:       [");
    for j in output_index.iter() {
            print!("{:?}, ", input[*j]);
    }
    println!("]");

    assert_eq!(output_index.len(), n);

    return output_index;
}
<commit_msg>Add note of PartialOrd to documentation.<commit_after>//! Insertion sort algorithm.
//!
//! The `insertion_sort` module contains the simple sorting algorithm "Insertion Sort".
//!
//! Source: https://en.wikipedia.org/wiki/Insertion_sort

use std::fmt::Debug;

/// Simple sort: insertion sort.
///
/// # Details
///
/// Insertion sort is one of the simplest sorting algorithm. Every elements of the list to sort
/// are picked one at a time and inserted into a the sorted list at the right location.
///
/// In the best case scenario, the list is already sorted and the work is simply to append the
/// element at the end of the list, hence a O(N) scaling.
///
/// In the worst case scenario, the list is in reverse order. Hence, every element is compared to
/// all previous elements already sorted until this new one gets inserted at the beginning of the
/// sorted list, thus making this case O(N^2).
///
/// Insertion sort is still useful as its simplicity (and thus small overhead) makes it ideal for
/// small vectors.
///
/// This function takes an immutable reference vector of any type and returns a vector of `usize`
/// of the same length containing the indices of the initial vector sorted.
///
/// # Notes
///
/// The type T of the vector elements to sort _must_ implement the `PartialOrd` trait so the
/// compiler knows how to compare the elements and sort them.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// assert_eq!(vec![3, 1, 2, 0], sorting::simplesorts::insertion::sort(&data));
/// ```
///
pub fn sort<T: Debug + PartialOrd>(input: &Vec<T>) -> Vec<usize> {
    let n = input.len();

    let mut output_index: Vec<usize> = Vec::with_capacity(n);

    // Place the first element's index (0) as the first element of the output_index vector.
    output_index.push(0);

    println!("n = {:?}", n);
    println!("input:        {:?}", input);
    println!("output_index: {:?}", output_index);

    if n > 1 {
        // Loop over input vector, skipping the first element as it's already inserted as the first
        // element of output_index.
        for (i_start_at_0,elem) in input.iter().skip(1).enumerate() {
            let i = i_start_at_0 + 1;
            println!("  i={:?} Finding where to put element {:?} (i={:?}) in output_index: {:?}", i, elem, i, output_index);

            // Verify next element if it is smaller
            // If next element is larger than the last stored index, just place that element at
            // the end.
            if *elem >= input[*output_index.last().unwrap()] {
                // Next element "elem" is already larger than the previous stored index.
                // Just use its index.
                output_index.push(i);

                println!("    1. Pushing element '{:?}' at the end.", elem);
            } else {
                println!("    2. elem ?< input[output_index[0]]");
                println!("           {:?} ?< {:?}", elem, input[output_index[0]]);
                // The next element of the input vector is smaller than the last stored on.
                // Let's find where it should go by looping back on the stored indices.
                // NOTE: we must use a loop on indices as to prevent problems with the borrow-checker.
                if *elem < input[output_index[0]] {
                    println!("        2a. Yes: --> putting i in front");
                    output_index.insert(0, i);
                } else {
                    println!("        2b. No: --> Looping back");
                    for j in (0..i).rev() {
                        println!("            j={:?}   elem={:?}   input[output_index[j]]={:?}", j, elem, input[output_index[j]]);
                        if *elem > input[output_index[j]] {
                            output_index.insert(j+1, i);
                            break;
                        }
                    }
                }
                println!("        3. output_index: {:?}", output_index);
            }

            print!("           output: [");
            for j in output_index.iter() {
                    print!("{:?}, ", input[*j]);
            }
            println!("]");
        }
    }

    println!("");
    println!("END: input:        {:?}", input);
    println!("END: output_index: {:?}", output_index);
    print!("END: output:       [");
    for j in output_index.iter() {
            print!("{:?}, ", input[*j]);
    }
    println!("]");

    assert_eq!(output_index.len(), n);

    return output_index;
}
<|endoftext|>


<commit_before>extern crate assert_cli;

#[cfg(test)]
mod integration {
    use assert_cli;

    #[test]
    fn mhost_wo_args() {
        assert_cli::Assert::command(&["cargo", "run", "--features=bin"])
            .fails()
            .and()
            .stderr().contains("error: The following required arguments were not provided:")
            .unwrap()
    }

    #[test]
    fn mhost_simple_lookup() {
        assert_cli::Assert::command(&["cargo", "run", "--features=bin"])
            .with_args(&["www.example.com"])
            .succeeds()
            .and()
            .stdout().contains("* IPv4: 93.184.216.34")
            .and()
            .stdout().contains("* IPv6: 2606:2800:220:1:248:1893:25c8:1946")
            .unwrap()
    }

    #[test]
    fn mhost_simple_ipv4_reverse_lookup() {
        assert_cli::Assert::command(&["cargo", "run", "--features=bin"])
            .with_args(&["8.8.8.8"])
            .succeeds()
            .and()
            .stdout().contains("* PTR: google-public-dns-a.google.com.")
            .unwrap()
    }

    #[test]
    fn mhost_simple_ipv6_reverse_lookup() {
        assert_cli::Assert::command(&["cargo", "run", "--features=bin"])
            .with_args(&["2001:4860:4860::8888"])
            .succeeds()
            .and()
            .stdout().contains("* PTR: google-public-dns-a.google.com.")
            .unwrap()
    }
}
<commit_msg>Ref intergration tests<commit_after>extern crate assert_cli;

#[cfg(test)]
mod integration {
    use assert_cli;

    #[test]
    fn mhost_wo_args() {
        mhost()
            .fails()
            .and()
            .stderr().contains("error: The following required arguments were not provided:")
            .unwrap()
    }

    #[test]
    fn mhost_simple_lookup() {
        mhost()
            .with_args(&["www.example.com"])
            .succeeds()
            .and()
            .stdout().contains("* IPv4: 93.184.216.34")
            .and()
            .stdout().contains("* IPv6: 2606:2800:220:1:248:1893:25c8:1946")
            .unwrap()
    }

    #[test]
    fn mhost_simple_ipv4_reverse_lookup() {
        mhost()
            .with_args(&["8.8.8.8"])
            .succeeds()
            .and()
            .stdout().contains("* PTR: google-public-dns-a.google.com.")
            .unwrap()
    }

    #[test]
    fn mhost_simple_ipv6_reverse_lookup() {
        mhost()
            .with_args(&["2001:4860:4860::8888"])
            .succeeds()
            .and()
            .stdout().contains("* PTR: google-public-dns-a.google.com.")
            .unwrap()
    }

    fn mhost() -> assert_cli::Assert {
        assert_cli::Assert::command(&["cargo", "run", "--features=bin"])
    }
}
<|endoftext|>