<commit_before>//! FIXME: write short doc here

use anyhow::{bail, Context, Result};
use std::{
    env,
    path::{Path, PathBuf},
    process::Command,
};

use ra_arena::{impl_arena_id, Arena, RawId};

#[derive(Default, Debug, Clone)]
pub struct Sysroot {
    crates: Arena<SysrootCrate, SysrootCrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysrootCrate(RawId);
impl_arena_id!(SysrootCrate);

#[derive(Debug, Clone)]
struct SysrootCrateData {
    name: String,
    root: PathBuf,
    deps: Vec<SysrootCrate>,
}

impl Sysroot {
    pub fn core(&self) -> Option<SysrootCrate> {
        self.by_name("core")
    }

    pub fn alloc(&self) -> Option<SysrootCrate> {
        self.by_name("alloc")
    }

    pub fn std(&self) -> Option<SysrootCrate> {
        self.by_name("std")
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
        let src = get_or_install_rust_src(cargo_toml)?;
        let mut sysroot = Sysroot { crates: Arena::default() };
        for name in SYSROOT_CRATES.trim().lines() {
            let root = src.join(format!("lib{}", name)).join("lib.rs");
            if root.exists() {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }
        if let Some(std) = sysroot.std() {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }
        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.core() {
                sysroot.crates[alloc].deps.push(core);
            }
        }
        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id)
    }
}

fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
    fn try_install_rust_src(cargo_toml: &Path) -> Result<()> {
        let rustup_output = Command::new("rustup")
            .current_dir(cargo_toml.parent().unwrap())
            .args(&["component", "add", "rust-src"])
            .output()
            .context("rustup component add rust-src failed")?;
        if !rustup_output.status.success() {
            match rustup_output.status.code() {
                Some(code) => bail!(
                    "failed to install rust-src: rustup component add rust-src exited with code {}",
                    code
                ),
                None => bail!(
                    "failed to install rust-src: rustup component add rust-src terminated by signal"
                ),
            };
        }
        Ok(())
    }

    if let Ok(path) = env::var("RUST_SRC_PATH") {
        return Ok(path.into());
    }
    let rustc_output = Command::new("rustc")
        .current_dir(cargo_toml.parent().unwrap())
        .args(&["--print", "sysroot"])
        .output()
        .context("rustc --print sysroot failed")?;
    if !rustc_output.status.success() {
        match rustc_output.status.code() {
            Some(code) => {
                bail!("failed to locate sysroot: rustc --print sysroot exited with code {}", code)
            }
            None => bail!("failed to locate sysroot: rustc --print sysroot terminated by signal"),
        };
    }
    let stdout = String::from_utf8(rustc_output.stdout)?;
    let sysroot_path = Path::new(stdout.trim());
    let src_path = sysroot_path.join("lib/rustlib/src/rust/src");

    if !src_path.exists() {
        try_install_rust_src(cargo_toml)?
    }
    if !src_path.exists() {
        bail!(
            "can't load standard library from sysroot\n\
            {}\n\
            (discovered via `rustc --print sysroot`)\n\
            try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
            src_path.display(),
        )
    }
    Ok(src_path)
}

impl SysrootCrate {
    pub fn name(self, sysroot: &Sysroot) -> &str {
        &sysroot.crates[self].name
    }
    pub fn root(self, sysroot: &Sysroot) -> &Path {
        sysroot.crates[self].root.as_path()
    }
    pub fn root_dir(self, sysroot: &Sysroot) -> &Path {
        self.root(sysroot).parent().unwrap()
    }
    pub fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
        sysroot.crates[self].deps.iter().copied()
    }
}

const SYSROOT_CRATES: &str = "
std
core
alloc
collections
libc
panic_unwind
proc_macro
rustc_unicode
std_unicode
test
alloc_jemalloc
alloc_system
compiler_builtins
getopts
panic_unwind
panic_abort
rand
term
unwind
build_helper
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
syntax";

const STD_DEPS: &str = "
alloc
alloc_jemalloc
alloc_system
core
panic_abort
rand
compiler_builtins
unwind
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
build_helper";
<commit_msg>Simplify the command execution<commit_after>//! FIXME: write short doc here

use anyhow::{bail, Context, Result};
use std::{
    env,
    path::{Path, PathBuf},
    process::{Command, Output},
};

use ra_arena::{impl_arena_id, Arena, RawId};

#[derive(Default, Debug, Clone)]
pub struct Sysroot {
    crates: Arena<SysrootCrate, SysrootCrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysrootCrate(RawId);
impl_arena_id!(SysrootCrate);

#[derive(Debug, Clone)]
struct SysrootCrateData {
    name: String,
    root: PathBuf,
    deps: Vec<SysrootCrate>,
}

impl Sysroot {
    pub fn core(&self) -> Option<SysrootCrate> {
        self.by_name("core")
    }

    pub fn alloc(&self) -> Option<SysrootCrate> {
        self.by_name("alloc")
    }

    pub fn std(&self) -> Option<SysrootCrate> {
        self.by_name("std")
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
        let src = get_or_install_rust_src(cargo_toml)?;
        let mut sysroot = Sysroot { crates: Arena::default() };
        for name in SYSROOT_CRATES.trim().lines() {
            let root = src.join(format!("lib{}", name)).join("lib.rs");
            if root.exists() {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }
        if let Some(std) = sysroot.std() {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }
        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.core() {
                sysroot.crates[alloc].deps.push(core);
            }
        }
        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id)
    }
}

fn create_command_text(program: &str, args: &[&str]) -> String {
    format!("{} {}", program, args.join(" "))
}

fn run_command_in_cargo_dir(cargo_toml: &Path, program: &str, args: &[&str]) -> Result<Output> {
    let output = Command::new(program)
        .current_dir(cargo_toml.parent().unwrap())
        .args(args)
        .output()
        .context(format!("{} failed", create_command_text(program, args)))?;
    if !output.status.success() {
        match output.status.code() {
            Some(code) => bail!(
                "failed to run the command: '{}' exited with code {}",
                create_command_text(program, args),
                code
            ),
            None => bail!(
                "failed to run the command: '{}' terminated by signal",
                create_command_text(program, args)
            ),
        };
    }
    Ok(output)
}

fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
    if let Ok(path) = env::var("RUST_SRC_PATH") {
        return Ok(path.into());
    }
    let rustc_output = run_command_in_cargo_dir(cargo_toml, "rustc", &["--print", "sysroot"])?;
    let stdout = String::from_utf8(rustc_output.stdout)?;
    let sysroot_path = Path::new(stdout.trim());
    let src_path = sysroot_path.join("lib/rustlib/src/rust/src");

    if !src_path.exists() {
        run_command_in_cargo_dir(cargo_toml, "rustup", &["component", "add", "rust-src"])?;
    }
    if !src_path.exists() {
        bail!(
            "can't load standard library from sysroot\n\
            {}\n\
            (discovered via `rustc --print sysroot`)\n\
            try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
            src_path.display(),
        )
    }
    Ok(src_path)
}

impl SysrootCrate {
    pub fn name(self, sysroot: &Sysroot) -> &str {
        &sysroot.crates[self].name
    }
    pub fn root(self, sysroot: &Sysroot) -> &Path {
        sysroot.crates[self].root.as_path()
    }
    pub fn root_dir(self, sysroot: &Sysroot) -> &Path {
        self.root(sysroot).parent().unwrap()
    }
    pub fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
        sysroot.crates[self].deps.iter().copied()
    }
}

const SYSROOT_CRATES: &str = "
std
core
alloc
collections
libc
panic_unwind
proc_macro
rustc_unicode
std_unicode
test
alloc_jemalloc
alloc_system
compiler_builtins
getopts
panic_unwind
panic_abort
rand
term
unwind
build_helper
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
syntax";

const STD_DEPS: &str = "
alloc
alloc_jemalloc
alloc_system
core
panic_abort
rand
compiler_builtins
unwind
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
build_helper";
<|endoftext|>


<commit_before>pub mod tag;
pub mod autotag;
pub mod update_tags;

pub use self::tag::*;
pub use self::autotag::*;
pub use self::update_tags::*;

use LalafellBot;
use database::AutotagUser;

use xivdb::error::*;

use discord::model::{UserId, LiveServer, Role, RoleId};

use std::sync::Arc;
use std::collections::HashMap;

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let mut params = HashMap::new();
    params.insert(String::from("one"), String::from("characters"));
    params.insert(String::from("strict"), String::from("on"));
    params.insert(String::from("server|et"), server.to_string());

    let res = bot.xivdb.search(character_name.to_string(), params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => return Err("character ID was not a u64".into())
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => return Err("character name was not a string".into())
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id, ignore_verified)
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let database = bot.database.lock().unwrap();
    let user = database.autotags.users.iter()
      .find(|u| u.user_id == who.0);
    let is_verified = user
      .map(|u| u.verification.verified)
      .unwrap_or_default();

    let member = bot.discord.get_member(on.id, who).chain_err(|| "could not get member for tagging")?;

    if let Some(u) = user {
      if is_verified && !ignore_verified && char_id != u.character_id {
        return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), u.character, u.server)));
      }
    }

    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    database.autotags.update_or_remove(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    let roles = &on.roles;
    let mut add_roles = Vec::new();
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.race.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.gender.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.server.to_lowercase()) {
      add_roles.push(r);
    }
    if is_verified {
      if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == "verified") {
        add_roles.push(r);
      }
    }

    let all_group_roles: Vec<&String> = bot.config.roles.groups.iter().flat_map(|x| x).collect();
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&&x.name)).collect();
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    role_set.sort();
    role_set.dedup();

    if !role_set.iter().all(|r| member.roles.contains(r)) {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    match member.nick {
      Some(ref nick) if *nick != character.name => {
        bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
      },
      _ => {}
    }
    Ok(None)
  }
}
<commit_msg>fix: mutably bind<commit_after>pub mod tag;
pub mod autotag;
pub mod update_tags;

