<commit_before>use std::io::fs::File;
use std::collections::EnumSet;

use config::builders::UbuntuRepoInfo;
use super::super::context::{BuildContext, Ubuntu, Unknown};
use super::super::download::download_file;
use super::super::tarcmd::unpack_file;
use super::generic::run_command;
use super::pip;
use container::sha256::{Sha256, Digest};

#[deriving(Show)]
pub struct UbuntuInfo {
    pub release: String,
    pub apt_update: bool,
    pub has_universe: bool,
}

pub fn fetch_ubuntu_core(ctx: &mut BuildContext, release: &String)
    -> Result<(), String>
{
    if let Unknown = ctx.distribution {
    } else {
        return Err(format!("Conflicting distribution"));
    };
    let kind = "core";
    let arch = "amd64";
    let url = format!(concat!(
        "http://cdimage.ubuntu.com/ubuntu-{kind}/{release}/",
        "daily/current/{release}-{kind}-{arch}.tar.gz",
        ), kind=kind, arch=arch, release=release);
    let filename = try!(download_file(ctx, &url));
    try!(unpack_file(ctx, &filename, &Path::new("/vagga/root"), &[],
        &[Path::new("dev")]));

    ctx.distribution = Ubuntu(UbuntuInfo {
        release: release.to_string(),
        apt_update: true,
        has_universe: false,
    });
    try!(init_debian_build(ctx));

    return Ok(());
}

fn init_debian_build(ctx: &mut BuildContext) -> Result<(), String> {
    // Do not attempt to start init scripts
    try!(File::create(
            &Path::new("/vagga/root/usr/sbin/policy-rc.d"))
        .and_then(|mut f| f.write(b"#!/bin/sh\nexit 101\n"))
        .map_err(|e| format!("Error writing policy-rc.d file: {}", e)));

    // Do not need to fsync() after package installation
    try!(File::create(
            &Path::new("/vagga/root/etc/dpkg/dpkg.cfg.d/02apt-speedup"))
        .and_then(|mut f| f.write(b"force-unsafe-io"))
        .map_err(|e| format!("Error writing dpkg config: {}", e)));

    try!(ctx.add_cache_dir(Path::new("/var/cache/apt"),
                           "apt-cache".to_string()));
    try!(ctx.add_cache_dir(Path::new("/var/lib/apt/lists"),
                          "apt-lists".to_string()));
    ctx.environ.insert("DEBIAN_FRONTEND".to_string(),
                       "noninteractive".to_string());
    ctx.environ.insert("LANG".to_string(),
                       "en_US.UTF-8".to_string());
    try!(run_command(ctx, &[
        "/usr/sbin/locale-gen".to_string(),
        "en_US.UTF-8".to_string(),
        ]));

    ctx.add_remove_dir(Path::new("/var/lib/apt"));
    ctx.add_remove_dir(Path::new("/var/lib/dpkg"));
    return Ok(());
}

pub fn apt_install(ctx: &mut BuildContext, pkgs: &Vec<String>)
    -> Result<(), String>
{
    let apt_update = if let Ubuntu(ref ubuntu) = ctx.distribution {
        ubuntu.apt_update
    } else {
        return Err(format!("Incompatible distribution: {}", ctx.distribution));
    };
    if apt_update {
        if let Ubuntu(ref mut ubuntu) = ctx.distribution {
            ubuntu.apt_update = true;
        }
        try!(run_command(ctx, &[
            "/usr/bin/apt-get".to_string(),
            "update".to_string(),
            ]));
    }
    let mut args = vec!(
        "/usr/bin/apt-get".to_string(),
        "install".to_string(),
        "-y".to_string(),
        );
    args.extend(pkgs.clone().into_iter());
    run_command(ctx, args.as_slice())
}

pub fn apt_remove(ctx: &mut BuildContext, pkgs: &Vec<String>)
    -> Result<(), String>
{
    let mut args = vec!(
        "/usr/bin/apt-get".to_string(),
        "remove".to_string(),
        "-y".to_string(),
        );
    args.extend(pkgs.clone().into_iter());
    run_command(ctx, args.as_slice())
}

pub fn finish(ctx: &mut BuildContext) -> Result<(), String>
{
    let pkgs = ctx.build_deps.clone().into_iter().collect();
    try!(apt_remove(ctx, &pkgs));
    run_command(ctx, &[
        "/usr/bin/apt-get".to_string(),
        "autoremove".to_string(),
        "-y".to_string(),
        ])
}

pub fn add_debian_repo(ctx: &mut BuildContext, repo: &UbuntuRepoInfo)
    -> Result<(), String>
{
    let mut hash = Sha256::new();
    hash.input_str(repo.url.as_slice());
    hash.input(&[0]);
    hash.input_str(repo.suite.as_slice());
    hash.input(&[0]);
    for cmp in repo.components.iter() {
        hash.input_str(cmp.as_slice());
        hash.input(&[0]);
    }
    let name = format!("{}-{}.list",
        hash.result_str()[..8].to_string(),
        repo.suite);

    if let Ubuntu(ref mut ubuntu) = ctx.distribution {
        ubuntu.apt_update = true;
    } else {
        return Err(format!("Incompatible distribution: {}", ctx.distribution));
    };

    File::create(&Path::new("/vagga/root/etc/apt/sources.list.d")
                 .join(name.as_slice()))
        .and_then(|mut f| {
            try!(write!(f, "deb {} {}", repo.url, repo.suite))
            for item in repo.components.iter() {
                try!(write!(f, " {}", item));
            }
            Ok(())
        })
        .map_err(|e| format!("Error writing {} file: {}", name, e))
    // TODO(tailhook) add `update` command
}

pub fn ubuntu_add_universe(ctx: &mut BuildContext)
    -> Result<(), String>
{
    let target = "/vagga/root/etc/apt/sources.list.d/universe.list";
    if let Ubuntu(ref ubuntu) = ctx.distribution {
        try!(File::create(&Path::new(target))
            .and_then(|mut f| {
            try!(writeln!(f,
                "deb http://archive.ubuntu.com/ubuntu/ {} universe",
                ubuntu.release));
            try!(writeln!(f,
                "deb http://archive.ubuntu.com/ubuntu/ {}-updates universe",
                ubuntu.release));
            try!(writeln!(f,
                "deb http://archive.ubuntu.com/ubuntu/ {}-security universe",
                ubuntu.release));
                Ok(())
            })
            .map_err(|e| format!("Error writing universe.list file: {}", e)));
    } else {
        return Err(format!("Incompatible distribution: {}", ctx.distribution));
    };
    match ctx.distribution {
        Ubuntu(ref mut ubuntu) => { ubuntu.has_universe = true; }
        _ => unreachable!(),
    }
    Ok(())
}

pub fn ensure_pip(ctx: &mut BuildContext, ver: u8,
    features: &EnumSet<pip::PipFeatures>)
    -> Result<Path, String>
{
    let needs_universe = match ctx.distribution {
        Ubuntu(ref ubuntu) => !ubuntu.has_universe,
        _ => unreachable!(),
    };
    if needs_universe {
        debug!("Add Universe");
        try!(ubuntu_add_universe(ctx));
    }
    let mut packages = vec!(
        (if ver == 2 { "python" } else { "python3" }).to_string(),
        );
    ctx.packages.extend(packages.clone().into_iter());
    for i in features.iter() {
        let dep = match i {
            pip::Dev => (if ver == 2 { "python-dev" }
                         else { "python3-dev" }).to_string(),
            pip::Pip => (if ver == 2 { "python-pip" }
                         else { "python3-pip" }).to_string(),
            pip::Git => "git".to_string(),
            pip::Hg => "hg".to_string(),
        };
        if !ctx.packages.contains(&dep) {
            if ctx.build_deps.insert(dep.clone()) {
                packages.push(dep);
            }
        }
    }
    if packages.len() > 0 {
        try!(apt_install(ctx, &packages));
    }
    return Ok(Path::new(format!("/usr/bin/pip{}", ver)));
}
<commit_msg>Do not delete debian package status any more<commit_after>use std::io::fs::File;
use std::collections::EnumSet;

use config::builders::UbuntuRepoInfo;
use super::super::context::{BuildContext, Ubuntu, Unknown};
use super::super::download::download_file;
use super::super::tarcmd::unpack_file;
use super::generic::run_command;
use super::pip;
use container::sha256::{Sha256, Digest};

#[deriving(Show)]
pub struct UbuntuInfo {
    pub release: String,
    pub apt_update: bool,
    pub has_universe: bool,
}

pub fn fetch_ubuntu_core(ctx: &mut BuildContext, release: &String)
    -> Result<(), String>
{
    if let Unknown = ctx.distribution {
    } else {
        return Err(format!("Conflicting distribution"));
    };
    let kind = "core";
    let arch = "amd64";
    let url = format!(concat!(
        "http://cdimage.ubuntu.com/ubuntu-{kind}/{release}/",
        "daily/current/{release}-{kind}-{arch}.tar.gz",
        ), kind=kind, arch=arch, release=release);
    let filename = try!(download_file(ctx, &url));
    try!(unpack_file(ctx, &filename, &Path::new("/vagga/root"), &[],
        &[Path::new("dev")]));

    ctx.distribution = Ubuntu(UbuntuInfo {
        release: release.to_string(),
        apt_update: true,
        has_universe: false,
    });
    try!(init_debian_build(ctx));

    return Ok(());
}

fn init_debian_build(ctx: &mut BuildContext) -> Result<(), String> {
    // Do not attempt to start init scripts
    try!(File::create(
            &Path::new("/vagga/root/usr/sbin/policy-rc.d"))
        .and_then(|mut f| f.write(b"#!/bin/sh\nexit 101\n"))
        .map_err(|e| format!("Error writing policy-rc.d file: {}", e)));

    // Do not need to fsync() after package installation
    try!(File::create(
            &Path::new("/vagga/root/etc/dpkg/dpkg.cfg.d/02apt-speedup"))
        .and_then(|mut f| f.write(b"force-unsafe-io"))
        .map_err(|e| format!("Error writing dpkg config: {}", e)));

    try!(ctx.add_cache_dir(Path::new("/var/cache/apt"),
                           "apt-cache".to_string()));
    try!(ctx.add_cache_dir(Path::new("/var/lib/apt/lists"),
                          "apt-lists".to_string()));
    ctx.environ.insert("DEBIAN_FRONTEND".to_string(),
                       "noninteractive".to_string());
    ctx.environ.insert("LANG".to_string(),
                       "en_US.UTF-8".to_string());
    try!(run_command(ctx, &[
        "/usr/sbin/locale-gen".to_string(),
        "en_US.UTF-8".to_string(),
        ]));

    ctx.add_remove_dir(Path::new("/var/lib/apt"));
    // TODO(tailhook) decide if we want to delete package databases
    // ctx.add_remove_dir(Path::new("/var/lib/dpkg"));
    return Ok(());
}

pub fn apt_install(ctx: &mut BuildContext, pkgs: &Vec<String>)
    -> Result<(), String>
{
    let apt_update = if let Ubuntu(ref ubuntu) = ctx.distribution {
        ubuntu.apt_update
    } else {
        return Err(format!("Incompatible distribution: {}", ctx.distribution));
    };
    if apt_update {
        if let Ubuntu(ref mut ubuntu) = ctx.distribution {
            ubuntu.apt_update = true;
        }
        try!(run_command(ctx, &[
            "/usr/bin/apt-get".to_string(),
            "update".to_string(),
            ]));
    }
    let mut args = vec!(
        "/usr/bin/apt-get".to_string(),
        "install".to_string(),
        "-y".to_string(),
        );
    args.extend(pkgs.clone().into_iter());
    run_command(ctx, args.as_slice())
}

pub fn apt_remove(ctx: &mut BuildContext, pkgs: &Vec<String>)
    -> Result<(), String>
{
    let mut args = vec!(
        "/usr/bin/apt-get".to_string(),
        "remove".to_string(),
        "-y".to_string(),
        );
    args.extend(pkgs.clone().into_iter());
    run_command(ctx, args.as_slice())
}

