<commit_before>use std::fmt;

use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::util::lev_distance::lev_distance;
use crate::util::{Config, VersionExt};
use anyhow::Error;

use super::context::Context;
use super::types::{ConflictMap, ConflictReason};

/// Error during resolution providing a path of `PackageId`s.
pub struct ResolveError {
    cause: Error,
    package_path: Vec<PackageId>,
}

impl ResolveError {
    pub fn new<E: Into<Error>>(cause: E, package_path: Vec<PackageId>) -> Self {
        Self {
            cause: cause.into(),
            package_path,
        }
    }

    /// Returns a path of packages from the package whose requirements could not be resolved up to
    /// the root.
    pub fn package_path(&self) -> &[PackageId] {
        &self.package_path
    }
}

impl std::error::Error for ResolveError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.cause.source()
    }
}

impl fmt::Debug for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

impl fmt::Display for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

pub type ActivateResult<T> = Result<T, ActivateError>;

#[derive(Debug)]
pub enum ActivateError {
    Fatal(anyhow::Error),
    Conflict(PackageId, ConflictReason),
}

impl From<::anyhow::Error> for ActivateError {
    fn from(t: ::anyhow::Error) -> Self {
        ActivateError::Fatal(t)
    }
}

impl From<(PackageId, ConflictReason)> for ActivateError {
    fn from(t: (PackageId, ConflictReason)) -> Self {
        ActivateError::Conflict(t.0, t.1)
    }
}

pub(super) fn activation_error(
    cx: &Context,
    registry: &mut dyn Registry,
    parent: &Summary,
    dep: &Dependency,
    conflicting_activations: &ConflictMap,
    candidates: &[Summary],
    config: Option<&Config>,
) -> ResolveError {
    let to_resolve_err = |err| {
        ResolveError::new(
            err,
            cx.parents
                .path_to_bottom(&parent.package_id())
                .into_iter()
                .cloned()
                .collect(),
        )
    };

    if !candidates.is_empty() {
        let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
        msg.push_str("\n    ... required by ");
        msg.push_str(&describe_path(
            &cx.parents.path_to_bottom(&parent.package_id()),
        ));

        msg.push_str("\nversions that meet the requirements `");
        msg.push_str(&dep.version_req().to_string());
        msg.push_str("` are: ");
        msg.push_str(
            &candidates
                .iter()
                .map(|v| v.version())
                .map(|v| v.to_string())
                .collect::<Vec<_>>()
                .join(", "),
        );

        let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
        conflicting_activations.sort_unstable();
        // This is reversed to show the newest versions first. I don't know if there is
        // a strong reason to do this, but that is how the code previously worked
        // (see https://github.com/rust-lang/cargo/pull/5037) and I don't feel like changing it.
        conflicting_activations.reverse();
        // Flag used for grouping all semver errors together.
        let mut has_semver = false;

        for (p, r) in &conflicting_activations {
            match r {
                ConflictReason::Semver => {
                    has_semver = true;
                }
                ConflictReason::Links(link) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` links to the native library `");
                    msg.push_str(link);
                    msg.push_str("`, but it conflicts with a previous package which links to `");
                    msg.push_str(link);
                    msg.push_str("` as well:\n");
                    msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
                    msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
                    msg.push_str("Try to adjust your dependencies so that only one package uses the links ='");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
                }
                ConflictReason::MissingFeatures(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    // p == parent so the full path is redundant.
                }
                ConflictReason::RequiredDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has a required dependency with that name, \
                         but only optional dependencies can be used as features.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::NonImplicitDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has an optional dependency with that name, \
                         but but that dependency uses the \"dep:\" \
                         syntax in the features table, so it does not have an \
                         implicit feature with that name.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::PublicDependency(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub dep {:?}", pkg_id);
                }
                ConflictReason::PubliclyExports(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub exp {:?}", pkg_id);
                }
            }
        }

        if has_semver {
            // Group these errors together.
            msg.push_str("\n\nall possible versions conflict with previously selected packages.");
            for (p, r) in &conflicting_activations {
                if let ConflictReason::Semver = r {
                    msg.push_str("\n\n  previously selected ");
                    msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
                }
            }
        }

        msg.push_str("\n\nfailed to select a version for `");
        msg.push_str(&*dep.package_name());
        msg.push_str("` which could resolve this conflict");

        return to_resolve_err(anyhow::format_err!("{}", msg));
    }

    // We didn't actually find any candidates, so we need to
    // give an error message that nothing was found.
    //
    // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"`
    // was meant. So we re-query the registry with `deb="*"` so we can
    // list a few versions that were actually found.
    let all_req = semver::VersionReq::parse("*").unwrap();
    let mut new_dep = dep.clone();
    new_dep.set_version_req(all_req);
    let mut candidates = match registry.query_vec(&new_dep, false) {
        Ok(candidates) => candidates,
        Err(e) => return to_resolve_err(e),
    };
    candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));

    let mut msg =
        if !candidates.is_empty() {
            let versions = {
                let mut versions = candidates
                    .iter()
                    .take(3)
                    .map(|cand| cand.version().to_string())
                    .collect::<Vec<_>>();

                if candidates.len() > 3 {
                    versions.push("...".into());
                }

                versions.join(", ")
            };

            let mut msg = format!(
                "failed to select a version for the requirement `{} = \"{}\"`\n\
                 candidate versions found which didn't match: {}\n\
                 location searched: {}\n",
                dep.package_name(),
                dep.version_req(),
                versions,
                registry.describe_source(dep.source_id()),
            );
            msg.push_str("required by ");
            msg.push_str(&describe_path(
                &cx.parents.path_to_bottom(&parent.package_id()),
            ));

            // If we have a path dependency with a locked version, then this may
            // indicate that we updated a sub-package and forgot to run `cargo
            // update`. In this case try to print a helpful error!
            if dep.source_id().is_path() && dep.version_req().to_string().starts_with('=') {
                msg.push_str(
                    "\nconsider running `cargo update` to update \
                     a path dependency's locked version",
                );
            }

            if registry.is_replaced(dep.source_id()) {
                msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
            }

            msg
        } else {
            // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
            // was meant. So we try asking the registry for a `fuzzy` search for suggestions.
            let mut candidates = Vec::new();
            if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s), true) {
                return to_resolve_err(e);
            };
            candidates.sort_unstable_by_key(|a| a.name());
            candidates.dedup_by(|a, b| a.name() == b.name());
            let mut candidates: Vec<_> = candidates
                .iter()
                .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n))
                .filter(|&(d, _)| d < 4)
                .collect();
            candidates.sort_by_key(|o| o.0);
            let mut msg = format!(
                "no matching package named `{}` found\n\
                 location searched: {}\n",
                dep.package_name(),
                dep.source_id()
            );
            if !candidates.is_empty() {
                // If dependency package name is equal to the name of the candidate here
                // it may be a prerelease package which hasn't been specified correctly
                if dep.package_name() == candidates[0].1.name()
                    && candidates[0].1.package_id().version().is_prerelease()
                {
                    msg.push_str("prerelease package needs to be specified explicitly\n");
                    msg.push_str(&format!(
                        "{name} = {{ version = \"{version}\" }}",
                        name = candidates[0].1.name(),
                        version = candidates[0].1.package_id().version()
                    ));
                } else {
                    let mut names = candidates
                        .iter()
                        .take(3)
                        .map(|c| c.1.name().as_str())
                        .collect::<Vec<_>>();

                    if candidates.len() > 3 {
                        names.push("...");
                    }

                    msg.push_str("perhaps you meant: ");
                    msg.push_str(&names.iter().enumerate().fold(
                        String::default(),
                        |acc, (i, el)| match i {
                            0 => acc + el,
                            i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
                            _ => acc + ", " + el,
                        },
                    ));
                }

                msg.push('\n');
            }
            msg.push_str("required by ");
            msg.push_str(&describe_path(
                &cx.parents.path_to_bottom(&parent.package_id()),
            ));

            msg
        };

    if let Some(config) = config {
        if config.offline() {
            msg.push_str(
                "\nAs a reminder, you're using offline mode (--offline) \
                 which can sometimes cause surprising resolution failures, \
                 if this error is too confusing you may wish to retry \
                 without the offline flag.",
            );
        }
    }

    to_resolve_err(anyhow::format_err!("{}", msg))
}