pub use self::tag::*;
pub use self::autotag::*;
pub use self::update_tags::*;

use LalafellBot;
use database::AutotagUser;

use xivdb::error::*;

use discord::model::{UserId, LiveServer, Role, RoleId};

use std::sync::Arc;
use std::collections::HashMap;

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let mut params = HashMap::new();
    params.insert(String::from("one"), String::from("characters"));
    params.insert(String::from("strict"), String::from("on"));
    params.insert(String::from("server|et"), server.to_string());

    let res = bot.xivdb.search(character_name.to_string(), params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => return Err("character ID was not a u64".into())
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => return Err("character name was not a string".into())
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id, ignore_verified)
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let mut database = bot.database.lock().unwrap();
    let user = database.autotags.users.iter()
      .find(|u| u.user_id == who.0);
    let is_verified = user
      .map(|u| u.verification.verified)
      .unwrap_or_default();

    let member = bot.discord.get_member(on.id, who).chain_err(|| "could not get member for tagging")?;

    if let Some(u) = user {
      if is_verified && !ignore_verified && char_id != u.character_id {
        return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), u.character, u.server)));
      }
    }

    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    database.autotags.update_or_remove(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    let roles = &on.roles;
    let mut add_roles = Vec::new();
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.race.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.gender.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.server.to_lowercase()) {
      add_roles.push(r);
    }
    if is_verified {
      if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == "verified") {
        add_roles.push(r);
      }
    }

    let all_group_roles: Vec<&String> = bot.config.roles.groups.iter().flat_map(|x| x).collect();
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&&x.name)).collect();
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    role_set.sort();
    role_set.dedup();

    if !role_set.iter().all(|r| member.roles.contains(r)) {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    match member.nick {
      Some(ref nick) if *nick != character.name => {
        bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
      },
      _ => {}
    }
    Ok(None)
  }
}
<|endoftext|>


<commit_before>use crate::cdsl::isa::TargetIsa;
use crate::shared::Definitions as SharedDefinitions;
use std::fmt;

mod arm32;
mod arm64;
mod riscv;
mod x86;

/// Represents known ISA target.
#[derive(Copy, Clone)]
pub enum Isa {
    Riscv,
    X86,
    Arm32,
    Arm64,
}

impl Isa {
    /// Creates isa target using name.
    pub fn from_name(name: &str) -> Option<Self> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.to_string() == name)
            .next()
    }

    /// Creates isa target from arch.
    pub fn from_arch(arch: &str) -> Option<Self> {
        match arch {
            "riscv" => Some(Isa::Riscv),
            "aarch64" => Some(Isa::Arm64),
            x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86),
            x if x.starts_with("arm") || arch.starts_with("thumb") => Some(Isa::Arm32),
            _ => None,
        }
    }

    /// Returns all supported isa targets.
    pub fn all() -> [Isa; 4] {
        [Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
    }
}

impl fmt::Display for Isa {
    // These names should be kept in sync with the crate features.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Isa::Riscv => write!(f, "riscv"),
            Isa::X86 => write!(f, "x86"),
            Isa::Arm32 => write!(f, "arm32"),
            Isa::Arm64 => write!(f, "arm64"),
        }
    }
}

pub(crate) fn define(isas: &Vec<Isa>, shared_defs: &mut SharedDefinitions) -> Vec<TargetIsa> {
    isas.iter()
        .map(|isa| match isa {
            Isa::Riscv => riscv::define(shared_defs),
            Isa::X86 => x86::define(shared_defs),
            Isa::Arm32 => arm32::define(shared_defs),
            Isa::Arm64 => arm64::define(shared_defs),
        })
        .collect()
}
<commit_msg>Change signature of all() function<commit_after>use crate::cdsl::isa::TargetIsa;
use crate::shared::Definitions as SharedDefinitions;
use std::fmt;

mod arm32;
mod arm64;
mod riscv;
mod x86;

/// Represents known ISA target.
#[derive(Copy, Clone)]
pub enum Isa {
    Riscv,
    X86,
    Arm32,
    Arm64,
}

impl Isa {
    /// Creates isa target using name.
    pub fn from_name(name: &str) -> Option<Self> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.to_string() == name)
            .next()
    }

    /// Creates isa target from arch.
    pub fn from_arch(arch: &str) -> Option<Self> {
        match arch {
            "riscv" => Some(Isa::Riscv),
            "aarch64" => Some(Isa::Arm64),
            x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86),
            x if x.starts_with("arm") || arch.starts_with("thumb") => Some(Isa::Arm32),
            _ => None,
        }
    }

    /// Returns all supported isa targets.
    pub fn all() -> &'static [Isa] {
        &[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
    }
}

impl fmt::Display for Isa {
    // These names should be kept in sync with the crate features.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Isa::Riscv => write!(f, "riscv"),
            Isa::X86 => write!(f, "x86"),
            Isa::Arm32 => write!(f, "arm32"),
            Isa::Arm64 => write!(f, "arm64"),
        }
    }
}

pub(crate) fn define(isas: &Vec<Isa>, shared_defs: &mut SharedDefinitions) -> Vec<TargetIsa> {
    isas.iter()
        .map(|isa| match isa {
            Isa::Riscv => riscv::define(shared_defs),
            Isa::X86 => x86::define(shared_defs),
            Isa::Arm32 => arm32::define(shared_defs),
            Isa::Arm64 => arm64::define(shared_defs),
        })
        .collect()
}
<|endoftext|>


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

//extern crate num;
#![feature(dir_builder, fs_walk)]
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs;
use std::fs::DirBuilder;
use std::path::{Path, PathBuf};
use std::process;
use std::process::{Command, Stdio};

fn main() {
    let (args, passthrough) = parse_arguments();

    // Find all the native shared libraries that exist in the target directory.
    let native_shared_libs = find_native_libs(&args);

    // Get the SDK path from the ANDROID_HOME env.
    let sdk_path = env::var("ANDROID_HOME").ok().expect("Please set the ANDROID_HOME environment variable");
    let sdk_path = Path::new(&sdk_path);

    // Get the NDK path from NDK_HOME env.
    let ndk_path = env::var("NDK_HOME").ok().expect("Please set the NDK_HOME environment variable");
    let ndk_path = Path::new(&ndk_path);

    // Get the standalone NDK path from NDK_STANDALONE env.
    //  let standalone_path = env::var("NDK_STANDALONE").ok().unwrap_or("/opt/ndk_standalone".to_string());
    //  let standalone_path = Path::new(&standalone_path);

    let debug = passthrough.contains(&"-d".to_string());

    // Set the build directory that will contain all the necessary files to create the apk
    let directory = args.root_path.join("support").join("android").join("apk");
    let resdir = args.root_path.join("resources/");

    // executing ndk-build
    env::set_var("V", "1");
    if debug {
        env::set_var("NDK_DEBUG", "1");
        env::set_var("APP_OPTIM", "0");
    } else{
        // Overrides android:debuggable propery in the .xml file.
        env::set_var("APP_OPTIM", "1");
    }

    // Copy libservo.so into the jni folder for inclusion in the build
    // TODO: pass/detect target architecture
    {
        let source = &args.target_path.join("libservo.so");
        let target_dir = &directory.join("jni").join("armeabi");
        let _ = DirBuilder::new().recursive(true).create(target_dir);
        let target = target_dir.join("libmain.so");
        println!("Copying the file {:?} to {:?}", source, target);
        fs::copy(source, target).unwrap();
    }

    let ndkcmd = Command::new(ndk_path.join("ndk-build"))
                              .arg("-B")
                              .stdout(Stdio::inherit())
                              .stderr(Stdio::inherit())
                              .current_dir(directory.clone())
                              .status();
    if ndkcmd.is_err() || ndkcmd.unwrap().code().unwrap() != 0 {
        println!("Error while executing program `ndk-build`, or missing program.");
        process::exit(1);
    }

    // Copy the additional native libs into the libs directory.
    for (name, path) in native_shared_libs.iter() {
        let target = &directory.join("libs").join("armeabi").join(name);
        println!("Copying the file {:?} to {:?}", name, target);
        fs::copy(path, target).unwrap();
    }

    // Copy over the resources
    let cpcmd= Command::new("cp")
                            .arg("-R")
                            .arg(&resdir)
                            .arg(&directory.join("assets"))
                            .stdout(Stdio::inherit())
                            .stderr(Stdio::inherit())
                            .current_dir(directory.clone())
                            .status();
    if cpcmd.is_err() || cpcmd.unwrap().code().unwrap() != 0 {
        println!("Error while copying files from the resources dir to the assets dir");
        process::exit(1);
    }

    // Update the project
    let androidcmd = Command::new(sdk_path.join("tools").join("android"))
                                  .arg("update")
                                  .arg("project")
                                  .arg("--name")
                                  .arg("Servo")
                                  .arg("--target")
                                  .arg("android-18")
                                  .arg("--path")
                                  .arg(".")
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
    if androidcmd.is_err() || androidcmd.unwrap().code().unwrap() != 0 {
        println!("Error while updating the project with the android command");
        process::exit(1);
    }

    // Build the APK
    let mut antcmd = Command::new("ant");
    if debug {
        antcmd.arg("debug");
    } else {
        antcmd.arg("release");
    }
    let antresult = antcmd
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .current_dir(directory.clone())
        .status();
    if antresult.is_err() || antresult.unwrap().code().unwrap() != 0 {
        println!("Error while executing program `ant`, or missing program.");
        process::exit(1);
    }

    // Copying apk file to the requested output
    // Release builds also need to be signed. For now, we use a simple debug
    // signing key.
    if debug {
        fs::copy(&directory.join("bin").join("Servo-debug.apk"),
                 &args.output).unwrap();
    } else {
        let keystore_dir = env::home_dir().expect("Please have a home directory");
        let keystore_dir = Path::new(&keystore_dir).join(".keystore");
        let keytoolcmd = Command::new("keytool")
                                  .arg("-list")
                                  .arg("-storepass")
                                  .arg("android")
                                  .arg("-alias")
                                  .arg("androiddebugkey")
                                  .arg("-keystore")
                                  .arg(&keystore_dir)
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
        if keytoolcmd.is_err() || keytoolcmd.unwrap().code().unwrap() != 0 {
            let keytoolcreatecmd = Command::new("keytool")
                                  .arg("-genkeypair")
                                  .arg("-keystore")
                                  .arg(&keystore_dir)
                                  .arg("-storepass")
                                  .arg("android")
                                  .arg("-alias")
                                  .arg("androiddebugkey")
                                  .arg("-keypass")
                                  .arg("android")
                                  .arg("-dname")
                                  .arg("CN=Android Debug,O=Android,C=US")
                                  .arg("-keyalg")
                                  .arg("RSA")
                                  .arg("-validity")
                                  .arg("365")
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
            if keytoolcreatecmd.is_err() ||
               keytoolcreatecmd.unwrap().code().unwrap() != 0 {
                   println!("Error while using `keytool` to create the debug keystore.");
                   process::exit(1);
               }
        }

        let jarsigncmd = Command::new("jarsigner")
                                  .arg("-digestalg")
                                  .arg("SHA1")
                                  .arg("-sigalg")
                                  .arg("MD5withRSA")
                                  .arg("-storepass")
                                  .arg("android")
                                  .arg("-keystore")
                                  .arg(&keystore_dir)
                                  .arg(&directory.join("bin").join("Servo-release-unsigned.apk"))
                                  .arg("androiddebugkey")
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
        if jarsigncmd.is_err() || jarsigncmd.unwrap().code().unwrap() != 0 {
            println!("Error while using `jarsign` to sign the APK.");
            process::exit(1);
        }

        let aligncmd = Command::new(sdk_path.join("tools").join("zipalign"))
                                  .arg("-f")
                                  .arg("-v")
                                  .arg("4")
                                  .arg(&directory.join("bin").join("Servo-release-unsigned.apk"))
                                  .arg(&directory.join("bin").join("Servo-release.apk"))
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
        if aligncmd.is_err() || aligncmd.unwrap().code().unwrap() != 0 {
            println!("Error while using `zipalign` to sign the APK.");
            process::exit(1);
        }

        fs::copy(&directory.join("bin").join("Servo-release.apk"),
                 &args.output).unwrap();
    }

}

