<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate toml;
extern crate rustc_serialize;

use std::collections::{BTreeMap, HashMap};
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};

static HOSTS: &'static [&'static str] = &[
    "aarch64-unknown-linux-gnu",
    "arm-unknown-linux-gnueabi",
    "arm-unknown-linux-gnueabihf",
    "armv7-unknown-linux-gnueabihf",
    "i686-apple-darwin",
    "i686-pc-windows-gnu",
    "i686-pc-windows-msvc",
    "i686-unknown-linux-gnu",
    "mips-unknown-linux-gnu",
    "mips64-unknown-linux-gnuabi64",
    "mips64el-unknown-linux-gnuabi64",
    "mipsel-unknown-linux-gnu",
    "powerpc-unknown-linux-gnu",
    "powerpc64-unknown-linux-gnu",
    "powerpc64le-unknown-linux-gnu",
    "s390x-unknown-linux-gnu",
    "x86_64-apple-darwin",
    "x86_64-pc-windows-gnu",
    "x86_64-pc-windows-msvc",
    "x86_64-unknown-freebsd",
    "x86_64-unknown-linux-gnu",
    "x86_64-unknown-netbsd",
];

static TARGETS: &'static [&'static str] = &[
    "aarch64-apple-ios",
    "aarch64-unknown-fuchsia",
    "aarch64-linux-android",
    "aarch64-unknown-linux-gnu",
    "arm-linux-androideabi",
    "arm-unknown-linux-gnueabi",
    "arm-unknown-linux-gnueabihf",
    "arm-unknown-linux-musleabi",
    "arm-unknown-linux-musleabihf",
    "armv7-apple-ios",
    "armv7-linux-androideabi",
    "armv7-unknown-linux-gnueabihf",
    "armv7-unknown-linux-musleabihf",
    "armv7s-apple-ios",
    "asmjs-unknown-emscripten",
    "i386-apple-ios",
    "i586-pc-windows-msvc",
    "i586-unknown-linux-gnu",
    "i686-apple-darwin",
    "i686-linux-android",
    "i686-pc-windows-gnu",
    "i686-pc-windows-msvc",
    "i686-unknown-freebsd",
    "i686-unknown-linux-gnu",
    "i686-unknown-linux-musl",
    "mips-unknown-linux-gnu",
    "mips-unknown-linux-musl",
    "mips64-unknown-linux-gnuabi64",
    "mips64el-unknown-linux-gnuabi64",
    "mipsel-unknown-linux-gnu",
    "mipsel-unknown-linux-musl",
    "powerpc-unknown-linux-gnu",
    "powerpc64-unknown-linux-gnu",
    "powerpc64le-unknown-linux-gnu",
    "s390x-unknown-linux-gnu",
    "sparc64-unknown-linux-gnu",
    "wasm32-unknown-emscripten",
    "x86_64-apple-darwin",
    "x86_64-apple-ios",
    "x86_64-pc-windows-gnu",
    "x86_64-pc-windows-msvc",
    "x86_64-rumprun-netbsd",
    "x86_64-unknown-freebsd",
    "x86_64-unknown-fuchsia",
    "x86_64-unknown-linux-gnu",
    "x86_64-unknown-linux-musl",
    "x86_64-unknown-netbsd",
];

static MINGW: &'static [&'static str] = &[
    "i686-pc-windows-gnu",
    "x86_64-pc-windows-gnu",
];

struct Manifest {
    manifest_version: String,
    date: String,
    pkg: HashMap<String, Package>,
}

#[derive(RustcEncodable)]
struct Package {
    version: String,
    target: HashMap<String, Target>,
}

#[derive(RustcEncodable)]
struct Target {
    available: bool,
    url: Option<String>,
    hash: Option<String>,
    components: Option<Vec<Component>>,
    extensions: Option<Vec<Component>>,
}

#[derive(RustcEncodable)]
struct Component {
    pkg: String,
    target: String,
}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

struct Builder {
    rust_release: String,
    cargo_release: String,
    input: PathBuf,
    output: PathBuf,
    gpg_passphrase: String,
    digests: HashMap<String, String>,
    s3_address: String,
    date: String,
    rust_version: String,
    cargo_version: String,
}

fn main() {
    let mut args = env::args().skip(1);
    let input = PathBuf::from(args.next().unwrap());
    let output = PathBuf::from(args.next().unwrap());
    let date = args.next().unwrap();
    let rust_release = args.next().unwrap();
    let cargo_release = args.next().unwrap();
    let s3_address = args.next().unwrap();
    let mut passphrase = String::new();
    t!(io::stdin().read_to_string(&mut passphrase));

    Builder {
        rust_release: rust_release,
        cargo_release: cargo_release,
        input: input,
        output: output,
        gpg_passphrase: passphrase,
        digests: HashMap::new(),
        s3_address: s3_address,
        date: date,
        rust_version: String::new(),
        cargo_version: String::new(),
    }.build();
}

impl Builder {
    fn build(&mut self) {
        self.rust_version = self.version("rust", "x86_64-unknown-linux-gnu");
        self.cargo_version = self.version("cargo", "x86_64-unknown-linux-gnu");

        self.digest_and_sign();
        let Manifest { manifest_version, date, pkg } = self.build_manifest();

        // Unfortunately we can't use derive(RustcEncodable) here because the
        // version field is called `manifest-version`, not `manifest_version`.
        // In lieu of that just create the table directly here with a `BTreeMap`
        // and wrap it up in a `Value::Table`.
        let mut manifest = BTreeMap::new();
        manifest.insert("manifest-version".to_string(),
                        toml::Value::String(manifest_version));
        manifest.insert("date".to_string(), toml::Value::String(date));
        manifest.insert("pkg".to_string(), toml::encode(&pkg));
        let manifest = toml::Value::Table(manifest).to_string();

        let filename = format!("channel-rust-{}.toml", self.rust_release);
        self.write_manifest(&manifest, &filename);

        if self.rust_release != "beta" && self.rust_release != "nightly" {
            self.write_manifest(&manifest, "channel-rust-stable.toml");
        }
    }

    fn digest_and_sign(&mut self) {
        for file in t!(self.input.read_dir()).map(|e| t!(e).path()) {
            let filename = file.file_name().unwrap().to_str().unwrap();
            let digest = self.hash(&file);
            self.sign(&file);
            assert!(self.digests.insert(filename.to_string(), digest).is_none());
        }
    }

    fn build_manifest(&mut self) -> Manifest {
        let mut manifest = Manifest {
            manifest_version: "2".to_string(),
            date: self.date.to_string(),
            pkg: HashMap::new(),
        };

        self.package("rustc", &mut manifest.pkg, HOSTS);
        self.package("cargo", &mut manifest.pkg, HOSTS);
        self.package("rust-mingw", &mut manifest.pkg, MINGW);
        self.package("rust-std", &mut manifest.pkg, TARGETS);
        self.package("rust-docs", &mut manifest.pkg, TARGETS);
        self.package("rust-src", &mut manifest.pkg, &["*"]);

        if self.channel == "nightly" {
            self.package("rust-analysis", &mut manifest.pkg, TARGETS);
        }

        let mut pkg = Package {
            version: self.cached_version("rust").to_string(),
            target: HashMap::new(),
        };
        for host in HOSTS {
            let filename = self.filename("rust", host);
            let digest = match self.digests.remove(&filename) {
                Some(digest) => digest,
                None => {
                    pkg.target.insert(host.to_string(), Target {
                        available: false,
                        url: None,
                        hash: None,
                        components: None,
                        extensions: None,
                    });
                    continue
                }
            };
            let mut components = Vec::new();
            let mut extensions = Vec::new();

            // rustc/rust-std/cargo are all required, and so is rust-mingw if it's
            // available for the target.
            components.extend(vec![
                Component { pkg: "rustc".to_string(), target: host.to_string() },
                Component { pkg: "rust-std".to_string(), target: host.to_string() },
                Component { pkg: "cargo".to_string(), target: host.to_string() },
            ]);
            if host.contains("pc-windows-gnu") {
                components.push(Component {
                    pkg: "rust-mingw".to_string(),
                    target: host.to_string(),
                });
            }

            // Docs, other standard libraries, and the source package are all
            // optional.
            extensions.push(Component {
                pkg: "rust-docs".to_string(),
                target: host.to_string(),
            });
            for target in TARGETS {
                if target != host {
                    extensions.push(Component {
                        pkg: "rust-std".to_string(),
                        target: target.to_string(),
                    });
                }
                if self.channel == "nightly" {
                    extensions.push(Component {
                        pkg: "rust-analysis".to_string(),
                        target: target.to_string(),
                    });
                }
            }
            extensions.push(Component {
                pkg: "rust-src".to_string(),
                target: "*".to_string(),
            });

            pkg.target.insert(host.to_string(), Target {
                available: true,
                url: Some(self.url("rust", host)),
                hash: Some(digest),
                components: Some(components),
                extensions: Some(extensions),
            });
        }
        manifest.pkg.insert("rust".to_string(), pkg);

        return manifest
    }

    fn package(&mut self,
               pkgname: &str,
               dst: &mut HashMap<String, Package>,
               targets: &[&str]) {
        let targets = targets.iter().map(|name| {
            let filename = self.filename(pkgname, name);
            let digest = match self.digests.remove(&filename) {
                Some(digest) => digest,
                None => {
                    return (name.to_string(), Target {
                        available: false,
                        url: None,
                        hash: None,
                        components: None,
                        extensions: None,
                    })
                }
            };

            (name.to_string(), Target {
                available: true,
                url: Some(self.url(pkgname, name)),
                hash: Some(digest),
                components: None,
                extensions: None,
            })
        }).collect();

        dst.insert(pkgname.to_string(), Package {
            version: self.cached_version(pkgname).to_string(),
            target: targets,
        });
    }

    fn url(&self, component: &str, target: &str) -> String {
        format!("{}/{}/{}",
                self.s3_address,
                self.date,
                self.filename(component, target))
    }

    fn filename(&self, component: &str, target: &str) -> String {
        if component == "rust-src" {
            format!("rust-src-{}.tar.gz", self.rust_release)
        } else if component == "cargo" {
            format!("cargo-{}-{}.tar.gz", self.cargo_release, target)
        } else {
            format!("{}-{}-{}.tar.gz", component, self.rust_release, target)
        }
    }

    fn cached_version(&self, component: &str) -> &str {
        if component == "cargo" {
            &self.cargo_version
        } else {
            &self.rust_version
        }
    }