/// Returns String representation of dependency chain for a particular `pkgid`.
pub(super) fn describe_path(path: &[&PackageId]) -> String {
    use std::fmt::Write;
    let mut dep_path_desc = format!("package `{}`", path[0]);
    for dep in path[1..].iter() {
        write!(dep_path_desc, "\n    ... which is depended on by `{}`", dep).unwrap();
    }
    dep_path_desc
}
<commit_msg>Spot the typo easily<commit_after>use std::fmt;

use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::util::lev_distance::lev_distance;
use crate::util::{Config, VersionExt};
use anyhow::Error;

use super::context::Context;
use super::types::{ConflictMap, ConflictReason};

/// Error during resolution providing a path of `PackageId`s.
pub struct ResolveError {
    cause: Error,
    package_path: Vec<PackageId>,
}

impl ResolveError {
    pub fn new<E: Into<Error>>(cause: E, package_path: Vec<PackageId>) -> Self {
        Self {
            cause: cause.into(),
            package_path,
        }
    }

    /// Returns a path of packages from the package whose requirements could not be resolved up to
    /// the root.
    pub fn package_path(&self) -> &[PackageId] {
        &self.package_path
    }
}

impl std::error::Error for ResolveError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.cause.source()
    }
}

impl fmt::Debug for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

impl fmt::Display for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

pub type ActivateResult<T> = Result<T, ActivateError>;

#[derive(Debug)]
pub enum ActivateError {
    Fatal(anyhow::Error),
    Conflict(PackageId, ConflictReason),
}

impl From<::anyhow::Error> for ActivateError {
    fn from(t: ::anyhow::Error) -> Self {
        ActivateError::Fatal(t)
    }
}

impl From<(PackageId, ConflictReason)> for ActivateError {
    fn from(t: (PackageId, ConflictReason)) -> Self {
        ActivateError::Conflict(t.0, t.1)
    }
}