struct Args {
    output: PathBuf,
    root_path: PathBuf,
    target_path: PathBuf,
    shared_libraries: HashSet<String>,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_root_path = None;
    let mut result_target_path = None;
    let mut result_shared_libraries = HashSet::new();
    let mut result_passthrough = Vec::new();

    let args = env::args();
    let mut args = args.skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument"),
                    root_path: result_root_path.expect("Could not find -r enlistment root argument"),
                    target_path: result_target_path.expect("Could not find -t target path argument"),
                    shared_libraries: result_shared_libraries,
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match &*arg {
            "-o" => {
                result_output = Some(PathBuf::from(args.next().expect("-o must be followed by the output name")));
            },
            "-r" => {
                result_root_path = Some(PathBuf::from(args.next().expect("-r must be followed by the enlistment root directory")));
            },
            "-t" => {
                result_target_path = Some(PathBuf::from(args.next().expect("-t must be followed by the target output directory")));
            },
            "-l" => {
                let name = args.next().expect("-l must be followed by a library name");
                result_shared_libraries.insert(vec!["lib", &name, ".so"].concat());

                // Also pass these through.
                result_passthrough.push(arg);
                result_passthrough.push(name);
            }
            _ => {
                if arg.starts_with("-l") {
                    result_shared_libraries.insert(vec!["lib", &arg[2..], ".so"].concat());
                }
                result_passthrough.push(arg)
            }
        };
    }
}

fn find_native_libs(args: &Args) -> HashMap<String, PathBuf> {
    let mut native_shared_libs: HashMap<String, PathBuf> = HashMap::new();

    // Add requested .so files
    fs::walk_dir(&args.target_path).and_then(|dir_walker| {
        for path in dir_walker {
            let path = path.unwrap().path();
            match (path.file_name(), path.extension()) {
                (Some(filename), Some(ext)) => {
                    let filename = filename.to_str().unwrap();
                    if filename.starts_with("lib")
                        && ext == "so"
                        && args.shared_libraries.contains(filename) {
                            println!("Adding the file {:?}", filename);
                            native_shared_libs.insert(filename.to_string(), path.clone());
                            break;
                        }
                }
                _ => {}
            }
        }
        Ok(())
    }).ok();

    native_shared_libs
}
<commit_msg>Auto merge of #8460 - larsbergstrom:remove_zipalign, r=Ms2ger<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//extern crate num;
#![feature(dir_builder, fs_walk)]
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs;
use std::fs::DirBuilder;
use std::path::{Path, PathBuf};
use std::process;
use std::process::{Command, Stdio};

fn main() {
    let (args, passthrough) = parse_arguments();

    // Find all the native shared libraries that exist in the target directory.
    let native_shared_libs = find_native_libs(&args);

    // Get the SDK path from the ANDROID_HOME env.
    let sdk_path = env::var("ANDROID_HOME").ok().expect("Please set the ANDROID_HOME environment variable");
    let sdk_path = Path::new(&sdk_path);

    // Get the NDK path from NDK_HOME env.
    let ndk_path = env::var("NDK_HOME").ok().expect("Please set the NDK_HOME environment variable");
    let ndk_path = Path::new(&ndk_path);

    // Get the standalone NDK path from NDK_STANDALONE env.
    //  let standalone_path = env::var("NDK_STANDALONE").ok().unwrap_or("/opt/ndk_standalone".to_string());
    //  let standalone_path = Path::new(&standalone_path);

    let debug = passthrough.contains(&"-d".to_string());

    // Set the build directory that will contain all the necessary files to create the apk
    let directory = args.root_path.join("support").join("android").join("apk");
    let resdir = args.root_path.join("resources/");

    // executing ndk-build
    env::set_var("V", "1");
    if debug {
        env::set_var("NDK_DEBUG", "1");
        env::set_var("APP_OPTIM", "0");
    } else{
        // Overrides android:debuggable propery in the .xml file.
        env::set_var("APP_OPTIM", "1");
    }

    // Copy libservo.so into the jni folder for inclusion in the build
    // TODO: pass/detect target architecture
    {
        let source = &args.target_path.join("libservo.so");
        let target_dir = &directory.join("jni").join("armeabi");
        let _ = DirBuilder::new().recursive(true).create(target_dir);
        let target = target_dir.join("libmain.so");
        println!("Copying the file {:?} to {:?}", source, target);
        fs::copy(source, target).unwrap();
    }

    let ndkcmd = Command::new(ndk_path.join("ndk-build"))
                              .arg("-B")
                              .stdout(Stdio::inherit())
                              .stderr(Stdio::inherit())
                              .current_dir(directory.clone())
                              .status();
    if ndkcmd.is_err() || ndkcmd.unwrap().code().unwrap() != 0 {
        println!("Error while executing program `ndk-build`, or missing program.");
        process::exit(1);
    }

    // Copy the additional native libs into the libs directory.
    for (name, path) in native_shared_libs.iter() {
        let target = &directory.join("libs").join("armeabi").join(name);
        println!("Copying the file {:?} to {:?}", name, target);
        fs::copy(path, target).unwrap();
    }

    // Copy over the resources
    let cpcmd= Command::new("cp")
                            .arg("-R")
                            .arg(&resdir)
                            .arg(&directory.join("assets"))
                            .stdout(Stdio::inherit())
                            .stderr(Stdio::inherit())
                            .current_dir(directory.clone())
                            .status();
    if cpcmd.is_err() || cpcmd.unwrap().code().unwrap() != 0 {
        println!("Error while copying files from the resources dir to the assets dir");
        process::exit(1);
    }

    // Update the project
    let androidcmd = Command::new(sdk_path.join("tools").join("android"))
                                  .arg("update")
                                  .arg("project")
                                  .arg("--name")
                                  .arg("Servo")
                                  .arg("--target")
                                  .arg("android-18")
                                  .arg("--path")
                                  .arg(".")
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
    if androidcmd.is_err() || androidcmd.unwrap().code().unwrap() != 0 {
        println!("Error while updating the project with the android command");
        process::exit(1);
    }

    // Build the APK
    let mut antcmd = Command::new("ant");
    if debug {
        antcmd.arg("debug");
    } else {
        antcmd.arg("release");
    }
    let antresult = antcmd
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .current_dir(directory.clone())
        .status();
    if antresult.is_err() || antresult.unwrap().code().unwrap() != 0 {
        println!("Error while executing program `ant`, or missing program.");
        process::exit(1);
    }

    // Copying apk file to the requested output
    // Release builds also need to be signed. For now, we use a simple debug
    // signing key.
    if debug {
        fs::copy(&directory.join("bin").join("Servo-debug.apk"),
                 &args.output).unwrap();
    } else {
        let keystore_dir = env::home_dir().expect("Please have a home directory");
        let keystore_dir = Path::new(&keystore_dir).join(".keystore");
        let keytoolcmd = Command::new("keytool")
                                  .arg("-list")
                                  .arg("-storepass")
                                  .arg("android")
                                  .arg("-alias")
                                  .arg("androiddebugkey")
                                  .arg("-keystore")
                                  .arg(&keystore_dir)
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
        if keytoolcmd.is_err() || keytoolcmd.unwrap().code().unwrap() != 0 {
            let keytoolcreatecmd = Command::new("keytool")
                                  .arg("-genkeypair")
                                  .arg("-keystore")
                                  .arg(&keystore_dir)
                                  .arg("-storepass")
                                  .arg("android")
                                  .arg("-alias")
                                  .arg("androiddebugkey")
                                  .arg("-keypass")
                                  .arg("android")
                                  .arg("-dname")
                                  .arg("CN=Android Debug,O=Android,C=US")
                                  .arg("-keyalg")
                                  .arg("RSA")
                                  .arg("-validity")
                                  .arg("365")
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
            if keytoolcreatecmd.is_err() ||
               keytoolcreatecmd.unwrap().code().unwrap() != 0 {
                   println!("Error while using `keytool` to create the debug keystore.");
                   process::exit(1);
               }
        }

        let jarsigncmd = Command::new("jarsigner")
                                  .arg("-digestalg")
                                  .arg("SHA1")
                                  .arg("-sigalg")
                                  .arg("MD5withRSA")
                                  .arg("-storepass")
                                  .arg("android")
                                  .arg("-keystore")
                                  .arg(&keystore_dir)
                                  .arg(&directory.join("bin").join("Servo-release-unsigned.apk"))
                                  .arg("androiddebugkey")
                                  .stdout(Stdio::inherit())
                                  .stderr(Stdio::inherit())
                                  .current_dir(directory.clone())
                                  .status();
        if jarsigncmd.is_err() || jarsigncmd.unwrap().code().unwrap() != 0 {
            println!("Error while using `jarsign` to sign the APK.");
            process::exit(1);
        }

        fs::copy(&directory.join("bin").join("Servo-release-unsigned.apk"),
                 &args.output).unwrap();
    }

}