    fn version(&self, component: &str, target: &str) -> String {
        let mut cmd = Command::new("tar");
        let filename = self.filename(component, target);
        cmd.arg("xf")
           .arg(self.input.join(&filename))
           .arg(format!("{}/version", filename.replace(".tar.gz", "")))
           .arg("-O");
        let version = t!(cmd.output());
        if !version.status.success() {
            panic!("failed to learn version:\n\n{:?}\n\n{}\n\n{}",
                   cmd,
                   String::from_utf8_lossy(&version.stdout),
                   String::from_utf8_lossy(&version.stderr));
        }
        String::from_utf8_lossy(&version.stdout).trim().to_string()
    }

    fn hash(&self, path: &Path) -> String {
        let sha = t!(Command::new("shasum")
                        .arg("-a").arg("256")
                        .arg(path.file_name().unwrap())
                        .current_dir(path.parent().unwrap())
                        .output());
        assert!(sha.status.success());

        let filename = path.file_name().unwrap().to_str().unwrap();
        let sha256 = self.output.join(format!("{}.sha256", filename));
        t!(t!(File::create(&sha256)).write_all(&sha.stdout));

        let stdout = String::from_utf8_lossy(&sha.stdout);
        stdout.split_whitespace().next().unwrap().to_string()
    }

    fn sign(&self, path: &Path) {
        let filename = path.file_name().unwrap().to_str().unwrap();
        let asc = self.output.join(format!("{}.asc", filename));
        println!("signing: {:?}", path);
        let mut cmd = Command::new("gpg");
        cmd.arg("--no-tty")
            .arg("--yes")
            .arg("--passphrase-fd").arg("0")
            .arg("--armor")
            .arg("--output").arg(&asc)
            .arg("--detach-sign").arg(path)
            .stdin(Stdio::piped());
        let mut child = t!(cmd.spawn());
        t!(child.stdin.take().unwrap().write_all(self.gpg_passphrase.as_bytes()));
        assert!(t!(child.wait()).success());
    }

    fn write_manifest(&self, manifest: &str, name: &str) {
        let dst = self.output.join(name);
        t!(t!(File::create(&dst)).write_all(manifest.as_bytes()));
        self.hash(&dst);
        self.sign(&dst);
    }
}
<commit_msg>Rollup merge of #40225 - shepmaster:restore-build-date-file, r=alexcrichton<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate toml;
extern crate rustc_serialize;

use std::collections::{BTreeMap, HashMap};
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};

static HOSTS: &'static [&'static str] = &[
    "aarch64-unknown-linux-gnu",
    "arm-unknown-linux-gnueabi",
    "arm-unknown-linux-gnueabihf",
    "armv7-unknown-linux-gnueabihf",
    "i686-apple-darwin",
    "i686-pc-windows-gnu",
    "i686-pc-windows-msvc",
    "i686-unknown-linux-gnu",
    "mips-unknown-linux-gnu",
    "mips64-unknown-linux-gnuabi64",
    "mips64el-unknown-linux-gnuabi64",
    "mipsel-unknown-linux-gnu",
    "powerpc-unknown-linux-gnu",
    "powerpc64-unknown-linux-gnu",
    "powerpc64le-unknown-linux-gnu",
    "s390x-unknown-linux-gnu",
    "x86_64-apple-darwin",
    "x86_64-pc-windows-gnu",
    "x86_64-pc-windows-msvc",
    "x86_64-unknown-freebsd",
    "x86_64-unknown-linux-gnu",
    "x86_64-unknown-netbsd",
];

static TARGETS: &'static [&'static str] = &[
    "aarch64-apple-ios",
    "aarch64-unknown-fuchsia",
    "aarch64-linux-android",
    "aarch64-unknown-linux-gnu",
    "arm-linux-androideabi",
    "arm-unknown-linux-gnueabi",
    "arm-unknown-linux-gnueabihf",
    "arm-unknown-linux-musleabi",
    "arm-unknown-linux-musleabihf",
    "armv7-apple-ios",
    "armv7-linux-androideabi",
    "armv7-unknown-linux-gnueabihf",
    "armv7-unknown-linux-musleabihf",
    "armv7s-apple-ios",
    "asmjs-unknown-emscripten",
    "i386-apple-ios",
    "i586-pc-windows-msvc",
    "i586-unknown-linux-gnu",
    "i686-apple-darwin",
    "i686-linux-android",
    "i686-pc-windows-gnu",
    "i686-pc-windows-msvc",
    "i686-unknown-freebsd",
    "i686-unknown-linux-gnu",
    "i686-unknown-linux-musl",
    "mips-unknown-linux-gnu",
    "mips-unknown-linux-musl",
    "mips64-unknown-linux-gnuabi64",
    "mips64el-unknown-linux-gnuabi64",
    "mipsel-unknown-linux-gnu",
    "mipsel-unknown-linux-musl",
    "powerpc-unknown-linux-gnu",
    "powerpc64-unknown-linux-gnu",
    "powerpc64le-unknown-linux-gnu",
    "s390x-unknown-linux-gnu",
    "sparc64-unknown-linux-gnu",
    "wasm32-unknown-emscripten",
    "x86_64-apple-darwin",
    "x86_64-apple-ios",
    "x86_64-pc-windows-gnu",
    "x86_64-pc-windows-msvc",
    "x86_64-rumprun-netbsd",
    "x86_64-unknown-freebsd",
    "x86_64-unknown-fuchsia",
    "x86_64-unknown-linux-gnu",
    "x86_64-unknown-linux-musl",
    "x86_64-unknown-netbsd",
];

static MINGW: &'static [&'static str] = &[
    "i686-pc-windows-gnu",
    "x86_64-pc-windows-gnu",
];

struct Manifest {
    manifest_version: String,
    date: String,
    pkg: HashMap<String, Package>,
}

#[derive(RustcEncodable)]
struct Package {
    version: String,
    target: HashMap<String, Target>,
}

#[derive(RustcEncodable)]
struct Target {
    available: bool,
    url: Option<String>,
    hash: Option<String>,
    components: Option<Vec<Component>>,
    extensions: Option<Vec<Component>>,
}

#[derive(RustcEncodable)]
struct Component {
    pkg: String,
    target: String,
}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

struct Builder {
    rust_release: String,
    cargo_release: String,
    input: PathBuf,
    output: PathBuf,
    gpg_passphrase: String,
    digests: HashMap<String, String>,
    s3_address: String,
    date: String,
    rust_version: String,
    cargo_version: String,
}

fn main() {
    let mut args = env::args().skip(1);
    let input = PathBuf::from(args.next().unwrap());
    let output = PathBuf::from(args.next().unwrap());
    let date = args.next().unwrap();
    let rust_release = args.next().unwrap();
    let cargo_release = args.next().unwrap();
    let s3_address = args.next().unwrap();
    let mut passphrase = String::new();
    t!(io::stdin().read_to_string(&mut passphrase));

    Builder {
        rust_release: rust_release,
        cargo_release: cargo_release,
        input: input,
        output: output,
        gpg_passphrase: passphrase,
        digests: HashMap::new(),
        s3_address: s3_address,
        date: date,
        rust_version: String::new(),
        cargo_version: String::new(),
    }.build();
}

impl Builder {
    fn build(&mut self) {
        self.rust_version = self.version("rust", "x86_64-unknown-linux-gnu");
        self.cargo_version = self.version("cargo", "x86_64-unknown-linux-gnu");

        self.digest_and_sign();
        let Manifest { manifest_version, date, pkg } = self.build_manifest();

        // Unfortunately we can't use derive(RustcEncodable) here because the
        // version field is called `manifest-version`, not `manifest_version`.
        // In lieu of that just create the table directly here with a `BTreeMap`
        // and wrap it up in a `Value::Table`.
        let mut manifest = BTreeMap::new();
        manifest.insert("manifest-version".to_string(),
                        toml::Value::String(manifest_version));
        manifest.insert("date".to_string(), toml::Value::String(date.clone()));
        manifest.insert("pkg".to_string(), toml::encode(&pkg));
        let manifest = toml::Value::Table(manifest).to_string();

        let filename = format!("channel-rust-{}.toml", self.rust_release);
        self.write_manifest(&manifest, &filename);

        let filename = format!("channel-rust-{}-date.txt", self.rust_release);
        self.write_date_stamp(&date, &filename);

        if self.rust_release != "beta" && self.rust_release != "nightly" {
            self.write_manifest(&manifest, "channel-rust-stable.toml");
            self.write_date_stamp(&date, "channel-rust-stable-date.txt");
        }
    }

    fn digest_and_sign(&mut self) {
        for file in t!(self.input.read_dir()).map(|e| t!(e).path()) {
            let filename = file.file_name().unwrap().to_str().unwrap();
            let digest = self.hash(&file);
            self.sign(&file);
            assert!(self.digests.insert(filename.to_string(), digest).is_none());
        }
    }

    fn build_manifest(&mut self) -> Manifest {
        let mut manifest = Manifest {
            manifest_version: "2".to_string(),
            date: self.date.to_string(),
            pkg: HashMap::new(),
        };

        self.package("rustc", &mut manifest.pkg, HOSTS);
        self.package("cargo", &mut manifest.pkg, HOSTS);
        self.package("rust-mingw", &mut manifest.pkg, MINGW);
        self.package("rust-std", &mut manifest.pkg, TARGETS);
        self.package("rust-docs", &mut manifest.pkg, TARGETS);
        self.package("rust-src", &mut manifest.pkg, &["*"]);

        if self.rust_release == "nightly" {
            self.package("rust-analysis", &mut manifest.pkg, TARGETS);
        }

        let mut pkg = Package {
            version: self.cached_version("rust").to_string(),
            target: HashMap::new(),
        };
        for host in HOSTS {
            let filename = self.filename("rust", host);
            let digest = match self.digests.remove(&filename) {
                Some(digest) => digest,
                None => {
                    pkg.target.insert(host.to_string(), Target {
                        available: false,
                        url: None,
                        hash: None,
                        components: None,
                        extensions: None,
                    });
                    continue
                }
            };
            let mut components = Vec::new();
            let mut extensions = Vec::new();

            // rustc/rust-std/cargo are all required, and so is rust-mingw if it's
            // available for the target.
            components.extend(vec![
                Component { pkg: "rustc".to_string(), target: host.to_string() },
                Component { pkg: "rust-std".to_string(), target: host.to_string() },
                Component { pkg: "cargo".to_string(), target: host.to_string() },
            ]);
            if host.contains("pc-windows-gnu") {
                components.push(Component {
                    pkg: "rust-mingw".to_string(),
                    target: host.to_string(),
                });
            }

            // Docs, other standard libraries, and the source package are all
            // optional.
            extensions.push(Component {
                pkg: "rust-docs".to_string(),
                target: host.to_string(),
            });
            for target in TARGETS {
                if target != host {
                    extensions.push(Component {
                        pkg: "rust-std".to_string(),
                        target: target.to_string(),
                    });
                }
                if self.rust_release == "nightly" {
                    extensions.push(Component {
                        pkg: "rust-analysis".to_string(),
                        target: target.to_string(),
                    });
                }
            }
            extensions.push(Component {
                pkg: "rust-src".to_string(),
                target: "*".to_string(),
            });

            pkg.target.insert(host.to_string(), Target {
                available: true,
                url: Some(self.url("rust", host)),
                hash: Some(digest),
                components: Some(components),
                extensions: Some(extensions),
            });
        }
        manifest.pkg.insert("rust".to_string(), pkg);

        return manifest
    }

