<commit_before>use std::hash::{Hash, Hasher, SipHasher};
use std::path::{Path, PathBuf};
use std::process::Command;

use tempdir::TempDir;

use errors::*;
use {fs, io, parse, rustc, toml, xargo};
use rustc::{Channel, VersionMeta};
use {CommandExt, Target};

pub struct Crate {
    td: TempDir,
    triple: String,
}

impl Crate {
    pub fn build(rust_src: &Path,
                 target: &Target,
                 profile: &Option<toml::Value>,
                 verbose: bool)
                 -> Result<Self> {
        const CARGO_TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"

[dependencies]
"#;

        let triple = target.triple();
        let sysroot = Crate {
            td: try!(TempDir::new("xargo")
                .chain_err(|| "couldn't create a temporary directory")),
            triple: triple.to_owned(),
        };
        let crate_dir = &sysroot.crate_dir().to_owned();

        let deps = if try!(target.has_atomics()) {
            vec!["alloc", "collections", "core", "rand", "rustc_unicode"]
        } else {
            vec!["core", "rand", "rustc_unicode"]
        };
        let mut toml = CARGO_TOML.to_owned();

        for dep in &deps {
            toml.push_str(&format!("{} = {{ path = \"{}\" }}\n",
                                   dep,
                                   rust_src.join(format!("src/lib{}", dep))
                                       .display()));
        }

        if let Some(profile) = profile.as_ref() {
            toml.push_str(&profile.to_string());
        }

        try!(io::write(&crate_dir.join("Cargo.toml"), &toml));
        try!(fs::mkdir(&crate_dir.join("src")));
        try!(io::write(&crate_dir.join("src/lib.rs"), ""));

        let cargo = || {
            let mut cmd = Command::new("cargo");
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.args(&["build", "--release", "--manifest-path"])
                .arg(crate_dir.join("Cargo.toml"));

            cmd.arg("--target");
            match *target {
                Target::BuiltIn { ref triple } |
                Target::Custom { ref triple, .. } => {
                    cmd.arg(triple);
                }
                Target::Path { ref json, .. } => {
                    cmd.arg(json);
                }
            }

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for dep in &deps {
            try!(cargo().arg("-p").arg(dep).run_or_error());
        }

        Ok(sysroot)
    }

    fn crate_dir(&self) -> &Path {
        self.td.path()
    }

    pub fn deps_dir(&self) -> PathBuf {
        self.td.path().join("target").join(&self.triple).join("release/deps")
    }
}

pub fn update(target: &Target, verbose: bool) -> Result<()> {
    let meta = try!(rustc::meta());

    if meta.channel != Channel::Nightly {
        try!(Err("Xargo requires the nightly channel. Run `rustup default \
                  nightly` or similar"))
    }

    try!(update_target_sysroot(target, &meta, verbose));
    try!(update_host_sysroot(&meta));

    Ok(())
}

fn update_target_sysroot(target: &Target,
                         meta: &VersionMeta,
                         verbose: bool)
                         -> Result<()> {
    // The hash is a digest of the following elements:
    // - RUSTFLAGS / build.rustflags / target.*.rustflags
    // - The [profile] in Cargo.toml
    // - The contents of the target specification file
    // - `rustc` version
    let hasher = &mut SipHasher::new();

    for flag in try!(rustc::flags(target, "rustflags")) {
        flag.hash(hasher);
    }

    let profile = try!(parse::profile_in_cargo_toml());
    if let Some(profile) = profile.as_ref() {
        profile.to_string().hash(hasher);
    }

    try!(target.hash(hasher));

    if let Some(commit_hash) = meta.commit_hash.as_ref() {
        commit_hash.hash(hasher);
    } else {
        meta.semver.hash(hasher);
    }

    let new_hash = hasher.finish();

    let triple = target.triple();
    let lock = try!(xargo::lock_rw(triple));
    let old_hash = try!(io::read_hash(&lock));

    if old_hash != Some(new_hash) {
        let rust_src = try!(rustc::rust_src());

        let sysroot = try!(Crate::build(&rust_src, target, &profile, verbose));

        try!(fs::remove_siblings(&lock));

        let dst = lock.parent().join("lib");
        try!(fs::cp_r(&sysroot.deps_dir(), &dst));

        try!(io::write_hash(&lock, new_hash));
    }

    Ok(())
}

fn update_host_sysroot(meta: &VersionMeta) -> Result<()> {
    let host = &meta.host;
    let lock = try!(xargo::lock_rw(host));

    let hasher = &mut SipHasher::new();
    host.hash(hasher);

    let new_hash = hasher.finish();
    let old_hash = try!(io::read_hash(&lock));

    if old_hash != Some(new_hash) {
        try!(fs::remove_siblings(&lock));

        let src =
            try!(rustc::sysroot()).join("lib/rustlib").join(host).join("lib");
        let dst = lock.parent().join("lib");
        try!(fs::cp_r(&src, &dst));

        try!(io::write_hash(&lock, new_hash));
    }

    Ok(())
}
<commit_msg>use single quotes in Cargo.toml<commit_after>use std::hash::{Hash, Hasher, SipHasher};
use std::path::{Path, PathBuf};
use std::process::Command;

use tempdir::TempDir;

use errors::*;
use {fs, io, parse, rustc, toml, xargo};
use rustc::{Channel, VersionMeta};
use {CommandExt, Target};

pub struct Crate {
    td: TempDir,
    triple: String,
}

impl Crate {
    pub fn build(rust_src: &Path,
                 target: &Target,
                 profile: &Option<toml::Value>,
                 verbose: bool)
                 -> Result<Self> {
        const CARGO_TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"

[dependencies]
"#;

        let triple = target.triple();
        let sysroot = Crate {
            td: try!(TempDir::new("xargo")
                .chain_err(|| "couldn't create a temporary directory")),
            triple: triple.to_owned(),
        };
        let crate_dir = &sysroot.crate_dir().to_owned();

        let deps = if try!(target.has_atomics()) {
            vec!["alloc", "collections", "core", "rand", "rustc_unicode"]
        } else {
            vec!["core", "rand", "rustc_unicode"]
        };
        let mut toml = CARGO_TOML.to_owned();

        for dep in &deps {
            toml.push_str(&format!("{} = {{ path = '{}' }}\n",
                                   dep,
                                   rust_src.join(format!("src/lib{}", dep))
                                       .display()));
        }

        if let Some(profile) = profile.as_ref() {
            toml.push_str(&profile.to_string());
        }

        try!(io::write(&crate_dir.join("Cargo.toml"), &toml));
        try!(fs::mkdir(&crate_dir.join("src")));
        try!(io::write(&crate_dir.join("src/lib.rs"), ""));

        let cargo = || {
            let mut cmd = Command::new("cargo");
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.args(&["build", "--release", "--manifest-path"])
                .arg(crate_dir.join("Cargo.toml"));

            cmd.arg("--target");
            match *target {
                Target::BuiltIn { ref triple } |
                Target::Custom { ref triple, .. } => {
                    cmd.arg(triple);
                }
                Target::Path { ref json, .. } => {
                    cmd.arg(json);
                }
            }

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for dep in &deps {
            try!(cargo().arg("-p").arg(dep).run_or_error());
        }

        Ok(sysroot)
    }

    fn crate_dir(&self) -> &Path {
        self.td.path()
    }

    pub fn deps_dir(&self) -> PathBuf {
        self.td.path().join("target").join(&self.triple).join("release/deps")
    }
}

pub fn update(target: &Target, verbose: bool) -> Result<()> {
    let meta = try!(rustc::meta());

    if meta.channel != Channel::Nightly {
        try!(Err("Xargo requires the nightly channel. Run `rustup default \
                  nightly` or similar"))
    }

    try!(update_target_sysroot(target, &meta, verbose));
    try!(update_host_sysroot(&meta));

    Ok(())
}

fn update_target_sysroot(target: &Target,
                         meta: &VersionMeta,
                         verbose: bool)
                         -> Result<()> {
    // The hash is a digest of the following elements:
    // - RUSTFLAGS / build.rustflags / target.*.rustflags
    // - The [profile] in Cargo.toml
    // - The contents of the target specification file
    // - `rustc` version
    let hasher = &mut SipHasher::new();

    for flag in try!(rustc::flags(target, "rustflags")) {
        flag.hash(hasher);
    }

    let profile = try!(parse::profile_in_cargo_toml());
    if let Some(profile) = profile.as_ref() {
        profile.to_string().hash(hasher);
    }

    try!(target.hash(hasher));

    if let Some(commit_hash) = meta.commit_hash.as_ref() {
        commit_hash.hash(hasher);
    } else {
        meta.semver.hash(hasher);
    }

    let new_hash = hasher.finish();

    let triple = target.triple();
    let lock = try!(xargo::lock_rw(triple));
    let old_hash = try!(io::read_hash(&lock));

    if old_hash != Some(new_hash) {
        let rust_src = try!(rustc::rust_src());

        let sysroot = try!(Crate::build(&rust_src, target, &profile, verbose));

        try!(fs::remove_siblings(&lock));

        let dst = lock.parent().join("lib");
        try!(fs::cp_r(&sysroot.deps_dir(), &dst));

        try!(io::write_hash(&lock, new_hash));
    }

    Ok(())
}

fn update_host_sysroot(meta: &VersionMeta) -> Result<()> {
    let host = &meta.host;
    let lock = try!(xargo::lock_rw(host));

    let hasher = &mut SipHasher::new();
    host.hash(hasher);

    let new_hash = hasher.finish();
    let old_hash = try!(io::read_hash(&lock));

    if old_hash != Some(new_hash) {
        try!(fs::remove_siblings(&lock));

        let src =
            try!(rustc::sysroot()).join("lib/rustlib").join(host).join("lib");
        let dst = lock.parent().join("lib");
        try!(fs::cp_r(&src, &dst));

        try!(io::write_hash(&lock, new_hash));
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! `i586`'s `xsave` and `xsaveopt` target feature intrinsics
#![allow(clippy::module_name_repetitions)]

#[cfg(test)]
use stdarch_test::assert_instr;

#[allow(improper_ctypes)]
extern "C" {
    #[link_name = "llvm.x86.xsave"]
    fn xsave(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xrstor"]
    fn xrstor(p: *const u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsetbv"]
    fn xsetbv(v: u32, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsaveopt"]
    fn xsaveopt(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsavec"]
    fn xsavec(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsaves"]
    fn xsaves(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xrstors"]
    fn xrstors(p: *const u8, hi: u32, lo: u32);
}

/// Performs a full or partial save of the enabled processor states to memory at
/// `mem_addr`.
///
/// State is saved based on bits `[62:0]` in `save_mask` and XCR0.
/// `mem_addr` must be aligned on a 64-byte boundary.
///
/// The format of the XSAVE area is detailed in Section 13.4, “XSAVE Area,” of
/// Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsave)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xsave))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsave(mem_addr: *mut u8, save_mask: u64) {
    xsave(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial restore of the enabled processor states using
/// the state information stored in memory at `mem_addr`.
///
/// State is restored based on bits `[62:0]` in `rs_mask`, `XCR0`, and
/// `mem_addr.HEADER.XSTATE_BV`. `mem_addr` must be aligned on a 64-byte
/// boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xrstor)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xrstor))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xrstor(mem_addr: *const u8, rs_mask: u64) {
    xrstor(mem_addr, (rs_mask >> 32) as u32, rs_mask as u32);
}

/// `XFEATURE_ENABLED_MASK` for `XCR`
///
/// This intrinsic maps to `XSETBV` instruction.
#[stable(feature = "simd_x86", since = "1.27.0")]
pub const _XCR_XFEATURE_ENABLED_MASK: u32 = 0;

/// Copies 64-bits from `val` to the extended control register (`XCR`) specified
/// by `a`.
///
/// Currently only `XFEATURE_ENABLED_MASK` `XCR` is supported.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsetbv)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xsetbv))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsetbv(a: u32, val: u64) {
    xsetbv(a, (val >> 32) as u32, val as u32);
}

/// Reads the contents of the extended control register `XCR`
/// specified in `xcr_no`.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xgetbv)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xgetbv))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xgetbv(xcr_no: u32) -> u64 {
    let eax: u32;
    let edx: u32;
    llvm_asm!("xgetbv" : "={eax}"(eax), "={edx}"(edx) : "{ecx}"(xcr_no));
    ((edx as u64) << 32) | (eax as u64)
}

/// Performs a full or partial save of the enabled processor states to memory at
/// `mem_addr`.
///
/// State is saved based on bits `[62:0]` in `save_mask` and `XCR0`.
/// `mem_addr` must be aligned on a 64-byte boundary. The hardware may optimize
/// the manner in which data is saved. The performance of this instruction will
/// be equal to or better than using the `XSAVE` instruction.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsaveopt)
#[inline]
#[target_feature(enable = "xsave,xsaveopt")]
#[cfg_attr(test, assert_instr(xsaveopt))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsaveopt(mem_addr: *mut u8, save_mask: u64) {
    xsaveopt(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial save of the enabled processor states to memory
/// at `mem_addr`.
///
/// `xsavec` differs from `xsave` in that it uses compaction and that it may
/// use init optimization. State is saved based on bits `[62:0]` in `save_mask`
/// and `XCR0`. `mem_addr` must be aligned on a 64-byte boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsavec)
#[inline]
#[target_feature(enable = "xsave,xsavec")]
#[cfg_attr(test, assert_instr(xsavec))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsavec(mem_addr: *mut u8, save_mask: u64) {
    xsavec(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial save of the enabled processor states to memory at
/// `mem_addr`
///
/// `xsaves` differs from xsave in that it can save state components
/// corresponding to bits set in `IA32_XSS` `MSR` and that it may use the
/// modified optimization. State is saved based on bits `[62:0]` in `save_mask`
/// and `XCR0`. `mem_addr` must be aligned on a 64-byte boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsaves)
#[inline]
#[target_feature(enable = "xsave,xsaves")]
#[cfg_attr(test, assert_instr(xsaves))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsaves(mem_addr: *mut u8, save_mask: u64) {
    xsaves(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial restore of the enabled processor states using the
/// state information stored in memory at `mem_addr`.
///
/// `xrstors` differs from `xrstor` in that it can restore state components
/// corresponding to bits set in the `IA32_XSS` `MSR`; `xrstors` cannot restore
/// from an `xsave` area in which the extended region is in the standard form.
/// State is restored based on bits `[62:0]` in `rs_mask`, `XCR0`, and
/// `mem_addr.HEADER.XSTATE_BV`. `mem_addr` must be aligned on a 64-byte
/// boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xrstors)
#[inline]
#[target_feature(enable = "xsave,xsaves")]
#[cfg_attr(test, assert_instr(xrstors))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xrstors(mem_addr: *const u8, rs_mask: u64) {
    xrstors(mem_addr, (rs_mask >> 32) as u32, rs_mask as u32);
}

#[cfg(test)]
mod tests {
    use std::{fmt, prelude::v1::*};

    use crate::core_arch::x86::*;
    use stdarch_test::simd_test;

    #[repr(align(64))]
    struct XsaveArea {
        // max size for 256-bit registers is 800 bytes:
        // see https://software.intel.com/en-us/node/682996
        // max size for 512-bit registers is 2560 bytes:
        // FIXME: add source
        data: [u8; 2560],
    }

    impl XsaveArea {
        fn new() -> XsaveArea {
            XsaveArea { data: [0; 2560] }
        }
        fn ptr(&mut self) -> *mut u8 {
            &mut self.data[0] as *mut _ as *mut u8
        }
    }

    impl PartialEq<XsaveArea> for XsaveArea {
        fn eq(&self, other: &XsaveArea) -> bool {
            for i in 0..self.data.len() {
                if self.data[i] != other.data[i] {
                    return false;
                }
            }
            true
        }
    }

    impl fmt::Debug for XsaveArea {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "[")?;
            for i in 0..self.data.len() {
                write!(f, "{}", self.data[i])?;
                if i != self.data.len() - 1 {
                    write!(f, ", ")?;
                }
            }
            write!(f, "]")
        }
    }

    // FIXME: https://github.com/rust-lang/stdarch/issues/209
    /*
    #[simd_test(enable = "xsave")]
    unsafe fn xsave() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsave(a.ptr(), m);
        _xrstor(a.ptr(), m);
        _xsave(b.ptr(), m);
        assert_eq!(a, b);
    }
    */

    #[simd_test(enable = "xsave")]
    unsafe fn xgetbv_xsetbv() {
        let xcr_n: u32 = _XCR_XFEATURE_ENABLED_MASK;

        let xcr: u64 = _xgetbv(xcr_n);
        // FIXME: XSETBV is a privileged instruction we should only test this
        // when running in privileged mode:
        //
        // _xsetbv(xcr_n, xcr);
        let xcr_cpy: u64 = _xgetbv(xcr_n);
        assert_eq!(xcr, xcr_cpy);
    }

    // FIXME: https://github.com/rust-lang/stdarch/issues/209
    /*
    #[simd_test(enable = "xsave,xsaveopt")]
    unsafe fn xsaveopt() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsaveopt(a.ptr(), m);
        _xrstor(a.ptr(), m);
        _xsaveopt(b.ptr(), m);
        assert_eq!(a, b);
    }
    */

    // FIXME: this looks like a bug in Intel's SDE:
    #[cfg(not(stdarch_intel_sde))]
    #[simd_test(enable = "xsave,xsavec")]
    unsafe fn xsavec() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsavec(a.ptr(), m);
        _xrstor(a.ptr(), m);
        _xsavec(b.ptr(), m);
        assert_eq!(a, b);
    }

    // FIXME: https://github.com/rust-lang/stdarch/issues/209
    /*
    #[simd_test(enable = "xsave,xsaves")]
    unsafe fn xsaves() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsaves(a.ptr(), m);
        _xrstors(a.ptr(), m);
        _xsaves(b.ptr(), m);
        assert_eq!(a, b);
    }
    */
}
<commit_msg>Reimplement `_xgetbv` with LLVM intrinsics (#958)<commit_after>//! `i586`'s `xsave` and `xsaveopt` target feature intrinsics
#![allow(clippy::module_name_repetitions)]

#[cfg(test)]
use stdarch_test::assert_instr;

#[allow(improper_ctypes)]
extern "C" {
    #[link_name = "llvm.x86.xsave"]
    fn xsave(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xrstor"]
    fn xrstor(p: *const u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsetbv"]
    fn xsetbv(v: u32, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xgetbv"]
    fn xgetbv(v: u32) -> i64;
    #[link_name = "llvm.x86.xsaveopt"]
    fn xsaveopt(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsavec"]
    fn xsavec(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xsaves"]
    fn xsaves(p: *mut u8, hi: u32, lo: u32);
    #[link_name = "llvm.x86.xrstors"]
    fn xrstors(p: *const u8, hi: u32, lo: u32);
}

/// Performs a full or partial save of the enabled processor states to memory at
/// `mem_addr`.
///
/// State is saved based on bits `[62:0]` in `save_mask` and XCR0.
/// `mem_addr` must be aligned on a 64-byte boundary.
///
/// The format of the XSAVE area is detailed in Section 13.4, “XSAVE Area,” of
/// Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsave)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xsave))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsave(mem_addr: *mut u8, save_mask: u64) {
    xsave(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial restore of the enabled processor states using
/// the state information stored in memory at `mem_addr`.
///
/// State is restored based on bits `[62:0]` in `rs_mask`, `XCR0`, and
/// `mem_addr.HEADER.XSTATE_BV`. `mem_addr` must be aligned on a 64-byte
/// boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xrstor)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xrstor))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xrstor(mem_addr: *const u8, rs_mask: u64) {
    xrstor(mem_addr, (rs_mask >> 32) as u32, rs_mask as u32);
}

/// `XFEATURE_ENABLED_MASK` for `XCR`
///
/// This intrinsic maps to `XSETBV` instruction.
#[stable(feature = "simd_x86", since = "1.27.0")]
pub const _XCR_XFEATURE_ENABLED_MASK: u32 = 0;

/// Copies 64-bits from `val` to the extended control register (`XCR`) specified
/// by `a`.
///
/// Currently only `XFEATURE_ENABLED_MASK` `XCR` is supported.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsetbv)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xsetbv))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsetbv(a: u32, val: u64) {
    xsetbv(a, (val >> 32) as u32, val as u32);
}

/// Reads the contents of the extended control register `XCR`
/// specified in `xcr_no`.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xgetbv)
#[inline]
#[target_feature(enable = "xsave")]
#[cfg_attr(test, assert_instr(xgetbv))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xgetbv(xcr_no: u32) -> u64 {
    xgetbv(xcr_no) as u64
}

/// Performs a full or partial save of the enabled processor states to memory at
/// `mem_addr`.
///
/// State is saved based on bits `[62:0]` in `save_mask` and `XCR0`.
/// `mem_addr` must be aligned on a 64-byte boundary. The hardware may optimize
/// the manner in which data is saved. The performance of this instruction will
/// be equal to or better than using the `XSAVE` instruction.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsaveopt)
#[inline]
#[target_feature(enable = "xsave,xsaveopt")]
#[cfg_attr(test, assert_instr(xsaveopt))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsaveopt(mem_addr: *mut u8, save_mask: u64) {
    xsaveopt(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial save of the enabled processor states to memory
/// at `mem_addr`.
///
/// `xsavec` differs from `xsave` in that it uses compaction and that it may
/// use init optimization. State is saved based on bits `[62:0]` in `save_mask`
/// and `XCR0`. `mem_addr` must be aligned on a 64-byte boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsavec)
#[inline]
#[target_feature(enable = "xsave,xsavec")]
#[cfg_attr(test, assert_instr(xsavec))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsavec(mem_addr: *mut u8, save_mask: u64) {
    xsavec(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial save of the enabled processor states to memory at
/// `mem_addr`
///
/// `xsaves` differs from xsave in that it can save state components
/// corresponding to bits set in `IA32_XSS` `MSR` and that it may use the
/// modified optimization. State is saved based on bits `[62:0]` in `save_mask`
/// and `XCR0`. `mem_addr` must be aligned on a 64-byte boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xsaves)
#[inline]
#[target_feature(enable = "xsave,xsaves")]
#[cfg_attr(test, assert_instr(xsaves))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xsaves(mem_addr: *mut u8, save_mask: u64) {
    xsaves(mem_addr, (save_mask >> 32) as u32, save_mask as u32);
}

/// Performs a full or partial restore of the enabled processor states using the
/// state information stored in memory at `mem_addr`.
///
/// `xrstors` differs from `xrstor` in that it can restore state components
/// corresponding to bits set in the `IA32_XSS` `MSR`; `xrstors` cannot restore
/// from an `xsave` area in which the extended region is in the standard form.
/// State is restored based on bits `[62:0]` in `rs_mask`, `XCR0`, and
/// `mem_addr.HEADER.XSTATE_BV`. `mem_addr` must be aligned on a 64-byte
/// boundary.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_xrstors)
#[inline]
#[target_feature(enable = "xsave,xsaves")]
#[cfg_attr(test, assert_instr(xrstors))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _xrstors(mem_addr: *const u8, rs_mask: u64) {
    xrstors(mem_addr, (rs_mask >> 32) as u32, rs_mask as u32);
}

#[cfg(test)]
mod tests {
    use std::{fmt, prelude::v1::*};

    use crate::core_arch::x86::*;
    use stdarch_test::simd_test;

    #[repr(align(64))]
    struct XsaveArea {
        // max size for 256-bit registers is 800 bytes:
        // see https://software.intel.com/en-us/node/682996
        // max size for 512-bit registers is 2560 bytes:
        // FIXME: add source
        data: [u8; 2560],
    }

    impl XsaveArea {
        fn new() -> XsaveArea {
            XsaveArea { data: [0; 2560] }
        }
        fn ptr(&mut self) -> *mut u8 {
            &mut self.data[0] as *mut _ as *mut u8
        }
    }

    impl PartialEq<XsaveArea> for XsaveArea {
        fn eq(&self, other: &XsaveArea) -> bool {
            for i in 0..self.data.len() {
                if self.data[i] != other.data[i] {
                    return false;
                }
            }
            true
        }
    }

    impl fmt::Debug for XsaveArea {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "[")?;
            for i in 0..self.data.len() {
                write!(f, "{}", self.data[i])?;
                if i != self.data.len() - 1 {
                    write!(f, ", ")?;
                }
            }
            write!(f, "]")
        }
    }

    // FIXME: https://github.com/rust-lang/stdarch/issues/209
    /*
    #[simd_test(enable = "xsave")]
    unsafe fn xsave() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsave(a.ptr(), m);
        _xrstor(a.ptr(), m);
        _xsave(b.ptr(), m);
        assert_eq!(a, b);
    }
    */

    #[simd_test(enable = "xsave")]
    unsafe fn xgetbv_xsetbv() {
        let xcr_n: u32 = _XCR_XFEATURE_ENABLED_MASK;

        let xcr: u64 = _xgetbv(xcr_n);
        // FIXME: XSETBV is a privileged instruction we should only test this
        // when running in privileged mode:
        //
        // _xsetbv(xcr_n, xcr);
        let xcr_cpy: u64 = _xgetbv(xcr_n);
        assert_eq!(xcr, xcr_cpy);
    }

    // FIXME: https://github.com/rust-lang/stdarch/issues/209
    /*
    #[simd_test(enable = "xsave,xsaveopt")]
    unsafe fn xsaveopt() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsaveopt(a.ptr(), m);
        _xrstor(a.ptr(), m);
        _xsaveopt(b.ptr(), m);
        assert_eq!(a, b);
    }
    */

    // FIXME: this looks like a bug in Intel's SDE:
    #[cfg(not(stdarch_intel_sde))]
    #[simd_test(enable = "xsave,xsavec")]
    unsafe fn xsavec() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsavec(a.ptr(), m);
        _xrstor(a.ptr(), m);
        _xsavec(b.ptr(), m);
        assert_eq!(a, b);
    }

    // FIXME: https://github.com/rust-lang/stdarch/issues/209
    /*
    #[simd_test(enable = "xsave,xsaves")]
    unsafe fn xsaves() {
        let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
        let mut a = XsaveArea::new();
        let mut b = XsaveArea::new();

        _xsaves(a.ptr(), m);
        _xrstors(a.ptr(), m);
        _xsaves(b.ptr(), m);
        assert_eq!(a, b);
    }
    */
}
<|endoftext|>


<commit_before>use std::{fmt, path::Path};

use image;

use crate::utils::*;

#[derive(Clone)]
pub struct Image {
    width: u32,
    height: u32,
    pub data: Vec<u32>,
    source: String,
}

impl Default for Image {
    fn default() -> Self {
        Image {
            width: 0,
            height: 0,
            data: vec![],
            source: String::default(),
        }
    }
}

impl fmt::Debug for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Image ( source: {})", self.source)
    }
}

impl std::cmp::PartialEq for Image {
    fn eq(&self, other: &Self) -> bool {
        self.source == other.source
    }
}

impl Image {
    pub fn new(width: u32, height: u32) -> Self {
        Image {
            width,
            height,
            data: vec![Color::rgba(0, 0, 0, 0).data; width as usize * height as usize],
            source: String::default(),
        }
    }

    /// Create a new image from a boxed slice of colors
    pub fn from_data(width: u32, height: u32, data: Vec<u32>) -> Result<Self, String> {
        Ok(Image {
            width: width,
            height: height,
            data: data,
            source: String::new(),
        })
    }

    fn from_rgba_image(image: image::RgbaImage) -> Result<Self, String> {
        let data: Vec<u32> = image
            .pixels()
            .map(|p| {
                ((p[3] as u32) << 24) | ((p[0] as u32) << 16) | ((p[1] as u32) << 8) | (p[2] as u32)
            })
            .collect();
        Self::from_data(image.width(), image.height(), data)
    }

    /// Load an image from file path. Supports BMP and PNG
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, String> {
        let img = image::open(path);
        Self::from_rgba_image(img.unwrap().to_rgba())
    }

    /// Gets the width.
    pub fn width(&self) -> f64 {
        self.width as f64
    }

    /// Gets the height.
    pub fn height(&self) -> f64 {
        self.height as f64
    }

    pub fn data(&self) -> &[u32] {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut [u32] {
        &mut self.data
    }
}
<commit_msg>Fix image error propagation.<commit_after>use std::{fmt, path::Path};

use image;

use crate::utils::*;

#[derive(Clone)]
pub struct Image {
    width: u32,
    height: u32,
    pub data: Vec<u32>,
    source: String,
}

impl Default for Image {
    fn default() -> Self {
        Image {
            width: 0,
            height: 0,
            data: vec![],
            source: String::default(),
        }
    }
}

impl fmt::Debug for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Image ( source: {})", self.source)
    }
}

impl std::cmp::PartialEq for Image {
    fn eq(&self, other: &Self) -> bool {
        self.source == other.source
    }
}

impl Image {
    pub fn new(width: u32, height: u32) -> Self {
        Image {
            width,
            height,
            data: vec![Color::rgba(0, 0, 0, 0).data; width as usize * height as usize],
            source: String::default(),
        }
    }

    /// Create a new image from a boxed slice of colors
    pub fn from_data(width: u32, height: u32, data: Vec<u32>) -> Result<Self, String> {
        Ok(Image {
            width: width,
            height: height,
            data: data,
            source: String::new(),
        })
    }

    fn from_rgba_image(image: image::RgbaImage) -> Result<Self, String> {
        let data: Vec<u32> = image
            .pixels()
            .map(|p| {
                ((p[3] as u32) << 24) | ((p[0] as u32) << 16) | ((p[1] as u32) << 8) | (p[2] as u32)
            })
            .collect();
        Self::from_data(image.width(), image.height(), data)
    }

    /// Load an image from file path. Supports BMP and PNG
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, String> {
        let img = image::open(path);
        if let Ok(img) = img {
            return Self::from_rgba_image(img.to_rgba());
        }

        Err("Could not load image.".to_string())
    }

    /// Gets the width.
    pub fn width(&self) -> f64 {
        self.width as f64
    }

    /// Gets the height.
    pub fn height(&self) -> f64 {
        self.height as f64
    }

    pub fn data(&self) -> &[u32] {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut [u32] {
        &mut self.data
    }
}
<|endoftext|>


<commit_before>use specs::*;
use util::*;

use amq_protocol_types::AMQPType;
use handlebars::{self, Context, Handlebars, Helper, HelperResult, Output, Renderable, RenderContext, RenderError, to_json};
use serde_json::{self};

use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::collections::BTreeMap;

/// Type alias to avoid making our users explicitely depend on an extra dependency
pub type CodeGenerator = Handlebars;

/// Our extension for better integration with Handlebars
pub trait HandlebarsAMQPExtension {
    /// Register the various standrad helpers we'll need for AMQP codegen
    fn register_amqp_helpers(self) -> Self;
    /// Generate code using the standard representation of specs and the given template, using the
    /// given name for the variable holding the [protocol definition](../specs.AMQProtocolDefinition.html).
    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str);
}

impl HandlebarsAMQPExtension for CodeGenerator {
    fn register_amqp_helpers(mut self) -> CodeGenerator {
        self.register_escape_fn(handlebars::no_escape);
        self.register_helper("camel",            Box::new(camel_helper));
        self.register_helper("snake",            Box::new(snake_helper));
        self.register_helper("snake_type",       Box::new(snake_type_helper));
        self.register_helper("sanitize_name",    Box::new(sanitize_name_helper));
        self.register_helper("method_has_flags", Box::new(method_has_flags_helper));
        self.register_helper("each_argument",    Box::new(each_argument_helper));
        self.register_helper("each_flag",        Box::new(each_flag_helper));
        self
    }

    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str) {
        let dest_path   = Path::new(out_dir).join(format!("{}.rs", target));
        let mut f       = File::create(&dest_path).unwrap_or_else(|_| panic!("Failed to create {}.rs", target));
        let specs       = AMQProtocolDefinition::load();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        let mut data    = BTreeMap::new();

        codegen.set_strict_mode(true);
        codegen.register_template_string(template_name, template.to_string()).unwrap_or_else(|_| panic!("Failed to register {} template", template_name));
        data.insert(var_name.to_string(), specs);

        writeln!(f, "{}", codegen.render(template_name, &data).unwrap_or_else(|err| panic!("Failed to render {} template: {}", template_name, err))).unwrap_or_else(|_| panic!("Failed to generate {}.rs", target));
    }
}

/// Helper for converting text to camel case
pub fn camel_helper (h: &Helper, _: &Handlebars, _: &Context, _: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"camel\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"camel\""))?;
    out.write(&camel_case(param))?;
    Ok(())
}

/// Helper for converting text to snake case
pub fn snake_helper (h: &Helper, _: &Handlebars, _: &Context, _: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"snake\""))?;
    out.write(&snake_case(param))?;
    Ok(())
}