struct Args {
    output: PathBuf,
    root_path: PathBuf,
    target_path: PathBuf,
    shared_libraries: HashSet<String>,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_root_path = None;
    let mut result_target_path = None;
    let mut result_shared_libraries = HashSet::new();
    let mut result_passthrough = Vec::new();

    let args = env::args();
    let mut args = args.skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument"),
                    root_path: result_root_path.expect("Could not find -r enlistment root argument"),
                    target_path: result_target_path.expect("Could not find -t target path argument"),
                    shared_libraries: result_shared_libraries,
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match &*arg {
            "-o" => {
                result_output = Some(PathBuf::from(args.next().expect("-o must be followed by the output name")));
            },
            "-r" => {
                result_root_path = Some(PathBuf::from(args.next().expect("-r must be followed by the enlistment root directory")));
            },
            "-t" => {
                result_target_path = Some(PathBuf::from(args.next().expect("-t must be followed by the target output directory")));
            },
            "-l" => {
                let name = args.next().expect("-l must be followed by a library name");
                result_shared_libraries.insert(vec!["lib", &name, ".so"].concat());

                // Also pass these through.
                result_passthrough.push(arg);
                result_passthrough.push(name);
            }
            _ => {
                if arg.starts_with("-l") {
                    result_shared_libraries.insert(vec!["lib", &arg[2..], ".so"].concat());
                }
                result_passthrough.push(arg)
            }
        };
    }
}

fn find_native_libs(args: &Args) -> HashMap<String, PathBuf> {
    let mut native_shared_libs: HashMap<String, PathBuf> = HashMap::new();

    // Add requested .so files
    fs::walk_dir(&args.target_path).and_then(|dir_walker| {
        for path in dir_walker {
            let path = path.unwrap().path();
            match (path.file_name(), path.extension()) {
                (Some(filename), Some(ext)) => {
                    let filename = filename.to_str().unwrap();
                    if filename.starts_with("lib")
                        && ext == "so"
                        && args.shared_libraries.contains(filename) {
                            println!("Adding the file {:?}", filename);
                            native_shared_libs.insert(filename.to_string(), path.clone());
                            break;
                        }
                }
                _ => {}
            }
        }
        Ok(())
    }).ok();

    native_shared_libs
}
<|endoftext|>


<commit_before>//! This example uses [hyper][] to create a http server which handles requests asynchronously in
//! gluon. To do this we define a few types and functions in Rust with which we register in gluon
//! so that we can communicate with `hyper`. The rest of the implementation is done in gluon,
//! routing the requests and constructing the responses.
//!
//! [hyper]:https://hyper.rs

extern crate gluon;
extern crate gluon_base as base;
#[macro_use]
extern crate gluon_vm as vm;

#[macro_use]
extern crate collect_mac;
extern crate env_logger;
extern crate futures;
extern crate http;
extern crate hyper;
extern crate tokio;
#[macro_use]
extern crate log;

use std::env;
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use hyper::service::Service;
use hyper::Chunk;
use hyper::Server;

use http::StatusCode;

use futures::future::Either;
use futures::stream::Stream;
use futures::sync::oneshot;
use futures::{future, Async, Future};

use base::types::{ArcType, Type};

use vm::{Error as VmError, ExternModule, Result as VmResult};

use gluon::import::add_extern_module;
use vm::api::{
    Function, FutureResult, OpaqueValue, OwnedFunction, PushAsRef, Userdata, VmType, WithVM, IO,
};
use vm::future::FutureValue;
use vm::gc::{Gc, Traverseable};
use vm::thread::{RootedThread, Thread};

use gluon::{new_vm, Compiler};

// `Handler` is a type defined in http.glu but since we need to refer to it in the signature of
// listen we define a phantom type which we can use with `OpaqueValue` to store a `Handler` in Rust
struct Handler<T>(PhantomData<T>);

impl<T: VmType + 'static> VmType for Handler<T> {
    type Type = Self;
    fn make_type(vm: &Thread) -> ArcType {
        let typ = vm
            .find_type_info("examples.http.types.Handler")
            .unwrap_or_else(|err| panic!("{}", err))
            .into_type();
        Type::app(typ, collect![T::make_type(vm)])
    }
}

// Representation of a http body that is in the prograss of being read
pub struct Body(Arc<Mutex<Box<Stream<Item = PushAsRef<Chunk, [u8]>, Error = VmError> + Send>>>);

// By implementing `Userdata` on `Body` it can be automatically pushed and retrieved from gluon
// threads
impl Userdata for Body {}

// Types implementing `Userdata` requires a `std::fmt::Debug` implementation so it can be displayed
impl fmt::Debug for Body {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Body")
    }
}

// `Traverseable` is required by `Userdata` so that the garbage collector knows how to scan the
// value for garbage collected references. Normally objects do not contain any references so this
// can be empty
impl Traverseable for Body {
    fn traverse(&self, _: &mut Gc) {}
}

// `VmType` is the last trait required for a type to implement `Userdata` and defines the type used
// in gluon for this Rust type. For opaque `Userdata` values this minimal implementation is enough
// as the default implementation of `make_type` will lookup `VmType::Type` from the virtual machine
// which should have been registered earlier with `Thread::register_type`
impl VmType for Body {
    type Type = Self;
}

// Since `Body` implements `Userdata` gluon will automatically marshal the gluon representation
// into `&Body` argument
fn read_chunk(
    body: &Body,
) -> FutureResult<impl Future<Item = IO<Option<PushAsRef<Chunk, [u8]>>>, Error = VmError>> {
    use futures::future::poll_fn;

    let body = body.0.clone();
    // `FutureResult` is a wrapper type around `Future` which when returned to the interpreter is
    // polled until completion. After `poll` returns `Ready` the value is then returned to the
    // gluon function which called `read_chunk`
    FutureResult(poll_fn(move || {
        let mut stream = body.lock().unwrap();
        stream.poll().map(|async| async.map(IO::Value))
    }))
}

// A http body that is being written
pub struct ResponseBody(Arc<Mutex<Option<hyper::body::Sender>>>);

impl fmt::Debug for ResponseBody {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Response")
    }
}

impl Userdata for ResponseBody {}

impl Traverseable for ResponseBody {
    fn traverse(&self, _: &mut Gc) {}
}

impl VmType for ResponseBody {
    type Type = Self;
}

fn write_response(
    response: &ResponseBody,
    bytes: &[u8],
) -> FutureResult<impl Future<Item = IO<()>, Error = VmError>> {
    use futures::future::poll_fn;

    // Turn `bytes´ into a `Chunk` which can be sent to the http body
    let mut unsent_chunk = Some(bytes.to_owned().into());
    let response = response.0.clone();
    FutureResult(poll_fn(move || {
        info!("Starting response send");
        let mut sender = response.lock().unwrap();
        let sender = sender
            .as_mut()
            .expect("Sender has been dropped while still in use");
        let chunk = unsent_chunk
            .take()
            .expect("Attempt to poll after chunk is sent");
        match sender.poll_ready() {
            Ok(Async::NotReady) => {
                unsent_chunk = Some(chunk);
                return Ok(Async::NotReady);
            }
            Ok(Async::Ready(_)) => (),
            Err(_) => {
                info!("Could not send http response");
                return Ok(Async::Ready(IO::Value(())));
            }
        }
        match sender.send_data(chunk) {
            Ok(()) => Ok(Async::Ready(IO::Value(()))),
            Err(chunk) => {
                info!("Could not send http response");
                unsent_chunk = Some(chunk);
                Ok(Async::NotReady)
            }
        }
    }))
}