pub(super) fn activation_error(
    cx: &Context,
    registry: &mut dyn Registry,
    parent: &Summary,
    dep: &Dependency,
    conflicting_activations: &ConflictMap,
    candidates: &[Summary],
    config: Option<&Config>,
) -> ResolveError {
    let to_resolve_err = |err| {
        ResolveError::new(
            err,
            cx.parents
                .path_to_bottom(&parent.package_id())
                .into_iter()
                .cloned()
                .collect(),
        )
    };

    if !candidates.is_empty() {
        let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
        msg.push_str("\n    ... required by ");
        msg.push_str(&describe_path(
            &cx.parents.path_to_bottom(&parent.package_id()),
        ));

        msg.push_str("\nversions that meet the requirements `");
        msg.push_str(&dep.version_req().to_string());
        msg.push_str("` are: ");
        msg.push_str(
            &candidates
                .iter()
                .map(|v| v.version())
                .map(|v| v.to_string())
                .collect::<Vec<_>>()
                .join(", "),
        );

        let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
        conflicting_activations.sort_unstable();
        // This is reversed to show the newest versions first. I don't know if there is
        // a strong reason to do this, but that is how the code previously worked
        // (see https://github.com/rust-lang/cargo/pull/5037) and I don't feel like changing it.
        conflicting_activations.reverse();
        // Flag used for grouping all semver errors together.
        let mut has_semver = false;

        for (p, r) in &conflicting_activations {
            match r {
                ConflictReason::Semver => {
                    has_semver = true;
                }
                ConflictReason::Links(link) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` links to the native library `");
                    msg.push_str(link);
                    msg.push_str("`, but it conflicts with a previous package which links to `");
                    msg.push_str(link);
                    msg.push_str("` as well:\n");
                    msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
                    msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
                    msg.push_str("Try to adjust your dependencies so that only one package uses the links ='");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
                }
                ConflictReason::MissingFeatures(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    // p == parent so the full path is redundant.
                }
                ConflictReason::RequiredDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has a required dependency with that name, \
                         but only optional dependencies can be used as features.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::NonImplicitDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has an optional dependency with that name, \
                         but but that dependency uses the \"dep:\" \
                         syntax in the features table, so it does not have an \
                         implicit feature with that name.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::PublicDependency(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub dep {:?}", pkg_id);
                }
                ConflictReason::PubliclyExports(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub exp {:?}", pkg_id);
                }
            }
        }

        if has_semver {
            // Group these errors together.
            msg.push_str("\n\nall possible versions conflict with previously selected packages.");
            for (p, r) in &conflicting_activations {
                if let ConflictReason::Semver = r {
                    msg.push_str("\n\n  previously selected ");
                    msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
                }
            }
        }

        msg.push_str("\n\nfailed to select a version for `");
        msg.push_str(&*dep.package_name());
        msg.push_str("` which could resolve this conflict");

        return to_resolve_err(anyhow::format_err!("{}", msg));
    }

    // We didn't actually find any candidates, so we need to
    // give an error message that nothing was found.
    //
    // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"`
    // was meant. So we re-query the registry with `deb="*"` so we can
    // list a few versions that were actually found.
    let all_req = semver::VersionReq::parse("*").unwrap();
    let mut new_dep = dep.clone();
    new_dep.set_version_req(all_req);
    let mut candidates = match registry.query_vec(&new_dep, false) {
        Ok(candidates) => candidates,
        Err(e) => return to_resolve_err(e),
    };
    candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));

    let mut msg =
        if !candidates.is_empty() {
            let versions = {
                let mut versions = candidates
                    .iter()
                    .take(3)
                    .map(|cand| cand.version().to_string())
                    .collect::<Vec<_>>();

                if candidates.len() > 3 {
                    versions.push("...".into());
                }

                versions.join(", ")
            };

            let mut msg = format!(
                "failed to select a version for the requirement `{} = \"{}\"`\n\
                 candidate versions found which didn't match: {}\n\
                 location searched: {}\n",
                dep.package_name(),
                dep.version_req(),
                versions,
                registry.describe_source(dep.source_id()),
            );
            msg.push_str("required by ");
            msg.push_str(&describe_path(
                &cx.parents.path_to_bottom(&parent.package_id()),
            ));

            // If we have a path dependency with a locked version, then this may
            // indicate that we updated a sub-package and forgot to run `cargo
            // update`. In this case try to print a helpful error!
            if dep.source_id().is_path() && dep.version_req().to_string().starts_with('=') {
                msg.push_str(
                    "\nconsider running `cargo update` to update \
                     a path dependency's locked version",
                );
            }

            if registry.is_replaced(dep.source_id()) {
                msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
            }

            msg
        } else {
            // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
            // was meant. So we try asking the registry for a `fuzzy` search for suggestions.
            let mut candidates = Vec::new();
            if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s), true) {
                return to_resolve_err(e);
            };
            candidates.sort_unstable_by_key(|a| a.name());
            candidates.dedup_by(|a, b| a.name() == b.name());
            let mut candidates: Vec<_> = candidates
                .iter()
                .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n))
                .filter(|&(d, _)| d < 4)
                .collect();
            candidates.sort_by_key(|o| o.0);
            let mut msg = format!(
                "no matching package named `{}` found\n", dep.package_name());
            if !candidates.is_empty() {
                // If dependency package name is equal to the name of the candidate here
                // it may be a prerelease package which hasn't been specified correctly
                if dep.package_name() == candidates[0].1.name()
                    && candidates[0].1.package_id().version().is_prerelease()
                {
                    msg.push_str("prerelease package needs to be specified explicitly\n");
                    msg.push_str(&format!(
                        "{name} = {{ version = \"{version}\" }}",
                        name = candidates[0].1.name(),
                        version = candidates[0].1.package_id().version()
                    ));
                } else {
                    let mut names = candidates
                        .iter()
                        .take(3)
                        .map(|c| c.1.name().as_str())
                        .collect::<Vec<_>>();

                    if candidates.len() > 3 {
                        names.push("...");
                    }
                    // Vertically align first suggestion with missing crate name
                    // so the silly typo you probably made jumps out at you.
                    msg.push_str("perhaps you meant:                ");
                    msg.push_str(&names.iter().enumerate().fold(
                        String::default(),
                        |acc, (i, el)| match i {
                            0 => acc + el,
                            i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
                            _ => acc + ", " + el,
                        },
                    ));
                }
                msg.push('\n');
            }
            msg.push_str(&format!("location searched: {}\n", dep.source_id()));
            msg.push_str("required by ");
            msg.push_str(&describe_path(
                &cx.parents.path_to_bottom(&parent.package_id()),
            ));

            msg
        };

    if let Some(config) = config {
        if config.offline() {
            msg.push_str(
                "\nAs a reminder, you're using offline mode (--offline) \
                 which can sometimes cause surprising resolution failures, \
                 if this error is too confusing you may wish to retry \
                 without the offline flag.",
            );
        }
    }

    to_resolve_err(anyhow::format_err!("{}", msg))
}

/// Returns String representation of dependency chain for a particular `pkgid`.
pub(super) fn describe_path(path: &[&PackageId]) -> String {
    use std::fmt::Write;
    let mut dep_path_desc = format!("package `{}`", path[0]);
    for dep in path[1..].iter() {
        write!(dep_path_desc, "\n    ... which is depended on by `{}`", dep).unwrap();
    }
    dep_path_desc
}
<|endoftext|>


<commit_before>
extern crate norx;
use std::str::from_utf8;
use norx::norx6441::{encrypt,decrypt};

pub fn main() {
    let k = [0u8, ..32];
    let n = [0u8, ..16];
    let m = "Hello, World!";
    let c = encrypt([], m.as_bytes(), [], n, k);
    for x in c.iter() {
        print!("{:02x} ", *x);
    }
    println!("");
    match decrypt([], c.as_slice(), [], n, k) {
        Some(m) => println!("{}", from_utf8(m.as_slice()).expect("bad utf-8")),
          None  => println!("bad ciphertext")
    }
    let mut c = c;
    *c.get_mut(0) ^= 1; // flip a bit
    match decrypt([], c.as_slice(), [], n, k) {
        Some(m) => println!("{}", from_utf8(m.as_slice()).expect("bad utf-8")),
          None  => println!("bad ciphertext")
    }
}