    fn package(&mut self,
               pkgname: &str,
               dst: &mut HashMap<String, Package>,
               targets: &[&str]) {
        let targets = targets.iter().map(|name| {
            let filename = self.filename(pkgname, name);
            let digest = match self.digests.remove(&filename) {
                Some(digest) => digest,
                None => {
                    return (name.to_string(), Target {
                        available: false,
                        url: None,
                        hash: None,
                        components: None,
                        extensions: None,
                    })
                }
            };

            (name.to_string(), Target {
                available: true,
                url: Some(self.url(pkgname, name)),
                hash: Some(digest),
                components: None,
                extensions: None,
            })
        }).collect();

        dst.insert(pkgname.to_string(), Package {
            version: self.cached_version(pkgname).to_string(),
            target: targets,
        });
    }

    fn url(&self, component: &str, target: &str) -> String {
        format!("{}/{}/{}",
                self.s3_address,
                self.date,
                self.filename(component, target))
    }

    fn filename(&self, component: &str, target: &str) -> String {
        if component == "rust-src" {
            format!("rust-src-{}.tar.gz", self.rust_release)
        } else if component == "cargo" {
            format!("cargo-{}-{}.tar.gz", self.cargo_release, target)
        } else {
            format!("{}-{}-{}.tar.gz", component, self.rust_release, target)
        }
    }

    fn cached_version(&self, component: &str) -> &str {
        if component == "cargo" {
            &self.cargo_version
        } else {
            &self.rust_version
        }
    }

    fn version(&self, component: &str, target: &str) -> String {
        let mut cmd = Command::new("tar");
        let filename = self.filename(component, target);
        cmd.arg("xf")
           .arg(self.input.join(&filename))
           .arg(format!("{}/version", filename.replace(".tar.gz", "")))
           .arg("-O");
        let version = t!(cmd.output());
        if !version.status.success() {
            panic!("failed to learn version:\n\n{:?}\n\n{}\n\n{}",
                   cmd,
                   String::from_utf8_lossy(&version.stdout),
                   String::from_utf8_lossy(&version.stderr));
        }
        String::from_utf8_lossy(&version.stdout).trim().to_string()
    }

    fn hash(&self, path: &Path) -> String {
        let sha = t!(Command::new("shasum")
                        .arg("-a").arg("256")
                        .arg(path.file_name().unwrap())
                        .current_dir(path.parent().unwrap())
                        .output());
        assert!(sha.status.success());

        let filename = path.file_name().unwrap().to_str().unwrap();
        let sha256 = self.output.join(format!("{}.sha256", filename));
        t!(t!(File::create(&sha256)).write_all(&sha.stdout));

        let stdout = String::from_utf8_lossy(&sha.stdout);
        stdout.split_whitespace().next().unwrap().to_string()
    }

    fn sign(&self, path: &Path) {
        let filename = path.file_name().unwrap().to_str().unwrap();
        let asc = self.output.join(format!("{}.asc", filename));
        println!("signing: {:?}", path);
        let mut cmd = Command::new("gpg");
        cmd.arg("--no-tty")
            .arg("--yes")
            .arg("--passphrase-fd").arg("0")
            .arg("--armor")
            .arg("--output").arg(&asc)
            .arg("--detach-sign").arg(path)
            .stdin(Stdio::piped());
        let mut child = t!(cmd.spawn());
        t!(child.stdin.take().unwrap().write_all(self.gpg_passphrase.as_bytes()));
        assert!(t!(child.wait()).success());
    }

    fn write_manifest(&self, manifest: &str, name: &str) {
        let dst = self.output.join(name);
        t!(t!(File::create(&dst)).write_all(manifest.as_bytes()));
        self.hash(&dst);
        self.sign(&dst);
    }

    fn write_date_stamp(&self, date: &str, name: &str) {
        let dst = self.output.join(name);
        t!(t!(File::create(&dst)).write_all(date.as_bytes()));
        self.hash(&dst);
        self.sign(&dst);
    }
}
<|endoftext|>


<commit_before>use std;
use std::io;
use std::sync::mpsc::RecvError;
use trackable::error::{self, IntoTrackableError, TrackableError, ErrorKindExt};
use fibers::sync::oneshot::MonitorError;

/// The error type for this crate.
pub type Error = TrackableError<ErrorKind>;

/// A list of error kind.
#[derive(Debug, Clone, Copy)]
pub enum ErrorKind {
    /// The operation timed out.
    Timeout,

    /// The target resource is full (maybe temporary).
    Full,

    /// The input is invalid.
    Invalid,

    /// The input is valid, but requires unsupported features by this agent.
    Unsupported,

    /// Other errors.
    Other,
}
impl error::ErrorKind for ErrorKind {
    fn description(&self) -> &str {
        match *self {
            ErrorKind::Timeout => "The operation timed out",
            ErrorKind::Full => "The target resource is full (maybe temporary)",
            ErrorKind::Invalid => "The input is invalid",
            ErrorKind::Unsupported => {
                "The input is valid, but requires unsupported features by this agent."
            }
            ErrorKind::Other => "Some error happend",
        }
    }
}
impl IntoTrackableError<MonitorError<Error>> for ErrorKind {
    fn into_trackable_error(f: MonitorError<Error>) -> Error {
        f.unwrap_or(ErrorKind::Other.into())
    }
}
impl IntoTrackableError<io::Error> for ErrorKind {
    fn into_trackable_error(f: io::Error) -> Error {
        ErrorKind::Other.cause(f)
    }
}
impl IntoTrackableError<RecvError> for ErrorKind {
    fn into_trackable_error(f: RecvError) -> Error {
        ErrorKind::Other.cause(f)
    }
}
impl IntoTrackableError<std::time::SystemTimeError> for ErrorKind {
    fn into_trackable_error(f: std::time::SystemTimeError) -> Error {
        ErrorKind::Other.cause(f)
    }
}
<commit_msg>Fix typo<commit_after>use std;
use std::io;
use std::sync::mpsc::RecvError;
use trackable::error::{self, IntoTrackableError, TrackableError, ErrorKindExt};
use fibers::sync::oneshot::MonitorError;

/// The error type for this crate.
pub type Error = TrackableError<ErrorKind>;

/// A list of error kind.
#[derive(Debug, Clone, Copy)]
pub enum ErrorKind {
    /// The operation timed out.
    Timeout,

    /// The target resource is full (maybe temporary).
    Full,

    /// The input is invalid.
    Invalid,

    /// The input is valid, but requires unsupported features by this agent.
    Unsupported,

    /// Other errors.
    Other,
}
impl error::ErrorKind for ErrorKind {
    fn description(&self) -> &str {
        match *self {
            ErrorKind::Timeout => "The operation timed out",
            ErrorKind::Full => "The target resource is full (maybe temporary)",
            ErrorKind::Invalid => "The input is invalid",
            ErrorKind::Unsupported => {
                "The input is valid, but requires unsupported features by this agent."
            }
            ErrorKind::Other => "Some error happened",
        }
    }
}
impl IntoTrackableError<MonitorError<Error>> for ErrorKind {
    fn into_trackable_error(f: MonitorError<Error>) -> Error {
        f.unwrap_or(ErrorKind::Other.into())
    }
}
impl IntoTrackableError<io::Error> for ErrorKind {
    fn into_trackable_error(f: io::Error) -> Error {
        ErrorKind::Other.cause(f)
    }
}
impl IntoTrackableError<RecvError> for ErrorKind {
    fn into_trackable_error(f: RecvError) -> Error {
        ErrorKind::Other.cause(f)
    }
}
impl IntoTrackableError<std::time::SystemTimeError> for ErrorKind {
    fn into_trackable_error(f: std::time::SystemTimeError) -> Error {
        ErrorKind::Other.cause(f)
    }
}
<|endoftext|>


<commit_before>use chrono;
use getopts;
use std::env;
use std::error::Error;
use std::fmt;
use std::io;
use timeclock::TimeClockError;

#[derive(Debug)]
pub enum WorklogError {
    Env(env::VarError),
    Io(io::Error),
    Opts(getopts::Fail),
    CronoParse(chrono::ParseError),
    TimeClock(TimeClockError),
}

impl From<env::VarError> for WorklogError {
    fn from(err: env::VarError) -> WorklogError {
        WorklogError::Env(err)
    }
}

impl From<io::Error> for WorklogError {
    fn from(err: io::Error) -> WorklogError {
        WorklogError::Io(err)
    }
}

impl From<getopts::Fail> for WorklogError {
    fn from(err: getopts::Fail) -> WorklogError {
        WorklogError::Opts(err)
    }
}

impl From<chrono::ParseError> for WorklogError {
    fn from(err: chrono::ParseError) -> WorklogError {
        WorklogError::CronoParse(err)
    }
}

impl From<TimeClockError> for WorklogError {
    fn from(err: TimeClockError) -> WorklogError {
        WorklogError::TimeClock(err)
    }
}

impl Error for WorklogError {
    fn description(&self) -> &str {
        match *self {
            WorklogError::Env(ref err) => err.description(),
            WorklogError::Io(ref err) => err.description(),
            WorklogError::Opts(ref err) => err.description(),
            WorklogError::CronoParse(ref err) => err.description(),
            WorklogError::TimeClock(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&Error> {
        Some(match *self {
            WorklogError::Env(ref err) => err as &Error,
            WorklogError::Io(ref err) => err as &Error,
            WorklogError::Opts(ref err) => err as &Error,
            WorklogError::CronoParse(ref err) => err as &Error,
            WorklogError::TimeClock(ref err) => err as &Error,
        })
    }
}

impl fmt::Display for WorklogError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WorklogError::Env(ref err) => fmt::Display::fmt(err, f),
            WorklogError::Io(ref err) => fmt::Display::fmt(err, f),
            WorklogError::Opts(ref err) => fmt::Display::fmt(err, f),
            WorklogError::CronoParse(ref err) => fmt::Display::fmt(err, f),
            WorklogError::TimeClock(ref err) => fmt::Display::fmt(err, f),
        }
    }
}
<commit_msg>Added MutExclOpt variant to WorklogError<commit_after>use chrono;
use getopts;
use std::env;
use std::error::Error;
use std::fmt;
use std::io;
use timeclock::TimeClockError;