pub fn finish(ctx: &mut BuildContext) -> Result<(), String>
{
    let pkgs = ctx.build_deps.clone().into_iter().collect();
    try!(apt_remove(ctx, &pkgs));
    run_command(ctx, &[
        "/usr/bin/apt-get".to_string(),
        "autoremove".to_string(),
        "-y".to_string(),
        ])
}

pub fn add_debian_repo(ctx: &mut BuildContext, repo: &UbuntuRepoInfo)
    -> Result<(), String>
{
    let mut hash = Sha256::new();
    hash.input_str(repo.url.as_slice());
    hash.input(&[0]);
    hash.input_str(repo.suite.as_slice());
    hash.input(&[0]);
    for cmp in repo.components.iter() {
        hash.input_str(cmp.as_slice());
        hash.input(&[0]);
    }
    let name = format!("{}-{}.list",
        hash.result_str()[..8].to_string(),
        repo.suite);

    if let Ubuntu(ref mut ubuntu) = ctx.distribution {
        ubuntu.apt_update = true;
    } else {
        return Err(format!("Incompatible distribution: {}", ctx.distribution));
    };

    File::create(&Path::new("/vagga/root/etc/apt/sources.list.d")
                 .join(name.as_slice()))
        .and_then(|mut f| {
            try!(write!(f, "deb {} {}", repo.url, repo.suite))
            for item in repo.components.iter() {
                try!(write!(f, " {}", item));
            }
            Ok(())
        })
        .map_err(|e| format!("Error writing {} file: {}", name, e))
    // TODO(tailhook) add `update` command
}

pub fn ubuntu_add_universe(ctx: &mut BuildContext)
    -> Result<(), String>
{
    let target = "/vagga/root/etc/apt/sources.list.d/universe.list";
    if let Ubuntu(ref ubuntu) = ctx.distribution {
        try!(File::create(&Path::new(target))
            .and_then(|mut f| {
            try!(writeln!(f,
                "deb http://archive.ubuntu.com/ubuntu/ {} universe",
                ubuntu.release));
            try!(writeln!(f,
                "deb http://archive.ubuntu.com/ubuntu/ {}-updates universe",
                ubuntu.release));
            try!(writeln!(f,
                "deb http://archive.ubuntu.com/ubuntu/ {}-security universe",
                ubuntu.release));
                Ok(())
            })
            .map_err(|e| format!("Error writing universe.list file: {}", e)));
    } else {
        return Err(format!("Incompatible distribution: {}", ctx.distribution));
    };
    match ctx.distribution {
        Ubuntu(ref mut ubuntu) => { ubuntu.has_universe = true; }
        _ => unreachable!(),
    }
    Ok(())
}

pub fn ensure_pip(ctx: &mut BuildContext, ver: u8,
    features: &EnumSet<pip::PipFeatures>)
    -> Result<Path, String>
{
    let needs_universe = match ctx.distribution {
        Ubuntu(ref ubuntu) => !ubuntu.has_universe,
        _ => unreachable!(),
    };
    if needs_universe {
        debug!("Add Universe");
        try!(ubuntu_add_universe(ctx));
    }
    let mut packages = vec!(
        (if ver == 2 { "python" } else { "python3" }).to_string(),
        );
    ctx.packages.extend(packages.clone().into_iter());
    for i in features.iter() {
        let dep = match i {
            pip::Dev => (if ver == 2 { "python-dev" }
                         else { "python3-dev" }).to_string(),
            pip::Pip => (if ver == 2 { "python-pip" }
                         else { "python3-pip" }).to_string(),
            pip::Git => "git".to_string(),
            pip::Hg => "hg".to_string(),
        };
        if !ctx.packages.contains(&dep) {
            if ctx.build_deps.insert(dep.clone()) {
                packages.push(dep);
            }
        }
    }
    if packages.len() > 0 {
        try!(apt_install(ctx, &packages));
    }
    return Ok(Path::new(format!("/usr/bin/pip{}", ver)));
}
<|endoftext|>


<commit_before>/**
 * Cargo compile currently does the following steps:
 *
 * All configurations are already injected as environment variables via the main cargo command
 *
 * 1. Read the manifest
 * 2. Shell out to `cargo-resolve` with a list of dependencies and sources as stdin
 *    a. Shell out to `--do update` and `--do list` for each source
 *    b. Resolve dependencies and return a list of name/version/source
 * 3. Shell out to `--do download` for each source
 * 4. Shell out to `--do get` for each source, and build up the list of paths to pass to rustc -L
 * 5. Call `cargo-rustc` with the results of the resolver zipped together with the results of the `get`
 *    a. Topologically sort the dependencies
 *    b. Compile each dependency in order, passing in the -L's pointing at each previously compiled dependency
 */

use std::os;
use std::result;
use util::config;
use util::config::{ConfigValue};
use core::{Package,PackageSet,Source,SourceSet};
use core::resolver::resolve;
use core::source::{GitKind,SourceId};
use sources::{PathSource,GitSource};
use sources::git::GitRemote;
use ops;
use util::{CargoResult, Wrap, Require, simple_human, other_error};

pub fn compile(manifest_path: &Path) -> CargoResult<()> {
    log!(4, "compile; manifest-path={}", manifest_path.display());

    // TODO: Move this into PathSource
    let package = try!(PathSource::read_package(manifest_path));
    debug!("loaded package; package={}", package);

    let sources = try!(sources_for(&package));

    try!(sources.update().wrap("unable to update sources"));
    let summaries = try!(sources.list().wrap("unable to list packages from source"));
    let resolved = try!(resolve(package.get_dependencies(), &summaries).wrap("unable to resolve dependencies"));

    try!(sources.download(resolved.as_slice()).wrap("unable to download packages"));

    let packages = try!(sources.get(resolved.as_slice()).wrap("unable to get packages from source"));

    log!(5, "fetch packages from source; packages={}; ids={}", packages, resolved);

    let package_set = PackageSet::new(packages.as_slice());

    try!(ops::compile_packages(&package, &package_set));

    Ok(())
}

fn sources_for(package: &Package) -> CargoResult<SourceSet> {
    let mut sources = try!(sources_from_config([package.get_manifest_path().dir_path()]));

    let git_sources: Vec<Box<Source>> = try!(result::collect(package.get_sources().iter().map(|source_id: &SourceId| {
        match source_id.kind {
            GitKind(ref reference) => {
                let remote = GitRemote::new(source_id.url.clone(), false);
                let home = try!(os::homedir().require(simple_human("Cargo couldn't find a home directory")));
                let git = home.join(".cargo").join("git");
                // .cargo/git/db
                // .cargo/git/checkouts
                let db_path = git.join("db").join(source_id.url.to_str());
                let checkout_path = git.join("checkouts").join(source_id.url.to_str()).join(reference.as_slice());
                Ok(box GitSource::new(remote, reference.clone(), db_path, checkout_path) as Box<Source>)
            }
        }
    })));

    sources.push_all_move(git_sources);

    Ok(SourceSet::new(sources))
}

fn sources_from_config(additional: &[Path]) -> CargoResult<Vec<Box<Source>>> {
    let configs = try!(config::all_configs(os::getcwd()));

    debug!("loaded config; configs={}", configs);

    let config_paths = configs.find_equiv(&"paths").map(|v| v.clone()).unwrap_or_else(|| ConfigValue::new());

    let mut paths: Vec<Path> = match config_paths.get_value() {
        &config::String(_) => return Err(other_error("The path was configured as a String instead of a List")),
        &config::List(ref list) => list.iter().map(|path| Path::new(path.as_slice())).collect()
    };

    paths.push_all(additional);

    Ok(vec!(box PathSource::new(paths) as Box<Source>))
}
<commit_msg>Better file names<commit_after>/**
 * Cargo compile currently does the following steps:
 *
 * All configurations are already injected as environment variables via the main cargo command
 *
 * 1. Read the manifest
 * 2. Shell out to `cargo-resolve` with a list of dependencies and sources as stdin
 *    a. Shell out to `--do update` and `--do list` for each source
 *    b. Resolve dependencies and return a list of name/version/source
 * 3. Shell out to `--do download` for each source
 * 4. Shell out to `--do get` for each source, and build up the list of paths to pass to rustc -L
 * 5. Call `cargo-rustc` with the results of the resolver zipped together with the results of the `get`
 *    a. Topologically sort the dependencies
 *    b. Compile each dependency in order, passing in the -L's pointing at each previously compiled dependency
 */

use std::io::MemWriter;
use std::os;
use std::result;
use std::hash::sip::SipHasher;
use std::hash::Hasher;
use serialize::hex::ToHex;
use url::Url;
use util::config;
use util::config::{ConfigValue};
use core::{Package,PackageSet,Source,SourceSet};
use core::resolver::resolve;
use core::source::{GitKind,SourceId};
use sources::{PathSource,GitSource};
use sources::git::GitRemote;
use ops;
use util::{CargoResult, Wrap, Require, simple_human, other_error};

pub fn compile(manifest_path: &Path) -> CargoResult<()> {
    log!(4, "compile; manifest-path={}", manifest_path.display());

    // TODO: Move this into PathSource
    let package = try!(PathSource::read_package(manifest_path));
    debug!("loaded package; package={}", package);

    let sources = try!(sources_for(&package));

    try!(sources.update().wrap("unable to update sources"));
    let summaries = try!(sources.list().wrap("unable to list packages from source"));
    let resolved = try!(resolve(package.get_dependencies(), &summaries).wrap("unable to resolve dependencies"));

    try!(sources.download(resolved.as_slice()).wrap("unable to download packages"));

    let packages = try!(sources.get(resolved.as_slice()).wrap("unable to get packages from source"));

    log!(5, "fetch packages from source; packages={}; ids={}", packages, resolved);

    let package_set = PackageSet::new(packages.as_slice());

    try!(ops::compile_packages(&package, &package_set));

    Ok(())
}

fn sources_for(package: &Package) -> CargoResult<SourceSet> {
    let mut sources = try!(sources_from_config([package.get_manifest_path().dir_path()]));

    let git_sources: Vec<Box<Source>> = try!(result::collect(package.get_sources().iter().map(|source_id: &SourceId| {
        match source_id.kind {
            GitKind(ref reference) => {
                let remote = GitRemote::new(source_id.url.clone(), false);
                let home = try!(os::homedir().require(simple_human("Cargo couldn't find a home directory")));
                let git = home.join(".cargo").join("git");
                let ident = url_to_path_ident(&source_id.url);

                // .cargo/git/db
                // .cargo/git/checkouts
                let db_path = git.join("db").join(ident.as_slice());
                let checkout_path = git.join("checkouts").join(ident.as_slice()).join(reference.as_slice());
                Ok(box GitSource::new(remote, reference.clone(), db_path, checkout_path) as Box<Source>)
            }
        }
    })));

    sources.push_all_move(git_sources);

    Ok(SourceSet::new(sources))
}

fn sources_from_config(additional: &[Path]) -> CargoResult<Vec<Box<Source>>> {
    let configs = try!(config::all_configs(os::getcwd()));

    debug!("loaded config; configs={}", configs);

    let config_paths = configs.find_equiv(&"paths").map(|v| v.clone()).unwrap_or_else(|| ConfigValue::new());

    let mut paths: Vec<Path> = match config_paths.get_value() {
        &config::String(_) => return Err(other_error("The path was configured as a String instead of a List")),
        &config::List(ref list) => list.iter().map(|path| Path::new(path.as_slice())).collect()
    };

    paths.push_all(additional);

    Ok(vec!(box PathSource::new(paths) as Box<Source>))
}