/// Helper for getting the type name converted to snake case
pub fn snake_type_helper (h: &Helper, _: &Handlebars, _: &Context, _: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value           = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
    let param: AMQPType = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not an AMQPType for helper \"snake_type\""))?;
    out.write(&snake_case(&param.to_string()))?;
    Ok(())
}

/// Helper to sanitize name so the it becomes a valid identifier
pub fn sanitize_name_helper (h: &Helper, _: &Handlebars, _: &Context, _: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"sanitize_name\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"sanitize_name\""))?;
    out.write(&param.replace('-', "_"))?;
    Ok(())
}

/// Helper for checking is a method has some flags argument
pub fn method_has_flags_helper (h: &Helper, _: &Handlebars, _: &Context, _: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value     = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"method_has_flags\""))?;
    let method    = serde_json::from_value::<AMQPMethod>(value.value().clone()).map_err(|_| RenderError::new("Non-AMQPMethod param given to helper \"camel\""))?;
    let has_flags = method.arguments.iter().any(|arg| match *arg {
        AMQPArgument::Value(_) => false,
        AMQPArgument::Flags(_) => true,
    });
    if has_flags {
        out.write("true")?;
    } else {
        /* FIXME: writing "false" evaluates to true */
        out.write("")?;
    }
    Ok(())
}