#[derive(Debug)]
pub enum WorklogError {
    Env(env::VarError),
    Io(io::Error),
    Opts(getopts::Fail),
    CronoParse(chrono::ParseError),
    TimeClock(TimeClockError),
    MutExclOpt,
}

impl From<env::VarError> for WorklogError {
    fn from(err: env::VarError) -> WorklogError {
        WorklogError::Env(err)
    }
}

impl From<io::Error> for WorklogError {
    fn from(err: io::Error) -> WorklogError {
        WorklogError::Io(err)
    }
}

impl From<getopts::Fail> for WorklogError {
    fn from(err: getopts::Fail) -> WorklogError {
        WorklogError::Opts(err)
    }
}

impl From<chrono::ParseError> for WorklogError {
    fn from(err: chrono::ParseError) -> WorklogError {
        WorklogError::CronoParse(err)
    }
}

impl From<TimeClockError> for WorklogError {
    fn from(err: TimeClockError) -> WorklogError {
        WorklogError::TimeClock(err)
    }
}

impl Error for WorklogError {
    fn description(&self) -> &str {
        match *self {
            WorklogError::Env(ref err) => err.description(),
            WorklogError::Io(ref err) => err.description(),
            WorklogError::Opts(ref err) => err.description(),
            WorklogError::CronoParse(ref err) => err.description(),
            WorklogError::TimeClock(ref err) => err.description(),
            WorklogError::MutExclOpt => "--in or --out, not both",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            WorklogError::Env(ref err) => Some(err as &Error),
            WorklogError::Io(ref err) => Some(err as &Error),
            WorklogError::Opts(ref err) => Some(err as &Error),
            WorklogError::CronoParse(ref err) => Some(err as &Error),
            WorklogError::TimeClock(ref err) => Some(err as &Error),
            WorklogError::MutExclOpt => None,
        }
    }
}

impl fmt::Display for WorklogError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WorklogError::Env(ref err) => fmt::Display::fmt(err, f),
            WorklogError::Io(ref err) => fmt::Display::fmt(err, f),
            WorklogError::Opts(ref err) => fmt::Display::fmt(err, f),
            WorklogError::CronoParse(ref err) => fmt::Display::fmt(err, f),
            WorklogError::TimeClock(ref err) => fmt::Display::fmt(err, f),
            WorklogError::MutExclOpt => write!(f, "{}", self.description()),
        }
    }
}
<|endoftext|>


<commit_before>use crate::{
    error_holder::ErrorHolder,
    internal_rpc::InternalRPCHandle,
    options::{BasicAckOptions, BasicNackOptions, BasicRejectOptions},
    DeliveryTag, Promise, PromiseResolver, Result,
};

#[derive(Default, Debug, Clone)]
pub struct Acker {
    channel_id: u16,
    delivery_tag: DeliveryTag,
    internal_rpc: Option<InternalRPCHandle>,
    error: Option<ErrorHolder>,
}

impl Acker {
    pub(crate) fn new(
        channel_id: u16,
        delivery_tag: DeliveryTag,
        internal_rpc: Option<InternalRPCHandle>,
        error: Option<ErrorHolder>,
    ) -> Self {
        Self {
            channel_id,
            delivery_tag,
            internal_rpc,
            error,
        }
    }

    pub async fn ack(&self, options: BasicAckOptions) -> Result<()> {
        self.rpc(|internal_rpc, resolver| {
            internal_rpc.basic_ack(
                self.channel_id,
                self.delivery_tag,
                options,
                resolver,
                self.error.clone(),
            )
        })
        .await
    }

    pub async fn nack(&self, options: BasicNackOptions) -> Result<()> {
        self.rpc(|internal_rpc, resolver| {
            internal_rpc.basic_nack(
                self.channel_id,
                self.delivery_tag,
                options,
                resolver,
                self.error.clone(),
            )
        })
        .await
    }

    pub async fn reject(&self, options: BasicRejectOptions) -> Result<()> {
        self.rpc(|internal_rpc, resolver| {
            internal_rpc.basic_reject(
                self.channel_id,
                self.delivery_tag,
                options,
                resolver,
                self.error.clone(),
            )
        })
        .await
    }

    async fn rpc<F: Fn(&InternalRPCHandle, PromiseResolver<()>)>(&self, f: F) -> Result<()> {
        if let Some(error) = self.error.as_ref() {
            error.check()?;
        }
        if let Some(internal_rpc) = self.internal_rpc.as_ref() {
            let (promise, resolver) = Promise::new();
            f(internal_rpc, resolver);
            promise.await
        } else {
            Ok(())
        }
    }
}

impl PartialEq for Acker {
    fn eq(&self, other: &Acker) -> bool {
        self.channel_id == other.channel_id && self.delivery_tag == other.delivery_tag
    }
}
<commit_msg>acker: prevent double acks<commit_after>use crate::{
    error_holder::ErrorHolder,
    internal_rpc::InternalRPCHandle,
    options::{BasicAckOptions, BasicNackOptions, BasicRejectOptions},
    protocol::{AMQPError, AMQPSoftError},
    DeliveryTag, Error, Promise, PromiseResolver, Result,
};

use std::sync::{Arc, atomic::{AtomicBool, Ordering}};

#[derive(Default, Debug, Clone)]
pub struct Acker {
    channel_id: u16,
    delivery_tag: DeliveryTag,
    internal_rpc: Option<InternalRPCHandle>,
    error: Option<ErrorHolder>,
    used: Arc<AtomicBool>,
}

impl Acker {
    pub(crate) fn new(
        channel_id: u16,
        delivery_tag: DeliveryTag,
        internal_rpc: Option<InternalRPCHandle>,
        error: Option<ErrorHolder>,
    ) -> Self {
        Self {
            channel_id,
            delivery_tag,
            internal_rpc,
            error,
            used: Arc::default(),
        }
    }

    // FIXME: consume self and drop used
    pub async fn ack(&self, options: BasicAckOptions) -> Result<()> {
        self.rpc(|internal_rpc, resolver| {
            internal_rpc.basic_ack(
                self.channel_id,
                self.delivery_tag,
                options,
                resolver,
                self.error.clone(),
            )
        })
        .await
    }

    // FIXME: consume self and drop used
    pub async fn nack(&self, options: BasicNackOptions) -> Result<()> {
        self.rpc(|internal_rpc, resolver| {
            internal_rpc.basic_nack(
                self.channel_id,
                self.delivery_tag,
                options,
                resolver,
                self.error.clone(),
            )
        })
        .await
    }

    // FIXME: consume self and drop used
    pub async fn reject(&self, options: BasicRejectOptions) -> Result<()> {
        self.rpc(|internal_rpc, resolver| {
            internal_rpc.basic_reject(
                self.channel_id,
                self.delivery_tag,
                options,
                resolver,
                self.error.clone(),
            )
        })
        .await
    }

    async fn rpc<F: Fn(&InternalRPCHandle, PromiseResolver<()>)>(&self, f: F) -> Result<()> {
        if self.used.swap(true, Ordering::SeqCst) {
            return Err(Error::ProtocolError(AMQPError::new(AMQPSoftError::PRECONDITIONFAILED.into(), "Attempted to use an already used Acker".into())));
        }
        if let Some(error) = self.error.as_ref() {
            error.check()?;
        }
        if let Some(internal_rpc) = self.internal_rpc.as_ref() {
            let (promise, resolver) = Promise::new();
            f(internal_rpc, resolver);
            promise.await
        } else {
            Ok(())
        }
    }
}

impl PartialEq for Acker {
    fn eq(&self, other: &Acker) -> bool {
        self.channel_id == other.channel_id && self.delivery_tag == other.delivery_tag
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
use std::result;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver, TryRecvError, RecvError, SendError};
use std::thread;

use time::{Duration, SteadyTime};

pub type InitResult<E> = result::Result<Option<u64>, E>;
pub type ActorResult<T> = result::Result<T, ActorError>;
pub type StartResult<T, E> = result::Result<Actor<T>, E>;

pub struct Actor<T> where T: Any + Send {
    pub sender: Sender<Message<T>>,
    pub receiver: Receiver<Message<T>>,
    pub handle: thread::JoinHandle<ActorResult<()>>,
    pub name: Option<String>,
}

impl<T> Actor<T> where T: Any + Send {
    /// Create a new actor handler struct.
    pub fn new(sender: Sender<Message<T>>, receiver: Receiver<Message<T>>,
        handle: thread::JoinHandle<ActorResult<()>>, name: Option<String>) -> Self {
        Actor {
            sender: sender,
            receiver: receiver,
            handle: handle,
            name: name,
        }
    }

    pub fn cast(&self, message: T) -> ActorResult<()> {
        self::cast(&self.sender, message)
    }

    pub fn call(&self, message: T) -> ActorResult<T> {
        self::call(&self.sender, &self.receiver, message)
    }
}

pub fn cast<T: Any + Send>(tx: &Sender<Message<T>>, message: T) -> ActorResult<()> {
    match tx.send(Message::Cast(message)) {
        Ok(()) => Ok(()),
        Err(err) => Err(ActorError::from(err)),
    }
}

pub fn call<T: Any + Send>(tx: &Sender<Message<T>>, rx: &Receiver<Message<T>>, message: T) -> ActorResult<T> {
    match tx.send(Message::Call(message)) {
        Ok(()) => {
            match rx.recv() {
                Ok(Message::Reply(msg)) => Ok(msg),
                Ok(_) => panic!("must reply from a call!"),
                Err(err) => Err(ActorError::from(err)),
            }
        },
        Err(err) => Err(ActorError::from(err)),
    }
}

pub struct Builder<T: GenServer> {
    name: Option<String>,
    spec: T,
}

impl<A: GenServer> Builder<A> {
    pub fn new(spec: A) -> Self {
        Builder {
            name: None,
            spec: spec,
        }
    }

    pub fn name(mut self, name: String) -> Builder<A> {
        self.name = Some(name);
        self
    }