fn url_to_path_ident(url: &Url) -> String {
    let hasher = SipHasher::new_with_keys(0,0);

    let mut ident = url.path.as_slice().split('/').last().unwrap();

    ident = if ident == "" {
        "_empty"
    } else {
        ident
    };

    format!("{}-{}", ident, to_hex(hasher.hash(&url.to_str())))
}

fn to_hex(num: u64) -> String {
    let mut writer = MemWriter::with_capacity(8);
    writer.write_le_u64(num).unwrap(); // this should never fail
    writer.get_ref().to_hex()
}

#[cfg(test)]
mod test {
    use url;
    use url::Url;
    use super::url_to_path_ident;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = url_to_path_ident(&url("https://github.com/carlhuda/cargo"));
        assert_eq!(ident.as_slice(), "cargo-0eed735c8ffd7c88");
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = url_to_path_ident(&url("https://github.com"));
        assert_eq!(ident.as_slice(), "_empty-fc065c9b6b16fc00");
    }

    fn url(s: &str) -> Url {
        url::from_str(s).unwrap()
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Mozilla
//
// 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::collections::{
    BTreeSet,
};

use core_traits::{
    ValueType,
    ValueTypeSet,
};

use types::{
    ValueTypeTag,
};

/// Type safe representation of the possible return values from SQLite's `typeof`
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum SQLTypeAffinity {
    Null,    // "null"
    Integer, // "integer"
    Real,    // "real"
    Text,    // "text"
    Blob,    // "blob"
}

// Put this here rather than in `db` simply because it's widely needed.
pub trait SQLValueType {
    fn value_type_tag(&self) -> ValueTypeTag;
    fn accommodates_integer(&self, int: i64) -> bool;

    /// Return a pair of the ValueTypeTag for this value type, and the SQLTypeAffinity required
    /// to distinguish it from any other types that share the same tag.
    ///
    /// Background: The tag alone is not enough to determine the type of a value, since multiple
    /// ValueTypes may share the same tag (for example, ValueType::Long and ValueType::Double).
    /// However, each ValueType can be determined by checking both the tag and the type's affinity.
    fn sql_representation(&self) -> (ValueTypeTag, Option<SQLTypeAffinity>);
}

impl SQLValueType for ValueType {
    fn sql_representation(&self) -> (ValueTypeTag, Option<SQLTypeAffinity>) {
        match *self {
            ValueType::Ref     => (0, None),
            ValueType::Boolean => (1, None),
            ValueType::Instant => (4, None),

            // SQLite distinguishes integral from decimal types, allowing long and double to share a tag.
            ValueType::Long    => (5, Some(SQLTypeAffinity::Integer)),
            ValueType::Double  => (5, Some(SQLTypeAffinity::Real)),
            ValueType::String  => (10, None),
            ValueType::Uuid    => (11, None),
            ValueType::Keyword => (13, None),
        }
    }

    #[inline]
    fn value_type_tag(&self) -> ValueTypeTag {
        self.sql_representation().0
    }

    /// Returns true if the provided integer is in the SQLite value space of this type. For
    /// example, `1` is how we encode `true`.
    ///
    /// ```
    /// extern crate core_traits;
    /// extern crate mentat_core;
    /// use core_traits::ValueType;
    /// use mentat_core::SQLValueType;
    /// assert!(!ValueType::Instant.accommodates_integer(1493399581314));
    /// assert!(!ValueType::Instant.accommodates_integer(1493399581314000));
    /// assert!(ValueType::Boolean.accommodates_integer(1));
    /// assert!(!ValueType::Boolean.accommodates_integer(-1));
    /// assert!(!ValueType::Boolean.accommodates_integer(10));
    /// assert!(!ValueType::String.accommodates_integer(10));
    /// ```
    fn accommodates_integer(&self, int: i64) -> bool {
        use ValueType::*;
        match *self {
            Instant                 => false,          // Always use #inst.
            Long | Double           => true,
            Ref                     => int >= 0,
            Boolean                 => (int == 0) || (int == 1),
            ValueType::String       => false,
            Keyword                 => false,
            Uuid                    => false,
        }
    }
}

/// We have an enum of types, `ValueType`. It can be collected into a set, `ValueTypeSet`. Each type
/// is associated with a type tag, which is how a type is represented in, e.g., SQL storage. Types
/// can share type tags, because backing SQL storage is able to differentiate between some types
/// (e.g., longs and doubles), and so distinct tags aren't necessary. That association is defined by
/// `SQLValueType`. That trait similarly extends to `ValueTypeSet`, which maps a collection of types
/// into a collection of tags.
pub trait SQLValueTypeSet {
    fn value_type_tags(&self) -> BTreeSet<ValueTypeTag>;
    fn has_unique_type_tag(&self) -> bool;
    fn unique_type_tag(&self) -> Option<ValueTypeTag>;
}

impl SQLValueTypeSet for ValueTypeSet {
    // This is inefficient, but it'll do for now.
    fn value_type_tags(&self) -> BTreeSet<ValueTypeTag> {
        let mut out = BTreeSet::new();
        for t in self.0.iter() {
            out.insert(t.value_type_tag());
        }
        out
    }

    fn unique_type_tag(&self) -> Option<ValueTypeTag> {
        if self.is_unit() || self.has_unique_type_tag() {
            self.exemplar().map(|t| t.value_type_tag())
        } else {
            None
        }
    }

    fn has_unique_type_tag(&self) -> bool {
        if self.is_unit() {
            return true;
        }

        let mut acc = BTreeSet::new();
        for t in self.0.iter() {
            if acc.insert(t.value_type_tag()) && acc.len() > 1 {
                // We inserted a second or subsequent value.
                return false;
            }
        }
        !acc.is_empty()
    }
}
<commit_msg>Post: Make tests pass on Rust 1.25.0<commit_after>// Copyright 2018 Mozilla
//
// 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::collections::{
    BTreeSet,
};

use core_traits::{
    ValueType,
    ValueTypeSet,
};

use types::{
    ValueTypeTag,
};

/// Type safe representation of the possible return values from SQLite's `typeof`
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum SQLTypeAffinity {
    Null,    // "null"
    Integer, // "integer"
    Real,    // "real"
    Text,    // "text"
    Blob,    // "blob"
}

// Put this here rather than in `db` simply because it's widely needed.
pub trait SQLValueType {
    fn value_type_tag(&self) -> ValueTypeTag;
    fn accommodates_integer(&self, int: i64) -> bool;

    /// Return a pair of the ValueTypeTag for this value type, and the SQLTypeAffinity required
    /// to distinguish it from any other types that share the same tag.
    ///
    /// Background: The tag alone is not enough to determine the type of a value, since multiple
    /// ValueTypes may share the same tag (for example, ValueType::Long and ValueType::Double).
    /// However, each ValueType can be determined by checking both the tag and the type's affinity.
    fn sql_representation(&self) -> (ValueTypeTag, Option<SQLTypeAffinity>);
}

impl SQLValueType for ValueType {
    fn sql_representation(&self) -> (ValueTypeTag, Option<SQLTypeAffinity>) {
        match *self {
            ValueType::Ref     => (0, None),
            ValueType::Boolean => (1, None),
            ValueType::Instant => (4, None),

            // SQLite distinguishes integral from decimal types, allowing long and double to share a tag.
            ValueType::Long    => (5, Some(SQLTypeAffinity::Integer)),
            ValueType::Double  => (5, Some(SQLTypeAffinity::Real)),
            ValueType::String  => (10, None),
            ValueType::Uuid    => (11, None),
            ValueType::Keyword => (13, None),
        }
    }

    #[inline]
    fn value_type_tag(&self) -> ValueTypeTag {
        self.sql_representation().0
    }

    /// Returns true if the provided integer is in the SQLite value space of this type. For
    /// example, `1` is how we encode `true`.
    fn accommodates_integer(&self, int: i64) -> bool {
        use ValueType::*;
        match *self {
            Instant                 => false,          // Always use #inst.
            Long | Double           => true,
            Ref                     => int >= 0,
            Boolean                 => (int == 0) || (int == 1),
            ValueType::String       => false,
            Keyword                 => false,
            Uuid                    => false,
        }
    }
}

/// We have an enum of types, `ValueType`. It can be collected into a set, `ValueTypeSet`. Each type
/// is associated with a type tag, which is how a type is represented in, e.g., SQL storage. Types
/// can share type tags, because backing SQL storage is able to differentiate between some types
/// (e.g., longs and doubles), and so distinct tags aren't necessary. That association is defined by
/// `SQLValueType`. That trait similarly extends to `ValueTypeSet`, which maps a collection of types
/// into a collection of tags.
pub trait SQLValueTypeSet {
    fn value_type_tags(&self) -> BTreeSet<ValueTypeTag>;
    fn has_unique_type_tag(&self) -> bool;
    fn unique_type_tag(&self) -> Option<ValueTypeTag>;
}

impl SQLValueTypeSet for ValueTypeSet {
    // This is inefficient, but it'll do for now.
    fn value_type_tags(&self) -> BTreeSet<ValueTypeTag> {
        let mut out = BTreeSet::new();
        for t in self.0.iter() {
            out.insert(t.value_type_tag());
        }
        out
    }

    fn unique_type_tag(&self) -> Option<ValueTypeTag> {
        if self.is_unit() || self.has_unique_type_tag() {
            self.exemplar().map(|t| t.value_type_tag())
        } else {
            None
        }
    }

    fn has_unique_type_tag(&self) -> bool {
        if self.is_unit() {
            return true;
        }

        let mut acc = BTreeSet::new();
        for t in self.0.iter() {
            if acc.insert(t.value_type_tag()) && acc.len() > 1 {
                // We inserted a second or subsequent value.
                return false;
            }
        }
        !acc.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use core_traits::{
        ValueType,
    };
    use sql_types::{
        SQLValueType,
    };

    #[test]
    fn test_accommodates_integer() {
        assert!(!ValueType::Instant.accommodates_integer(1493399581314));
        assert!(!ValueType::Instant.accommodates_integer(1493399581314000));
        assert!(ValueType::Boolean.accommodates_integer(1));
        assert!(!ValueType::Boolean.accommodates_integer(-1));
        assert!(!ValueType::Boolean.accommodates_integer(10));
        assert!(!ValueType::String.accommodates_integer(10));
    }
}
<|endoftext|>


<commit_before>// UNKNOWN accessiblity
const UNKNOWN: i8 = -1;

// FOOT_FORBIDDEN that no pedestrian is allowed
const FOOT_FORBIDDEN: i8 = 0;
// FOOT_ALLOWED pedestrians are allowed in both directions
const FOOT_ALLOWED: i8 = 1;

// CAR_FORBIDDEN no car is allowed
const CAR_FORBIDDEN: i8 = 0;
// CAR_RESIDENTIAL http://wiki.openstreetmap.org/wiki/Tag:highway%3Dresidential
const CAR_RESIDENTIAL: i8 = 1;
// CAR_TERTIARY http://wiki.openstreetmap.org/wiki/Tag:highway%3Dtertiary
const CAR_TERTIARY: i8 = 2;
// CAR_SECONDARY http://wiki.openstreetmap.org/wiki/Tag:highway%3Dsecondary
const CAR_SECONDARY: i8 = 3;
// car_forward http://wiki.http://wiki.openstreetmap.org/wiki/Tag:highway%3Dprimary
const CAR_PRIMARY: i8 = 4;
// CAR_TRUNK http://wiki.openstreetmap.org/wiki/Tag:highway%3Dtrunk
const CAR_TRUNK: i8 = 5;
// CAR_MOTORWAY http://wiki.openstreetmap.org/wiki/Tag:highway%3Dmotorway
const CAR_MOTORWAY: i8 = 6;

// BIKE_FORBIDDEN BIKE_ can not use this edge
const BIKE_FORBIDDEN: i8 = 0;
// BIKE_ALLOWED means that it can be used by a BIKE_, but the traffic might be shared with CAR_s
const BIKE_ALLOWED: i8 = 2;
// BIKE_LANE is a narrow lane dedicated for BIKE_, without physical separation from other traffic
const BIKE_LANE: i8 = 3;
// BIKE_BUSWAY means that BIKE_s are allowed on the bus lane
const BIKE_BUSWAY: i8 = 4;
// BIKE_TRACK is a physically separated for any other traffic
const BIKE_TRACK: i8 = 5;

// Edgeself contains what mode can use the edge in each direction
#[derive(Clone, Copy, Default)]
pub struct EdgeProperties {
    pub foot: i8,
    pub car_forward: i8,
    pub car_backward: i8,
    pub bike_forward: i8,
    pub bike_backward: i8,
}

impl EdgeProperties {
    pub fn default() -> EdgeProperties {
        EdgeProperties {
            foot: UNKNOWN,
            car_forward: UNKNOWN,
            car_backward: UNKNOWN,
            bike_forward: UNKNOWN,
            bike_backward: UNKNOWN,
        }
    }