<commit_msg>Fix example<commit_after>
extern crate norx;
use std::str::from_utf8;
use norx::norx6441::{encrypt,decrypt};

pub fn main() {
    let k = [0u8, ..32];
    let n = [0u8, ..16];
    let m = "Hello, World!";
    let c = encrypt(&[], m.as_bytes(), &[], &n, &k);
    for x in c.iter() {
        print!("{:02x} ", *x);
    }
    println!("");
    match decrypt(&[], c.as_slice(), &[], &n, &k) {
        Some(m) => println!("{}", from_utf8(m.as_slice()).expect("bad utf-8")),
          None  => println!("bad ciphertext")
    }
    let mut c = c;
    c[0] ^= 1; // flip a bit
    match decrypt(&[], c.as_slice(), &[], &n, &k) {
        Some(m) => println!("{}", from_utf8(m.as_slice()).expect("bad utf-8")),
          None  => println!("bad ciphertext")
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate log4rs;
extern crate iron;
extern crate logger;
extern crate router;
extern crate persistent;
extern crate r2d2;
extern crate r2d2_postgres;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate potboiler_common;
extern crate serde_json;
extern crate hyper;
extern crate hybrid_clocks;
mod tables;

use iron::prelude::*;
use iron::status;
use logger::Logger;
use persistent::Read as PRead;
use persistent::State;
use potboiler_common::{db, iron_str_error, server_id};
use potboiler_common::string_error::StringError;
use potboiler_common::types::{CRDT, LWW, Log};
use r2d2_postgres::PostgresConnectionManager;
use router::Router;
use std::env;
use std::io::Read;
use std::ops::Deref;

pub type PostgresConnection = r2d2::PooledConnection<PostgresConnectionManager>;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
}

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

fn get_key(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "get_key")))
}

fn update_key(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let mut json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    let map = json.as_object_mut().unwrap();
    map.insert("table".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "table").unwrap()));
    map.insert("key".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "key").unwrap()));

    let client = hyper::client::Client::new();
    let res = client.post(SERVER_URL.deref())
        .body(&serde_json::ser::to_string(map).unwrap())
        .send()
        .expect("sender ok");
    assert_eq!(res.status, hyper::status::StatusCode::Created);
    Ok(Response::with((status::Ok, "update_key")))
}

fn make_table(conn: &PostgresConnection, table_name: &str, kind: &CRDT) -> IronResult<()> {
    match kind {
        &CRDT::LWW => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, value \
                                   JSONB, crdt JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::ORSET => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, crdt \
                                   JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {}_items (\
                                   key VARCHAR(1024), \
                                   item VARCHAR(1024), PRIMARY KEY(key, item))",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::GSET => {
            error!("No G-Set make table yet");
        }
    }
    return Ok(());
}

fn string_iron_error(error: &str) -> IronResult<Response> {
    Err(IronError::new(StringError(error.to_string()), (status::BadRequest, error)))
}

fn get_crdt(conn: &PostgresConnection,
            table: &String,
            key: &String)
            -> IronResult<Option<serde_json::Value>> {
    let stmt = conn.prepare(&format!("select crdt from {} where key=$1", table)).map_err(iron_str_error)?;
    let results = stmt.query(&[&key]).map_err(iron_str_error)?;
    if results.is_empty() {
        Ok(None)
    } else if results.len() == 1 {
        let row = results.get(0);
        let raw_crdt: serde_json::Value = row.get("crdt");
        Ok(Some(raw_crdt))
    } else {
        Err(iron_str_error(StringError::from(format!("{} entries for key {}", results.len(), key))))
    }
}