    /// Start an actor on a new thread and return an Actor.
    pub fn start(self, mut state: A::S) -> StartResult<A::T, A::E> {
        let (otx, orx) = mpsc::channel::<Message<A::T>>();
        let (itx, irx) = mpsc::channel::<Message<A::T>>();
        let initial_wait_ms = match self.spec.init(&itx, &mut state) {
            Ok(result) => result,
            Err(err) => return Err(err),
        };
        let itx2 = itx.clone(); // clone inner receive loop's sender for actor struct
        let name = self.name.clone();
        let thread_name = name.clone().unwrap_or("GenServer".to_string());
        let handle = thread::Builder::new().name(thread_name.clone()).spawn(move || {
            let mut timeout: Option<SteadyTime> = None;
            if let Some(ms) = initial_wait_ms {
                set_timeout(ms, &mut timeout);
            }
            loop {
                if let Some(go_time) = timeout {
                    if go_time >= SteadyTime::now() {
                        match self.spec.handle_timeout(&otx, &mut state) {
                            HandleResult::Stop(reason, None) => return shutdown(reason, None, &otx),
                            HandleResult::NoReply(Some(0)) => {
                                set_timeout(0, &mut timeout);
                                continue;
                            },
                            HandleResult::NoReply(new_timeout) => {
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            hr => panic!("unexpected `HandleResult` returned from handle_timeout: {:?}", hr),
                        }
                    }
                }
                match irx.try_recv() {
                    Ok(Message::Call(msg)) => {
                        match self.spec.handle_call(msg, &otx, &itx, &mut state) {
                            HandleResult::Reply(msg, new_timeout) => {
                                try!(otx.send(Message::Reply(msg)));
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            HandleResult::NoReply(new_timeout) => {
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            HandleResult::Stop(reason, reply) => return shutdown(reason, reply, &otx),
                        }
                    },
                    Ok(Message::Cast(msg)) => {
                        match self.spec.handle_cast(msg, &otx, &mut state) {
                            HandleResult::Stop(reason, reply) => return shutdown(reason, reply, &otx),
                            HandleResult::NoReply(new_timeout) => {
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            hr => panic!("unexpected `HandleResult` returned from handle_cast: {:?}", hr),
                        }
                    },
                    Ok(hr) => panic!("received unexpected message type: {:?}", hr),
                    Err(TryRecvError::Disconnected) => { break; },
                    Err(TryRecvError::Empty) => { },
                }
            }
            Ok(())
        }).unwrap();
        Ok(Actor::new(itx2, orx, handle, name))
    }
}

#[derive(Debug)]
pub enum ActorError {
    InitFailure(String),
    AbnormalShutdown(String),
    SendError,
    RecvError,
}

impl<T: Any + Send> From<mpsc::SendError<Message<T>>> for ActorError {
    fn from(_err: mpsc::SendError<Message<T>>) -> Self {
        ActorError::SendError
    }
}

impl From<mpsc::RecvError> for ActorError {
    fn from(_err: mpsc::RecvError) -> Self {
        ActorError::RecvError
    }
}

#[derive(Debug)]
pub enum StopReason {
    Normal,
    Fatal(String),
}

#[derive(Debug)]
pub enum HandleResult<T> where T: Any + Send {
    Reply(T, Option<u64>),
    NoReply(Option<u64>),
    Stop(StopReason, Option<T>),
}

pub enum Message<T> where T: Any + Send {
    Call(T),
    Cast(T),
    Reply(T),
}

impl<T> Debug for Message<T> where T: Any + Send + Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Message::Call(ref msg) => write!(f, "CALL: {:?}", msg),
            &Message::Cast(ref msg) => write!(f, "CAST: {:?}", msg),
            &Message::Reply(ref msg) => write!(f, "REPLY: {:?}", msg)
        }
    }
}

pub trait GenServer : Send + 'static {
    type T: Send + Any + Debug;
    type S: Send + Any;
    type E: Error + 'static;

    fn init(&self, _tx: &Sender<Message<Self::T>>, state: &mut Self::S) -> InitResult<Self::E>;

    fn handle_call(&self, _message: Self::T, _tx: &Sender<Message<Self::T>>, _caller: &Sender<Message<Self::T>>, _state: &mut Self::S) -> HandleResult<Self::T> {
        panic!("handle_call callback not implemented");
    }
    fn handle_cast(&self, _message: Self::T, _tx: &Sender<Message<Self::T>>, _state: &mut Self::S) -> HandleResult<Self::T> {
        panic!("handle_cast callback not implemented");
    }
    fn handle_timeout(&self, _tx: &Sender<Message<Self::T>>, _state: &mut Self::S) -> HandleResult<Self::T> {
        HandleResult::NoReply(None)
    }
}

fn set_timeout(wait_ms: u64, current_timeout: &mut Option<SteadyTime>) {
    *current_timeout = Some(SteadyTime::now() + Duration::milliseconds(wait_ms as i64));
}

fn shutdown<T: Any + Send>(reason: StopReason, reply: Option<T>, sender: &Sender<Message<T>>) -> Result<(), ActorError> {
    if let Some(msg) = reply {
        let _result = sender.send(Message::Reply(msg));
    }
    match reason {
        StopReason::Normal => Ok(()),
        StopReason::Fatal(e) => Err(ActorError::AbnormalShutdown(e)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fmt;
    use std::error::Error;
    use std::sync::mpsc::Sender;

    struct Worker;

    struct MyState {
        pub initialized: bool,
    }

    impl MyState {
        pub fn new() -> Self {
            MyState {
                initialized: false,
            }
        }
    }

    #[derive(Debug)]
    enum MyError {
        DirtyState,
    }

    #[derive(Debug)]
    enum MyMessage {
        Ok,
        Stop,
        State(bool),
        GetState,
        SetState(bool),
    }

    impl GenServer for Worker {
        type T = MyMessage;
        type S = MyState;
        type E = MyError;

        fn init(&self, _tx: &Sender<Message<Self::T>>, state: &mut Self::S) -> InitResult<Self::E> {
            if state.initialized {
                Err(MyError::DirtyState)
            } else {
                state.initialized = true;
                Ok(None)
            }
        }

        fn handle_call(&self, msg: Self::T, _: &Sender<Message<Self::T>>, _: &Sender<Message<Self::T>>, state: &mut Self::S) -> HandleResult<Self::T> {
            match msg {
                MyMessage::Stop => HandleResult::Stop(StopReason::Normal, Some(MyMessage::Ok)),
                MyMessage::GetState => HandleResult::Reply(MyMessage::State(state.initialized), None),
                MyMessage::SetState(value) => {
                    state.initialized = value;
                    HandleResult::Reply(MyMessage::Ok, None)
                }
                _ => HandleResult::Stop(StopReason::Fatal(String::from("Nope")), None),
            }
        }

        fn handle_cast(&self, msg: Self::T, _: &Sender<Message<Self::T>>, state: &mut Self::S) -> HandleResult<Self::T> {
            match msg {
                MyMessage::SetState(value) => {
                    state.initialized = value;
                    HandleResult::NoReply(None)
                },
                _ => HandleResult::NoReply(None)
            }
        }
    }

    impl fmt::Display for MyError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match *self {
                MyError::DirtyState => write!(f, "state already initialized"),
            }
        }
    }

    impl Error for MyError {
        fn description(&self) -> &str {
            match *self {
                MyError::DirtyState => "state already initialized"
            }
        }
    }

    #[test]
    fn error_on_init() {
        let mut state = MyState::new();
        state.initialized = true;
        match Builder::new(Worker).start(state) {
            Err(MyError::DirtyState) => assert!(true),
            Ok(_) => assert!(false),
        }
    }

    #[test]
    fn call_set_get_state() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        match actor.call(MyMessage::GetState) {
            Ok(MyMessage::State(true)) => assert!(true),
            _ => assert!(false),
        }
        assert!(actor.call(MyMessage::SetState(false)).is_ok());
        match actor.call(MyMessage::GetState) {
            Ok(MyMessage::State(false)) => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn multiple_cast_and_call_ordered() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        assert!(actor.cast(MyMessage::SetState(false)).is_ok());
        assert!(actor.cast(MyMessage::SetState(true)).is_ok());
        assert!(actor.cast(MyMessage::SetState(false)).is_ok());
        match actor.call(MyMessage::GetState) {
            Ok(MyMessage::State(result)) => assert_eq!(result, false),
            _ => assert!(false),
        }
    }

    #[test]
    fn stopping_an_actor() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        match actor.call(MyMessage::Stop) {
            Ok(MyMessage::Ok) => assert!(true),
            _ => assert!(false),
        }
        match actor.handle.join() {
            Ok(_) => assert!(true),
            Err(_) => assert!(false),
        }
    }

    #[test]
    fn explicitly_naming_actor() {
        let state = MyState::new();
        let actor = Builder::new(Worker).name("batman".to_string()).start(state).unwrap();
        assert!(actor.name.is_some());
        assert_eq!(actor.name.unwrap(), "batman".to_string());
    }

    #[test]
    fn default_named_actor() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        assert!(actor.name.is_none());
    }
}
<commit_msg>send proper "self" channel to handle_timeout and handle_cast<commit_after>use std::any::Any;
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
use std::result;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver, TryRecvError, RecvError, SendError};
use std::thread;

use time::{Duration, SteadyTime};

pub type InitResult<E> = result::Result<Option<u64>, E>;
pub type ActorResult<T> = result::Result<T, ActorError>;
pub type StartResult<T, E> = result::Result<Actor<T>, E>;

pub struct Actor<T> where T: Any + Send {
    pub sender: Sender<Message<T>>,
    pub receiver: Receiver<Message<T>>,
    pub handle: thread::JoinHandle<ActorResult<()>>,
    pub name: Option<String>,
}

impl<T> Actor<T> where T: Any + Send {
    /// Create a new actor handler struct.
    pub fn new(sender: Sender<Message<T>>, receiver: Receiver<Message<T>>,
        handle: thread::JoinHandle<ActorResult<()>>, name: Option<String>) -> Self {
        Actor {
            sender: sender,
            receiver: receiver,
            handle: handle,
            name: name,
        }
    }

    pub fn cast(&self, message: T) -> ActorResult<()> {
        self::cast(&self.sender, message)
    }

    pub fn call(&self, message: T) -> ActorResult<T> {
        self::call(&self.sender, &self.receiver, message)
    }
}

pub fn cast<T: Any + Send>(tx: &Sender<Message<T>>, message: T) -> ActorResult<()> {
    match tx.send(Message::Cast(message)) {
        Ok(()) => Ok(()),
        Err(err) => Err(ActorError::from(err)),
    }
}

pub fn call<T: Any + Send>(tx: &Sender<Message<T>>, rx: &Receiver<Message<T>>, message: T) -> ActorResult<T> {
    match tx.send(Message::Call(message)) {
        Ok(()) => {
            match rx.recv() {
                Ok(Message::Reply(msg)) => Ok(msg),
                Ok(_) => panic!("must reply from a call!"),
                Err(err) => Err(ActorError::from(err)),
            }
        },
        Err(err) => Err(ActorError::from(err)),
    }
}