    // Normalize fills UNKNOWN fields
    pub fn normalize(&mut self) {
        if self.car_backward == UNKNOWN {
            self.car_backward = self.car_forward;
        }
        if self.bike_backward == UNKNOWN {
            self.bike_backward = self.bike_forward;
        }
        if self.car_forward == UNKNOWN {
            self.car_forward = CAR_FORBIDDEN;
        }
        if self.bike_forward == UNKNOWN {
            self.bike_forward = BIKE_FORBIDDEN;
        }
        if self.car_backward == UNKNOWN {
            self.car_backward = CAR_FORBIDDEN;
        }
        if self.bike_backward == UNKNOWN {
            self.bike_backward = BIKE_FORBIDDEN;
        }
        if self.foot == UNKNOWN {
            self.foot = FOOT_FORBIDDEN;
        }
    }

    // Accessible means that at least one mean of transportation can use it in one direction
    pub fn accessible(&self) -> bool {
        self.bike_forward != BIKE_FORBIDDEN || self.bike_backward != BIKE_FORBIDDEN ||
        self.car_forward != CAR_FORBIDDEN || self.car_backward != CAR_FORBIDDEN ||
        self.foot != FOOT_FORBIDDEN
    }

    pub fn update(&mut self, key_string: String, val_string: String) {
        let key = key_string.as_str();
        let val = val_string.as_str();
        self.update_with_str(key, val);
    }