/// Helper to walk through a Vec of [AMQPArgument](../specs.AMQPArgument.html).
pub fn each_argument_helper (h: &Helper, r: &Handlebars, ctx: &Context, rc: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_argument\""))?;

    if let Some(t) = h.template() {
        rc.promote_local_vars();
        let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
        let arguments : Vec<AMQPArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPArgument> for helper \"each_argument\""))?;
        for (index, argument) in arguments.iter().enumerate() {
            let mut local_rc = rc.derive();
            if let Some(ref p) = local_path_root {
                local_rc.push_local_path_root(p.clone());
            }
            local_rc.set_local_var("@index".to_string(), to_json(&index));
            if let Some(inner_path) = value.path() {
                let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                local_rc.set_path(new_path.clone());
            }
            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                match *argument {
                    AMQPArgument::Value(ref v) => {
                        map.insert(block_param.to_string(), to_json(v));
                        map.insert("argument_is_value".to_string(), to_json(&true));
                    },
                    AMQPArgument::Flags(ref f) => {
                        map.insert(block_param.to_string(), to_json(f));
                        map.insert("argument_is_value".to_string(), to_json(&false));
                    },
                };
                local_rc.push_block_context(&map)?;
            }
            t.render(r, ctx, &mut local_rc, out)?;
            if h.block_param().is_some() {
                local_rc.pop_block_context();
            }
            if local_path_root.is_some() {
                local_rc.pop_local_path_root();
            }
        }
        rc.demote_local_vars();
    }
    Ok(())
}