// Next we define some record types which are marshalled to and from gluon. These have equivalent
// definitions in http_types.glu
field_decl! { method, uri, status, body, request, response }

type Request = record_type!{
    method => String,
    uri => String,
    body => Body
};

type Response = record_type!{
    status => u16
};

type HttpState = record_type!{
    request => Request,
    response => ResponseBody
};

fn listen(
    port: i32,
    value: WithVM<OpaqueValue<RootedThread, Handler<Response>>>,
) -> FutureResult<impl Future<Item = IO<()>, Error = vm::Error> + Send + 'static> {
    let WithVM {
        value: handler,
        vm: thread,
    } = value;

    let thread = match thread.new_thread() {
        Ok(thread) => thread,
        Err(err) => return FutureResult(Either::A(future::err(err))),
    };

    // Retrieve the `handle` function from the http module which we use to evaluate values of type
    // `Handler Response`
    type ListenFn = fn(OpaqueValue<RootedThread, Handler<Response>>, HttpState) -> IO<Response>;
    let handle: Function<RootedThread, ListenFn> = thread
        .get_global("examples.http.http.handle")
        .unwrap_or_else(|err| panic!("{}", err));

    struct Listen {
        handle: Function<RootedThread, ListenFn>,
        handler: OpaqueValue<RootedThread, Handler<Response>>,
    }

    impl Service for Listen {
        type ReqBody = hyper::Body;
        type ResBody = hyper::Body;
        type Error = hyper::Error;
        type Future =
            Box<Future<Item = http::Response<hyper::Body>, Error = hyper::Error> + Send + 'static>;

        fn call(&mut self, request: http::Request<hyper::Body>) -> Self::Future {
            let gluon_request = record_no_decl! {
                method => request.method().as_str().to_owned(),
                uri => request.uri().to_string(),
                // Since `Body` implements `Userdata` it can be directly pushed to gluon
                body => Body(Arc::new(Mutex::new(Box::new(request.into_body()
                    .map_err(|err| VmError::Message(format!("{}", err)))
                    // `PushAsRef` makes the `body` parameter act as a `&[u8]` which means it is
                    // marshalled to `Array Byte` in gluon
                    .map(PushAsRef::<_, [u8]>::new)))))
            };
            let (response_sender, response_body) = hyper::Body::channel();
            let response_sender = Arc::new(Mutex::new(Some(response_sender)));
            let http_state = record_no_decl!{
                request => gluon_request,
                response => ResponseBody(response_sender.clone())
            };

            Box::new(
                self.handle
                    .clone()
                    .call_async(self.handler.clone(), http_state)
                    .then(move |result| match result {
                        Ok(value) => {
                            match value {
                                IO::Value(record_p!{ status }) => {
                                    // Drop the sender to so that it the receiver stops waiting for
                                    // more chunks
                                    *response_sender.lock().unwrap() = None;

                                    let status = StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
                                    Ok(
                                        http::Response::builder()
                                            .status(status)
                                            .body(response_body).unwrap(),
                                    )
                                }
                                IO::Exception(err) => {
                                    eprintln!("{}", err);
                                    Ok(
                                        http::Response::builder()
                                            .status(StatusCode::INTERNAL_SERVER_ERROR).body("".into()).unwrap()
                                    )
                                }
                            }
                        }
                        Err(err) => {
                            eprintln!("{}", err);
                            Ok(http::Response::builder()
                                .status(StatusCode::INTERNAL_SERVER_ERROR)
                                .body("".into())
                                .unwrap())
                        }
                    }),
            )
        }
    }

    let addr = format!("127.0.0.1:{}", port).parse().unwrap();

    let (sender, receiver) = oneshot::channel();
    tokio::spawn(
        Server::bind(&addr)
            .serve(move || -> Result<_, hyper::Error> {
                Ok(Listen {
                    handle: handle.clone(),
                    handler: handler.clone(),
                })
            })
            .map_err(|err| {
                error!("{}", err);
            })
            .and_then(|_| sender.send(IO::Value(())).map_err(|_| ())),
    );
    FutureResult(Either::B(
        receiver.map_err(|err| vm::Error::from(format!("Server error: {}", err))),
    ))
}

// To let the `http_types` module refer to `Body` and `ResponseBody` we register these types in a
// separate function which is called before loading `http_types`
pub fn load_types(vm: &Thread) -> VmResult<ExternModule> {
    vm.register_type::<Body>("Body", &[])?;
    vm.register_type::<ResponseBody>("ResponseBody", &[])?;

    ExternModule::new(
        vm,
        record! {
            // Define the types so that they can be used from gluon
            type Body => Body,
            type ResponseBody => ResponseBody,
            type Method => String,
            type StatusCode => u16,
            type Request => Request,
            type Response => Response,
            type HttpState => HttpState
        },
    )
}

pub fn load(vm: &Thread) -> VmResult<ExternModule> {
    ExternModule::new(
        vm,
        record! {
            listen => primitive!(2 listen),
            read_chunk => primitive!(1 read_chunk),
            write_response => primitive!(2 write_response)
        },
    )
}

fn main() {
    env_logger::init();

    let port = env::args()
        .nth(1)
        .map(|port| port.parse::<u16>().expect("port"))
        .unwrap_or(80);

    let thread = new_vm();
    tokio::run(start(&thread, port).map_err(|err| panic!("{}", err)));
}

fn start(
    thread: &Thread,
    port: u16,
) -> impl Future<Item = (), Error = Box<StdError + Send + Sync + 'static>> {
    add_extern_module(&thread, "http.prim_types", load_types);
    add_extern_module(&thread, "http.prim", load);

    let thread = thread.root_thread();
    future::lazy(|| -> Result<_, Box<StdError + Send + Sync>> {
        // Last we run our `http_server.glu` module which returns a function which starts listening
        // on the port we passed from the command line
        let mut expr = String::new();
        {
            let mut file = File::open("examples/http/server.glu")?;
            file.read_to_string(&mut expr)?;
        }
        Ok(expr)
    }).and_then(move |expr| {
        Compiler::new()
            .run_expr_async::<OwnedFunction<fn(u16) -> IO<()>>>(
                &thread,
                "examples/http/server.glu",
                &expr,
            )
            .from_err()
            .and_then(move |(mut listen, _)| {
                FutureValue::Future(listen.call_async(port))
                    .from_err()
                    .map(|_| ())
            })
    })
}

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

    extern crate tokio_retry;

    use std::str;

    use hyper::Client;

    use tokio::runtime::Runtime;

    #[test]
    fn hello_world() {
        let mut runtime = Runtime::new().unwrap();

        let port = 12235;
        let thread = new_vm();

        runtime.spawn(future::lazy(move || {
            start(&thread, port).map_err(|err| error!("{}", err))
        }));

        let future =
            move || Client::new().get(format!("http://localhost:{}", port).parse().unwrap());

        let retry_strategy = tokio_retry::strategy::FixedInterval::from_millis(400).take(40);

        runtime
            .block_on(
                tokio_retry::Retry::spawn(retry_strategy, future)
                    .map_err(|err| panic!("{}", err))
                    .and_then(|response| {
                        response.into_body().concat2().map(|body| {
                            assert_eq!(str::from_utf8(&body).unwrap(), "Hello World");
                        })
                    }),
            )
            .unwrap();
    }

    #[test]
    fn echo() {
        let mut runtime = Runtime::new().unwrap();

        let port = 12234;
        let thread = new_vm();
        runtime.spawn(future::lazy(move || {
            start(&thread, port).map_err(|err| error!("{}", err))
        }));

        let future = move || {
            let request = hyper::Request::post(format!("http://localhost:{}/echo", port))
                .body(hyper::Body::from("test"))
                .unwrap();

            Client::new().request(request)
        };

        let retry_strategy = tokio_retry::strategy::FixedInterval::from_millis(400).take(40);

        runtime
            .block_on(
                tokio_retry::Retry::spawn(retry_strategy, future)
                    .map_err(|err| panic!("{}", err))
                    .and_then(|response| {
                        response.into_body().concat2().map(|body| {
                            assert_eq!(str::from_utf8(&body).unwrap(), "test");
                        })
                    }),
            )
            .unwrap();
    }
}
<commit_msg>Bind on all interfaces in the http example<commit_after>//! This example uses [hyper][] to create a http server which handles requests asynchronously in
//! gluon. To do this we define a few types and functions in Rust with which we register in gluon
//! so that we can communicate with `hyper`. The rest of the implementation is done in gluon,
//! routing the requests and constructing the responses.
//!
//! [hyper]:https://hyper.rs

extern crate gluon;
extern crate gluon_base as base;
#[macro_use]
extern crate gluon_vm as vm;

#[macro_use]
extern crate collect_mac;
extern crate env_logger;
extern crate futures;
extern crate http;
extern crate hyper;
extern crate tokio;
#[macro_use]
extern crate log;

use std::env;
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use hyper::service::Service;
use hyper::Chunk;
use hyper::Server;

use http::StatusCode;

use futures::future::Either;
use futures::stream::Stream;
use futures::sync::oneshot;
use futures::{future, Async, Future};

use base::types::{ArcType, Type};

use vm::{Error as VmError, ExternModule, Result as VmResult};

use gluon::import::add_extern_module;
use vm::api::{
    Function, FutureResult, OpaqueValue, OwnedFunction, PushAsRef, Userdata, VmType, WithVM, IO,
};
use vm::future::FutureValue;
use vm::gc::{Gc, Traverseable};
use vm::thread::{RootedThread, Thread};

use gluon::{new_vm, Compiler};