    pub fn update_with_str(&mut self, key: &str, val: &str) {
        match key {
            "highway" => {
                match val {
                    "cycleway" | "path" | "footway" | "steps" | "pedestrian" => {
                        self.bike_forward = BIKE_TRACK;
                        self.foot = FOOT_ALLOWED;
                    }
                    "primary" | "primary_link" => {
                        self.car_forward = CAR_PRIMARY;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "secondary" => {
                        self.car_forward = CAR_SECONDARY;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "tertiary" => {
                        self.car_forward = CAR_TERTIARY;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "unclassified" | "residential" | "living_street" | "road" | "service" |
                    "track" => {
                        self.car_forward = CAR_RESIDENTIAL;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "motorway" | "motorway_link" => {
                        self.car_forward = CAR_MOTORWAY;
                        self.foot = FOOT_FORBIDDEN;
                        self.bike_forward = BIKE_FORBIDDEN;
                    }
                    "trunk" | "trunk_link" => {
                        self.car_forward = CAR_TRUNK;
                        self.foot = FOOT_FORBIDDEN;
                        self.bike_forward = BIKE_FORBIDDEN;
                    }
                    _ => {}
                }
            }
            "pedestrian" | "foot" => {
                match val {
                    "no" => self.foot = FOOT_FORBIDDEN,
                    _ => self.foot = FOOT_ALLOWED,
                }
            }

            // http://wiki.openstreetmap.org/wiki/Cycleway
            // http://wiki.openstreetmap.org/wiki/Map_Features#Cycleway
            "cycleway" => {
                match val {
                    "track" => self.bike_forward = BIKE_TRACK,
                    "opposite_track" => self.bike_backward = BIKE_TRACK,
                    "opposite" => self.bike_backward = BIKE_ALLOWED,
                    "share_busway" => self.bike_forward = BIKE_BUSWAY,
                    "lane_left" | "opposite_lane" => self.bike_backward = BIKE_LANE,
                    _ => self.bike_forward = BIKE_LANE,
                }
            }

            "bicycle" => {
                match val {
                    "no" | "false" => self.bike_forward = BIKE_FORBIDDEN,
                    _ => self.bike_forward = BIKE_ALLOWED,
                }
            }
            "busway" => {
                match val {
                    "opposite_lane" | "opposite_track" => self.bike_backward = BIKE_BUSWAY,
                    _ => self.bike_forward = BIKE_BUSWAY,
                }
            }
            "oneway" => {
                match val {
                    "yes" | "true" | "1" => {
                        self.car_backward = CAR_FORBIDDEN;
                        if self.bike_backward == UNKNOWN {
                            self.bike_backward = BIKE_FORBIDDEN;
                        }
                    }
                    _ => {}
                }
            }
            "junction" => if val == "roundabout" {
                    self.car_backward = CAR_FORBIDDEN;
                    if self.bike_backward == UNKNOWN {
                        self.bike_backward = BIKE_FORBIDDEN;
                }
            }
            _ => {}
        }
    }
}

#[test]
fn test_accessible() {
    let mut p = EdgeProperties::default();
    p.normalize();
    assert!(!p.accessible());

    p.foot = FOOT_ALLOWED;
    assert!(p.accessible())
}

#[test]
fn test_normalize() {
    let mut p = EdgeProperties::default();
    p.bike_forward = BIKE_LANE;
    p.normalize();
    assert_eq!(BIKE_LANE, p.bike_backward);
    p.bike_forward = BIKE_ALLOWED;
    p.normalize();
    assert_eq!(BIKE_LANE, p.bike_backward);

    p.car_forward = CAR_SECONDARY;
    p.car_backward = UNKNOWN;
    p.normalize();
    assert_eq!(CAR_SECONDARY, p.car_backward)
}

#[test]
fn test_update() {
    let mut p = EdgeProperties::default();
    p.update_with_str("highway", "secondary");
    assert_eq!(CAR_SECONDARY, p.car_forward);

    p.update_with_str("highway", "primary_link");
    assert_eq!(CAR_PRIMARY, p.car_forward);

    p.update_with_str("highway", "motorway");
    assert_eq!(CAR_MOTORWAY, p.car_forward);

    p.update_with_str("highway", "residential");
    assert_eq!(CAR_RESIDENTIAL, p.car_forward);

    p.update_with_str("highway", "tertiary");
    assert_eq!(CAR_TERTIARY, p.car_forward);

    p.update_with_str("highway", "trunk");
    assert_eq!(CAR_TRUNK, p.car_forward);

    p.update_with_str("highway", "cycleway");
    assert_eq!(BIKE_TRACK, p.bike_forward);
    assert_eq!(FOOT_ALLOWED, p.foot);

    p.update_with_str("foot", "designated");
    assert_eq!(FOOT_ALLOWED, p.foot);

    p.update_with_str("foot", "no");
    assert_eq!(FOOT_FORBIDDEN, p.foot);

    p.update_with_str("cycleway", "lane");
    assert_eq!(BIKE_LANE, p.bike_forward);

    p.update_with_str("cycleway", "track");
    assert_eq!(BIKE_TRACK, p.bike_forward);

    p.update_with_str("cycleway", "opposite_lane");
    assert_eq!(BIKE_LANE, p.bike_backward);

    p.update_with_str("cycleway", "opposite_track");
    assert_eq!(BIKE_TRACK, p.bike_backward);

    p.update_with_str("cycleway", "opposite");
    assert_eq!(BIKE_ALLOWED, p.bike_backward);

    p.update_with_str("cycleway", "share_busway");
    assert_eq!(BIKE_BUSWAY, p.bike_forward);

    p.update_with_str("cycleway", "lane_left");
    assert_eq!(BIKE_LANE, p.bike_backward);

    p.update_with_str("bicycle", "yes");
    assert_eq!(BIKE_ALLOWED, p.bike_forward);

    p.update_with_str("bicycle", "no");
    assert_eq!(BIKE_FORBIDDEN, p.bike_forward);

    p.update_with_str("busway", "yes");
    assert_eq!(BIKE_BUSWAY, p.bike_forward);

    p.update_with_str("busway", "opposite_track");
    assert_eq!(BIKE_BUSWAY, p.bike_backward);

    p.update_with_str("oneway", "yes");
    assert_eq!(BIKE_FORBIDDEN, p.car_backward);
    assert!(p.bike_backward != BIKE_FORBIDDEN);

    p.bike_backward = UNKNOWN;
    p.update_with_str("oneway", "yes");
    assert_eq!(BIKE_FORBIDDEN, p.bike_backward);

    p.update_with_str("junction", "roundabout");
    assert_eq!(BIKE_FORBIDDEN, p.car_backward);

    p.bike_backward = UNKNOWN;
    p.update_with_str("junction", "roundabout");
    assert_eq!(BIKE_FORBIDDEN, p.bike_backward);
}
<commit_msg>Clippy: pass by value not reference<commit_after>// UNKNOWN accessiblity
const UNKNOWN: i8 = -1;

// FOOT_FORBIDDEN that no pedestrian is allowed
const FOOT_FORBIDDEN: i8 = 0;
// FOOT_ALLOWED pedestrians are allowed in both directions
const FOOT_ALLOWED: i8 = 1;

// CAR_FORBIDDEN no car is allowed
const CAR_FORBIDDEN: i8 = 0;
// CAR_RESIDENTIAL http://wiki.openstreetmap.org/wiki/Tag:highway%3Dresidential
const CAR_RESIDENTIAL: i8 = 1;
// CAR_TERTIARY http://wiki.openstreetmap.org/wiki/Tag:highway%3Dtertiary
const CAR_TERTIARY: i8 = 2;
// CAR_SECONDARY http://wiki.openstreetmap.org/wiki/Tag:highway%3Dsecondary
const CAR_SECONDARY: i8 = 3;
// car_forward http://wiki.http://wiki.openstreetmap.org/wiki/Tag:highway%3Dprimary
const CAR_PRIMARY: i8 = 4;
// CAR_TRUNK http://wiki.openstreetmap.org/wiki/Tag:highway%3Dtrunk
const CAR_TRUNK: i8 = 5;
// CAR_MOTORWAY http://wiki.openstreetmap.org/wiki/Tag:highway%3Dmotorway
const CAR_MOTORWAY: i8 = 6;

// BIKE_FORBIDDEN BIKE_ can not use this edge
const BIKE_FORBIDDEN: i8 = 0;
// BIKE_ALLOWED means that it can be used by a BIKE_, but the traffic might be shared with CAR_s
const BIKE_ALLOWED: i8 = 2;
// BIKE_LANE is a narrow lane dedicated for BIKE_, without physical separation from other traffic
const BIKE_LANE: i8 = 3;
// BIKE_BUSWAY means that BIKE_s are allowed on the bus lane
const BIKE_BUSWAY: i8 = 4;
// BIKE_TRACK is a physically separated for any other traffic
const BIKE_TRACK: i8 = 5;

// Edgeself contains what mode can use the edge in each direction
#[derive(Clone, Copy, Default)]
pub struct EdgeProperties {
    pub foot: i8,
    pub car_forward: i8,
    pub car_backward: i8,
    pub bike_forward: i8,
    pub bike_backward: i8,
}

impl EdgeProperties {
    pub fn default() -> EdgeProperties {
        EdgeProperties {
            foot: UNKNOWN,
            car_forward: UNKNOWN,
            car_backward: UNKNOWN,
            bike_forward: UNKNOWN,
            bike_backward: UNKNOWN,
        }
    }

    // Normalize fills UNKNOWN fields
    pub fn normalize(&mut self) {
        if self.car_backward == UNKNOWN {
            self.car_backward = self.car_forward;
        }
        if self.bike_backward == UNKNOWN {
            self.bike_backward = self.bike_forward;
        }
        if self.car_forward == UNKNOWN {
            self.car_forward = CAR_FORBIDDEN;
        }
        if self.bike_forward == UNKNOWN {
            self.bike_forward = BIKE_FORBIDDEN;
        }
        if self.car_backward == UNKNOWN {
            self.car_backward = CAR_FORBIDDEN;
        }
        if self.bike_backward == UNKNOWN {
            self.bike_backward = BIKE_FORBIDDEN;
        }
        if self.foot == UNKNOWN {
            self.foot = FOOT_FORBIDDEN;
        }
    }

    // Accessible means that at least one mean of transportation can use it in one direction
    pub fn accessible(self) -> bool {
        self.bike_forward != BIKE_FORBIDDEN || self.bike_backward != BIKE_FORBIDDEN ||
        self.car_forward != CAR_FORBIDDEN || self.car_backward != CAR_FORBIDDEN ||
        self.foot != FOOT_FORBIDDEN
    }

    pub fn update(&mut self, key_string: String, val_string: String) {
        let key = key_string.as_str();
        let val = val_string.as_str();
        self.update_with_str(key, val);
    }

    pub fn update_with_str(&mut self, key: &str, val: &str) {
        match key {
            "highway" => {
                match val {
                    "cycleway" | "path" | "footway" | "steps" | "pedestrian" => {
                        self.bike_forward = BIKE_TRACK;
                        self.foot = FOOT_ALLOWED;
                    }
                    "primary" | "primary_link" => {
                        self.car_forward = CAR_PRIMARY;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "secondary" => {
                        self.car_forward = CAR_SECONDARY;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "tertiary" => {
                        self.car_forward = CAR_TERTIARY;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "unclassified" | "residential" | "living_street" | "road" | "service" |
                    "track" => {
                        self.car_forward = CAR_RESIDENTIAL;
                        self.foot = FOOT_ALLOWED;
                        self.bike_forward = BIKE_ALLOWED;
                    }
                    "motorway" | "motorway_link" => {
                        self.car_forward = CAR_MOTORWAY;
                        self.foot = FOOT_FORBIDDEN;
                        self.bike_forward = BIKE_FORBIDDEN;
                    }
                    "trunk" | "trunk_link" => {
                        self.car_forward = CAR_TRUNK;
                        self.foot = FOOT_FORBIDDEN;
                        self.bike_forward = BIKE_FORBIDDEN;
                    }
                    _ => {}
                }
            }
            "pedestrian" | "foot" => {
                match val {
                    "no" => self.foot = FOOT_FORBIDDEN,
                    _ => self.foot = FOOT_ALLOWED,
                }
            }

            // http://wiki.openstreetmap.org/wiki/Cycleway
            // http://wiki.openstreetmap.org/wiki/Map_Features#Cycleway
            "cycleway" => {
                match val {
                    "track" => self.bike_forward = BIKE_TRACK,
                    "opposite_track" => self.bike_backward = BIKE_TRACK,
                    "opposite" => self.bike_backward = BIKE_ALLOWED,
                    "share_busway" => self.bike_forward = BIKE_BUSWAY,
                    "lane_left" | "opposite_lane" => self.bike_backward = BIKE_LANE,
                    _ => self.bike_forward = BIKE_LANE,
                }
            }

            "bicycle" => {
                match val {
                    "no" | "false" => self.bike_forward = BIKE_FORBIDDEN,
                    _ => self.bike_forward = BIKE_ALLOWED,
                }
            }
            "busway" => {
                match val {
                    "opposite_lane" | "opposite_track" => self.bike_backward = BIKE_BUSWAY,
                    _ => self.bike_forward = BIKE_BUSWAY,
                }
            }
            "oneway" => {
                match val {
                    "yes" | "true" | "1" => {
                        self.car_backward = CAR_FORBIDDEN;
                        if self.bike_backward == UNKNOWN {
                            self.bike_backward = BIKE_FORBIDDEN;
                        }
                    }
                    _ => {}
                }
            }
            "junction" => if val == "roundabout" {
                    self.car_backward = CAR_FORBIDDEN;
                    if self.bike_backward == UNKNOWN {
                        self.bike_backward = BIKE_FORBIDDEN;
                }
            }
            _ => {}
        }
    }
}

#[test]
fn test_accessible() {
    let mut p = EdgeProperties::default();
    p.normalize();
    assert!(!p.accessible());

    p.foot = FOOT_ALLOWED;
    assert!(p.accessible())
}

#[test]
fn test_normalize() {
    let mut p = EdgeProperties::default();
    p.bike_forward = BIKE_LANE;
    p.normalize();
    assert_eq!(BIKE_LANE, p.bike_backward);
    p.bike_forward = BIKE_ALLOWED;
    p.normalize();
    assert_eq!(BIKE_LANE, p.bike_backward);

    p.car_forward = CAR_SECONDARY;
    p.car_backward = UNKNOWN;
    p.normalize();
    assert_eq!(CAR_SECONDARY, p.car_backward)
}

#[test]
fn test_update() {
    let mut p = EdgeProperties::default();
    p.update_with_str("highway", "secondary");
    assert_eq!(CAR_SECONDARY, p.car_forward);

    p.update_with_str("highway", "primary_link");
    assert_eq!(CAR_PRIMARY, p.car_forward);

    p.update_with_str("highway", "motorway");
    assert_eq!(CAR_MOTORWAY, p.car_forward);

    p.update_with_str("highway", "residential");
    assert_eq!(CAR_RESIDENTIAL, p.car_forward);

    p.update_with_str("highway", "tertiary");
    assert_eq!(CAR_TERTIARY, p.car_forward);

    p.update_with_str("highway", "trunk");
    assert_eq!(CAR_TRUNK, p.car_forward);

    p.update_with_str("highway", "cycleway");
    assert_eq!(BIKE_TRACK, p.bike_forward);
    assert_eq!(FOOT_ALLOWED, p.foot);

    p.update_with_str("foot", "designated");
    assert_eq!(FOOT_ALLOWED, p.foot);

    p.update_with_str("foot", "no");
    assert_eq!(FOOT_FORBIDDEN, p.foot);

    p.update_with_str("cycleway", "lane");
    assert_eq!(BIKE_LANE, p.bike_forward);

    p.update_with_str("cycleway", "track");
    assert_eq!(BIKE_TRACK, p.bike_forward);

    p.update_with_str("cycleway", "opposite_lane");
    assert_eq!(BIKE_LANE, p.bike_backward);

    p.update_with_str("cycleway", "opposite_track");
    assert_eq!(BIKE_TRACK, p.bike_backward);

    p.update_with_str("cycleway", "opposite");
    assert_eq!(BIKE_ALLOWED, p.bike_backward);

    p.update_with_str("cycleway", "share_busway");
    assert_eq!(BIKE_BUSWAY, p.bike_forward);

    p.update_with_str("cycleway", "lane_left");
    assert_eq!(BIKE_LANE, p.bike_backward);

    p.update_with_str("bicycle", "yes");
    assert_eq!(BIKE_ALLOWED, p.bike_forward);

    p.update_with_str("bicycle", "no");
    assert_eq!(BIKE_FORBIDDEN, p.bike_forward);

    p.update_with_str("busway", "yes");
    assert_eq!(BIKE_BUSWAY, p.bike_forward);

    p.update_with_str("busway", "opposite_track");
    assert_eq!(BIKE_BUSWAY, p.bike_backward);

    p.update_with_str("oneway", "yes");
    assert_eq!(BIKE_FORBIDDEN, p.car_backward);
    assert!(p.bike_backward != BIKE_FORBIDDEN);

    p.bike_backward = UNKNOWN;
    p.update_with_str("oneway", "yes");
    assert_eq!(BIKE_FORBIDDEN, p.bike_backward);

    p.update_with_str("junction", "roundabout");
    assert_eq!(BIKE_FORBIDDEN, p.car_backward);

    p.bike_backward = UNKNOWN;
    p.update_with_str("junction", "roundabout");
    assert_eq!(BIKE_FORBIDDEN, p.bike_backward);
}
<|endoftext|>


<commit_before>#[macro_use] extern crate cookie_factory;
#[macro_use] extern crate nom;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

mod types;
mod value;

pub use types::*;
pub use value::*;

pub mod flags;
pub mod generation;
///! Parsing utilities for the various AMQP types.
pub mod parsing;
<commit_msg>types: document modules<commit_after>#[macro_use] extern crate cookie_factory;
#[macro_use] extern crate nom;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

mod types;
mod value;

pub use types::*;
pub use value::*;

///! Helpers to handle AMQP flags.
pub mod flags;
///! Serialization utilities for the various AMQP types.
pub mod generation;
///! Parsing utilities for the various AMQP types.
pub mod parsing;
<|endoftext|>


<commit_before>use async_std::net::*;
use futures::channel::mpsc;
use futures::prelude::*;
use futures::stream::FusedStream;
use nhanh::*;
use std::collections::HashMap;
use std::ffi::c_void;
use std::pin::Pin;
use std::task::{Context, Poll};

pub const MAX_CHANNELS: u64 = 256;

#[allow(warnings)]
mod enet {
    include!(concat!(env!("OUT_DIR"), "/enet.rs"));
}

#[derive(Debug)]
struct NewPeer {
    peer: u64,
    peer_event_stream: mpsc::UnboundedReceiver<Datagram>,
}

struct EnetCmd {
    peer: u64,
    channel: u8,
    data: Vec<u8>,
}

pub struct EnetServer {
    new_peer_stream: mpsc::UnboundedReceiver<NewPeer>,
    command_sink: mpsc::Sender<EnetCmd>,
}

async fn socket_addr_to_enet_addr(
    addr: impl ToSocketAddrs,
) -> enet::ENetAddress {
    let socket_address = addr
        .to_socket_addrs()
        .await
        .expect("addresses")
        .next()
        .expect("address");

    unsafe {
        let mut address: enet::ENetAddress = std::mem::uninitialized();
        address.host = match socket_address.ip() {
            IpAddr::V4(v4) => u32::from_le_bytes(v4.octets()),
            _ => panic!("Enet does not support ipv6"),
        };
        address.port = socket_address.port();
        address
    }
}

impl EnetServer {
    pub async fn bind(address: impl ToSocketAddrs) -> Self {
        let address = socket_addr_to_enet_addr(address).await;

        let (command_sink, command_stream) = mpsc::channel(0);
        let (new_peer_sink, new_peer_stream) = mpsc::unbounded();

        std::thread::spawn(enet_service_loop(
            command_stream,
            new_peer_sink,
            HostType::Server,
            address,
        ));

        Self {
            new_peer_stream,
            command_sink,
        }
    }
}

impl Server<EnetConnection> for EnetServer {}

impl FusedStream for EnetServer {
    fn is_terminated(&self) -> bool {
        self.new_peer_stream.is_terminated()
    }
}

impl Stream for EnetServer {
    type Item = Result<EnetConnection>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        let new_peer = match Pin::new(&mut self.new_peer_stream).poll_next(ctx)
        {
            Poll::Pending => return Poll::Pending,
            Poll::Ready(Some(event)) => event,
            Poll::Ready(None) => return Poll::Ready(None),
        };

        Poll::Ready(Some(Ok(EnetConnection {
            peer: new_peer.peer,
            command_sink: self.command_sink.clone(),
            peer_event_stream: new_peer.peer_event_stream,
        })))
    }
}

pub struct EnetConnection {
    peer: u64,
    command_sink: mpsc::Sender<EnetCmd>,
    peer_event_stream: mpsc::UnboundedReceiver<Datagram>,
}

impl EnetConnection {
    pub async fn connect(server_addr: impl ToSocketAddrs) -> Self {
        let address = socket_addr_to_enet_addr(server_addr).await;

        let (command_sink, command_stream) = mpsc::channel(0);
        let (new_peer_sink, mut new_peer_stream) = mpsc::unbounded();

        std::thread::spawn(enet_service_loop(
            command_stream,
            new_peer_sink,
            HostType::Client,
            address,
        ));

        let peer = new_peer_stream.next().await.expect("connection to server");
        Self {
            peer: peer.peer,
            command_sink,
            peer_event_stream: peer.peer_event_stream,
        }
    }
}

impl Connection for EnetConnection {}

impl FusedStream for EnetConnection {
    fn is_terminated(&self) -> bool {
        self.peer_event_stream.is_terminated()
    }
}

impl Stream for EnetConnection {
    type Item = Result<Datagram>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.peer_event_stream)
            .poll_next(ctx)
            .map(|d| d.map(Ok))
    }
}

impl Sink<SendCmd> for EnetConnection {
    type Error = Box<dyn std::error::Error>;
    fn poll_ready(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.command_sink)
            .poll_ready(ctx)
            .map_err(Into::into)
    }
    fn start_send(mut self: Pin<&mut Self>, item: SendCmd) -> Result<()> {
        let channel = match item.delivery_mode {
            DeliveryMode::ReliableOrdered(StreamId(channel)) => channel as u8,
            _ => panic!("benchmark only supports reliable ordered datagrams"),
        };

        let peer = self.peer;
        Pin::new(&mut self.command_sink)
            .start_send(EnetCmd {
                peer,
                channel,
                data: item.data,
            })
            .map_err(Into::into)
    }
    fn poll_flush(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.command_sink)
            .poll_flush(ctx)
            .map_err(Into::into)
    }
    fn poll_close(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.command_sink)
            .poll_close(ctx)
            .map_err(Into::into)
    }
}

enum HostType {
    Server,
    Client,
}

impl HostType {
    fn create(&self, server_addr: enet::ENetAddress) -> *mut enet::ENetHost {
        unsafe {
            let host = match self {
                HostType::Server => {
                    enet::enet_host_create(&server_addr, 32, MAX_CHANNELS, 0, 0)
                }
                HostType::Client => {
                    let client = enet::enet_host_create(
                        0 as *const enet::ENetAddress,
                        32,
                        2,
                        0,
                        0,
                    );
                    assert!(
                        enet::enet_host_connect(
                            client,
                            &server_addr,
                            MAX_CHANNELS,
                            0,
                        ) != 0 as *mut enet::ENetPeer
                    );
                    client
                }
            };
            assert!(host != 0 as *mut enet::ENetHost);
            host
        }
    }
}

fn enet_service_command(command: EnetCmd) {
    let packet = unsafe {
        enet::enet_packet_create(
            command.data.as_ptr() as *const c_void,
            command.data.len() as u64,
            enet::_ENetPacketFlag_ENET_PACKET_FLAG_RELIABLE,
        )
    };

    unsafe {
        let peer = command.peer as *mut enet::ENetPeer;
        enet::enet_peer_send(peer, command.channel, packet)
    };
}

fn enet_service_loop(
    mut command_stream: mpsc::Receiver<EnetCmd>,
    mut new_peer_sink: mpsc::UnboundedSender<NewPeer>,
    host_type: HostType,
    server_addr: enet::ENetAddress,
) -> impl FnOnce() {
    move || {
        assert_eq!(unsafe { enet::enet_initialize() }, 0);

        let host = host_type.create(server_addr);
        let mut total_sent = 0;
        let mut peers = HashMap::new();
        loop {
            match command_stream.try_next().transpose() {
                Some(command) => command,
                None => return,
            }
            .ok()
            .into_iter()
            .for_each(enet_service_command);

            let mut event: enet::ENetEvent =
                unsafe { std::mem::uninitialized() };
            while unsafe {
                enet::enet_host_service(
                    host,
                    { &mut event as *mut enet::ENetEvent },
                    /*ms=*/ 1,
                )
            } > 0
            {
                match event.type_ {
                    enet::_ENetEventType_ENET_EVENT_TYPE_CONNECT => {
                        let (peer_event_sink, peer_event_stream) =
                            mpsc::unbounded();
                        assert!(
                            peers.insert(event.peer, peer_event_sink).is_none(),
                            "Peer already connected."
                        );
                        new_peer_sink
                            .unbounded_send(NewPeer {
                                peer: event.peer as u64,
                                peer_event_stream,
                            })
                            .expect("sending new peer event");
                    }
                    enet::_ENetEventType_ENET_EVENT_TYPE_DISCONNECT => {
                        peers.remove(&event.peer);
                    }
                    enet::_ENetEventType_ENET_EVENT_TYPE_RECEIVE => {
                        let sink =
                            peers.get_mut(&event.peer).expect("peer sink");
                        let data: &'static [u8] = unsafe {
                            let packet = &mut *event.packet;
                            std::slice::from_raw_parts(
                                packet.data,
                                packet.dataLength as usize,
                            )
                        };
                        sink.unbounded_send(Datagram {
                            data: data.to_vec(),
                            stream_position: Some(StreamPosition {
                                stream_id: StreamId(event.channelID),
                                index: StreamIndex::Ordinal(total_sent),
                            }),
                        })
                        .expect("sending peer the event");

                        total_sent += 1;
                    }
                    e => println!("other event type: {:?}", e),
                }
            }
        }
    }
}
<commit_msg>Tear down service threads<commit_after>use async_std::net::*;
use futures::channel::mpsc;
use futures::prelude::*;
use futures::stream::FusedStream;
use nhanh::*;
use std::collections::HashMap;
use std::ffi::c_void;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::thread::{self, JoinHandle};
use std::sync::Arc;

pub const MAX_CHANNELS: u64 = 256;

#[allow(warnings)]
mod enet {
    include!(concat!(env!("OUT_DIR"), "/enet.rs"));
}

#[derive(Debug)]
struct NewPeer {
    peer: u64,
    peer_event_stream: mpsc::UnboundedReceiver<Datagram>,
}

struct EnetCmd {
    peer: u64,
    channel: u8,
    data: Vec<u8>,
}

pub struct EnetServer {
    marker: Arc<()>,
    new_peer_stream: mpsc::UnboundedReceiver<NewPeer>,
    command_sink: mpsc::Sender<EnetCmd>,
}

async fn socket_addr_to_enet_addr(
    addr: impl ToSocketAddrs,
) -> enet::ENetAddress {
    let socket_address = addr
        .to_socket_addrs()
        .await
        .expect("addresses")
        .next()
        .expect("address");

    unsafe {
        let mut address: enet::ENetAddress = std::mem::uninitialized();
        address.host = match socket_address.ip() {
            IpAddr::V4(v4) => u32::from_le_bytes(v4.octets()),
            _ => panic!("Enet does not support ipv6"),
        };
        address.port = socket_address.port();
        address
    }
}

impl EnetServer {
    pub async fn bind(address: impl ToSocketAddrs) -> Self {
        let address = socket_addr_to_enet_addr(address).await;

        let (command_sink, command_stream) = mpsc::channel(0);
        let (new_peer_sink, new_peer_stream) = mpsc::unbounded();

        let marker = Arc::new(());

        std::thread::spawn(enet_service_loop(
                marker.clone(),
            command_stream,
            new_peer_sink,
            HostType::Server,
            address,
        ));

        Self {
            marker,
            new_peer_stream,
            command_sink,
        }
    }
}

impl Server<EnetConnection> for EnetServer {}

impl FusedStream for EnetServer {
    fn is_terminated(&self) -> bool {
        self.new_peer_stream.is_terminated()
    }
}

impl Stream for EnetServer {
    type Item = Result<EnetConnection>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        let new_peer = match Pin::new(&mut self.new_peer_stream).poll_next(ctx)
        {
            Poll::Pending => return Poll::Pending,
            Poll::Ready(Some(event)) => event,
            Poll::Ready(None) => return Poll::Ready(None),
        };