/// Helper to walk through a Vec of [AMQPFlagArgument](../specs.AMQPFlagArgument.html).
pub fn each_flag_helper (h: &Helper, r: &Handlebars, ctx: &Context, rc: &mut RenderContext, out: &mut Output) -> HelperResult {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_flag\""))?;

    if let Some(t) = h.template() {
        rc.promote_local_vars();
        let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
        let flags : Vec<AMQPFlagArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPFlagArgument> for helper \"each_flag\""))?;
        for (index, flag) in flags.iter().enumerate() {
            let mut local_rc = rc.derive();
            if let Some(ref p) = local_path_root {
                local_rc.push_local_path_root(p.clone());
            }
            local_rc.set_local_var("@index".to_string(), to_json(&index));
            if let Some(inner_path) = value.path() {
                let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                local_rc.set_path(new_path.clone());
            }
            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                map.insert(block_param.to_string(), to_json(flag));
                local_rc.push_block_context(&map)?;
            }
            t.render(r, ctx, &mut local_rc, out)?;
            if h.block_param().is_some() {
                local_rc.pop_block_context();
            }
            if local_path_root.is_some() {
                local_rc.pop_local_path_root();
            }
        }
        rc.demote_local_vars();
    }
    Ok(())
}

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

    use amq_protocol_types::*;

    use std::collections::BTreeMap;

    pub const TEMPLATE: &'static str = r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{protocol.copyright}}