// `Handler` is a type defined in http.glu but since we need to refer to it in the signature of
// listen we define a phantom type which we can use with `OpaqueValue` to store a `Handler` in Rust
struct Handler<T>(PhantomData<T>);

impl<T: VmType + 'static> VmType for Handler<T> {
    type Type = Self;
    fn make_type(vm: &Thread) -> ArcType {
        let typ = vm
            .find_type_info("examples.http.types.Handler")
            .unwrap_or_else(|err| panic!("{}", err))
            .into_type();
        Type::app(typ, collect![T::make_type(vm)])
    }
}

// Representation of a http body that is in the prograss of being read
pub struct Body(Arc<Mutex<Box<Stream<Item = PushAsRef<Chunk, [u8]>, Error = VmError> + Send>>>);

// By implementing `Userdata` on `Body` it can be automatically pushed and retrieved from gluon
// threads
impl Userdata for Body {}

// Types implementing `Userdata` requires a `std::fmt::Debug` implementation so it can be displayed
impl fmt::Debug for Body {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Body")
    }
}

// `Traverseable` is required by `Userdata` so that the garbage collector knows how to scan the
// value for garbage collected references. Normally objects do not contain any references so this
// can be empty
impl Traverseable for Body {
    fn traverse(&self, _: &mut Gc) {}
}

// `VmType` is the last trait required for a type to implement `Userdata` and defines the type used
// in gluon for this Rust type. For opaque `Userdata` values this minimal implementation is enough
// as the default implementation of `make_type` will lookup `VmType::Type` from the virtual machine
// which should have been registered earlier with `Thread::register_type`
impl VmType for Body {
    type Type = Self;
}

// Since `Body` implements `Userdata` gluon will automatically marshal the gluon representation
// into `&Body` argument
fn read_chunk(
    body: &Body,
) -> FutureResult<impl Future<Item = IO<Option<PushAsRef<Chunk, [u8]>>>, Error = VmError>> {
    use futures::future::poll_fn;

    let body = body.0.clone();
    // `FutureResult` is a wrapper type around `Future` which when returned to the interpreter is
    // polled until completion. After `poll` returns `Ready` the value is then returned to the
    // gluon function which called `read_chunk`
    FutureResult(poll_fn(move || {
        let mut stream = body.lock().unwrap();
        stream.poll().map(|async| async.map(IO::Value))
    }))
}

// A http body that is being written
pub struct ResponseBody(Arc<Mutex<Option<hyper::body::Sender>>>);

impl fmt::Debug for ResponseBody {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Response")
    }
}

impl Userdata for ResponseBody {}

impl Traverseable for ResponseBody {
    fn traverse(&self, _: &mut Gc) {}
}

impl VmType for ResponseBody {
    type Type = Self;
}

fn write_response(
    response: &ResponseBody,
    bytes: &[u8],
) -> FutureResult<impl Future<Item = IO<()>, Error = VmError>> {
    use futures::future::poll_fn;

    // Turn `bytes´ into a `Chunk` which can be sent to the http body
    let mut unsent_chunk = Some(bytes.to_owned().into());
    let response = response.0.clone();
    FutureResult(poll_fn(move || {
        info!("Starting response send");
        let mut sender = response.lock().unwrap();
        let sender = sender
            .as_mut()
            .expect("Sender has been dropped while still in use");
        let chunk = unsent_chunk
            .take()
            .expect("Attempt to poll after chunk is sent");
        match sender.poll_ready() {
            Ok(Async::NotReady) => {
                unsent_chunk = Some(chunk);
                return Ok(Async::NotReady);
            }
            Ok(Async::Ready(_)) => (),
            Err(_) => {
                info!("Could not send http response");
                return Ok(Async::Ready(IO::Value(())));
            }
        }
        match sender.send_data(chunk) {
            Ok(()) => Ok(Async::Ready(IO::Value(()))),
            Err(chunk) => {
                info!("Could not send http response");
                unsent_chunk = Some(chunk);
                Ok(Async::NotReady)
            }
        }
    }))
}

// Next we define some record types which are marshalled to and from gluon. These have equivalent
// definitions in http_types.glu
field_decl! { method, uri, status, body, request, response }

type Request = record_type!{
    method => String,
    uri => String,
    body => Body
};

type Response = record_type!{
    status => u16
};

type HttpState = record_type!{
    request => Request,
    response => ResponseBody
};

fn listen(
    port: i32,
    value: WithVM<OpaqueValue<RootedThread, Handler<Response>>>,
) -> FutureResult<impl Future<Item = IO<()>, Error = vm::Error> + Send + 'static> {
    let WithVM {
        value: handler,
        vm: thread,
    } = value;

    let thread = match thread.new_thread() {
        Ok(thread) => thread,
        Err(err) => return FutureResult(Either::A(future::err(err))),
    };

    // Retrieve the `handle` function from the http module which we use to evaluate values of type
    // `Handler Response`
    type ListenFn = fn(OpaqueValue<RootedThread, Handler<Response>>, HttpState) -> IO<Response>;
    let handle: Function<RootedThread, ListenFn> = thread
        .get_global("examples.http.http.handle")
        .unwrap_or_else(|err| panic!("{}", err));

    struct Listen {
        handle: Function<RootedThread, ListenFn>,
        handler: OpaqueValue<RootedThread, Handler<Response>>,
    }

    impl Service for Listen {
        type ReqBody = hyper::Body;
        type ResBody = hyper::Body;
        type Error = hyper::Error;
        type Future =
            Box<Future<Item = http::Response<hyper::Body>, Error = hyper::Error> + Send + 'static>;

        fn call(&mut self, request: http::Request<hyper::Body>) -> Self::Future {
            let gluon_request = record_no_decl! {
                method => request.method().as_str().to_owned(),
                uri => request.uri().to_string(),
                // Since `Body` implements `Userdata` it can be directly pushed to gluon
                body => Body(Arc::new(Mutex::new(Box::new(request.into_body()
                    .map_err(|err| VmError::Message(format!("{}", err)))
                    // `PushAsRef` makes the `body` parameter act as a `&[u8]` which means it is
                    // marshalled to `Array Byte` in gluon
                    .map(PushAsRef::<_, [u8]>::new)))))
            };
            let (response_sender, response_body) = hyper::Body::channel();
            let response_sender = Arc::new(Mutex::new(Some(response_sender)));
            let http_state = record_no_decl!{
                request => gluon_request,
                response => ResponseBody(response_sender.clone())
            };

            Box::new(
                self.handle
                    .clone()
                    .call_async(self.handler.clone(), http_state)
                    .then(move |result| match result {
                        Ok(value) => {
                            match value {
                                IO::Value(record_p!{ status }) => {
                                    // Drop the sender to so that it the receiver stops waiting for
                                    // more chunks
                                    *response_sender.lock().unwrap() = None;

                                    let status = StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
                                    Ok(
                                        http::Response::builder()
                                            .status(status)
                                            .body(response_body).unwrap(),
                                    )
                                }
                                IO::Exception(err) => {
                                    eprintln!("{}", err);
                                    Ok(
                                        http::Response::builder()
                                            .status(StatusCode::INTERNAL_SERVER_ERROR).body("".into()).unwrap()
                                    )
                                }
                            }
                        }
                        Err(err) => {
                            eprintln!("{}", err);
                            Ok(http::Response::builder()
                                .status(StatusCode::INTERNAL_SERVER_ERROR)
                                .body("".into())
                                .unwrap())
                        }
                    }),
            )
        }
    }

    let addr = format!("0.0.0.0:{}", port).parse().unwrap();

    let (sender, receiver) = oneshot::channel();
    tokio::spawn(
        Server::bind(&addr)
            .serve(move || -> Result<_, hyper::Error> {
                Ok(Listen {
                    handle: handle.clone(),
                    handler: handler.clone(),
                })
            })
            .map_err(|err| {
                error!("{}", err);
            })
            .and_then(|_| sender.send(IO::Value(())).map_err(|_| ())),
    );
    FutureResult(Either::B(
        receiver.map_err(|err| vm::Error::from(format!("Server error: {}", err))),
    ))
}

// To let the `http_types` module refer to `Body` and `ResponseBody` we register these types in a
// separate function which is called before loading `http_types`
pub fn load_types(vm: &Thread) -> VmResult<ExternModule> {
    vm.register_type::<Body>("Body", &[])?;
    vm.register_type::<ResponseBody>("ResponseBody", &[])?;

    ExternModule::new(
        vm,
        record! {
            // Define the types so that they can be used from gluon
            type Body => Body,
            type ResponseBody => ResponseBody,
            type Method => String,
            type StatusCode => u16,
            type Request => Request,
            type Response => Response,
            type HttpState => HttpState
        },
    )
}

pub fn load(vm: &Thread) -> VmResult<ExternModule> {
    ExternModule::new(
        vm,
        record! {
            listen => primitive!(2 listen),
            read_chunk => primitive!(1 read_chunk),
            write_response => primitive!(2 write_response)
        },
    )
}

fn main() {
    env_logger::init();

    let port = env::args()
        .nth(1)
        .map(|port| port.parse::<u16>().expect("port"))
        .unwrap_or(80);

    let thread = new_vm();
    tokio::run(start(&thread, port).map_err(|err| panic!("{}", err)));
}