        Poll::Ready(Some(Ok(EnetConnection {
            marker: self.marker.clone(),
            peer: new_peer.peer,
            command_sink: self.command_sink.clone(),
            peer_event_stream: new_peer.peer_event_stream,
        })))
    }
}

pub struct EnetConnection {
    marker: Arc<()>,
    peer: u64,
    command_sink: mpsc::Sender<EnetCmd>,
    peer_event_stream: mpsc::UnboundedReceiver<Datagram>,
}

impl EnetConnection {
    pub async fn connect(server_addr: impl ToSocketAddrs) -> Self {
        let address = socket_addr_to_enet_addr(server_addr).await;

        let (command_sink, command_stream) = mpsc::channel(0);
        let (new_peer_sink, mut new_peer_stream) = mpsc::unbounded();

        let marker = Arc::new(());

        std::thread::spawn(enet_service_loop(
                marker.clone(),
            command_stream,
            new_peer_sink,
            HostType::Client,
            address,
        ));

        let peer = new_peer_stream.next().await.expect("connection to server");
        Self {
            marker,
            peer: peer.peer,
            command_sink,
            peer_event_stream: peer.peer_event_stream,
        }
    }
}

impl Connection for EnetConnection {}

impl FusedStream for EnetConnection {
    fn is_terminated(&self) -> bool {
        self.peer_event_stream.is_terminated()
    }
}

impl Stream for EnetConnection {
    type Item = Result<Datagram>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.peer_event_stream)
            .poll_next(ctx)
            .map(|d| d.map(Ok))
    }
}

impl Sink<SendCmd> for EnetConnection {
    type Error = Box<dyn std::error::Error>;
    fn poll_ready(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.command_sink)
            .poll_ready(ctx)
            .map_err(Into::into)
    }
    fn start_send(mut self: Pin<&mut Self>, item: SendCmd) -> Result<()> {
        let channel = match item.delivery_mode {
            DeliveryMode::ReliableOrdered(StreamId(channel)) => channel as u8,
            _ => panic!("benchmark only supports reliable ordered datagrams"),
        };

        let peer = self.peer;
        Pin::new(&mut self.command_sink)
            .start_send(EnetCmd {
                peer,
                channel,
                data: item.data,
            })
            .map_err(Into::into)
    }
    fn poll_flush(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.command_sink)
            .poll_flush(ctx)
            .map_err(Into::into)
    }
    fn poll_close(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.command_sink)
            .poll_close(ctx)
            .map_err(Into::into)
    }
}

enum HostType {
    Server,
    Client,
}

impl HostType {
    fn create(&self, server_addr: enet::ENetAddress) -> *mut enet::ENetHost {
        unsafe {
            let host = match self {
                HostType::Server => {
                    enet::enet_host_create(&server_addr, 32, MAX_CHANNELS, 0, 0)
                }
                HostType::Client => {
                    let client = enet::enet_host_create(
                        0 as *const enet::ENetAddress,
                        32,
                        2,
                        0,
                        0,
                    );
                    assert!(
                        enet::enet_host_connect(
                            client,
                            &server_addr,
                            MAX_CHANNELS,
                            0,
                        ) != 0 as *mut enet::ENetPeer
                    );
                    client
                }
            };
            assert!(host != 0 as *mut enet::ENetHost);
            host
        }
    }
}

fn enet_service_command(command: EnetCmd) {
    let packet = unsafe {
        enet::enet_packet_create(
            command.data.as_ptr() as *const c_void,
            command.data.len() as u64,
            enet::_ENetPacketFlag_ENET_PACKET_FLAG_RELIABLE,
        )
    };

    unsafe {
        let peer = command.peer as *mut enet::ENetPeer;
        enet::enet_peer_send(peer, command.channel, packet)
    };
}