port {{protocol.port}}
{{#each protocol.domains ~}}
{{@key}}: {{this}}
{{/each ~}}
{{#each protocol.constants as |constant| ~}}
{{constant.name}} = {{constant.value}}
{{/each ~}}
{{#each protocol.classes as |class| ~}}
{{class.id}} - {{class.name}}
{{#each class.properties as |property| ~}}
{{property.name}}: {{property.type}}
{{/each ~}}
{{#each class.methods as |method| ~}}
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{#each_argument method.arguments as |argument| ~}}
{{#if argument_is_value ~}}
{{argument.name}}({{argument.domain}}): {{argument.type}}
{{else}}
{{#each_flag argument as |flag| ~}}
{{flag.name}}: {{flag.default_value}}
{{/each_flag ~}}
{{/if ~}}
{{/each_argument ~}}
{{/each ~}}
{{/each ~}}
"#;

    fn specs() -> AMQProtocolDefinition {
        let mut domains = BTreeMap::new();
        domains.insert("domain1".to_string(), AMQPType::LongString);
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     "Copyright 1\nCopyright 2".to_string(),
            domains:       domains,
            constants:     vec![
                AMQPConstant {
                    name:      "constant1".to_string(),
                    amqp_type: AMQPType::ShortUInt,
                    value:     128,
                }
            ],
            soft_errors:   Vec::new(),
            hard_errors:   Vec::new(),
            classes:       vec![
                AMQPClass {
                    id:             42,
                    methods:        vec![
                        AMQPMethod {
                            id:            64,
                            arguments:     vec![
                                AMQPArgument::Value(AMQPValueArgument {
                                    amqp_type:     AMQPType::LongString,
                                    name:          "argument1".to_string(),
                                    default_value: Some(AMQPValue::LongString("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }),
                                AMQPArgument::Flags(vec![
                                    AMQPFlagArgument {
                                        name:         "flag1".to_string(),
                                        default_value: true,
                                    },
                                    AMQPFlagArgument {
                                        name:         "flag2".to_string(),
                                        default_value: false,
                                    },
                                ]),
                            ],
                            name:          "method1".to_string(),
                            synchronous:   true,
                        }
                    ],
                    name:           "class1".to_string(),
                    properties:     vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ],
                }
            ],
        }
    }

    #[test]
    fn main_template() {
        let mut data    = BTreeMap::new();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        data.insert("protocol".to_string(), specs());
        assert!(codegen.register_template_string("main", TEMPLATE.to_string()).is_ok());
        assert_eq!(codegen.render("main", &data).unwrap(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: LongString
constant1 = 128
42 - class1
property1: LongString
64 - method1
synchronous: true
argument1(domain1): LongString

flag1: true
flag2: false

"#);
    }
}
<commit_msg>codegen: use structs to implement helpers<commit_after>use specs::*;
use util::*;

use amq_protocol_types::AMQPType;
use handlebars::{self, Context, Handlebars, Helper, HelperDef, HelperResult, JsonValue, Output, Renderable, RenderContext, RenderError, ScopedJson, to_json};
use serde_json::{self};

use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::collections::BTreeMap;

/// Type alias to avoid making our users explicitely depend on an extra dependency
pub type CodeGenerator = Handlebars;

/// Our extension for better integration with Handlebars
pub trait HandlebarsAMQPExtension {
    /// Register the various standrad helpers we'll need for AMQP codegen
    fn register_amqp_helpers(self) -> Self;
    /// Generate code using the standard representation of specs and the given template, using the
    /// given name for the variable holding the [protocol definition](../specs.AMQProtocolDefinition.html).
    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str);
}

impl HandlebarsAMQPExtension for CodeGenerator {
    fn register_amqp_helpers(mut self) -> CodeGenerator {
        self.register_escape_fn(handlebars::no_escape);
        self.register_helper("camel",            Box::new(CamelHelper));
        self.register_helper("snake",            Box::new(SnakeHelper));
        self.register_helper("snake_type",       Box::new(SnakeTypeHelper));
        self.register_helper("sanitize_name",    Box::new(SanitizeNameHelper));
        self.register_helper("method_has_flags", Box::new(MethodHasFlagsHelper));
        self.register_helper("each_argument",    Box::new(EachArgumentHelper));
        self.register_helper("each_flag",        Box::new(EachFlagHelper));
        self
    }

    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str) {
        let dest_path   = Path::new(out_dir).join(format!("{}.rs", target));
        let mut f       = File::create(&dest_path).unwrap_or_else(|_| panic!("Failed to create {}.rs", target));
        let specs       = AMQProtocolDefinition::load();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        let mut data    = BTreeMap::new();

        codegen.set_strict_mode(true);
        codegen.register_template_string(template_name, template.to_string()).unwrap_or_else(|_| panic!("Failed to register {} template", template_name));
        data.insert(var_name.to_string(), specs);

        writeln!(f, "{}", codegen.render(template_name, &data).unwrap_or_else(|err| panic!("Failed to render {} template: {}", template_name, err))).unwrap_or_else(|_| panic!("Failed to generate {}.rs", target));
    }
}

/// Helper for converting text to camel case
pub struct CamelHelper;
impl HelperDef for CamelHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"camel\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"camel\""))?;
        out.write(&camel_case(param))?;
        Ok(())
    }
}

/// Helper for converting text to snake case
pub struct SnakeHelper;
impl HelperDef for SnakeHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"snake\""))?;
        out.write(&snake_case(param))?;
        Ok(())
    }
}

/// Helper for getting the type name converted to snake case
pub struct SnakeTypeHelper;
impl HelperDef for SnakeTypeHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value           = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
        let param: AMQPType = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not an AMQPType for helper \"snake_type\""))?;
        out.write(&snake_case(&param.to_string()))?;
        Ok(())
    }
}

/// Helper to sanitize name so the it becomes a valid identifier
pub struct SanitizeNameHelper;
impl HelperDef for SanitizeNameHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"sanitize_name\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"sanitize_name\""))?;
        out.write(&param.replace('-', "_"))?;
        Ok(())
    }
}