fn start(
    thread: &Thread,
    port: u16,
) -> impl Future<Item = (), Error = Box<StdError + Send + Sync + 'static>> {
    add_extern_module(&thread, "http.prim_types", load_types);
    add_extern_module(&thread, "http.prim", load);

    let thread = thread.root_thread();
    future::lazy(|| -> Result<_, Box<StdError + Send + Sync>> {
        // Last we run our `http_server.glu` module which returns a function which starts listening
        // on the port we passed from the command line
        let mut expr = String::new();
        {
            let mut file = File::open("examples/http/server.glu")?;
            file.read_to_string(&mut expr)?;
        }
        Ok(expr)
    }).and_then(move |expr| {
        Compiler::new()
            .run_expr_async::<OwnedFunction<fn(u16) -> IO<()>>>(
                &thread,
                "examples/http/server.glu",
                &expr,
            )
            .from_err()
            .and_then(move |(mut listen, _)| {
                FutureValue::Future(listen.call_async(port))
                    .from_err()
                    .map(|_| ())
            })
    })
}

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

    extern crate tokio_retry;

    use std::str;

    use hyper::Client;

    use tokio::runtime::Runtime;

    #[test]
    fn hello_world() {
        let mut runtime = Runtime::new().unwrap();

        let port = 12235;
        let thread = new_vm();

        runtime.spawn(future::lazy(move || {
            start(&thread, port).map_err(|err| error!("{}", err))
        }));

        let future =
            move || Client::new().get(format!("http://localhost:{}", port).parse().unwrap());

        let retry_strategy = tokio_retry::strategy::FixedInterval::from_millis(400).take(40);

        runtime
            .block_on(
                tokio_retry::Retry::spawn(retry_strategy, future)
                    .map_err(|err| panic!("{}", err))
                    .and_then(|response| {
                        response.into_body().concat2().map(|body| {
                            assert_eq!(str::from_utf8(&body).unwrap(), "Hello World");
                        })
                    }),
            )
            .unwrap();
    }

    #[test]
    fn echo() {
        let mut runtime = Runtime::new().unwrap();

        let port = 12234;
        let thread = new_vm();
        runtime.spawn(future::lazy(move || {
            start(&thread, port).map_err(|err| error!("{}", err))
        }));

        let future = move || {
            let request = hyper::Request::post(format!("http://localhost:{}/echo", port))
                .body(hyper::Body::from("test"))
                .unwrap();

            Client::new().request(request)
        };

        let retry_strategy = tokio_retry::strategy::FixedInterval::from_millis(400).take(40);

        runtime
            .block_on(
                tokio_retry::Retry::spawn(retry_strategy, future)
                    .map_err(|err| panic!("{}", err))
                    .and_then(|response| {
                        response.into_body().concat2().map(|body| {
                            assert_eq!(str::from_utf8(&body).unwrap(), "test");
                        })
                    }),
            )
            .unwrap();
    }
}
<|endoftext|>


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

use libimagdiary::entry::DiaryEntry;
use libimagstore::store::Entry;

use error::LogError as LE;
use error::LogErrorKind as LEK;
use error::Result;

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

pub trait Log : DiaryEntry {
    fn is_log(&self) -> Result<bool>;
    fn make_log_entry(&mut self) -> Result<()>;
}

impl Log for Entry {
    fn is_log(&self) -> Result<bool> {
        let location = "log.is_log";
        match self.get_header().read(location)? {
            Some(&Value::Boolean(b)) => Ok(b),
            Some(_) => Err(LE::from_kind(LEK::HeaderTypeError("boolean", location))),
            None    => Ok(false)
        }
    }

    fn make_log_entry(&mut self) -> Result<()> {
        self.get_header_mut()
            .insert("log.is_log", Value::Boolean(true))
            .map_err(LE::from)
            .map(|_| ())
    }

}

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

use libimagdiary::entry::DiaryEntry;
use libimagstore::store::Entry;

use error::LogError as LE;
use error::LogErrorKind as LEK;
use error::Result;

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

pub trait Log : DiaryEntry {
    fn is_log(&self) -> Result<bool>;
    fn make_log_entry(&mut self) -> Result<()>;
}

impl Log for Entry {
    fn is_log(&self) -> Result<bool> {
        let location = "log.is_log";
        self.get_header()
            .read(location)?
            .ok_or(LE::from_kind(LEK::HeaderTypeError("boolean", location)))
            .map(|v| v.as_bool().unwrap_or(false))
    }

    fn make_log_entry(&mut self) -> Result<()> {
        self.get_header_mut()
            .insert("log.is_log", Value::Boolean(true))
            .map_err(LE::from)
            .map(|_| ())
    }

}

<|endoftext|>


<commit_before>use std::fs::File;

use frappe::classfile::ClassFile;
use frappe::classfile::constant_pool as cp;
use frappe::classfile::constant_pool::Tag;

#[test]
fn test_load_hello_world_class() {
    // Given
    let mut file = File::open("test-classes/HelloWorld.class").unwrap();

    // When
    let classfile = ClassFile::read(&mut file).unwrap();

    // Then
    assert_eq!(0xcafebabe, classfile.magic);
    assert_eq!(52, classfile.major_version);
    assert_eq!(0, classfile.minor_version);
    assert_eq!(29, classfile.constant_pool.len());
    assert!(classfile.access_flags.is_public());
    assert!(classfile.access_flags.is_super());

    // Constant pool entries
    assert_eq!(
        Tag::Methodref(cp::MethodrefTag {
            class_index: 6,
            name_and_type_index: 15
        }),
        classfile.constant_pool[1]);
    assert_eq!(
        Tag::Fieldref(cp::FieldrefTag {
            class_index: 16,
            name_and_type_index: 17
        }),
        classfile.constant_pool[2]);
    assert_eq!(
        Tag::String(cp::StringTag {
            string_index: 18
        }),
        classfile.constant_pool[3]);
    assert_eq!(
        Tag::Methodref(cp::MethodrefTag {
            class_index: 19,
            name_and_type_index: 20
        }),
        classfile.constant_pool[4]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 21
        }),
        classfile.constant_pool[5]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 22
        }),
        classfile.constant_pool[6]);
    assert_utf8_tag(
        "<init>",
        &classfile.constant_pool[7]);
    assert_utf8_tag(
        "()V",
        &classfile.constant_pool[8]);
    assert_utf8_tag(
        "Code",
        &classfile.constant_pool[9]);
    assert_utf8_tag(
        "LineNumberTable",
        &classfile.constant_pool[10]);
    assert_utf8_tag(
        "main",
        &classfile.constant_pool[11]);
    assert_utf8_tag(
        "([Ljava/lang/String;)V",
        &classfile.constant_pool[12]);
    assert_utf8_tag(
        "SourceFile",
        &classfile.constant_pool[13]);
    assert_utf8_tag(
        "HelloWorld.java",
        &classfile.constant_pool[14]);
    assert_eq!(
        Tag::NameAndType {
            name_index: 7,
            descriptor_index: 8
        },
        classfile.constant_pool[15]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 23
        }),
        classfile.constant_pool[16]);
    assert_eq!(
        Tag::NameAndType {
            name_index: 24,
            descriptor_index: 25
        },
        classfile.constant_pool[17]);
    assert_utf8_tag(
        "Hello World!",
        &classfile.constant_pool[18]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 26
        }),
        classfile.constant_pool[19]);
    assert_eq!(
        Tag::NameAndType {
            name_index: 27,
            descriptor_index: 28
        },
        classfile.constant_pool[20]);
    assert_utf8_tag(
        "io/hcf/frappe/HelloWorld",
        &classfile.constant_pool[21]);
    assert_utf8_tag(
        "java/lang/Object",
        &classfile.constant_pool[22]);
    assert_utf8_tag(
        "java/lang/System",
        &classfile.constant_pool[23]);
    assert_utf8_tag(
        "out",
        &classfile.constant_pool[24]);
    assert_utf8_tag(
        "Ljava/io/PrintStream;",
        &classfile.constant_pool[25]);
    assert_utf8_tag(
        "java/io/PrintStream",
        &classfile.constant_pool[26]);
    assert_utf8_tag(
        "println",
        &classfile.constant_pool[27]);
    assert_utf8_tag(
        "(Ljava/lang/String;)V",
        &classfile.constant_pool[28]);

    assert_eq!(0, classfile.fields.len());
    assert_eq!(0, classfile.fields.len());

    // 2 methods
    assert_eq!(2, classfile.methods.len());
    // ctor
    let ctor_info = &classfile.methods[0];
    assert!(ctor_info.access_flags.is_public());
    assert!(!ctor_info.access_flags.is_static());
    assert_utf8_tag(
        "<init>",
        &classfile.constant_pool[ctor_info.name_index]);
    assert_utf8_tag(
        "()V",
        &classfile.constant_pool[ctor_info.descriptor_index]);
    // main
    let main_info = &classfile.methods[1];
    assert!(main_info.access_flags.is_static());
    assert!(main_info.access_flags.is_public());
    assert_utf8_tag(
        "main",
        &classfile.constant_pool[main_info.name_index]);
    assert_utf8_tag(
        "([Ljava/lang/String;)V",
        &classfile.constant_pool[main_info.descriptor_index]);

    // 1 attribute
    assert_eq!(1, classfile.attributes.len());
}

#[test]
fn should_load_point_class() {
    // Given
    let mut file = File::open("test-classes/Point.class").unwrap();

    // When
    let class = ClassFile::read(&mut file).unwrap();

    // Then
    assert_eq!(2, class.fields.len());
    assert!(!class.access_flags.is_interface());
    assert!(!class.access_flags.is_annotation());
    let this_class_desc = &class.constant_pool[class.this_class().name_index];
    assert_utf8_tag("io/hcf/frappe/Point", &this_class_desc);
    let super_class_desc = &class.constant_pool[class.super_class().unwrap().name_index];
    assert_utf8_tag("java/lang/Object", &super_class_desc);

    let x_field = &class.fields[0];
    assert_utf8_tag("x", &class.constant_pool[x_field.name_index]);
    assert!(x_field.access_flags.is_private());
    assert!(!x_field.access_flags.is_protected());
    assert!(!x_field.access_flags.is_public());
    assert!(!x_field.access_flags.is_static());
    assert_utf8_tag("I", &class.constant_pool[x_field.descriptor_index]);

    let y_field = &class.fields[1];
    assert_utf8_tag("y", &class.constant_pool[y_field.name_index]);
    assert!(y_field.access_flags.is_private());
    assert!(!y_field.access_flags.is_protected());
    assert!(!y_field.access_flags.is_public());
    assert!(!y_field.access_flags.is_static());
    assert_utf8_tag("I", &class.constant_pool[y_field.descriptor_index]);
}