fn enet_service_loop(
    marker: Arc<()>,
    mut command_stream: mpsc::Receiver<EnetCmd>,
    mut new_peer_sink: mpsc::UnboundedSender<NewPeer>,
    host_type: HostType,
    server_addr: enet::ENetAddress,
) -> impl FnOnce() {
    move || {
        assert_eq!(unsafe { enet::enet_initialize() }, 0);

        let host = host_type.create(server_addr);
        let mut total_sent = 0;
        let mut peers = HashMap::new();
        loop {
            if Arc::strong_count(&marker) == 1 {
                return;
            }

            match command_stream.try_next().transpose() {
                Some(command) => command,
                None => return,
            }
            .ok()
            .into_iter()
            .for_each(enet_service_command);

            let mut event: enet::ENetEvent =
                unsafe { std::mem::uninitialized() };
            while unsafe {
                enet::enet_host_service(
                    host,
                    { &mut event as *mut enet::ENetEvent },
                    /*ms=*/ 1,
                )
            } > 0
            {
                match event.type_ {
                    enet::_ENetEventType_ENET_EVENT_TYPE_CONNECT => {
                        let (peer_event_sink, peer_event_stream) =
                            mpsc::unbounded();
                        assert!(
                            peers.insert(event.peer, peer_event_sink).is_none(),
                            "Peer already connected."
                        );
                        new_peer_sink
                            .unbounded_send(NewPeer {
                                peer: event.peer as u64,
                                peer_event_stream,
                            })
                            .expect("sending new peer event");
                    }
                    enet::_ENetEventType_ENET_EVENT_TYPE_DISCONNECT => {
                        peers.remove(&event.peer);
                    }
                    enet::_ENetEventType_ENET_EVENT_TYPE_RECEIVE => {
                        let sink =
                            peers.get_mut(&event.peer).expect("peer sink");
                        let data: &'static [u8] = unsafe {
                            let packet = &mut *event.packet;
                            std::slice::from_raw_parts(
                                packet.data,
                                packet.dataLength as usize,
                            )
                        };
                        sink.unbounded_send(Datagram {
                            data: data.to_vec(),
                            stream_position: Some(StreamPosition {
                                stream_id: StreamId(event.channelID),
                                index: StreamIndex::Ordinal(total_sent),
                            }),
                        })
                        .expect("sending peer the event");

                        total_sent += 1;
                    }
                    e => println!("other event type: {:?}", e),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate itertools;

use test::{black_box, Bencher};
use itertools::Itertools;

#[bench]
fn tuples_2(b: &mut Bencher) {
    b.iter(|| {
        for (a, b) in (0..20_000).tuples() {
            black_box(a + b);
        }
    })
}

#[bench]
fn tuples_3(b: &mut Bencher) {
    b.iter(|| {
        for (a, b, c) in (0..30_000).tuples() {
            black_box(a + b + c);
        }
    })
}

#[bench]
fn tuples_4(b: &mut Bencher) {
    b.iter(|| {
        for (a, b, c, d) in (0..40_000).tuples() {
            black_box(a + b + c + d);
        }
    })
}

#[bench]
fn tuple_windows_2(b: &mut Bencher) {
    b.iter(|| {
        for (a, b) in (0..10_000).tuple_windows() {
            black_box(a + b);
        }
    })
}

#[bench]
fn tuple_windows_3(b: &mut Bencher) {
    b.iter(|| {
        for (a, b, c) in (0..10_000).tuple_windows() {
            black_box(a + b + c);
        }
    })
}

#[bench]
fn tuple_windows_4(b: &mut Bencher) {
    b.iter(|| {
        for (a, b, c, d) in (0..10_000).tuple_windows() {
            black_box(a + b + c + d);
        }
    })
}
<commit_msg>Improve tuple related benchs<commit_after>#![feature(test)]

extern crate test;
extern crate itertools;

use test::{black_box, Bencher};
use itertools::Itertools;
use itertools::tuples::TupleCollect;

macro_rules! def_benchs {
    ($N:expr; $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident; $T:ty) => (
        #[bench]
        fn $A(b: &mut Bencher) {
            let v: Vec<u32> = (0.. $N * 10_000).collect();
            b.iter(|| {
                for x in v.iter().tuples::<$T>() {
                    black_box(&x);
                }
            });
        }

        #[bench]
        fn $B(b: &mut Bencher) {
            let v: Vec<u32> = (0.. $N * 10_000).collect();
            b.iter(|| {
                for x in v.chunks($N) {
                    black_box(&x);
                }
            });
        }

        #[bench]
        fn $C(b: &mut Bencher) {
            let v: Vec<u32> = (0.. $N * 10_000).collect();
            b.iter(|| {
                for x in v.chunks($N) {
                    // create a tuple from the slice
                    let x = <$T>::collect_from_iter(x);
                    black_box(&x);
                }
            });
        }

        #[bench]
        fn $D(b: &mut Bencher) {
            let v: Vec<u32> = (0..10_000).collect();
            b.iter(|| {
                for x in v.iter().tuple_windows::<$T>() {
                    black_box(&x);
                }
            });
        }

        #[bench]
        fn $E(b: &mut Bencher) {
            let v: Vec<u32> = (0..10_000).collect();
            b.iter(|| {
                for x in v.windows($N) {
                    black_box(&x);
                }
            });
        }

        #[bench]
        fn $F(b: &mut Bencher) {
            let v: Vec<u32> = (0..10_000).collect();
            b.iter(|| {
                for x in v.windows($N) {
                    // create a tuple from the slice
                    let x = <$T>::collect_from_iter(x);
                    black_box(&x);
                }
            });
        }
    )
}

def_benchs!{
    1;
    chunks_tuple_1,
    chunks_slice_1,
    chunks_slice_tuple_1,
    windows_tuple_1,
    windows_slice_1,
    windows_slice_tuple_1;
    (&u32, )
}

def_benchs!{
    2;
    chunks_tuple_2,
    chunks_slice_2,
    chunks_slice_tuple_2,
    windows_tuple_2,
    windows_slice_2,
    windows_slice_tuple_2;
    (&u32, &u32)
}

def_benchs!{
    3;
    chunks_tuple_3,
    chunks_slice_3,
    chunks_slice_tuple_3,
    windows_tuple_3,
    windows_slice_3,
    windows_slice_tuple_3;
    (&u32, &u32, &u32)
}

def_benchs!{
    4;
    chunks_tuple_4,
    chunks_slice_4,
    chunks_slice_tuple_4,
    windows_tuple_4,
    windows_slice_4,
    windows_slice_tuple_4;
    (&u32, &u32, &u32, &u32)
}
<|endoftext|>


<commit_before>//! Protocol Data Unit module
//!
//! This module comprises multiple data structures representing possible
//! protocol data units (PDUs) according to
//! the standard message exchange mechanisms,
//! as well as readers and writers of PDUs from arbitrary data sources.
pub mod reader;
pub mod writer;

pub use reader::read_pdu;
pub use writer::write_pdu;

/// Message component for a proposed presentation context.
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub struct PresentationContextProposed {
    /// the presentation context identifier
    pub id: u8,
    /// the expected abstract syntax UID
    /// (commonly referrering to the expected SOP class)
    pub abstract_syntax: String,
    /// a list of transfer syntax UIDs to support in this interaction
    pub transfer_syntaxes: Vec<String>,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub struct PresentationContextResult {
    pub id: u8,
    pub reason: PresentationContextResultReason,
    pub transfer_syntax: String,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum PresentationContextResultReason {
    Acceptance = 0,
    UserRejection = 1,
    NoReason = 2,
    AbstractSyntaxNotSupported = 3,
    TransferSyntaxesNotSupported = 4,
}

impl PresentationContextResultReason {
    fn from(reason: u8) -> Option<PresentationContextResultReason> {
        let result = match reason {
            0 => PresentationContextResultReason::Acceptance,
            1 => PresentationContextResultReason::UserRejection,
            2 => PresentationContextResultReason::NoReason,
            3 => PresentationContextResultReason::AbstractSyntaxNotSupported,
            4 => PresentationContextResultReason::TransferSyntaxesNotSupported,
            _ => {
                return None;
            }
        };

        Some(result)
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJResult {
    Permanent = 1,
    Transient = 2,
}

impl AssociationRJResult {
    fn from(value: u8) -> Option<AssociationRJResult> {
        match value {
            1 => Some(AssociationRJResult::Permanent),
            2 => Some(AssociationRJResult::Transient),
            _ => None,
        }
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJSource {
    ServiceUser(AssociationRJServiceUserReason),
    ServiceProviderASCE(AssociationRJServiceProviderASCEReason),
    ServiceProviderPresentation(AssociationRJServiceProviderPresentationReason),
}

impl AssociationRJSource {
    fn from(source: u8, reason: u8) -> Option<AssociationRJSource> {
        let result = match (source, reason) {
            (1, 1) => {
                AssociationRJSource::ServiceUser(AssociationRJServiceUserReason::NoReasonGiven)
            }
            (1, 2) => AssociationRJSource::ServiceUser(
                AssociationRJServiceUserReason::ApplicationContextNameNotSupported,
            ),
            (1, 3) => AssociationRJSource::ServiceUser(
                AssociationRJServiceUserReason::CallingAETitleNotRecognized,
            ),
            (1, x) if x == 4 || x == 5 || x == 6 => {
                AssociationRJSource::ServiceUser(AssociationRJServiceUserReason::Reserved(x))
            }
            (1, 7) => AssociationRJSource::ServiceUser(
                AssociationRJServiceUserReason::CalledAETitleNotRecognized,
            ),
            //(1, 8) | (1, 9) | (1, 10) => {
            (1, x) if x == 8 || x == 9 || x == 10 => {
                AssociationRJSource::ServiceUser(AssociationRJServiceUserReason::Reserved(x))
            }
            (1, _) => {
                return None;
            }
            (2, 1) => AssociationRJSource::ServiceProviderASCE(
                AssociationRJServiceProviderASCEReason::NoReasonGiven,
            ),
            (2, 2) => AssociationRJSource::ServiceProviderASCE(
                AssociationRJServiceProviderASCEReason::ProtocolVersionNotSupported,
            ),
            (2, _) => {
                return None;
            }
            (3, 0) => AssociationRJSource::ServiceProviderPresentation(
                AssociationRJServiceProviderPresentationReason::Reserved(0),
            ),
            (3, 1) => AssociationRJSource::ServiceProviderPresentation(
                AssociationRJServiceProviderPresentationReason::TemporaryCongestion,
            ),
            (3, 2) => AssociationRJSource::ServiceProviderPresentation(
                AssociationRJServiceProviderPresentationReason::LocalLimitExceeded,
            ),
            (3, x) if x == 3 || x == 4 || x == 5 || x == 6 || x == 7 => {
                AssociationRJSource::ServiceProviderPresentation(
                    AssociationRJServiceProviderPresentationReason::Reserved(x),
                )
            }
            _ => {
                return None;
            }
        };
        Some(result)
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJServiceUserReason {
    NoReasonGiven,
    ApplicationContextNameNotSupported,
    CallingAETitleNotRecognized,
    CalledAETitleNotRecognized,
    Reserved(u8),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJServiceProviderASCEReason {
    NoReasonGiven,
    ProtocolVersionNotSupported,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJServiceProviderPresentationReason {
    TemporaryCongestion,
    LocalLimitExceeded,
    Reserved(u8),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub struct PDataValue {
    pub presentation_context_id: u8,
    pub value_type: PDataValueType,
    pub is_last: bool,
    pub data: Vec<u8>,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum PDataValueType {
    Command,
    Data,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AbortRQSource {
    ServiceUser,
    ServiceProvider(AbortRQServiceProviderReason),
    Reserved,
}

impl AbortRQSource {
    fn from(source: u8, reason: u8) -> Option<AbortRQSource> {
        let result = match (source, reason) {
            (0, _) => AbortRQSource::ServiceUser,
            (1, _) => AbortRQSource::Reserved,
            (2, 0) => AbortRQSource::ServiceProvider(
                AbortRQServiceProviderReason::ReasonNotSpecifiedUnrecognizedPdu,
            ),
            (2, 2) => AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::UnexpectedPdu),
            (2, 3) => AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::Reserved),
            (2, 4) => AbortRQSource::ServiceProvider(
                AbortRQServiceProviderReason::UnrecognizedPduParameter,
            ),
            (2, 5) => {
                AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::UnexpectedPduParameter)
            }
            (2, 6) => {
                AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::InvalidPduParameter)
            }
            (_, _) => {
                return None;
            }
        };

        Some(result)
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AbortRQServiceProviderReason {
    ReasonNotSpecifiedUnrecognizedPdu,
    UnexpectedPdu,
    Reserved,
    UnrecognizedPduParameter,
    UnexpectedPduParameter,
    InvalidPduParameter,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum PduVariableItem {
    Unknown(u8),
    ApplicationContext(String),
    PresentationContextProposed(PresentationContextProposed),
    PresentationContextResult(PresentationContextResult),
    UserVariables(Vec<UserVariableItem>),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum UserVariableItem {
    Unknown(u8, Vec<u8>),
    MaxLength(u32),
    ImplementationClassUID(String),
    ImplementationVersionName(String),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum Pdu {
    Unknown {
        pdu_type: u8,
        data: Vec<u8>,
    },
    AssociationRQ {
        protocol_version: u16,
        calling_ae_title: String,
        called_ae_title: String,
        application_context_name: String,
        presentation_contexts: Vec<PresentationContextProposed>,
        user_variables: Vec<UserVariableItem>,
    },
    AssociationAC {
        protocol_version: u16,
        calling_ae_title: String,
        called_ae_title: String,
        application_context_name: String,
        presentation_contexts: Vec<PresentationContextResult>,
        user_variables: Vec<UserVariableItem>,
    },
    AssociationRJ {
        result: AssociationRJResult,
        source: AssociationRJSource,
    },
    PData {
        data: Vec<PDataValue>,
    },
    ReleaseRQ,
    ReleaseRP,
    AbortRQ {
        source: AbortRQSource,
    },
}

impl Pdu {
    pub fn short_description(&self) -> String {
        match self {
            Pdu::Unknown { pdu_type, data: _ } => {
                format!("Unknown {{pdu_type: {}, data: ...}}", pdu_type)
            }
            Pdu::AssociationRQ { .. }
            | Pdu::AssociationAC { .. }
            | Pdu::AssociationRJ { .. }
            | Pdu::ReleaseRQ
            | Pdu::ReleaseRP
            | Pdu::AbortRQ { .. } => format!("{:?}", self),
            Pdu::PData { data: _ } => "PData { data: ... }".into(),
        }
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct AssociationRQ {
    protocol_version: u16,
    calling_ae_title: String,
    called_ae_title: String,
    application_context_name: String,
    presentation_contexts: Vec<PresentationContextProposed>,
    user_variables: Vec<UserVariableItem>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct AssociationAC {
    protocol_version: u16,
    application_context_name: String,
    presentation_contexts: Vec<PresentationContextResult>,
    user_variables: Vec<UserVariableItem>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct AssociationRJ {
    result: AssociationRJResult,
    source: AssociationRJSource,
}
<commit_msg>[ul] Detect A-ABORT Unrecognized PDU reason<commit_after>//! Protocol Data Unit module
//!
//! This module comprises multiple data structures representing possible
//! protocol data units (PDUs) according to
//! the standard message exchange mechanisms,
//! as well as readers and writers of PDUs from arbitrary data sources.
pub mod reader;
pub mod writer;

pub use reader::read_pdu;
pub use writer::write_pdu;

/// Message component for a proposed presentation context.
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub struct PresentationContextProposed {
    /// the presentation context identifier
    pub id: u8,
    /// the expected abstract syntax UID
    /// (commonly referrering to the expected SOP class)
    pub abstract_syntax: String,
    /// a list of transfer syntax UIDs to support in this interaction
    pub transfer_syntaxes: Vec<String>,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub struct PresentationContextResult {
    pub id: u8,
    pub reason: PresentationContextResultReason,
    pub transfer_syntax: String,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum PresentationContextResultReason {
    Acceptance = 0,
    UserRejection = 1,
    NoReason = 2,
    AbstractSyntaxNotSupported = 3,
    TransferSyntaxesNotSupported = 4,
}

impl PresentationContextResultReason {
    fn from(reason: u8) -> Option<PresentationContextResultReason> {
        let result = match reason {
            0 => PresentationContextResultReason::Acceptance,
            1 => PresentationContextResultReason::UserRejection,
            2 => PresentationContextResultReason::NoReason,
            3 => PresentationContextResultReason::AbstractSyntaxNotSupported,
            4 => PresentationContextResultReason::TransferSyntaxesNotSupported,
            _ => {
                return None;
            }
        };

        Some(result)
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJResult {
    Permanent = 1,
    Transient = 2,
}

impl AssociationRJResult {
    fn from(value: u8) -> Option<AssociationRJResult> {
        match value {
            1 => Some(AssociationRJResult::Permanent),
            2 => Some(AssociationRJResult::Transient),
            _ => None,
        }
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJSource {
    ServiceUser(AssociationRJServiceUserReason),
    ServiceProviderASCE(AssociationRJServiceProviderASCEReason),
    ServiceProviderPresentation(AssociationRJServiceProviderPresentationReason),
}

impl AssociationRJSource {
    fn from(source: u8, reason: u8) -> Option<AssociationRJSource> {
        let result = match (source, reason) {
            (1, 1) => {
                AssociationRJSource::ServiceUser(AssociationRJServiceUserReason::NoReasonGiven)
            }
            (1, 2) => AssociationRJSource::ServiceUser(
                AssociationRJServiceUserReason::ApplicationContextNameNotSupported,
            ),
            (1, 3) => AssociationRJSource::ServiceUser(
                AssociationRJServiceUserReason::CallingAETitleNotRecognized,
            ),
            (1, x) if x == 4 || x == 5 || x == 6 => {
                AssociationRJSource::ServiceUser(AssociationRJServiceUserReason::Reserved(x))
            }
            (1, 7) => AssociationRJSource::ServiceUser(
                AssociationRJServiceUserReason::CalledAETitleNotRecognized,
            ),
            //(1, 8) | (1, 9) | (1, 10) => {
            (1, x) if x == 8 || x == 9 || x == 10 => {
                AssociationRJSource::ServiceUser(AssociationRJServiceUserReason::Reserved(x))
            }
            (1, _) => {
                return None;
            }
            (2, 1) => AssociationRJSource::ServiceProviderASCE(
                AssociationRJServiceProviderASCEReason::NoReasonGiven,
            ),
            (2, 2) => AssociationRJSource::ServiceProviderASCE(
                AssociationRJServiceProviderASCEReason::ProtocolVersionNotSupported,
            ),
            (2, _) => {
                return None;
            }
            (3, 0) => AssociationRJSource::ServiceProviderPresentation(
                AssociationRJServiceProviderPresentationReason::Reserved(0),
            ),
            (3, 1) => AssociationRJSource::ServiceProviderPresentation(
                AssociationRJServiceProviderPresentationReason::TemporaryCongestion,
            ),
            (3, 2) => AssociationRJSource::ServiceProviderPresentation(
                AssociationRJServiceProviderPresentationReason::LocalLimitExceeded,
            ),
            (3, x) if x == 3 || x == 4 || x == 5 || x == 6 || x == 7 => {
                AssociationRJSource::ServiceProviderPresentation(
                    AssociationRJServiceProviderPresentationReason::Reserved(x),
                )
            }
            _ => {
                return None;
            }
        };
        Some(result)
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJServiceUserReason {
    NoReasonGiven,
    ApplicationContextNameNotSupported,
    CallingAETitleNotRecognized,
    CalledAETitleNotRecognized,
    Reserved(u8),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJServiceProviderASCEReason {
    NoReasonGiven,
    ProtocolVersionNotSupported,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AssociationRJServiceProviderPresentationReason {
    TemporaryCongestion,
    LocalLimitExceeded,
    Reserved(u8),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub struct PDataValue {
    pub presentation_context_id: u8,
    pub value_type: PDataValueType,
    pub is_last: bool,
    pub data: Vec<u8>,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum PDataValueType {
    Command,
    Data,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AbortRQSource {
    ServiceUser,
    ServiceProvider(AbortRQServiceProviderReason),
    Reserved,
}

impl AbortRQSource {
    fn from(source: u8, reason: u8) -> Option<AbortRQSource> {
        let result = match (source, reason) {
            (0, _) => AbortRQSource::ServiceUser,
            (1, _) => AbortRQSource::Reserved,
            (2, 0) | (2, 1) => AbortRQSource::ServiceProvider(
                AbortRQServiceProviderReason::ReasonNotSpecifiedUnrecognizedPdu,
            ),
            (2, 2) => AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::UnexpectedPdu),
            (2, 3) => AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::Reserved),
            (2, 4) => AbortRQSource::ServiceProvider(
                AbortRQServiceProviderReason::UnrecognizedPduParameter,
            ),
            (2, 5) => {
                AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::UnexpectedPduParameter)
            }
            (2, 6) => {
                AbortRQSource::ServiceProvider(AbortRQServiceProviderReason::InvalidPduParameter)
            }
            (_, _) => {
                return None;
            }
        };

        Some(result)
    }
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum AbortRQServiceProviderReason {
    ReasonNotSpecifiedUnrecognizedPdu,
    UnexpectedPdu,
    Reserved,
    UnrecognizedPduParameter,
    UnexpectedPduParameter,
    InvalidPduParameter,
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum PduVariableItem {
    Unknown(u8),
    ApplicationContext(String),
    PresentationContextProposed(PresentationContextProposed),
    PresentationContextResult(PresentationContextResult),
    UserVariables(Vec<UserVariableItem>),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum UserVariableItem {
    Unknown(u8, Vec<u8>),
    MaxLength(u32),
    ImplementationClassUID(String),
    ImplementationVersionName(String),
}

#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug)]
pub enum Pdu {
    Unknown {
        pdu_type: u8,
        data: Vec<u8>,
    },
    AssociationRQ {
        protocol_version: u16,
        calling_ae_title: String,
        called_ae_title: String,
        application_context_name: String,
        presentation_contexts: Vec<PresentationContextProposed>,
        user_variables: Vec<UserVariableItem>,
    },
    AssociationAC {
        protocol_version: u16,
        calling_ae_title: String,
        called_ae_title: String,
        application_context_name: String,
        presentation_contexts: Vec<PresentationContextResult>,
        user_variables: Vec<UserVariableItem>,
    },
    AssociationRJ {
        result: AssociationRJResult,
        source: AssociationRJSource,
    },
    PData {
        data: Vec<PDataValue>,
    },
    ReleaseRQ,
    ReleaseRP,
    AbortRQ {
        source: AbortRQSource,
    },
}

impl Pdu {
    pub fn short_description(&self) -> String {
        match self {
            Pdu::Unknown { pdu_type, data: _ } => {
                format!("Unknown {{pdu_type: {}, data: ...}}", pdu_type)
            }
            Pdu::AssociationRQ { .. }
            | Pdu::AssociationAC { .. }
            | Pdu::AssociationRJ { .. }
            | Pdu::ReleaseRQ
            | Pdu::ReleaseRP
            | Pdu::AbortRQ { .. } => format!("{:?}", self),
            Pdu::PData { data: _ } => "PData { data: ... }".into(),
        }
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct AssociationRQ {
    protocol_version: u16,
    calling_ae_title: String,
    called_ae_title: String,
    application_context_name: String,
    presentation_contexts: Vec<PresentationContextProposed>,
    user_variables: Vec<UserVariableItem>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct AssociationAC {
    protocol_version: u16,
    application_context_name: String,
    presentation_contexts: Vec<PresentationContextResult>,
    user_variables: Vec<UserVariableItem>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct AssociationRJ {
    result: AssociationRJResult,
    source: AssociationRJSource,
}
<|endoftext|>