/// Helper for checking is a method has some flags argument
pub struct MethodHasFlagsHelper;
impl HelperDef for MethodHasFlagsHelper {
    fn call_inner<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        let value     = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"method_has_flags\""))?;
        let method    = serde_json::from_value::<AMQPMethod>(value.value().clone()).map_err(|_| RenderError::new("Non-AMQPMethod param given to helper \"camel\""))?;
        let has_flags = method.arguments.iter().any(|arg| match *arg {
            AMQPArgument::Value(_) => false,
            AMQPArgument::Flags(_) => true,
        });
        Ok(Some(ScopedJson::Derived(JsonValue::from(has_flags))))
    }
}

/// Helper to walk through a Vec of [AMQPArgument](../specs.AMQPArgument.html).
pub struct EachArgumentHelper;
impl HelperDef for EachArgumentHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars, ctx: &'rc Context, rc: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_argument\""))?;

        if let Some(t) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
            let arguments : Vec<AMQPArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPArgument> for helper \"each_argument\""))?;
            for (index, argument) in arguments.iter().enumerate() {
                let mut local_rc = rc.derive();
                if let Some(ref p) = local_path_root {
                    local_rc.push_local_path_root(p.clone());
                }
                local_rc.set_local_var("@index".to_string(), to_json(&index));
                if let Some(inner_path) = value.path() {
                    let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                    local_rc.set_path(new_path.clone());
                }
                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    match *argument {
                        AMQPArgument::Value(ref v) => {
                            map.insert(block_param.to_string(), to_json(v));
                            map.insert("argument_is_value".to_string(), to_json(&true));
                        },
                        AMQPArgument::Flags(ref f) => {
                            map.insert(block_param.to_string(), to_json(f));
                            map.insert("argument_is_value".to_string(), to_json(&false));
                        },
                    };
                    local_rc.push_block_context(&map)?;
                }
                t.render(r, ctx, &mut local_rc, out)?;
                if h.block_param().is_some() {
                    local_rc.pop_block_context();
                }
                if local_path_root.is_some() {
                    local_rc.pop_local_path_root();
                }
            }
            rc.demote_local_vars();
        }
        Ok(())
    }
}