fn assert_utf8_tag(value: &str, tag: &Tag) {
    let expected_tag = Tag::Utf8(value.into());
    assert_eq!(expected_tag, *tag);
}
<commit_msg>Update tests to use bitflags.<commit_after>use std::fs::File;

use frappe::classfile;
use frappe::classfile::ClassFile;
use frappe::classfile::constant_pool as cp;
use frappe::classfile::constant_pool::Tag;
use frappe::classfile::method;
use frappe::classfile::field;

#[test]
fn test_load_hello_world_class() {
    // Given
    let mut file = File::open("test-classes/HelloWorld.class").unwrap();

    // When
    let classfile = ClassFile::read(&mut file).unwrap();

    // Then
    assert_eq!(0xcafebabe, classfile.magic);
    assert_eq!(52, classfile.major_version);
    assert_eq!(0, classfile.minor_version);
    assert_eq!(29, classfile.constant_pool.len());
    assert_eq!(classfile::ACC_PUBLIC |
               classfile::ACC_SUPER,
               classfile.access_flags);

    // Constant pool entries
    assert_eq!(
        Tag::Methodref(cp::MethodrefTag {
            class_index: 6,
            name_and_type_index: 15
        }),
        classfile.constant_pool[1]);
    assert_eq!(
        Tag::Fieldref(cp::FieldrefTag {
            class_index: 16,
            name_and_type_index: 17
        }),
        classfile.constant_pool[2]);
    assert_eq!(
        Tag::String(cp::StringTag {
            string_index: 18
        }),
        classfile.constant_pool[3]);
    assert_eq!(
        Tag::Methodref(cp::MethodrefTag {
            class_index: 19,
            name_and_type_index: 20
        }),
        classfile.constant_pool[4]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 21
        }),
        classfile.constant_pool[5]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 22
        }),
        classfile.constant_pool[6]);
    assert_utf8_tag(
        "<init>",
        &classfile.constant_pool[7]);
    assert_utf8_tag(
        "()V",
        &classfile.constant_pool[8]);
    assert_utf8_tag(
        "Code",
        &classfile.constant_pool[9]);
    assert_utf8_tag(
        "LineNumberTable",
        &classfile.constant_pool[10]);
    assert_utf8_tag(
        "main",
        &classfile.constant_pool[11]);
    assert_utf8_tag(
        "([Ljava/lang/String;)V",
        &classfile.constant_pool[12]);
    assert_utf8_tag(
        "SourceFile",
        &classfile.constant_pool[13]);
    assert_utf8_tag(
        "HelloWorld.java",
        &classfile.constant_pool[14]);
    assert_eq!(
        Tag::NameAndType {
            name_index: 7,
            descriptor_index: 8
        },
        classfile.constant_pool[15]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 23
        }),
        classfile.constant_pool[16]);
    assert_eq!(
        Tag::NameAndType {
            name_index: 24,
            descriptor_index: 25
        },
        classfile.constant_pool[17]);
    assert_utf8_tag(
        "Hello World!",
        &classfile.constant_pool[18]);
    assert_eq!(
        Tag::Class(cp::ClassTag {
            name_index: 26
        }),
        classfile.constant_pool[19]);
    assert_eq!(
        Tag::NameAndType {
            name_index: 27,
            descriptor_index: 28
        },
        classfile.constant_pool[20]);
    assert_utf8_tag(
        "io/hcf/frappe/HelloWorld",
        &classfile.constant_pool[21]);
    assert_utf8_tag(
        "java/lang/Object",
        &classfile.constant_pool[22]);
    assert_utf8_tag(
        "java/lang/System",
        &classfile.constant_pool[23]);
    assert_utf8_tag(
        "out",
        &classfile.constant_pool[24]);
    assert_utf8_tag(
        "Ljava/io/PrintStream;",
        &classfile.constant_pool[25]);
    assert_utf8_tag(
        "java/io/PrintStream",
        &classfile.constant_pool[26]);
    assert_utf8_tag(
        "println",
        &classfile.constant_pool[27]);
    assert_utf8_tag( "(Ljava/lang/String;)V",
        &classfile.constant_pool[28]);

    assert_eq!(0, classfile.fields.len());
    assert_eq!(0, classfile.fields.len());

    // 2 methods
    assert_eq!(2, classfile.methods.len());
    // ctor
    let ctor_info = &classfile.methods[0];
    assert_eq!(method::ACC_PUBLIC,
               ctor_info.access_flags);
    assert_utf8_tag(
        "<init>",
        &classfile.constant_pool[ctor_info.name_index]);
    assert_utf8_tag(
        "()V",
        &classfile.constant_pool[ctor_info.descriptor_index]);
    // main
    let main_info = &classfile.methods[1];
    assert_eq!(method::ACC_STATIC |
               method::ACC_PUBLIC,
               main_info.access_flags);
    assert_utf8_tag(
        "main",
        &classfile.constant_pool[main_info.name_index]);
    assert_utf8_tag(
        "([Ljava/lang/String;)V",
        &classfile.constant_pool[main_info.descriptor_index]);

    // 1 attribute
    assert_eq!(1, classfile.attributes.len());
}

#[test]
fn should_load_point_class() {
    // Given
    let mut file = File::open("test-classes/Point.class").unwrap();

    // When
    let class = ClassFile::read(&mut file).unwrap();

    // Then
    assert_eq!(2, class.fields.len());
    assert_eq!(classfile::ACC_PUBLIC |
               classfile::ACC_SUPER,
               class.access_flags);
    let this_class_desc = &class.constant_pool[class.this_class().name_index];
    assert_utf8_tag("io/hcf/frappe/Point", &this_class_desc);
    let super_class_desc = &class.constant_pool[class.super_class().unwrap().name_index];
    assert_utf8_tag("java/lang/Object", &super_class_desc);

    let x_field = &class.fields[0];
    assert_utf8_tag("x", &class.constant_pool[x_field.name_index]);
    assert_eq!(field::ACC_PRIVATE, x_field.access_flags);
    assert_utf8_tag("I", &class.constant_pool[x_field.descriptor_index]);

    let y_field = &class.fields[1];
    assert_utf8_tag("y", &class.constant_pool[y_field.name_index]);
    assert_eq!(field::ACC_PRIVATE, y_field.access_flags);
    assert_utf8_tag("I", &class.constant_pool[y_field.descriptor_index]);
}

fn assert_utf8_tag(value: &str, tag: &Tag) {
    let expected_tag = Tag::Utf8(value.into());
    assert_eq!(expected_tag, *tag);
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Error as FmtError;
use std::fmt::{Display, Formatter};
use std::convert::Into;

/**
 * Kind of error
 */
#[derive(Clone, Copy, Debug)]
pub enum HookErrorKind {
    HookExecutionError,
    AccessTypeViolation,
}

pub trait IntoHookError {
    fn into_hookerror(self) -> HookError;
    fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
}

impl Into<HookError> for HookErrorKind {

    fn into(self) -> HookError {
        HookError::new(self, None)
    }

}

impl Into<HookError> for (HookErrorKind, Box<Error>) {

    fn into(self) -> HookError {
        HookError::new(self.0, Some(self.1))
    }

}

fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
    match *e {
        HookErrorKind::HookExecutionError  => "Hook exec error",
        HookErrorKind::AccessTypeViolation => "Hook access type violation",
    }
}

impl Display for HookErrorKind {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "{}", hook_error_type_as_str(self)));
        Ok(())
    }

}

/**
 * Error type
 */
#[derive(Debug)]
pub struct HookError {
    err_type: HookErrorKind,
    cause: Option<Box<Error>>,
}

impl HookError {

    /**
     * Build a new HookError from an HookErrorKind, optionally with cause
     */
    pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
        -> HookError
        {
            HookError {
                err_type: errtype,
                cause: cause,
            }
        }

    /**
     * Get the error type of this HookError
     */
    pub fn err_type(&self) -> HookErrorKind {
        self.err_type
    }

}

impl Display for HookError {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type)));
        Ok(())
    }

}

impl Error for HookError {

    fn description(&self) -> &str {
        hook_error_type_as_str(&self.err_type)
    }

    fn cause(&self) -> Option<&Error> {
        self.cause.as_ref().map(|e| &**e)
    }

}

<commit_msg>Replace error code with macro for code generation<commit_after>use std::error::Error;
use std::fmt::Error as FmtError;
use std::fmt::{Display, Formatter};
use std::convert::Into;

generate_error_types!(HookError, HookErrorKind,
    HookExecutionError  => "Hook exec error",
    AccessTypeViolation => "Hook access type violation"
);

pub trait IntoHookError {
    fn into_hookerror(self) -> HookError;
    fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
}

impl Into<HookError> for HookErrorKind {

    fn into(self) -> HookError {
        HookError::new(self, None)
    }

}

impl Into<HookError> for (HookErrorKind, Box<Error>) {

    fn into(self) -> HookError {
        HookError::new(self.0, Some(self.1))
    }

}

<|endoftext|>