pub struct Builder<T: GenServer> {
    name: Option<String>,
    spec: T,
}

impl<A: GenServer> Builder<A> {
    pub fn new(spec: A) -> Self {
        Builder {
            name: None,
            spec: spec,
        }
    }

    pub fn name(mut self, name: String) -> Builder<A> {
        self.name = Some(name);
        self
    }

    /// Start an actor on a new thread and return an Actor.
    pub fn start(self, mut state: A::S) -> StartResult<A::T, A::E> {
        let (otx, orx) = mpsc::channel::<Message<A::T>>();
        let (itx, irx) = mpsc::channel::<Message<A::T>>();
        let initial_wait_ms = match self.spec.init(&itx, &mut state) {
            Ok(result) => result,
            Err(err) => return Err(err),
        };
        let itx2 = itx.clone(); // clone inner receive loop's sender for actor struct
        let name = self.name.clone();
        let thread_name = name.clone().unwrap_or("GenServer".to_string());
        let handle = thread::Builder::new().name(thread_name.clone()).spawn(move || {
            let mut timeout: Option<SteadyTime> = None;
            if let Some(ms) = initial_wait_ms {
                set_timeout(ms, &mut timeout);
            }
            loop {
                if let Some(go_time) = timeout {
                    if go_time >= SteadyTime::now() {
                        match self.spec.handle_timeout(&itx, &mut state) {
                            HandleResult::Stop(reason, None) => return shutdown(reason, None, &otx),
                            HandleResult::NoReply(Some(0)) => {
                                set_timeout(0, &mut timeout);
                                continue;
                            },
                            HandleResult::NoReply(new_timeout) => {
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            hr => panic!("unexpected `HandleResult` returned from handle_timeout: {:?}", hr),
                        }
                    }
                }
                match irx.try_recv() {
                    Ok(Message::Call(msg)) => {
                        match self.spec.handle_call(msg, &otx, &itx, &mut state) {
                            HandleResult::Reply(msg, new_timeout) => {
                                try!(otx.send(Message::Reply(msg)));
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            HandleResult::NoReply(new_timeout) => {
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            HandleResult::Stop(reason, reply) => return shutdown(reason, reply, &otx),
                        }
                    },
                    Ok(Message::Cast(msg)) => {
                        match self.spec.handle_cast(msg, &itx, &mut state) {
                            HandleResult::Stop(reason, reply) => return shutdown(reason, reply, &otx),
                            HandleResult::NoReply(new_timeout) => {
                                if let Some(ms) = new_timeout {
                                    set_timeout(ms, &mut timeout);
                                }
                            },
                            hr => panic!("unexpected `HandleResult` returned from handle_cast: {:?}", hr),
                        }
                    },
                    Ok(hr) => panic!("received unexpected message type: {:?}", hr),
                    Err(TryRecvError::Disconnected) => { break; },
                    Err(TryRecvError::Empty) => { },
                }
            }
            Ok(())
        }).unwrap();
        Ok(Actor::new(itx2, orx, handle, name))
    }
}

#[derive(Debug)]
pub enum ActorError {
    InitFailure(String),
    AbnormalShutdown(String),
    SendError,
    RecvError,
}

impl<T: Any + Send> From<mpsc::SendError<Message<T>>> for ActorError {
    fn from(_err: mpsc::SendError<Message<T>>) -> Self {
        ActorError::SendError
    }
}

impl From<mpsc::RecvError> for ActorError {
    fn from(_err: mpsc::RecvError) -> Self {
        ActorError::RecvError
    }
}

#[derive(Debug)]
pub enum StopReason {
    Normal,
    Fatal(String),
}

#[derive(Debug)]
pub enum HandleResult<T> where T: Any + Send {
    Reply(T, Option<u64>),
    NoReply(Option<u64>),
    Stop(StopReason, Option<T>),
}

pub enum Message<T> where T: Any + Send {
    Call(T),
    Cast(T),
    Reply(T),
}

impl<T> Debug for Message<T> where T: Any + Send + Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Message::Call(ref msg) => write!(f, "CALL: {:?}", msg),
            &Message::Cast(ref msg) => write!(f, "CAST: {:?}", msg),
            &Message::Reply(ref msg) => write!(f, "REPLY: {:?}", msg)
        }
    }
}

pub trait GenServer : Send + 'static {
    type T: Send + Any + Debug;
    type S: Send + Any;
    type E: Error + 'static;

    fn init(&self, _tx: &Sender<Message<Self::T>>, state: &mut Self::S) -> InitResult<Self::E>;

    fn handle_call(&self, _message: Self::T, _tx: &Sender<Message<Self::T>>, _caller: &Sender<Message<Self::T>>, _state: &mut Self::S) -> HandleResult<Self::T> {
        panic!("handle_call callback not implemented");
    }
    fn handle_cast(&self, _message: Self::T, _tx: &Sender<Message<Self::T>>, _state: &mut Self::S) -> HandleResult<Self::T> {
        panic!("handle_cast callback not implemented");
    }
    fn handle_timeout(&self, _tx: &Sender<Message<Self::T>>, _state: &mut Self::S) -> HandleResult<Self::T> {
        HandleResult::NoReply(None)
    }
}

fn set_timeout(wait_ms: u64, current_timeout: &mut Option<SteadyTime>) {
    *current_timeout = Some(SteadyTime::now() + Duration::milliseconds(wait_ms as i64));
}

fn shutdown<T: Any + Send>(reason: StopReason, reply: Option<T>, sender: &Sender<Message<T>>) -> Result<(), ActorError> {
    if let Some(msg) = reply {
        let _result = sender.send(Message::Reply(msg));
    }
    match reason {
        StopReason::Normal => Ok(()),
        StopReason::Fatal(e) => Err(ActorError::AbnormalShutdown(e)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fmt;
    use std::error::Error;
    use std::sync::mpsc::Sender;

    struct Worker;

    struct MyState {
        pub initialized: bool,
    }

    impl MyState {
        pub fn new() -> Self {
            MyState {
                initialized: false,
            }
        }
    }

    #[derive(Debug)]
    enum MyError {
        DirtyState,
    }

    #[derive(Debug)]
    enum MyMessage {
        Ok,
        Stop,
        State(bool),
        GetState,
        SetState(bool),
    }

    impl GenServer for Worker {
        type T = MyMessage;
        type S = MyState;
        type E = MyError;

        fn init(&self, _tx: &Sender<Message<Self::T>>, state: &mut Self::S) -> InitResult<Self::E> {
            if state.initialized {
                Err(MyError::DirtyState)
            } else {
                state.initialized = true;
                Ok(None)
            }
        }

        fn handle_call(&self, msg: Self::T, _: &Sender<Message<Self::T>>, _: &Sender<Message<Self::T>>, state: &mut Self::S) -> HandleResult<Self::T> {
            match msg {
                MyMessage::Stop => HandleResult::Stop(StopReason::Normal, Some(MyMessage::Ok)),
                MyMessage::GetState => HandleResult::Reply(MyMessage::State(state.initialized), None),
                MyMessage::SetState(value) => {
                    state.initialized = value;
                    HandleResult::Reply(MyMessage::Ok, None)
                }
                _ => HandleResult::Stop(StopReason::Fatal(String::from("Nope")), None),
            }
        }

        fn handle_cast(&self, msg: Self::T, _: &Sender<Message<Self::T>>, state: &mut Self::S) -> HandleResult<Self::T> {
            match msg {
                MyMessage::SetState(value) => {
                    state.initialized = value;
                    HandleResult::NoReply(None)
                },
                _ => HandleResult::NoReply(None)
            }
        }
    }

    impl fmt::Display for MyError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match *self {
                MyError::DirtyState => write!(f, "state already initialized"),
            }
        }
    }

    impl Error for MyError {
        fn description(&self) -> &str {
            match *self {
                MyError::DirtyState => "state already initialized"
            }
        }
    }

    #[test]
    fn error_on_init() {
        let mut state = MyState::new();
        state.initialized = true;
        match Builder::new(Worker).start(state) {
            Err(MyError::DirtyState) => assert!(true),
            Ok(_) => assert!(false),
        }
    }

    #[test]
    fn call_set_get_state() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        match actor.call(MyMessage::GetState) {
            Ok(MyMessage::State(true)) => assert!(true),
            _ => assert!(false),
        }
        assert!(actor.call(MyMessage::SetState(false)).is_ok());
        match actor.call(MyMessage::GetState) {
            Ok(MyMessage::State(false)) => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn multiple_cast_and_call_ordered() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        assert!(actor.cast(MyMessage::SetState(false)).is_ok());
        assert!(actor.cast(MyMessage::SetState(true)).is_ok());
        assert!(actor.cast(MyMessage::SetState(false)).is_ok());
        match actor.call(MyMessage::GetState) {
            Ok(MyMessage::State(result)) => assert_eq!(result, false),
            _ => assert!(false),
        }
    }

    #[test]
    fn stopping_an_actor() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        match actor.call(MyMessage::Stop) {
            Ok(MyMessage::Ok) => assert!(true),
            _ => assert!(false),
        }
        match actor.handle.join() {
            Ok(_) => assert!(true),
            Err(_) => assert!(false),
        }
    }

    #[test]
    fn explicitly_naming_actor() {
        let state = MyState::new();
        let actor = Builder::new(Worker).name("batman".to_string()).start(state).unwrap();
        assert!(actor.name.is_some());
        assert_eq!(actor.name.unwrap(), "batman".to_string());
    }

    #[test]
    fn default_named_actor() {
        let state = MyState::new();
        let actor = Builder::new(Worker).start(state).unwrap();
        assert!(actor.name.is_none());
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::slice;
use std::fmt;
use approx;
use num;
use channel::{ColorChannel, BoundedChannel};
use color;
use color::{Color3, Color};

pub struct AlphaTag;

#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Alpha<T, InnerColor>
        where InnerColor: Color<Component=T>,
              T: ColorChannel {
    color: InnerColor,
    alpha: BoundedChannel<T>,
}

impl<T, InnerColor> Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + Color3 {

    pub fn from_channels(c1: T, c2: T, c3: T, alpha: T) -> Self {
        Alpha{
            color: InnerColor::from_tuple(&(c1, c2, c3)),
            alpha: BoundedChannel::new(alpha),
        }
    }
}
impl<T, InnerColor> Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> {
    pub fn from_color_and_alpha(color: InnerColor, alpha: T) -> Self {
        Alpha{
            color: color,
            alpha: BoundedChannel::new(alpha),
        }
    }

    pub fn alpha(&self) -> T {
        self.alpha.0
    }
    pub fn color(&self) -> &InnerColor {
        &self.color
    }
    pub fn alpha_mut(&mut self) -> &mut T {
        &mut self.alpha.0
    }
    pub fn color_mut(&mut self) -> &mut InnerColor {
        &mut self.color
    }
}

impl<T, InnerColor> Color for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> {
    type Component = T;
    type Tag = AlphaTag;

    fn num_channels() -> u32 {
        InnerColor::num_channels() + 1
    }

    fn from_slice(values: &[Self::Component]) -> Self {
        Alpha{
            color: InnerColor::from_slice(values),
            alpha: BoundedChannel::new(values[Self::num_channels() as usize - 1]),
        }
    }

    fn as_slice(&self) -> &[Self::Component] {
        unsafe {
            let ptr: *const Self::Component = mem::transmute(self);
            slice::from_raw_parts(ptr, Self::num_channels() as usize)
        }
    }

    fn broadcast(value: T) -> Self {
        Alpha{
            color: InnerColor::broadcast(value),
            alpha: BoundedChannel::new(value),
        }
    }
}

impl<T, InnerColor> color::Color4 for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::Color3 {

    fn as_tuple(&self) -> (Self::Component, Self::Component,
                           Self::Component, Self::Component) {
        let (c1, c2, c3) = self.color.as_tuple();
        (c1, c2, c3, self.alpha())
    }

    fn as_array(&self) -> [Self::Component; 4] {
        let (c1, c2, c3) = self.color.as_tuple();
        [c1, c2, c3, self.alpha()]
    }

    fn from_tuple(values: &(Self::Component, Self::Component,
                           Self::Component, Self::Component)) -> Self {
        Self::from_channels(values.0, values.1, values.2, values.3)
    }
}

impl<T, InnerColor> color::Invert for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::Invert {
    fn invert(&self) -> Self {
        Alpha{
            color: self.color.clone().invert(),
            alpha: self.alpha.clone().invert(),
        }
    }
}

impl<T, InnerColor> color::ComponentMap for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::ComponentMap {
    fn component_map<F>(&self, mut f: F) -> Self
            where F: FnMut(Self::Component) -> Self::Component {
        Alpha{
            alpha: BoundedChannel::new(f(self.alpha())),
            color: self.color.component_map(f),
        }
    }

    fn component_map_binary<F>(&self, other: &Self, mut f: F) -> Self
            where F: FnMut(Self::Component, Self::Component) -> Self::Component {
        Alpha{
            alpha: BoundedChannel::new(f(self.alpha(), other.alpha())),
            color: self.color.component_map_binary(&other.color, f),
        }
    }

}

impl<T, InnerColor> color::Bounded for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::Bounded {
    fn clamp(&self, min: Self::Component, max: Self::Component) -> Self {
        Alpha{
            color: self.color.clamp(min, max),
            alpha: self.alpha.clamp(min, max),
        }
    }

    fn normalize(&self) -> Self {
        Alpha{
            color: self.color.normalize(),
            alpha: self.alpha.normalize(),
        }
    }

    fn is_normalized(&self) -> bool {
        self.color.is_normalized() && self.alpha.is_normalized()
    }
}

impl<T, InnerColor> approx::ApproxEq for Alpha<T, InnerColor>
        where T: ColorChannel + num::Float + approx::ApproxEq,
              InnerColor: Color<Component=T> + approx::ApproxEq<Epsilon=T::Epsilon>,
              T::Epsilon: Clone {
    type Epsilon = T::Epsilon;

    fn default_epsilon() -> Self::Epsilon {
        T::default_epsilon()
    }

    fn default_max_relative() -> Self::Epsilon {
        T::default_max_relative()
    }

    fn default_max_ulps() -> u32 {
        T::default_max_ulps()
    }

    fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon,
           max_relative: Self::Epsilon) -> bool {
        self.color.relative_eq(&other.color, epsilon.clone(), max_relative.clone())
        && self.alpha().relative_eq(&other.alpha(), epsilon, max_relative)
    }

    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        self.color.ulps_eq(other.color(), epsilon.clone(), max_ulps)
        && self.alpha().ulps_eq(&other.alpha(), epsilon.clone(), max_ulps)
    }

}