/// Helper to walk through a Vec of [AMQPFlagArgument](../specs.AMQPFlagArgument.html).
pub struct EachFlagHelper;
impl HelperDef for EachFlagHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars, ctx: &'rc Context, rc: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_flag\""))?;

        if let Some(t) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
            let flags : Vec<AMQPFlagArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPFlagArgument> for helper \"each_flag\""))?;
            for (index, flag) in flags.iter().enumerate() {
                let mut local_rc = rc.derive();
                if let Some(ref p) = local_path_root {
                    local_rc.push_local_path_root(p.clone());
                }
                local_rc.set_local_var("@index".to_string(), to_json(&index));
                if let Some(inner_path) = value.path() {
                    let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                    local_rc.set_path(new_path.clone());
                }
                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    map.insert(block_param.to_string(), to_json(flag));
                    local_rc.push_block_context(&map)?;
                }
                t.render(r, ctx, &mut local_rc, out)?;
                if h.block_param().is_some() {
                    local_rc.pop_block_context();
                }
                if local_path_root.is_some() {
                    local_rc.pop_local_path_root();
                }
            }
            rc.demote_local_vars();
        }
        Ok(())
    }
}

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

    use amq_protocol_types::*;

    use std::collections::BTreeMap;

    pub const TEMPLATE: &'static str = r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{protocol.copyright}}