fn new_event(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    info!("body: {:?}", json);
    let log = try!(serde_json::from_value::<Log>(json).map_err(iron_str_error));
    info!("log: {:?}", log);
    let change: Change = serde_json::from_value(log.data).unwrap();
    info!("change: {:?}", change);
    let tables = tables::get_tables(req);
    let table_type = match tables.get(&change.table) {
        None => return string_iron_error("Can't find table"),
        Some(&val) => val,
    };
    match table_type {
        CRDT::LWW => {
            match change.op {
                Operation::Set => {
                    let crdt_to_use: Option<CRDT> = if &change.table == tables::CONFIG_TABLE {
                        let crdt_type: &str = change.change
                            .find("crdt")
                            .ok_or(StringError::from("No CRDT key"))
                            .map_err(iron_str_error)?
                            .as_str()
                            .ok_or(StringError::from("CRDT value isn't string!"))
                            .map_err(iron_str_error)?;
                        // FIXME: format! bit is a hacky workaround for
                        // https://github.com/serde-rs/serde/issues/251
                        serde_json::from_str(&format!("\"{}\"", crdt_type)).map_err(iron_str_error)?
                    } else {
                        None
                    };
                    let conn = get_pg_connection!(&req);
                    let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
                    match raw_crdt {
                        None => {
                            let lww = LWW {
                                when: log.when,
                                data: change.change.clone(),
                            };
                            conn.execute(&format!("INSERT INTO {} (key, value, crdt) VALUES ($1, $2, $3)",
                                                  &change.table),
                                         &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                .expect("insert worked");
                            if &change.table == tables::CONFIG_TABLE {
                                let crdt = crdt_to_use.unwrap();
                                make_table(&conn, &change.key, &crdt)?;
                                tables::add_table(req, &change.key, &crdt);
                            }
                        }
                        Some(raw_crdt) => {
                            let mut lww: LWW = serde_json::from_value(raw_crdt).expect("bad raw crdt");
                            if lww.when < log.when {
                                lww.when = log.when;
                                lww.data = change.change.clone();
                                conn.execute(&format!("UPDATE {} set value=$2, crdt=$3 where key=$1",
                                                      &change.table),
                                             &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                    .expect("update worked");
                            } else {
                                info!("Earlier event, skipping");
                            }
                        }
                    }
                }
                _ => {
                    return string_iron_error("LWW only supports Set, not Add/Remove");
                }
            }
        }
        CRDT::ORSET => {
            let op: ORSetOp = serde_json::from_value(change.change).map_err(iron_str_error)?;
            let conn = get_pg_connection!(&req);
            let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
            let (mut crdt, existing) = match raw_crdt {
                Some(val) => (serde_json::from_value(val).map_err(iron_str_error)?, true),
                None => {
                    (ORSet {
                         adds: HashMap::new(),
                         removes: HashMap::new(),
                     },
                     false)
                }
            };
            let trans = conn.transaction().unwrap();
            match change.op {
                Operation::Add => {
                    if !crdt.removes.contains_key(&op.key) && !crdt.adds.contains_key(&op.key) {
                        trans.execute(&format!("INSERT INTO {}_items (key, item) VALUES ($1, $2)",
                                              &change.table),
                                     &[&op.key, &op.item])
                            .map_err(iron_str_error)?;
                        crdt.adds.insert(op.key, op.item);
                    }
                }
                Operation::Remove => {
                    trans.execute(&format!("DELETE FROM {}_items where key=$1", &change.table),
                                 &[&op.key])
                        .map_err(iron_str_error)?;
                    crdt.adds.remove(&op.key);
                    crdt.removes.insert(op.key, op.item);
                }
                _ => {
                    return string_iron_error("ORSET only supports Add/Remove");
                }
            }
            debug!("OR-Set for {}: {:?}", &change.table, &crdt);
            if existing {
                trans.execute(&format!("UPDATE {} set crdt=$2 where key=$1", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            } else {
                trans.execute(&format!("INSERT INTO {} (key, crdt) VALUES ($1, $2)", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            }
            let mut items: Vec<&str> = Vec::new();
            for key in crdt.adds.keys() {
                if crdt.removes.contains_key(key) {
                    continue;
                }
                items.push(crdt.adds.get(key).unwrap());
            }
            debug!("Items: {:?}", items);
            trans.commit().map_err(iron_str_error)?;
        }
        _ => {
            return string_iron_error("Only support LWW and OR-Set so far");
        }
    }
    Ok(Response::with(status::NoContent))
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).expect("log config ok");
    let client = hyper::client::Client::new();

    let mut map: serde_json::Map<String, String> = serde_json::Map::new();
    let host: &str = &env::var("HOST").unwrap_or("localhost".to_string());
    map.insert("url".to_string(),
               format!("http://{}:8001/kv/event", host).to_string());
    let res = client.post(&format!("{}/register", SERVER_URL.deref()))
        .body(&serde_json::ser::to_string(&map).unwrap())
        .send()
        .expect("Register ok");
    assert_eq!(res.status, hyper::status::StatusCode::NoContent);

    let db_url: &str = &env::var("DATABASE_URL").expect("Needed DATABASE_URL");
    let pool = db::get_pool(db_url);
    let conn = pool.get().unwrap();
    match make_table(&conn, "_config", &CRDT::LWW) {
        Ok(_) => {}
        Err(err) => {
            error!("Error while making config table: {}", err);
            return;
        }
    }
    let (logger_before, logger_after) = Logger::new(None);
    let mut router = Router::new();
    router.get("/kv/:table/:key", get_key);
    router.post("/kv/:table/:key", update_key);
    router.post("/kv/event", new_event);
    let mut chain = Chain::new(router);
    chain.link_before(logger_before);
    chain.link_after(logger_after);
    chain.link_before(PRead::<server_id::ServerId>::one(server_id::setup()));
    chain.link(PRead::<db::PostgresDB>::both(pool));
    let tables = tables::init_tables(&conn);
    chain.link(State::<tables::Tables>::both(tables));
    info!("Potboiler-kv booted");
    Iron::new(chain).http("0.0.0.0:8001").unwrap();
}
<commit_msg>Add all three keys to ORSET changes<commit_after>#[macro_use]
extern crate log;
extern crate log4rs;
extern crate iron;
extern crate logger;
extern crate router;
extern crate persistent;
extern crate r2d2;
extern crate r2d2_postgres;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate potboiler_common;
extern crate serde_json;
extern crate hyper;
extern crate hybrid_clocks;
mod tables;

use iron::prelude::*;
use iron::status;
use logger::Logger;
use persistent::Read as PRead;
use persistent::State;
use potboiler_common::{db, iron_str_error, server_id};
use potboiler_common::string_error::StringError;
use potboiler_common::types::{CRDT, LWW, Log};
use r2d2_postgres::PostgresConnectionManager;
use router::Router;
use std::env;
use std::io::Read;
use std::ops::Deref;

pub type PostgresConnection = r2d2::PooledConnection<PostgresConnectionManager>;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
}

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

fn get_key(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "get_key")))
}

fn update_key(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let mut json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    let map = json.as_object_mut().unwrap();
    map.insert("table".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "table").unwrap()));
    map.insert("key".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "key").unwrap()));

    let client = hyper::client::Client::new();
    let res = client.post(SERVER_URL.deref())
        .body(&serde_json::ser::to_string(map).unwrap())
        .send()
        .expect("sender ok");
    assert_eq!(res.status, hyper::status::StatusCode::Created);
    Ok(Response::with((status::Ok, "update_key")))
}

fn make_table(conn: &PostgresConnection, table_name: &str, kind: &CRDT) -> IronResult<()> {
    match kind {
        &CRDT::LWW => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, value \
                                   JSONB, crdt JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::ORSET => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, crdt \
                                   JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {}_items (\
                                   collection VARCHAR(1024),
                                   key VARCHAR(1024), \
                                   item VARCHAR(1024), PRIMARY KEY(collection, key, item))",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::GSET => {
            error!("No G-Set make table yet");
        }
    }
    return Ok(());
}

fn string_iron_error(error: &str) -> IronResult<Response> {
    Err(IronError::new(StringError(error.to_string()), (status::BadRequest, error)))
}

fn get_crdt(conn: &PostgresConnection,
            table: &String,
            key: &String)
            -> IronResult<Option<serde_json::Value>> {
    let stmt = conn.prepare(&format!("select crdt from {} where key=$1", table)).map_err(iron_str_error)?;
    let results = stmt.query(&[&key]).map_err(iron_str_error)?;
    if results.is_empty() {
        Ok(None)
    } else if results.len() == 1 {
        let row = results.get(0);
        let raw_crdt: serde_json::Value = row.get("crdt");
        Ok(Some(raw_crdt))
    } else {
        Err(iron_str_error(StringError::from(format!("{} entries for key {}", results.len(), key))))
    }
}