impl<T, InnerColor> Default for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + Default {
    fn default() -> Self {
        Alpha{
            color: InnerColor::default(),
            alpha: BoundedChannel::min(),
        }
    }
}

impl<T, InnerColor> fmt::Display for Alpha<T, InnerColor>
        where T: ColorChannel + fmt::Display,
              InnerColor: Color<Component=T> + fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Alpha({}, {})", self.color, self.alpha)
    }
}
<commit_msg>Implemented unit tests for Alpha<T, InnerColor>.<commit_after>use std::mem;
use std::slice;
use std::fmt;
use approx;
use num;
use channel::{ColorChannel, BoundedChannel};
use color;
use color::{Color3, Color};

pub struct AlphaTag;

#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Alpha<T, InnerColor>
        where InnerColor: Color<Component=T>,
              T: ColorChannel {
    color: InnerColor,
    alpha: BoundedChannel<T>,
}

impl<T, InnerColor> Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + Color3 {

    pub fn from_channels(c1: T, c2: T, c3: T, alpha: T) -> Self {
        Alpha{
            color: InnerColor::from_tuple(&(c1, c2, c3)),
            alpha: BoundedChannel::new(alpha),
        }
    }
}
impl<T, InnerColor> Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> {
    pub fn from_color_and_alpha(color: InnerColor, alpha: T) -> Self {
        Alpha{
            color: color,
            alpha: BoundedChannel::new(alpha),
        }
    }

    pub fn alpha(&self) -> T {
        self.alpha.0
    }
    pub fn color(&self) -> &InnerColor {
        &self.color
    }
    pub fn alpha_mut(&mut self) -> &mut T {
        &mut self.alpha.0
    }
    pub fn color_mut(&mut self) -> &mut InnerColor {
        &mut self.color
    }
}

impl<T, InnerColor> Color for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> {
    type Component = T;
    type Tag = AlphaTag;

    fn num_channels() -> u32 {
        InnerColor::num_channels() + 1
    }

    fn from_slice(values: &[Self::Component]) -> Self {
        Alpha{
            color: InnerColor::from_slice(values),
            alpha: BoundedChannel::new(values[Self::num_channels() as usize - 1]),
        }
    }

    fn as_slice(&self) -> &[Self::Component] {
        unsafe {
            let ptr: *const Self::Component = mem::transmute(self);
            slice::from_raw_parts(ptr, Self::num_channels() as usize)
        }
    }

    fn broadcast(value: T) -> Self {
        Alpha{
            color: InnerColor::broadcast(value),
            alpha: BoundedChannel::new(value),
        }
    }
}

impl<T, InnerColor> color::Color4 for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::Color3 {

    fn as_tuple(&self) -> (Self::Component, Self::Component,
                           Self::Component, Self::Component) {
        let (c1, c2, c3) = self.color.as_tuple();
        (c1, c2, c3, self.alpha())
    }

    fn as_array(&self) -> [Self::Component; 4] {
        let (c1, c2, c3) = self.color.as_tuple();
        [c1, c2, c3, self.alpha()]
    }

    fn from_tuple(values: &(Self::Component, Self::Component,
                           Self::Component, Self::Component)) -> Self {
        Self::from_channels(values.0, values.1, values.2, values.3)
    }
}

impl<T, InnerColor> color::Invert for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::Invert {
    fn invert(&self) -> Self {
        Alpha{
            color: self.color.clone().invert(),
            alpha: self.alpha.clone().invert(),
        }
    }
}

impl<T, InnerColor> color::ComponentMap for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::ComponentMap {
    fn component_map<F>(&self, mut f: F) -> Self
            where F: FnMut(Self::Component) -> Self::Component {
        Alpha{
            alpha: BoundedChannel::new(f(self.alpha())),
            color: self.color.component_map(f),
        }
    }

    fn component_map_binary<F>(&self, other: &Self, mut f: F) -> Self
            where F: FnMut(Self::Component, Self::Component) -> Self::Component {
        Alpha{
            alpha: BoundedChannel::new(f(self.alpha(), other.alpha())),
            color: self.color.component_map_binary(&other.color, f),
        }
    }

}

impl<T, InnerColor> color::Bounded for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + color::Bounded {
    fn clamp(&self, min: Self::Component, max: Self::Component) -> Self {
        Alpha{
            color: self.color.clamp(min, max),
            alpha: self.alpha.clamp(min, max),
        }
    }

    fn normalize(&self) -> Self {
        Alpha{
            color: self.color.normalize(),
            alpha: self.alpha.normalize(),
        }
    }

    fn is_normalized(&self) -> bool {
        self.color.is_normalized() && self.alpha.is_normalized()
    }
}

impl<T, InnerColor> approx::ApproxEq for Alpha<T, InnerColor>
        where T: ColorChannel + num::Float + approx::ApproxEq,
              InnerColor: Color<Component=T> + approx::ApproxEq<Epsilon=T::Epsilon>,
              T::Epsilon: Clone {
    type Epsilon = T::Epsilon;

    fn default_epsilon() -> Self::Epsilon {
        T::default_epsilon()
    }

    fn default_max_relative() -> Self::Epsilon {
        T::default_max_relative()
    }

    fn default_max_ulps() -> u32 {
        T::default_max_ulps()
    }

    fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon,
           max_relative: Self::Epsilon) -> bool {
        self.color.relative_eq(&other.color, epsilon.clone(), max_relative.clone())
        && self.alpha().relative_eq(&other.alpha(), epsilon, max_relative)
    }

    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        self.color.ulps_eq(other.color(), epsilon.clone(), max_ulps)
        && self.alpha().ulps_eq(&other.alpha(), epsilon.clone(), max_ulps)
    }

}

impl<T, InnerColor> Default for Alpha<T, InnerColor>
        where T: ColorChannel,
              InnerColor: Color<Component=T> + Default {
    fn default() -> Self {
        Alpha{
            color: InnerColor::default(),
            alpha: BoundedChannel::min(),
        }
    }
}

impl<T, InnerColor> fmt::Display for Alpha<T, InnerColor>
        where T: ColorChannel + fmt::Display,
              InnerColor: Color<Component=T> + fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Alpha({}, {})", self.color, self.alpha)
    }
}

#[cfg(test)]
mod test {
    use ::alpha::*;
    use ::rgb;
    use ::color;
    use ::color::{Color, Invert, Bounded};