port {{protocol.port}}
{{#each protocol.domains ~}}
{{@key}}: {{this}}
{{/each ~}}
{{#each protocol.constants as |constant| ~}}
{{constant.name}} = {{constant.value}}
{{/each ~}}
{{#each protocol.classes as |class| ~}}
{{class.id}} - {{class.name}}
{{#each class.properties as |property| ~}}
{{property.name}}: {{property.type}}
{{/each ~}}
{{#each class.methods as |method| ~}}
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{#each_argument method.arguments as |argument| ~}}
{{#if argument_is_value ~}}
{{argument.name}}({{argument.domain}}): {{argument.type}}
{{else}}
{{#each_flag argument as |flag| ~}}
{{flag.name}}: {{flag.default_value}}
{{/each_flag ~}}
{{/if ~}}
{{/each_argument ~}}
{{/each ~}}
{{/each ~}}
"#;

    fn specs() -> AMQProtocolDefinition {
        let mut domains = BTreeMap::new();
        domains.insert("domain1".to_string(), AMQPType::LongString);
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     "Copyright 1\nCopyright 2".to_string(),
            domains:       domains,
            constants:     vec![
                AMQPConstant {
                    name:      "constant1".to_string(),
                    amqp_type: AMQPType::ShortUInt,
                    value:     128,
                }
            ],
            soft_errors:   Vec::new(),
            hard_errors:   Vec::new(),
            classes:       vec![
                AMQPClass {
                    id:             42,
                    methods:        vec![
                        AMQPMethod {
                            id:            64,
                            arguments:     vec![
                                AMQPArgument::Value(AMQPValueArgument {
                                    amqp_type:     AMQPType::LongString,
                                    name:          "argument1".to_string(),
                                    default_value: Some(AMQPValue::LongString("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }),
                                AMQPArgument::Flags(vec![
                                    AMQPFlagArgument {
                                        name:         "flag1".to_string(),
                                        default_value: true,
                                    },
                                    AMQPFlagArgument {
                                        name:         "flag2".to_string(),
                                        default_value: false,
                                    },
                                ]),
                            ],
                            name:          "method1".to_string(),
                            synchronous:   true,
                        }
                    ],
                    name:           "class1".to_string(),
                    properties:     vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ],
                }
            ],
        }
    }

    #[test]
    fn main_template() {
        let mut data    = BTreeMap::new();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        data.insert("protocol".to_string(), specs());
        assert!(codegen.register_template_string("main", TEMPLATE.to_string()).is_ok());
        assert_eq!(codegen.render("main", &data).unwrap(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: LongString
constant1 = 128
42 - class1
property1: LongString
64 - method1
synchronous: true
argument1(domain1): LongString

flag1: true
flag2: false

"#);
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use hyper::header::Connection;
use std::collections::BTreeMap;
use rustc_serialize::json;

use get_env;
use http;

static BASE_URL:    &'static str = "https://www.googleapis.com/youtube/v3";
static MAX_RESULTS: i32          = 50;
lazy_static! {
    static ref API_KEY: String = {
        get_env::var("YOUTUBE_API_KEY").unwrap_or("".to_string())
    };
}

pub static EMBED: &'static str = r"www.youtube.com/embed/([a-zA-Z0-9_-].+)";
pub static LIST:  &'static str = r"www.youtube.com/embed/videoseries\?list=([a-zA-Z0-9_-]+)";
pub static WATCH: &'static str = r"www.youtube.com/watch\?v=([a-zA-Z0-9_-]+)";

pub trait HasThumbnail {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail>;
    fn get_thumbnail_url(&self) -> Option<String> {
        let thumbs = self.get_thumbnails();
        thumbs.get(         "default").map(|t| t.url.to_string())
            .or(thumbs.get("medium"  ).map(|t| t.url.to_string()))
            .or(thumbs.get("high"    ).map(|t| t.url.to_string()))
            .or(thumbs.get("standard").map(|t| t.url.to_string()))
            .or(thumbs.get("maxres"  ).map(|t| t.url.to_string()))
    }
    fn get_artwork_url(&self) -> Option<String> {
        let thumbs = self.get_thumbnails();
        thumbs.get(          "maxres").map(|t| t.url.to_string())
            .or(thumbs.get("standard").map(|t| t.url.to_string()))
            .or(thumbs.get("high"    ).map(|t| t.url.to_string()))
            .or(thumbs.get("medium"  ).map(|t| t.url.to_string()))
            .or(thumbs.get("default" ).map(|t| t.url.to_string()))
    }
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Thumbnail {
    pub url:    String,
    pub width:  i32,
    pub height: i32
}

impl PartialEq for Thumbnail {
    fn eq(&self, t: &Thumbnail) -> bool {
        return self.url == t.url;
    }
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Playlist>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct Playlist {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    Option<BTreeMap<String, Thumbnail>>,
    pub tags:          Option<Vec<String>>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<PlaylistItem>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistItemSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    Option<BTreeMap<String, Thumbnail>>,
    pub position:      i32,
    pub playlistId:    String,
    pub resourceId:    BTreeMap<String, String>
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct VideoResponse {
    pub kind:          String,
    pub etag:          String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Video>,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Video {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: VideoSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct VideoSnippet {
    pub title:                String,
    pub description:          String,
    pub publishedAt:          String,
    pub channelId:            String,
    pub channelTitle:         String,
    pub thumbnails:           Option<BTreeMap<String, Thumbnail>>,
    pub tags:                 Option<Vec<String>>,
    pub categoryId:           String,
    pub liveBroadcastContent: String,
}

impl HasThumbnail for PlaylistSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

impl HasThumbnail for PlaylistItemSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

impl HasThumbnail for VideoSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

pub fn fetch_playlist(id: &str) -> json::DecodeResult<PlaylistResponse> {
    let params = format!("key={}&part=snippet&id={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url     = format!("{}/{}?{}", BASE_URL, "playlists", params);
    let mut res = http::client().get(&url)
                                .header(Connection::close())
                                .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    json::decode::<PlaylistResponse>(&body)
}

pub fn fetch_playlist_items(id: &str) -> json::DecodeResult<PlaylistItemResponse> {
    let params = format!("key={}&part=snippet&playlistId={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url    = format!("{}/{}?{}", BASE_URL, "playlistItems", params);
    let mut res = http::client().get(&url)
                                .header(Connection::close())
                                .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    json::decode::<PlaylistItemResponse>(&body)
}

pub fn fetch_video(id: &str) -> json::DecodeResult<Video> {
    let params = format!("key={}&part=snippet&id={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "videos", params);
    let mut res = http::client().get(&url)
                                .header(Connection::close())
                                .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    let res = try!(json::decode::<VideoResponse>(&body));
    if res.items.len() > 0 {
        return Ok(res.items[0].clone());
    }
    Err(json::DecoderError::ApplicationError("track not found".to_string()))
}
<commit_msg>Refactor with serde_json instead of rustc_serialize in youtube.rs<commit_after>use std::io::Read;
use hyper::header::Connection;
use std::collections::BTreeMap;
use serde_json;
use serde::de::Error;

use get_env;
use http;

static BASE_URL:    &'static str = "https://www.googleapis.com/youtube/v3";
static MAX_RESULTS: i32          = 50;
lazy_static! {
    static ref API_KEY: String = {
        get_env::var("YOUTUBE_API_KEY").unwrap_or("".to_string())
    };
}

pub static EMBED: &'static str = r"www.youtube.com/embed/([a-zA-Z0-9_-].+)";
pub static LIST:  &'static str = r"www.youtube.com/embed/videoseries\?list=([a-zA-Z0-9_-]+)";
pub static WATCH: &'static str = r"www.youtube.com/watch\?v=([a-zA-Z0-9_-]+)";

pub trait HasThumbnail {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail>;
    fn get_thumbnail_url(&self) -> Option<String> {
        let thumbs = self.get_thumbnails();
        thumbs.get(         "default").map(|t| t.url.to_string())
            .or(thumbs.get("medium"  ).map(|t| t.url.to_string()))
            .or(thumbs.get("high"    ).map(|t| t.url.to_string()))
            .or(thumbs.get("standard").map(|t| t.url.to_string()))
            .or(thumbs.get("maxres"  ).map(|t| t.url.to_string()))
    }
    fn get_artwork_url(&self) -> Option<String> {
        let thumbs = self.get_thumbnails();
        thumbs.get(          "maxres").map(|t| t.url.to_string())
            .or(thumbs.get("standard").map(|t| t.url.to_string()))
            .or(thumbs.get("high"    ).map(|t| t.url.to_string()))
            .or(thumbs.get("medium"  ).map(|t| t.url.to_string()))
            .or(thumbs.get("default" ).map(|t| t.url.to_string()))
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Thumbnail {
    pub url:    String,
    pub width:  i32,
    pub height: i32
}

impl PartialEq for Thumbnail {
    fn eq(&self, t: &Thumbnail) -> bool {
        return self.url == t.url;
    }
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct PlaylistResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Playlist>,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct Playlist {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistSnippet,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct PlaylistSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    Option<BTreeMap<String, Thumbnail>>,
    pub tags:          Option<Vec<String>>,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct PlaylistItemResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<PlaylistItem>,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct PlaylistItem {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistItemSnippet,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct PlaylistItemSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    Option<BTreeMap<String, Thumbnail>>,
    pub position:      i32,
    pub playlistId:    String,
    pub resourceId:    BTreeMap<String, String>
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct VideoResponse {
    pub kind:          String,
    pub etag:          String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Video>,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Video {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: VideoSnippet,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VideoSnippet {
    pub title:                String,
    pub description:          String,
    pub publishedAt:          String,
    pub channelId:            String,
    pub channelTitle:         String,
    pub thumbnails:           Option<BTreeMap<String, Thumbnail>>,
    pub tags:                 Option<Vec<String>>,
    pub categoryId:           String,
    pub liveBroadcastContent: String,
}

impl HasThumbnail for PlaylistSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

impl HasThumbnail for PlaylistItemSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

impl HasThumbnail for VideoSnippet {
    fn get_thumbnails(&self) -> BTreeMap<String, Thumbnail> {
        self.thumbnails.clone().unwrap_or(BTreeMap::new())
    }
}

pub fn fetch_playlist(id: &str) -> serde_json::Result<PlaylistResponse> {
    let params = format!("key={}&part=snippet&id={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url     = format!("{}/{}?{}", BASE_URL, "playlists", params);
    let mut res = http::client().get(&url)
                                .header(Connection::close())
                                .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    serde_json::from_str(&body)
}

pub fn fetch_playlist_items(id: &str) -> serde_json::Result<PlaylistItemResponse> {
    let params = format!("key={}&part=snippet&playlistId={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url    = format!("{}/{}?{}", BASE_URL, "playlistItems", params);
    let mut res = http::client().get(&url)
                                .header(Connection::close())
                                .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    serde_json::from_str(&body)
}

pub fn fetch_video(id: &str) -> serde_json::Result<Video> {
    let params = format!("key={}&part=snippet&id={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "videos", params);
    let mut res = http::client().get(&url)
                                .header(Connection::close())
                                .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    let vr: VideoResponse = try!(serde_json::from_str(&body));
    if vr.items.len() > 0 {
        return Ok(vr.items[0].clone());
    }
    Err(serde_json::error::Error::custom("track not found".to_string()))
}
<|endoftext|>