fn new_event(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    info!("body: {:?}", json);
    let log = try!(serde_json::from_value::<Log>(json).map_err(iron_str_error));
    info!("log: {:?}", log);
    let change: Change = serde_json::from_value(log.data).unwrap();
    info!("change: {:?}", change);
    let tables = tables::get_tables(req);
    let table_type = match tables.get(&change.table) {
        None => return string_iron_error("Can't find table"),
        Some(&val) => val,
    };
    match table_type {
        CRDT::LWW => {
            match change.op {
                Operation::Set => {
                    let crdt_to_use: Option<CRDT> = if &change.table == tables::CONFIG_TABLE {
                        let crdt_type: &str = change.change
                            .find("crdt")
                            .ok_or(StringError::from("No CRDT key"))
                            .map_err(iron_str_error)?
                            .as_str()
                            .ok_or(StringError::from("CRDT value isn't string!"))
                            .map_err(iron_str_error)?;
                        // FIXME: format! bit is a hacky workaround for
                        // https://github.com/serde-rs/serde/issues/251
                        serde_json::from_str(&format!("\"{}\"", crdt_type)).map_err(iron_str_error)?
                    } else {
                        None
                    };
                    let conn = get_pg_connection!(&req);
                    let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
                    match raw_crdt {
                        None => {
                            let lww = LWW {
                                when: log.when,
                                data: change.change.clone(),
                            };
                            conn.execute(&format!("INSERT INTO {} (key, value, crdt) VALUES ($1, $2, $3)",
                                                  &change.table),
                                         &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                .expect("insert worked");
                            if &change.table == tables::CONFIG_TABLE {
                                let crdt = crdt_to_use.unwrap();
                                make_table(&conn, &change.key, &crdt)?;
                                tables::add_table(req, &change.key, &crdt);
                            }
                        }
                        Some(raw_crdt) => {
                            let mut lww: LWW = serde_json::from_value(raw_crdt).expect("bad raw crdt");
                            if lww.when < log.when {
                                lww.when = log.when;
                                lww.data = change.change.clone();
                                conn.execute(&format!("UPDATE {} set value=$2, crdt=$3 where key=$1",
                                                      &change.table),
                                             &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                    .expect("update worked");
                            } else {
                                info!("Earlier event, skipping");
                            }
                        }
                    }
                }
                _ => {
                    return string_iron_error("LWW only supports Set, not Add/Remove");
                }
            }
        }
        CRDT::ORSET => {
            let op: ORSetOp = serde_json::from_value(change.change).map_err(iron_str_error)?;
            let conn = get_pg_connection!(&req);
            let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
            let (mut crdt, existing) = match raw_crdt {
                Some(val) => (serde_json::from_value(val).map_err(iron_str_error)?, true),
                None => {
                    (ORSet {
                         adds: HashMap::new(),
                         removes: HashMap::new(),
                     },
                     false)
                }
            };
            let trans = conn.transaction().unwrap();
            match change.op {
                Operation::Add => {
                    if !crdt.removes.contains_key(&op.key) && !crdt.adds.contains_key(&op.key) {
                        trans.execute(&format!("INSERT INTO {}_items (collection, key, item) VALUES ($1, \
                                               $2, $3)",
                                              &change.table),
                                     &[&change.key, &op.key, &op.item])
                            .map_err(iron_str_error)?;
                        crdt.adds.insert(op.key, op.item);
                    }
                }
                Operation::Remove => {
                    trans.execute(&format!("DELETE FROM {}_items where collection=$1 and key=$2",
                                          &change.table),
                                 &[&change.key, &op.key])
                        .map_err(iron_str_error)?;
                    crdt.adds.remove(&op.key);
                    crdt.removes.insert(op.key, op.item);
                }
                _ => {
                    return string_iron_error("ORSET only supports Add/Remove");
                }
            }
            debug!("OR-Set for {}: {:?}", &change.table, &crdt);
            if existing {
                trans.execute(&format!("UPDATE {} set crdt=$2 where key=$1", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            } else {
                trans.execute(&format!("INSERT INTO {} (key, crdt) VALUES ($1, $2)", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            }
            let mut items: Vec<&str> = Vec::new();
            for key in crdt.adds.keys() {
                if crdt.removes.contains_key(key) {
                    continue;
                }
                items.push(crdt.adds.get(key).unwrap());
            }
            debug!("Items: {:?}", items);
            trans.commit().map_err(iron_str_error)?;
        }
        _ => {
            return string_iron_error("Only support LWW and OR-Set so far");
        }
    }
    Ok(Response::with(status::NoContent))
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).expect("log config ok");
    let client = hyper::client::Client::new();

    let mut map: serde_json::Map<String, String> = serde_json::Map::new();
    let host: &str = &env::var("HOST").unwrap_or("localhost".to_string());
    map.insert("url".to_string(),
               format!("http://{}:8001/kv/event", host).to_string());
    let res = client.post(&format!("{}/register", SERVER_URL.deref()))
        .body(&serde_json::ser::to_string(&map).unwrap())
        .send()
        .expect("Register ok");
    assert_eq!(res.status, hyper::status::StatusCode::NoContent);

    let db_url: &str = &env::var("DATABASE_URL").expect("Needed DATABASE_URL");
    let pool = db::get_pool(db_url);
    let conn = pool.get().unwrap();
    match make_table(&conn, "_config", &CRDT::LWW) {
        Ok(_) => {}
        Err(err) => {
            error!("Error while making config table: {}", err);
            return;
        }
    }
    let (logger_before, logger_after) = Logger::new(None);
    let mut router = Router::new();
    router.get("/kv/:table/:key", get_key);
    router.post("/kv/:table/:key", update_key);
    router.post("/kv/event", new_event);
    let mut chain = Chain::new(router);
    chain.link_before(logger_before);
    chain.link_after(logger_after);
    chain.link_before(PRead::<server_id::ServerId>::one(server_id::setup()));
    chain.link(PRead::<db::PostgresDB>::both(pool));
    let tables = tables::init_tables(&conn);
    chain.link(State::<tables::Tables>::both(tables));
    info!("Potboiler-kv booted");
    Iron::new(chain).http("0.0.0.0:8001").unwrap();
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;

use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;

use libimagerror::into::IntoError;

use error::StoreErrorKind as SEK;
use error::MapErrInto;
use store::Result;

/// The Index into the Store
#[derive(Debug, Clone, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl PartialEq for StoreId {
    fn eq(&self, other: &StoreId) -> bool {
        self.id == other.id
    }
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
        StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
    }

    /// Try to create a StoreId object from a filesystem-absolute path.
    ///
    /// Automatically creates a StoreId object which has a `base` set to `store_part` if stripping
    /// the `store_part` from the `full_path` succeeded.
    ///
    /// Returns a `StoreErrorKind::StoreIdBuildFromFullPathError` if stripping failes.
    pub fn from_full_path<D>(store_part: &PathBuf, full_path: D) -> Result<StoreId>
        where D: Deref<Target = Path>
    {
        let p = try!(
            full_path.strip_prefix(store_part).map_err_into(SEK::StoreIdBuildFromFullPathError)
        );
        StoreId::new(Some(store_part.clone()), PathBuf::from(p))
    }

    pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
        debug!("Trying to get a new baseless id from: {:?}", id);
        if id.is_absolute() {
            Err(SEK::StoreIdLocalPartAbsoluteError.into_error())
        } else {
            Ok(StoreId {
                base: None,
                id: id
            })
        }
    }

    pub fn without_base(mut self) -> StoreId {
        self.base = None;
        self
    }

    pub fn with_base(mut self, base: PathBuf) -> Self {
        self.base = Some(base);
        self
    }

    /// Transform the StoreId object into a PathBuf, error if the base of the StoreId is not
    /// specified.
    pub fn into_pathbuf(self) -> Result<PathBuf> {
        let mut base = try!(self.base.ok_or(SEK::StoreIdHasNoBaseError.into_error()));
        base.push(self.id);
        Ok(base)
    }

    pub fn exists(&self) -> Result<bool> {
        self.clone().into_pathbuf().map(|pb| pb.exists())
    }

    pub fn to_str(&self) -> Result<String> {
        self.base
            .as_ref()
            .cloned()
            .map(|mut base| { base.push(self.id.clone()); base })
            .unwrap_or_else(|| self.id.clone())
            .to_str()
            .map(String::from)
            .ok_or(SEK::StoreIdHandlingError.into_error())
    }

    /// Returns the components of the `id` part of the StoreId object.
    ///
    /// Can be used to check whether a StoreId points to an entry in a specific collection of
    /// StoreIds.
    pub fn components(&self) -> Components {
        self.id.components()
    }

    /// Get the _local_ part of a StoreId object, as in "the part from the store root to the entry".
    pub fn local(&self) -> &PathBuf {
        &self.id
    }

    pub fn local_push<P: AsRef<Path>>(&mut self, path: P) {
        self.id.push(path)
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> Result<StoreId>;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> Result<StoreId> {
        Ok(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> Result<StoreId> {
        StoreId::new_baseless(self)
    }
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;
            use $crate::store::Result;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(name);
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
                    StoreId::new(None, self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {
    use std::path::PathBuf;

    use storeid::StoreId;
    use storeid::IntoStoreId;
    use error::StoreErrorKind as SEK;

    module_entry_path_mod!("test");

    #[test]
    fn test_correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
    }

    #[test]
    fn test_baseless_path() {
        let id = StoreId::new_baseless(PathBuf::from("test"));
        assert!(id.is_ok());
        assert_eq!(id.unwrap(), StoreId {
            base: None,
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_base_path() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
        assert!(id.is_ok());
        assert_eq!(id.unwrap(), StoreId {
            base: Some(PathBuf::from("/tmp/")),
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_adding_base_to_baseless_path() {
        let id = StoreId::new_baseless(PathBuf::from("test"));

        assert!(id.is_ok());
        let id = id.unwrap();

        assert_eq!(id, StoreId { base: None, id: PathBuf::from("test") });

        let id = id.with_base(PathBuf::from("/tmp/"));
        assert_eq!(id, StoreId {
            base: Some(PathBuf::from("/tmp/")),
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_removing_base_from_base_path() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));

        assert!(id.is_ok());
        let id = id.unwrap();

        assert_eq!(id, StoreId {
            base: Some(PathBuf::from("/tmp/")),
            id: PathBuf::from("test")
        });

        let id = id.without_base();
        assert_eq!(id, StoreId {
            base: None,
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_baseless_into_pathbuf_is_err() {
        let id = StoreId::new_baseless(PathBuf::from("test"));
        assert!(id.is_ok());
        assert!(id.unwrap().into_pathbuf().is_err());
    }

    #[test]
    fn test_baseless_into_pathbuf_is_storeidhasnobaseerror() {
        let id = StoreId::new_baseless(PathBuf::from("test"));
        assert!(id.is_ok());

        let pb = id.unwrap().into_pathbuf();
        assert!(pb.is_err());

        assert_eq!(pb.unwrap_err().err_type(), SEK::StoreIdHasNoBaseError);
    }

    #[test]
    fn test_basefull_into_pathbuf_is_ok() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
        assert!(id.is_ok());
        assert!(id.unwrap().into_pathbuf().is_ok());
    }

    #[test]
    fn test_basefull_into_pathbuf_is_correct() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
        assert!(id.is_ok());

        let pb = id.unwrap().into_pathbuf();
        assert!(pb.is_ok());

        assert_eq!(pb.unwrap(), PathBuf::from("/tmp/test"));
    }

}
<commit_msg>Add StoreId::is_in_collection()<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;

use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;

use libimagerror::into::IntoError;

use error::StoreErrorKind as SEK;
use error::MapErrInto;
use store::Result;

/// The Index into the Store
#[derive(Debug, Clone, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl PartialEq for StoreId {
    fn eq(&self, other: &StoreId) -> bool {
        self.id == other.id
    }
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
        StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
    }

    /// Try to create a StoreId object from a filesystem-absolute path.
    ///
    /// Automatically creates a StoreId object which has a `base` set to `store_part` if stripping
    /// the `store_part` from the `full_path` succeeded.
    ///
    /// Returns a `StoreErrorKind::StoreIdBuildFromFullPathError` if stripping failes.
    pub fn from_full_path<D>(store_part: &PathBuf, full_path: D) -> Result<StoreId>
        where D: Deref<Target = Path>
    {
        let p = try!(
            full_path.strip_prefix(store_part).map_err_into(SEK::StoreIdBuildFromFullPathError)
        );
        StoreId::new(Some(store_part.clone()), PathBuf::from(p))
    }

    pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
        debug!("Trying to get a new baseless id from: {:?}", id);
        if id.is_absolute() {
            Err(SEK::StoreIdLocalPartAbsoluteError.into_error())
        } else {
            Ok(StoreId {
                base: None,
                id: id
            })
        }
    }

    pub fn without_base(mut self) -> StoreId {
        self.base = None;
        self
    }

    pub fn with_base(mut self, base: PathBuf) -> Self {
        self.base = Some(base);
        self
    }

    /// Transform the StoreId object into a PathBuf, error if the base of the StoreId is not
    /// specified.
    pub fn into_pathbuf(self) -> Result<PathBuf> {
        let mut base = try!(self.base.ok_or(SEK::StoreIdHasNoBaseError.into_error()));
        base.push(self.id);
        Ok(base)
    }

    pub fn exists(&self) -> Result<bool> {
        self.clone().into_pathbuf().map(|pb| pb.exists())
    }

    pub fn to_str(&self) -> Result<String> {
        self.base
            .as_ref()
            .cloned()
            .map(|mut base| { base.push(self.id.clone()); base })
            .unwrap_or_else(|| self.id.clone())
            .to_str()
            .map(String::from)
            .ok_or(SEK::StoreIdHandlingError.into_error())
    }

    /// Returns the components of the `id` part of the StoreId object.
    ///
    /// Can be used to check whether a StoreId points to an entry in a specific collection of
    /// StoreIds.
    pub fn components(&self) -> Components {
        self.id.components()
    }

    /// Get the _local_ part of a StoreId object, as in "the part from the store root to the entry".
    pub fn local(&self) -> &PathBuf {
        &self.id
    }

    /// Check whether a StoreId points to an entry in a specific collection.
    ///
    /// A "collection" here is simply a directory. So `foo/bar/baz` is an entry which is in
    /// collection ["foo", "bar", "baz"], but also in ["foo", "bar"] and ["foo"].
    ///
    /// # Warning
    ///
    /// The collection specification _has_ to start with the module name. Otherwise this function
    /// may return false negatives.
    ///
    pub fn is_in_collection(&self, colls: &[&str]) -> bool {
        use std::path::Component;

        self.id
            .components()
            .zip(colls)
            .map(|(component, pred_coll)| match component {
                Component::Normal(ref s) => s.to_str().map(|ref s| s == pred_coll).unwrap_or(false),
                _ => false
            })
            .all(|x| x)
    }

    pub fn local_push<P: AsRef<Path>>(&mut self, path: P) {
        self.id.push(path)
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> Result<StoreId>;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> Result<StoreId> {
        Ok(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> Result<StoreId> {
        StoreId::new_baseless(self)
    }
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;
            use $crate::store::Result;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(name);
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
                    StoreId::new(None, self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {
    use std::path::PathBuf;

    use storeid::StoreId;
    use storeid::IntoStoreId;
    use error::StoreErrorKind as SEK;

    module_entry_path_mod!("test");

    #[test]
    fn test_correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
    }

    #[test]
    fn test_baseless_path() {
        let id = StoreId::new_baseless(PathBuf::from("test"));
        assert!(id.is_ok());
        assert_eq!(id.unwrap(), StoreId {
            base: None,
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_base_path() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
        assert!(id.is_ok());
        assert_eq!(id.unwrap(), StoreId {
            base: Some(PathBuf::from("/tmp/")),
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_adding_base_to_baseless_path() {
        let id = StoreId::new_baseless(PathBuf::from("test"));

        assert!(id.is_ok());
        let id = id.unwrap();

        assert_eq!(id, StoreId { base: None, id: PathBuf::from("test") });

        let id = id.with_base(PathBuf::from("/tmp/"));
        assert_eq!(id, StoreId {
            base: Some(PathBuf::from("/tmp/")),
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_removing_base_from_base_path() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));

        assert!(id.is_ok());
        let id = id.unwrap();

        assert_eq!(id, StoreId {
            base: Some(PathBuf::from("/tmp/")),
            id: PathBuf::from("test")
        });

        let id = id.without_base();
        assert_eq!(id, StoreId {
            base: None,
            id: PathBuf::from("test")
        });
    }

    #[test]
    fn test_baseless_into_pathbuf_is_err() {
        let id = StoreId::new_baseless(PathBuf::from("test"));
        assert!(id.is_ok());
        assert!(id.unwrap().into_pathbuf().is_err());
    }

    #[test]
    fn test_baseless_into_pathbuf_is_storeidhasnobaseerror() {
        let id = StoreId::new_baseless(PathBuf::from("test"));
        assert!(id.is_ok());

        let pb = id.unwrap().into_pathbuf();
        assert!(pb.is_err());

        assert_eq!(pb.unwrap_err().err_type(), SEK::StoreIdHasNoBaseError);
    }

    #[test]
    fn test_basefull_into_pathbuf_is_ok() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
        assert!(id.is_ok());
        assert!(id.unwrap().into_pathbuf().is_ok());
    }

    #[test]
    fn test_basefull_into_pathbuf_is_correct() {
        let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
        assert!(id.is_ok());

        let pb = id.unwrap().into_pathbuf();
        assert!(pb.is_ok());

        assert_eq!(pb.unwrap(), PathBuf::from("/tmp/test"));
    }

    #[test]
    fn storeid_in_collection() {
        let p = module_path::ModuleEntryPath::new("1/2/3/4/5/6/7/8/9/0").into_storeid().unwrap();

        assert!(p.is_in_collection(&["test", "1"]));
        assert!(p.is_in_collection(&["test", "1", "2"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8", "9"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]));

        assert!(!p.is_in_collection(&["test", "0", "2", "3", "4", "5", "6", "7", "8", "9", "0"]));
        assert!(!p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "8"]));
        assert!(!p.is_in_collection(&["test", "1", "2", "3", "leet", "5", "6", "7"]));
    }

}
<|endoftext|>