    #[test]
    fn test_construct() {
        {
            let c = Alpha::from_color_and_alpha(
                rgb::Rgb::from_channels(200u8, 75u8, 0u8), 100u8);

            assert_eq!(c.alpha(), 100u8);
            assert_eq!(c.color().red(), 200u8);
            assert_eq!(c.color().green(), 75u8);
            assert_eq!(c.color().blue(), 0u8);
        }
        {
            let c = Alpha::<_, rgb::Rgb<_>>::broadcast(0.25f32);
            assert_ulps_eq!(c,
                Alpha::from_color_and_alpha(rgb::Rgb::from_channels(0.25f32, 0.25, 0.25),
                    0.25));
        }
        {
            let c = Alpha::from_channels(0.5f32, 0.4f32, 0.3f32, 0.2f32);
            assert_ulps_eq!(c,
                Alpha::from_color_and_alpha(rgb::Rgb::from_channels(0.5f32, 0.4, 0.3),
                    0.2));
        }
    }

    #[test]
    fn test_as_slice() {
        let c = Alpha::<_, rgb::Rgb<_>>::from_channels(100u8, 75, 50, 25);

        assert_eq!(c.as_slice(), &[100u8, 75, 50, 25]);

        let c2 = Alpha::<_, rgb::Rgb<_>>::from_slice(&[50u8, 100, 150, 200]);

        assert_eq!(*c2.color(), rgb::Rgb::from_channels(50, 100, 150));
        assert_eq!(c2.alpha(), 200);
    }

    #[test]
    fn test_invert() {
        let c = Alpha::<_, rgb::Rgb<_>>::from_channels(100u8, 0, 255, 200);
        let c2 = c.invert();
        assert_eq!(c2, Alpha::from_channels(155u8, 255, 0, 55));
    }

    #[test]
    fn test_clamp() {
        let c = Alpha::from_color_and_alpha(rgb::Rgb::from_channels(200u8, 50, 255), 125);
        let c2 = Alpha::from_color_and_alpha(rgb::Rgb::from_channels(255u8, 250, 255), 254);
        let c3 = Alpha::from_color_and_alpha(rgb::Rgb::from_channels(0.5f32, 0.1, 0.99), 0.80);

        assert_eq!(c.clamp(75, 225), Alpha::from_channels(200, 75, 225, 125));
        assert_eq!(c2.clamp(75, 225), Alpha::from_channels(225, 225, 225, 225));
        assert_ulps_eq!(c3.clamp(0.25, 0.75), Alpha::from_channels(0.5, 0.25, 0.75, 0.75));
    }

    #[test]
    fn test_normalize() {
        let c = Alpha::<_, rgb::Rgb<_>>::from_channels(1.2, 0.88, 1.1, -2.0);

        assert_eq!(c.normalize(), Alpha::from_channels(1.0, 0.88, 1.0, 0.0));
        assert_eq!(c.is_normalized(), false);
        assert_eq!(c.normalize().is_normalized(), true);
    }
}
<|endoftext|>


<commit_before>use std::ops::{BitXorAssign, MulAssign, ShrAssign};
use byteorder::{BigEndian, ByteOrder};

const R: GFBlock = GFBlock([0xe1 << 56, 0]);

#[derive(Clone, Copy)]
struct GFBlock([u64; 2]);

pub fn h_xpoly(key: &[u8], bytes: &[u8]) -> [u8; 16] {
    let h = GFBlock::new(key);
    let mut y = GFBlock([0; 2]);
    for chunk in bytes.chunks(16) {
        y ^= GFBlock::new(chunk);
        y *= h;
    }
    y.into()
}

impl GFBlock {
    fn new(bytes: &[u8]) -> Self {
        assert_eq!(16, bytes.len());
        let mut longs = [0; 2];
        for (long, chunk) in longs.iter_mut().zip(bytes.chunks(8)) {
            *long = BigEndian::read_u64(chunk);
        }
        GFBlock(longs)
    }
}

impl From<GFBlock> for [u8; 16] {
    fn from(block: GFBlock) -> Self {
        let mut bytes = [0; 16];
        for (chunk, &long) in bytes.chunks_mut(8).zip(&block.0) {
            BigEndian::write_u64(chunk, long);
        }
        bytes
    }
}

impl BitXorAssign for GFBlock {
    fn bitxor_assign(&mut self, rhs: Self) {
        self.0[0] ^= rhs.0[0];
        self.0[1] ^= rhs.0[1];
    }
}

impl MulAssign for GFBlock {
    fn mul_assign(&mut self, rhs: Self) {
        let mut z = GFBlock([0; 2]);
        let mut v = rhs;
        for xp in self.0.iter_mut() {
            for _ in 0..64 {
                if *xp & (1 << 63) != 0 {
                    z ^= v;
                }
                *xp <<= 1;
                let h = v.0[1] & 1;
                v >>= 1;
                if h == 1 {
                    v ^= R;
                }
            }
        }
        self.0 = z.0;
    }
}

impl ShrAssign<usize> for GFBlock {
    fn shr_assign(&mut self, rhs: usize) {
        if rhs < 64 {
            self.0[1] >>= rhs;
            self.0[1] |= self.0[0] << (64 - rhs);
            self.0[0] >>= rhs;
        } else {
            self.0[1] = self.0[0] >> (rhs - 64);
            self.0[0] = 0;
        }
    }
}

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

    #[test]
    fn test_ghash() {
        let h = [
            0x66,
            0xe9,
            0x4b,
            0xd4,
            0xef,
            0x8a,
            0x2c,
            0x3b,
            0x88,
            0x4c,
            0xfa,
            0x59,
            0xca,
            0x34,
            0x2b,
            0x2e,
        ];
        assert_eq!([0; 16], h_xpoly(&h, &[0; 16]));

        let c = [
            0x03,
            0x88,
            0xda,
            0xce,
            0x60,
            0xb6,
            0xa3,
            0x92,
            0xf3,
            0x28,
            0xc2,
            0xb9,
            0x71,
            0xb2,
            0xfe,
            0x78,
        ];
        let mut bytes = [0; 32];
        bytes[..16].copy_from_slice(&c);
        bytes[31] = 0x80;
        let expected = [
            0xf3,
            0x8c,
            0xbb,
            0x1a,
            0xd6,
            0x92,
            0x23,
            0xdc,
            0xc3,
            0x45,
            0x7a,
            0xe5,
            0xb6,
            0xb0,
            0xf8,
            0x85,
        ];
        assert_eq!(expected, h_xpoly(&h, &bytes));
    }
}
<commit_msg>ghash: ghash(h, a, c) wrapper around h_xpoly<commit_after>use std::iter;
use std::ops::{BitXorAssign, MulAssign, ShrAssign};
use byteorder::{BigEndian, ByteOrder};

const R: GFBlock = GFBlock([0xe1 << 56, 0]);

#[derive(Clone, Copy)]
struct GFBlock([u64; 2]);

pub fn ghash(key: &[u8], data: &[u8], ciphertext: &[u8]) -> [u8; 16] {
    let mut bytes = data.to_vec();
    pad(&mut bytes);
    bytes.extend_from_slice(ciphertext);
    pad(&mut bytes);
    bytes.extend_from_slice(&len(data));
    bytes.extend_from_slice(&len(ciphertext));
    h_xpoly(key, &bytes)
}

fn h_xpoly(key: &[u8], bytes: &[u8]) -> [u8; 16] {
    let h = GFBlock::new(key);
    let mut y = GFBlock([0; 2]);
    for chunk in bytes.chunks(16) {
        y ^= GFBlock::new(chunk);
        y *= h;
    }
    y.into()
}

fn pad(bytes: &mut Vec<u8>) {
    let padding = (16 - bytes.len() % 16) % 16;
    bytes.extend(iter::repeat(0).take(padding));
}

fn len(bytes: &[u8]) -> [u8; 8] {
    let mut len = [0; 8];
    BigEndian::write_u64(&mut len, 8 * bytes.len() as u64);
    len
}

impl GFBlock {
    fn new(bytes: &[u8]) -> Self {
        assert_eq!(16, bytes.len());
        let mut longs = [0; 2];
        for (long, chunk) in longs.iter_mut().zip(bytes.chunks(8)) {
            *long = BigEndian::read_u64(chunk);
        }
        GFBlock(longs)
    }
}

impl From<GFBlock> for [u8; 16] {
    fn from(block: GFBlock) -> Self {
        let mut bytes = [0; 16];
        for (chunk, &long) in bytes.chunks_mut(8).zip(&block.0) {
            BigEndian::write_u64(chunk, long);
        }
        bytes
    }
}

impl BitXorAssign for GFBlock {
    fn bitxor_assign(&mut self, rhs: Self) {
        self.0[0] ^= rhs.0[0];
        self.0[1] ^= rhs.0[1];
    }
}

impl MulAssign for GFBlock {
    fn mul_assign(&mut self, rhs: Self) {
        let mut z = GFBlock([0; 2]);
        let mut v = rhs;
        for xp in self.0.iter_mut() {
            for _ in 0..64 {
                if *xp & (1 << 63) != 0 {
                    z ^= v;
                }
                *xp <<= 1;
                let h = v.0[1] & 1;
                v >>= 1;
                if h == 1 {
                    v ^= R;
                }
            }
        }
        self.0 = z.0;
    }
}

impl ShrAssign<usize> for GFBlock {
    fn shr_assign(&mut self, rhs: usize) {
        if rhs < 64 {
            self.0[1] >>= rhs;
            self.0[1] |= self.0[0] << (64 - rhs);
            self.0[0] >>= rhs;
        } else {
            self.0[1] = self.0[0] >> (rhs - 64);
            self.0[0] = 0;
        }
    }
}

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

    #[test]
    fn test_ghash() {
        let h = [
            0x66,
            0xe9,
            0x4b,
            0xd4,
            0xef,
            0x8a,
            0x2c,
            0x3b,
            0x88,
            0x4c,
            0xfa,
            0x59,
            0xca,
            0x34,
            0x2b,
            0x2e,
        ];
        assert_eq!([0; 16], h_xpoly(&h, &[0; 16]));
        assert_eq!([0; 16], ghash(&h, &[], &[]));

        let c = [
            0x03,
            0x88,
            0xda,
            0xce,
            0x60,
            0xb6,
            0xa3,
            0x92,
            0xf3,
            0x28,
            0xc2,
            0xb9,
            0x71,
            0xb2,
            0xfe,
            0x78,
        ];
        let mut bytes = [0; 32];
        bytes[..16].copy_from_slice(&c);
        bytes[31] = 0x80;
        let expected = [
            0xf3,
            0x8c,
            0xbb,
            0x1a,
            0xd6,
            0x92,
            0x23,
            0xdc,
            0xc3,
            0x45,
            0x7a,
            0xe5,
            0xb6,
            0xb0,
            0xf8,
            0x85,
        ];
        assert_eq!(expected, h_xpoly(&h, &bytes));
        assert_eq!(expected, ghash(&h, &[], &c));
    }
}
<|endoftext|>