<commit_before>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) |
            Err(Ignored(s)) |
            Err(Mismatch(s)) |
            Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, ref_mode: RefMode) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let ok = |s: &str| Ok(s.into());
    let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => if ref_mode.is_ref() { ok("str") } else { ok("String") },
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    }
                    else {
                        ok("std::path::PathBuf")
                    }
                }
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode)
                .map_any(|_| alias.name.clone())
        }
        Record(library::Record { ref c_type, .. }) if c_type == "GVariantType" => {
            if ref_mode.is_ref() { ok("VariantTy") } else { ok("VariantType") }
        }
        Enumeration(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            }
            else {
                Ok(name)
            }
        }
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) |
                    Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| format!("{}::{}",
            env.namespaces[type_id.ns_id].higher_crate_name, s));
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => rust_type = rust_type.map_any(|s| format!("&{}", s)),
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer
                | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
                           direction: library::ParameterDirection, nullable: Nullable,
                           ref_mode: RefMode) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
                }
            } else {
                rust_type.map_any(|s| format_parameter(s, direction))
            }
        }
        Alias(ref alias) => {
            rust_type.and_then(|s| {
                parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                    .map_any(|_| s)
            })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) |
            Union(..) |
            Bitfield(..) => rust_type.map_any(|s| format_parameter(s, direction)),

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Out |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) |
            SList(..) |
            CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

//TODO: remove
fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    match &*type_id.full_name(library) {
        "GLib.Error" => return true,
        _ => (),
    }
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        _ => false,
    }
}
<commit_msg>Add long and short types<commit_after>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) |
            Err(Ignored(s)) |
            Err(Mismatch(s)) |
            Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, ref_mode: RefMode) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let ok = |s: &str| Ok(s.into());
    let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Short => ok("libc::c_short"),   //depends of target system
                UShort => ok("libc::c_ushort"), //depends of target system
                Long => ok("libc::c_long"),     //depends of target system
                ULong => ok("libc::c_ulong"),   //depends of target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => if ref_mode.is_ref() { ok("str") } else { ok("String") },
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    }
                    else {
                        ok("std::path::PathBuf")
                    }
                }
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode)
                .map_any(|_| alias.name.clone())
        }
        Record(library::Record { ref c_type, .. }) if c_type == "GVariantType" => {
            if ref_mode.is_ref() { ok("VariantTy") } else { ok("VariantType") }
        }
        Enumeration(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            }
            else {
                Ok(name)
            }
        }
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) |
                    Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| format!("{}::{}",
            env.namespaces[type_id.ns_id].higher_crate_name, s));
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => rust_type = rust_type.map_any(|s| format!("&{}", s)),
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer
                | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Fundamental(library::Fundamental::Short) |
            Fundamental(library::Fundamental::UShort) |
            Fundamental(library::Fundamental::Long) |
            Fundamental(library::Fundamental::ULong) |
            Fundamental(library::Fundamental::Filename) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
                           direction: library::ParameterDirection, nullable: Nullable,
                           ref_mode: RefMode) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
                }
            } else {
                rust_type.map_any(|s| format_parameter(s, direction))
            }
        }
        Alias(ref alias) => {
            rust_type.and_then(|s| {
                parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                    .map_any(|_| s)
            })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) |
            Union(..) |
            Bitfield(..) => rust_type.map_any(|s| format_parameter(s, direction)),

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Out |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) |
            SList(..) |
            CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

//TODO: remove
fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    match &*type_id.full_name(library) {
        "GLib.Error" => return true,
        _ => (),
    }
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        _ => false,
    }
}
<|endoftext|>


<commit_before>extern crate centralgithook;
extern crate fern;
extern crate git2;
extern crate regex;
extern crate tempdir;

#[macro_use]
extern crate log;

use centralgithook::ModuleToSubdir;
use centralgithook::Scratch;
use centralgithook::Shell;
use centralgithook::module_ref;
use centralgithook::module_ref_root;
use git2::Oid;
use regex::Regex;
use std::env::current_exe;
use std::env;
use std::fs::File;
use std::io::Read;
use std::os::unix::fs::symlink;
use std::path::Path;
use std::process::Command;
use std::process::Stdio;
use std::process::exit;
use tempdir::TempDir;

fn main()
{
    exit(main_ret())
}

fn setup_tmp_repo(td: &Path, scratch_dir: &Path, view: Option<&str>)
{
    let root = match view {
        Some(view) => module_ref_root(&view),
        None => "refs".to_string(),
    };

    debug!("setup_tmp_repo, root: {:?}", &root);
    let shell = Shell { cwd: td.to_path_buf() };

    let ce = current_exe().expect("can't find path to exe");
    shell.command("mkdir hooks");
    symlink(ce, td.join("hooks").join("update")).expect("can't symlink update hook");

    shell.command(&format!("cp {:?} {:?}", scratch_dir.join("HEAD"), td));
    symlink(scratch_dir.join(root), td.join("refs")).expect("can't symlink refs");
    symlink(scratch_dir.join("objects"), td.join("objects")).expect("can't symlink objects");

    shell.command(&format!("printf {} > view",
                           match view {
                               Some(view) => view,
                               None => ".",
                           }));
}

fn cg_command(subcommand: &str) -> i32
{
    // let subcommand = format!("{}", subcommand[0]);
    debug!("Command cg {:?}", &subcommand);
    if subcommand == "status" {
        println!("centralgit OK");
        return 0;
    }
    if subcommand == "log" {

        let _ = if let Ok(status) = Command::new("cat")
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .arg("/tmp/centralgit.log")
            .status() {
            debug!("call ok");
            return status.code().unwrap();
        }
        else {
            debug!("failed to call cat /tmp/centralgit.log");
            return 1;
        };
    }
    return 0;
}

fn make_argvec(args: &str) -> Vec<String>
{
    let mut argvec = vec![];
    for c in args.split_whitespace() {
        argvec.push(format!("{}", c).trim_matches('\'').to_string());
    }
    return argvec;
}

fn call_git(command: &str, td: &Path, args: &str) -> i32
{
    let re_repo = Regex::new(r"(?P<repo>/.*[.]git\S*)").expect("can't compile regex");
    let rewritten_args = re_repo.replace_all(args, format!("{:?}", td).trim_matches('"'));

    return if let Ok(status) = Command::new(command)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .args(&make_argvec(&rewritten_args))
        .status() {
        debug!("call ok");
        status.code().unwrap()
    }
    else {
        debug!("failed to call git");
        1
    };
}

fn git_command(command: &str, args: &str) -> i32
{
    let td = TempDir::new("centralgit").expect("failed to create tempdir");

    let scratch_dir = Path::new("/tmp").join("centralgit_central");
    let scratch = Scratch::new(&scratch_dir);

    let re_view = Regex::new(r".*'.*[.]git/(?P<view>\S+)'").expect("can't compile regex");
    if let Some(caps) = re_view.captures(&args) {
        let view = caps.name("view").unwrap();
        let master = scratch.repo.refname_to_id("refs/heads/master").expect("no ref: master");
        let subdir_commit = scratch.split_subdir(&view, master).expect("can't split subdir");
        scratch.repo
            .reference(&module_ref(&view, "master"),
                       subdir_commit,
                       true,
                       "subtree_split")
            .expect("can't create reference");

        setup_tmp_repo(&td.path(), &scratch_dir, Some(view));
    }
    else {
        setup_tmp_repo(&td.path(), &scratch_dir, None);
    };

    return call_git(command, td.path(), &args);
}

fn ssh_wrap(command: &str) -> i32
{
    debug!("\n\n############\nssh orig command {:?}", &command);

    let re_cg = Regex::new(r"cg (?P<subcommand>.*)").expect("can't compile regex");
    let re_git = Regex::new(r"(?P<gitcommand>git-\S*) (?P<args>.*)").expect("can't compile regex");

    if let Some(caps) = re_cg.captures(command) {
        let subcommand = caps.name("subcommand").unwrap();
        debug!("cg subcommand: {}", subcommand);
        return cg_command(subcommand);
    }
    if let Some(caps) = re_git.captures(command) {
        let gitcommand = caps.name("gitcommand").unwrap();
        let args = caps.name("args").unwrap();
        return git_command(gitcommand, args);
    }
    return 1;
}

fn update_hook(old: &str, new: &str) -> i32
{
    let scratch_dir = Path::new("/tmp").join("centralgit_central");
    let scratch = Scratch::new(&scratch_dir);

    let mut s = String::new();
    File::open(&Path::new("view"))
        .unwrap()
        .read_to_string(&mut s)
        .expect("could not read view name");

    if s.starts_with(".") {
        return 0;
    }

    let central_head = scratch.repo.refname_to_id("refs/heads/master").expect("no ref: master");

    match scratch.module_to_subdir(central_head,
                                   Some(Path::new(&s)),
                                   Oid::from_str(old).ok(),
                                   Oid::from_str(new).expect("can't parse new OID")) {

        ModuleToSubdir::Done(rewritten, _) => {
            scratch.repo
                .reference("refs/heads/master", rewritten, true, "module_to_subdir")
                .expect("can't create master reference");
        }
        _ => {}
    };

    return 0;
}

fn main_ret() -> i32
{
    let logfilename = Path::new("/tmp/centralgit.log");
    let logger_config = fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            format!("[{}] {}", level, msg)
        }),
        output: vec![fern::OutputConfig::file(logfilename)],
        level: log::LogLevelFilter::Trace,
    };
    fern::init_global_logger(logger_config, log::LogLevelFilter::Trace).expect("can't init logger");

    let mut args = vec![];
    for arg in env::args() {
        args.push(arg);
    }
    debug!("args: {:?}", args);

    if args[0].ends_with("/update") {
        debug!("================= HOOK {:?}", args);
        return update_hook(&args[2], &args[3]);
    }

    if let Ok(command) = env::var("SSH_ORIGINAL_COMMAND") {
        return ssh_wrap(&command);
    }

    return 1;
}
<commit_msg>Remove hardcoding of "master" branch<commit_after>extern crate centralgithook;
extern crate fern;
extern crate git2;
extern crate regex;
extern crate tempdir;

#[macro_use]
extern crate log;

use centralgithook::ModuleToSubdir;
use centralgithook::Scratch;
use centralgithook::Shell;
use centralgithook::module_ref;
use centralgithook::module_ref_root;
use git2::Oid;
use regex::Regex;
use std::env::current_exe;
use std::env;
use std::fs::File;
use std::io::Read;
use std::os::unix::fs::symlink;
use std::path::Path;
use std::process::Command;
use std::process::Stdio;
use std::process::exit;
use tempdir::TempDir;

fn main()
{
    exit(main_ret())
}

fn setup_tmp_repo(td: &Path, scratch_dir: &Path, view: Option<&str>)
{
    let root = match view {
        Some(view) => module_ref_root(&view),
        None => "refs".to_string(),
    };

    debug!("setup_tmp_repo, root: {:?}", &root);
    let shell = Shell { cwd: td.to_path_buf() };

    let ce = current_exe().expect("can't find path to exe");
    shell.command("mkdir hooks");
    symlink(ce, td.join("hooks").join("update")).expect("can't symlink update hook");

    shell.command(&format!("cp {:?} {:?}", scratch_dir.join("HEAD"), td));
    symlink(scratch_dir.join(root), td.join("refs")).expect("can't symlink refs");
    symlink(scratch_dir.join("objects"), td.join("objects")).expect("can't symlink objects");

    shell.command(&format!("printf {} > view",
                           match view {
                               Some(view) => view,
                               None => ".",
                           }));
}

fn cg_command(subcommand: &str) -> i32
{
    // let subcommand = format!("{}", subcommand[0]);
    debug!("Command cg {:?}", &subcommand);
    if subcommand == "status" {
        println!("centralgit OK");
        return 0;
    }
    if subcommand == "log" {

        let _ = if let Ok(status) = Command::new("cat")
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .arg("/tmp/centralgit.log")
            .status() {
            debug!("call ok");
            return status.code().unwrap();
        }
        else {
            debug!("failed to call cat /tmp/centralgit.log");
            return 1;
        };
    }
    return 0;
}

fn make_argvec(args: &str) -> Vec<String>
{
    let mut argvec = vec![];
    for c in args.split_whitespace() {
        argvec.push(format!("{}", c).trim_matches('\'').to_string());
    }
    return argvec;
}

fn call_git(command: &str, td: &Path, args: &str) -> i32
{
    let re_repo = Regex::new(r"(?P<repo>/.*[.]git\S*)").expect("can't compile regex");
    let rewritten_args = re_repo.replace_all(args, format!("{:?}", td).trim_matches('"'));

    return if let Ok(status) = Command::new(command)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .args(&make_argvec(&rewritten_args))
        .status() {
        debug!("call ok");
        status.code().unwrap()
    }
    else {
        debug!("failed to call git");
        1
    };
}

fn git_command(command: &str, args: &str) -> i32
{
    let td = TempDir::new("centralgit").expect("failed to create tempdir");

    let scratch_dir = Path::new("/tmp").join("centralgit_central");
    let scratch = Scratch::new(&scratch_dir);

    let re_view = Regex::new(r".*'.*[.]git/(?P<view>\S+)'").expect("can't compile regex");
    if let Some(caps) = re_view.captures(&args) {
        for branch in scratch.repo.branches(None).unwrap() {
            if let Ok((branch, _)) = branch {
                let branchname = branch.name().unwrap().unwrap().to_string();
                let branchrefname = branch.into_reference().name().unwrap().to_string();
                let view = caps.name("view").unwrap();
                let r = scratch.repo.refname_to_id(&branchrefname).expect("no ref");
                let subdir_commit = scratch.split_subdir(&view, r).expect("can't split subdir");
                scratch.repo
                    .reference(&module_ref(&view, &branchname),
                               subdir_commit,
                               true,
                               "subtree_split")
                    .expect("can't create reference");

                setup_tmp_repo(&td.path(), &scratch_dir, Some(view));
            };
        }
    }
    else {
        setup_tmp_repo(&td.path(), &scratch_dir, None);
    };

    return call_git(command, td.path(), &args);
}

fn ssh_wrap(command: &str) -> i32
{
    debug!("\n\n############\nssh orig command {:?}", &command);

    let re_cg = Regex::new(r"cg (?P<subcommand>.*)").expect("can't compile regex");
    let re_git = Regex::new(r"(?P<gitcommand>git-\S*) (?P<args>.*)").expect("can't compile regex");

    if let Some(caps) = re_cg.captures(command) {
        let subcommand = caps.name("subcommand").unwrap();
        debug!("cg subcommand: {}", subcommand);
        return cg_command(subcommand);
    }
    if let Some(caps) = re_git.captures(command) {
        let gitcommand = caps.name("gitcommand").unwrap();
        let args = caps.name("args").unwrap();
        return git_command(gitcommand, args);
    }
    return 1;
}

fn update_hook(refname: &str, old: &str, new: &str) -> i32
{
    let scratch_dir = Path::new("/tmp").join("centralgit_central");
    let scratch = Scratch::new(&scratch_dir);

    let mut s = String::new();
    File::open(&Path::new("view"))
        .unwrap()
        .read_to_string(&mut s)
        .expect("could not read view name");

    if s.starts_with(".") {
        return 0;
    }

    let central_head = scratch.repo.refname_to_id(&refname).expect("no ref: master");

    match scratch.module_to_subdir(central_head,
                                   Some(Path::new(&s)),
                                   Oid::from_str(old).ok(),
                                   Oid::from_str(new).expect("can't parse new OID")) {

        ModuleToSubdir::Done(rewritten, _) => {
            scratch.repo
                .reference(&refname, rewritten, true, "module_to_subdir")
                .expect("can't create master reference");
        }
        _ => {}
    };

    return 0;
}

fn main_ret() -> i32
{
    let logfilename = Path::new("/tmp/centralgit.log");
    let logger_config = fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            format!("[{}] {}", level, msg)
        }),
        output: vec![fern::OutputConfig::file(logfilename)],
        level: log::LogLevelFilter::Trace,
    };
    fern::init_global_logger(logger_config, log::LogLevelFilter::Trace).expect("can't init logger");

    let mut args = vec![];
    for arg in env::args() {
        args.push(arg);
    }
    debug!("args: {:?}", args);

    if args[0].ends_with("/update") {
        debug!("================= HOOK {:?}", args);
        return update_hook(&args[1], &args[2], &args[3]);
    }

    if let Ok(command) = env::var("SSH_ORIGINAL_COMMAND") {
        return ssh_wrap(&command);
    }

    return 1;
}
<|endoftext|>


<commit_before>//! This module contains some useful constants.

#![deny(clippy::missing_docs_in_private_items)]

/// List of the built-in types names.
///
/// See also [the reference][reference-types] for a list of such types.
///
/// [reference-types]: https://doc.rust-lang.org/reference/types.html
pub const BUILTIN_TYPES: &[&str] = &[
    "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "isize", "usize", "f32", "f64", "bool",
    "str", "char",
];
<commit_msg>Remove file accidentally re-added during rebase<commit_after><|endoftext|>


<commit_before>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.

use std::net::{UdpSocket,TcpListener};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::sync::{Arc, Mutex};

extern crate time;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::HeartbeatPacket;

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let j : Vec<_> = env::args().collect();
        j[1].to_string()
    }
    else if count == 1 {
        "0.0.0.0".to_string()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = match UdpSocket::bind((&ip as &str,27900)) {
        Err(_) => {
            println!("Error creating a UDP socket at {}:27900.",ip);
            return;
        },
        Ok(halo_socket) => halo_socket
    };

    let client_socket = match TcpListener::bind((&ip as &str,29920)) {
        Err(_) => {
            println!("Error listening to TCP at {}:29920.",ip);
            return;
        },
        Ok(client_socket) => client_socket
    };

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    thread::spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = time::now().to_timespec().sec;
            servers.retain(|x| x.last_alive + DROP_TIME > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist: Vec<String> = Vec::new();
    let blacklist_update = Arc::new(Mutex::new(blacklist));
    let blacklist_udp = blacklist_update.clone();

    // Blacklist read thread.
    thread::spawn(move || {
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_new : Vec<String> = Vec::new();
                match File::open(BLACKLIST_FILE) {
                    Ok(file) => {
                        let reader = BufReader::new(&file);
                        for line_possibly in reader.lines() {
                            match line_possibly {
                                Err(_) => break,
                                Ok(line) => {
                                    blacklist_new.push(line);
                                }
                            }
                        }
                    },
                    Err(_) => {}
                };

                let mut blacklist_ref = blacklist_update.lock().unwrap();
                blacklist_ref.clear();

                for b in blacklist_new {
                    blacklist_ref.push(b.clone());
                }
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    thread::spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = match stream {
                    Err(_) => continue,
                    Ok(the_stream) => the_stream
                };
                // Unwrap the IP.
                let ip = match client.peer_addr() {
                    Err(_) => continue,
                    Ok(ip) => ip.to_string().split(":").next().unwrap().to_string()
                };

                let mut ips = String::new();

                let servers_ref = servers_mut_tcp.lock().unwrap();
                let servers = (*servers_ref).iter();

                for j in servers {
                    ips = ips + &(j.to_string()) + "\n";
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips = ips + &ip + ":49149:3425";
                let _ = client.write_all(ips.as_bytes());
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    // These are the allowed game versions. HaloMD and Halo PC 1.09 uses 01.00.09.0620, while Halo PC servers on 1.10 use 01.00.10.0621 (these are also joinable).
    let game_versions = [ "01.00.09.0620".to_string(), "01.00.10.0621".to_string() ];

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = match halo_socket.recv_from(&mut buffer) {
            Err(_) => continue,
            Ok((length, source)) => (length,source)
        };
        if length == 0 {
            continue;
        }
        let client_ip = source.to_string().split(":").next().unwrap().to_string();

        if buffer[0] == 3 || buffer[0] == 8 {
            let blacklist_ref = blacklist_udp.lock().unwrap();
            if blacklist_ref.contains(&client_ip) {
                continue;
            }

            // Heartbeat packet.
            if buffer[0] == 3 {

                let mut servers = servers_mut_udp.lock().unwrap();

                let packet = HeartbeatPacket::from_buffer(&buffer, length);

                if packet.localport != 0 && length > 5 && game_versions.contains(&packet.gamever) {
                    let updatetime = time::now().to_timespec().sec;
                    match servers.iter_mut().position(|x| x.ip == client_ip.clone() && x.port == packet.localport) {
                        None => {
                            let serverness = HaloServer { ip:client_ip.clone(), port: packet.localport, last_alive: updatetime };
                            (*servers).push(serverness);
                            continue;
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == 2 {
                                servers.remove(k);
                            }
                        }
                    };
                }
            }

            // Keepalive packet. We need to rely on the origin's port for this, unfortunately.
            else if buffer[0] == 8 {
                let mut servers_ref = servers_mut_udp.lock().unwrap();
                let servers = (*servers_ref).iter_mut();

                for i in servers {
                    if i.ip == client_ip && i.port == source.port() {
                        i.last_alive = time::now().to_timespec().sec;
                        break;
                    }
                }
            }
        }
    }
}
<commit_msg>Read blacklist from a vector instead of one line at a time<commit_after>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.

use std::net::{UdpSocket,TcpListener};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::sync::{Arc, Mutex};

extern crate time;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::HeartbeatPacket;

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let j : Vec<_> = env::args().collect();
        j[1].to_string()
    }
    else if count == 1 {
        "0.0.0.0".to_string()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = match UdpSocket::bind((&ip as &str,27900)) {
        Err(_) => {
            println!("Error creating a UDP socket at {}:27900.",ip);
            return;
        },
        Ok(halo_socket) => halo_socket
    };

    let client_socket = match TcpListener::bind((&ip as &str,29920)) {
        Err(_) => {
            println!("Error listening to TCP at {}:29920.",ip);
            return;
        },
        Ok(client_socket) => client_socket
    };

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    thread::spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = time::now().to_timespec().sec;
            servers.retain(|x| x.last_alive + DROP_TIME > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist: Vec<String> = Vec::new();
    let blacklist_update = Arc::new(Mutex::new(blacklist));
    let blacklist_udp = blacklist_update.clone();

    // Blacklist read thread.
    thread::spawn(move || {
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                match File::open(BLACKLIST_FILE) {
                    Ok(file) => {
                        let reader = BufReader::new(&file);
                        match reader.lines().collect() {
                            Err(_) => {
                                blacklist_ref.clear();
                            },
                            Ok(t) => {
                                *blacklist_ref = t;
                            }
                        }
                    },
                    Err(_) => {}
                };
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    thread::spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = match stream {
                    Err(_) => continue,
                    Ok(the_stream) => the_stream
                };
                // Unwrap the IP.
                let ip = match client.peer_addr() {
                    Err(_) => continue,
                    Ok(ip) => ip.to_string().split(":").next().unwrap().to_string()
                };

                let mut ips = String::new();

                let servers_ref = servers_mut_tcp.lock().unwrap();
                let servers = (*servers_ref).iter();

                for j in servers {
                    ips = ips + &(j.to_string()) + "\n";
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips = ips + &ip + ":49149:3425";
                let _ = client.write_all(ips.as_bytes());
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    // These are the allowed game versions. HaloMD and Halo PC 1.09 uses 01.00.09.0620, while Halo PC servers on 1.10 use 01.00.10.0621 (these are also joinable).
    let game_versions = [ "01.00.09.0620".to_string(), "01.00.10.0621".to_string() ];

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = match halo_socket.recv_from(&mut buffer) {
            Err(_) => continue,
            Ok((length, source)) => (length,source)
        };
        if length == 0 {
            continue;
        }
        let client_ip = source.to_string().split(":").next().unwrap().to_string();

        if buffer[0] == 3 || buffer[0] == 8 {
            let blacklist_ref = blacklist_udp.lock().unwrap();
            if blacklist_ref.contains(&client_ip) {
                continue;
            }

            // Heartbeat packet.
            if buffer[0] == 3 {

                let mut servers = servers_mut_udp.lock().unwrap();

                let packet = HeartbeatPacket::from_buffer(&buffer, length);

                if packet.localport != 0 && length > 5 && game_versions.contains(&packet.gamever) {
                    let updatetime = time::now().to_timespec().sec;
                    match servers.iter_mut().position(|x| x.ip == client_ip.clone() && x.port == packet.localport) {
                        None => {
                            let serverness = HaloServer { ip:client_ip.clone(), port: packet.localport, last_alive: updatetime };
                            (*servers).push(serverness);
                            continue;
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == 2 {
                                servers.remove(k);
                            }
                        }
                    };
                }
            }

            // Keepalive packet. We need to rely on the origin's port for this, unfortunately.
            else if buffer[0] == 8 {
                let mut servers_ref = servers_mut_udp.lock().unwrap();
                let servers = (*servers_ref).iter_mut();

                for i in servers {
                    if i.ip == client_ip && i.port == source.port() {
                        i.last_alive = time::now().to_timespec().sec;
                        break;
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![crate_name = "mentat_cli"]

#[macro_use] extern crate log;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate error_chain;

extern crate combine;
extern crate env_logger;
extern crate getopts;
extern crate linefeed;
extern crate rusqlite;
extern crate tabwriter;
extern crate termion;
extern crate time;

extern crate mentat;
extern crate edn;
extern crate mentat_query;
extern crate mentat_core;
extern crate mentat_db;

use getopts::Options;

use termion::{
    color,
};

static BLUE: color::Rgb = color::Rgb(0x99, 0xaa, 0xFF);
static GREEN: color::Rgb = color::Rgb(0x77, 0xFF, 0x99);

pub mod command_parser;
pub mod input;
pub mod repl;
pub mod errors;

pub fn run() -> i32 {
    env_logger::init();

    let args = std::env::args().collect::<Vec<_>>();
    let mut opts = Options::new();

    opts.optopt("d", "", "The path to a database to open", "DATABASE");
    if cfg!(feature = "sqlcipher") {
        opts.optopt("k", "key", "The key to use to open the database (only available when using sqlcipher)", "KEY");
    }
    opts.optflag("h", "help", "Print this help message and exit");
    opts.optmulti("q", "query", "Execute a query on startup. Queries are executed after any transacts.", "QUERY");
    opts.optmulti("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT");
    opts.optmulti("i", "import", "Execute an import on startup. Imports are executed before queries.", "PATH");
    opts.optflag("v", "version", "Print version and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => {
            println!("{}: {}", args[0], e);
            return 1;
        }
    };

    if matches.opt_present("version") {
        print_version();
        return 0;
    }

    if matches.opt_present("help") {
        print_usage(&args[0], &opts);
        return 0;
    }

    // It's still possible to pass this in even if it's not a documented flag above.
    let key = matches.opt_str("key");
    if key.is_some() && !cfg!(feature = "sqlcipher") {
        eprintln!("Decryption key provided, but this build does not have sqlcipher support");
        return 1;
    }

    let mut last_arg: Option<&str> = None;

    let cmds:Vec<command_parser::Command> = args.iter().filter_map(|arg| {
        match last_arg {
            Some("-d") => {
                last_arg = None;
                if let &Some(ref k) = &key {
                    Some(command_parser::Command::OpenEncrypted(arg.clone(), k.clone()))
                } else {
                    Some(command_parser::Command::Open(arg.clone()))
                }
            },
            Some("-q") => {
                last_arg = None;
                Some(command_parser::Command::Query(arg.clone()))
            },
            Some("-i") => {
                last_arg = None;
                Some(command_parser::Command::Import(arg.clone()))
            },
            Some("-t") => {
                last_arg = None;
                Some(command_parser::Command::Transact(arg.clone()))
            },
            Some(_) |
            None => {
                last_arg = Some(&arg);
                None
            },
        }
    }).collect();

    let repl = repl::Repl::new();
    if repl.is_ok() {
        repl.unwrap().run(Some(cmds));

    } else {
        println!("{}", repl.err().unwrap());
    }

    0
}

/// Returns a version string.
pub fn version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

fn print_usage(arg0: &str, opts: &Options) {
    print!("{}", opts.usage(&format!(
        "Usage: {} [OPTIONS] [FILE]", arg0)));
}

fn print_version() {
    println!("mentat {}", version());
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Don't try to match "key" when not using sqlcipher. (#752, #753) r=nalexander<commit_after>// Copyright 2017 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![crate_name = "mentat_cli"]

#[macro_use] extern crate log;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate error_chain;

extern crate combine;
extern crate env_logger;
extern crate getopts;
extern crate linefeed;
extern crate rusqlite;
extern crate tabwriter;
extern crate termion;
extern crate time;

extern crate mentat;
extern crate edn;
extern crate mentat_query;
extern crate mentat_core;
extern crate mentat_db;

use getopts::Options;

use termion::{
    color,
};

static BLUE: color::Rgb = color::Rgb(0x99, 0xaa, 0xFF);
static GREEN: color::Rgb = color::Rgb(0x77, 0xFF, 0x99);

pub mod command_parser;
pub mod input;
pub mod repl;
pub mod errors;

pub fn run() -> i32 {
    env_logger::init();

    let args = std::env::args().collect::<Vec<_>>();
    let mut opts = Options::new();

    opts.optopt("d", "", "The path to a database to open", "DATABASE");
    if cfg!(feature = "sqlcipher") {
        opts.optopt("k", "key", "The key to use to open the database (only available when using sqlcipher)", "KEY");
    }
    opts.optflag("h", "help", "Print this help message and exit");
    opts.optmulti("q", "query", "Execute a query on startup. Queries are executed after any transacts.", "QUERY");
    opts.optmulti("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT");
    opts.optmulti("i", "import", "Execute an import on startup. Imports are executed before queries.", "PATH");
    opts.optflag("v", "version", "Print version and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => {
            println!("{}: {}", args[0], e);
            return 1;
        }
    };

    if matches.opt_present("version") {
        print_version();
        return 0;
    }

    if matches.opt_present("help") {
        print_usage(&args[0], &opts);
        return 0;
    }

    // It's still possible to pass this in even if it's not a documented flag above.
    let key = match cfg!(feature = "sqlcipher") {
        true => matches.opt_str("key"),
        false => None,
    };

    let mut last_arg: Option<&str> = None;

    let cmds:Vec<command_parser::Command> = args.iter().filter_map(|arg| {
        match last_arg {
            Some("-d") => {
                last_arg = None;
                if let &Some(ref k) = &key {
                    Some(command_parser::Command::OpenEncrypted(arg.clone(), k.clone()))
                } else {
                    Some(command_parser::Command::Open(arg.clone()))
                }
            },
            Some("-q") => {
                last_arg = None;
                Some(command_parser::Command::Query(arg.clone()))
            },
            Some("-i") => {
                last_arg = None;
                Some(command_parser::Command::Import(arg.clone()))
            },
            Some("-t") => {
                last_arg = None;
                Some(command_parser::Command::Transact(arg.clone()))
            },
            Some(_) |
            None => {
                last_arg = Some(&arg);
                None
            },
        }
    }).collect();

    let repl = repl::Repl::new();
    if repl.is_ok() {
        repl.unwrap().run(Some(cmds));

    } else {
        println!("{}", repl.err().unwrap());
    }

    0
}

/// Returns a version string.
pub fn version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

fn print_usage(arg0: &str, opts: &Options) {
    print!("{}", opts.usage(&format!(
        "Usage: {} [OPTIONS] [FILE]", arg0)));
}

fn print_version() {
    println!("mentat {}", version());
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>#![feature(nll)]

extern crate postgres;
extern crate getopts;

use postgres::{Connection, TlsMode};

use std::env;
use getopts::Options;
use std::process;

extern crate pombase;

use pombase::db::*;
use pombase::web::config::*;
use pombase::web::data_build::*;
use pombase::interpro::parse_interpro;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() -> Result<(), std::io::Error> {
    print!("{} v{}\n", PKG_NAME, VERSION);

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "print this help message");
    opts.optopt("c", "config-file", "Configuration file name", "CONFIG");
    opts.optopt("C", "doc-config-file",
                "Documentation configuration file name", "DOC_CONFIG");
    opts.optopt("p", "postgresql-connection-string",
                "PostgresSQL connection string like: postgres://user:pass@host/db_name",
                "CONN_STR");
    opts.optopt("i", "interpro-data-file",
                "The name of the file generated by 'pombase-interpro'", "FILE");
    opts.optopt("", "go-eco-mapping",
                "GO evidence code to ECO ID mapping from http://purl.obolibrary.org/obo/eco/gaf-eco-mapping.txt", "FILE");
    opts.optopt("d", "output-directory",
                "Destination directory for the output", "DIR");
    opts.optflag("j", "store-json",
                 "optionally create a 'web_json' schema to store the generated JSON in the database");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let program = args[0].clone();

    if matches.opt_present("help") {
        print_usage(&program, opts);
        process::exit(0);
    }

    if !matches.opt_present("config-file") {
        print!("no -c|--config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("doc-config-file") {
        print!("no --doc-config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("postgresql-connection-string") {
        print!("no -p|--postgresql-connection-string option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("go-eco-mapping") {
        print!("no --go-eco-mapping option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("output-directory") {
        print!("no -d|--output-directory option\n");
        print_usage(&program, opts);
        process::exit(1);
    }

    let config = Config::read(&matches.opt_str("c").unwrap());
    let doc_config = DocConfig::read(&matches.opt_str("C").unwrap());
    let connection_string = matches.opt_str("p").unwrap();
    let interpro_json = matches.opt_str("i").unwrap();
    let go_eco_mapping = GoEcoMapping::read(&matches.opt_str("go-eco-mapping").unwrap())?;
    let output_dir = matches.opt_str("d").unwrap();

    let conn = match Connection::connect(connection_string.as_str(), TlsMode::None) {
        Ok(conn) => conn,
        Err(err) => panic!("failed to connect using: {}, err: {}", connection_string, err)
    };

    let raw = Raw::new(&conn);
    let interpro_data = parse_interpro(&config, &interpro_json);
    let web_data_build = WebDataBuild::new(&raw, &interpro_data, &config);
    let web_data = web_data_build.get_web_data();

    match web_data.write(&config, &go_eco_mapping, &doc_config, &output_dir) {
        Ok(_) => (),
        Err(e) => {
            panic!("error while writing: {}", e);
        },
    }

    if matches.opt_present("store-json") {
        conn.execute("DROP SCHEMA IF EXISTS web_json CASCADE", &[])?;
        conn.execute("CREATE SCHEMA web_json", &[])?;
        conn.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;", &[])?;
        conn.execute("CREATE TABLE web_json.gene (uniquename TEXT, data JSONB)", &[])?;
        conn.execute("CREATE INDEX gene_uniquename_idx ON web_json.gene(uniquename)", &[])?;
        conn.execute("CREATE TABLE web_json.term (termid TEXT, data JSONB)", &[])?;
        conn.execute("CREATE INDEX term_termid_idx ON web_json.term(termid)", &[])?;
        conn.execute("CREATE TABLE web_json.reference (uniquename TEXT, data JSONB)", &[])?;
        conn.execute("CREATE INDEX reference_uniquename_idx on web_json.reference(uniquename)", &[])?;

        web_data.store_jsonb(&conn);

        print!("stored results as JSONB using {}\n", &connection_string);
    }

    Ok(())
}
<commit_msg>Update usage docs<commit_after>#![feature(nll)]

extern crate postgres;
extern crate getopts;

use postgres::{Connection, TlsMode};

use std::env;
use getopts::Options;
use std::process;

extern crate pombase;

use pombase::db::*;
use pombase::web::config::*;
use pombase::web::data_build::*;
use pombase::interpro::parse_interpro;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() -> Result<(), std::io::Error> {
    print!("{} v{}\n", PKG_NAME, VERSION);

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "print this help message");
    opts.optopt("c", "config-file", "Configuration file name", "CONFIG");
    opts.optopt("C", "doc-config-file",
                "Documentation configuration file name", "DOC_CONFIG");
    opts.optopt("p", "postgresql-connection-string",
                "PostgresSQL connection string like: postgres://user:pass@host/db_name",
                "CONN_STR");
    opts.optopt("i", "domain-data-file",
                "The name of the InterPro data file generated by 'pombase-domain-process'",
                "FILE");
    opts.optopt("", "go-eco-mapping",
                "GO evidence code to ECO ID mapping from http://purl.obolibrary.org/obo/eco/gaf-eco-mapping.txt", "FILE");
    opts.optopt("d", "output-directory",
                "Destination directory for the output", "DIR");
    opts.optflag("j", "store-json",
                 "optionally create a 'web_json' schema to store the generated JSON in the database");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let program = args[0].clone();

    if matches.opt_present("help") {
        print_usage(&program, opts);
        process::exit(0);
    }

    if !matches.opt_present("config-file") {
        print!("no -c|--config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("doc-config-file") {
        print!("no --doc-config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("postgresql-connection-string") {
        print!("no -p|--postgresql-connection-string option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("go-eco-mapping") {
        print!("no --go-eco-mapping option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("output-directory") {
        print!("no -d|--output-directory option\n");
        print_usage(&program, opts);
        process::exit(1);
    }

    let config = Config::read(&matches.opt_str("c").unwrap());
    let doc_config = DocConfig::read(&matches.opt_str("C").unwrap());
    let connection_string = matches.opt_str("p").unwrap();
    let interpro_json = matches.opt_str("i").unwrap();
    let go_eco_mapping = GoEcoMapping::read(&matches.opt_str("go-eco-mapping").unwrap())?;
    let output_dir = matches.opt_str("d").unwrap();

    let conn = match Connection::connect(connection_string.as_str(), TlsMode::None) {
        Ok(conn) => conn,
        Err(err) => panic!("failed to connect using: {}, err: {}", connection_string, err)
    };

    let raw = Raw::new(&conn);
    let interpro_data = parse_interpro(&config, &interpro_json);
    let web_data_build = WebDataBuild::new(&raw, &interpro_data, &config);
    let web_data = web_data_build.get_web_data();

    match web_data.write(&config, &go_eco_mapping, &doc_config, &output_dir) {
        Ok(_) => (),
        Err(e) => {
            panic!("error while writing: {}", e);
        },
    }

    if matches.opt_present("store-json") {
        conn.execute("DROP SCHEMA IF EXISTS web_json CASCADE", &[])?;
        conn.execute("CREATE SCHEMA web_json", &[])?;
        conn.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;", &[])?;
        conn.execute("CREATE TABLE web_json.gene (uniquename TEXT, data JSONB)", &[])?;
        conn.execute("CREATE INDEX gene_uniquename_idx ON web_json.gene(uniquename)", &[])?;
        conn.execute("CREATE TABLE web_json.term (termid TEXT, data JSONB)", &[])?;
        conn.execute("CREATE INDEX term_termid_idx ON web_json.term(termid)", &[])?;
        conn.execute("CREATE TABLE web_json.reference (uniquename TEXT, data JSONB)", &[])?;
        conn.execute("CREATE INDEX reference_uniquename_idx on web_json.reference(uniquename)", &[])?;

        web_data.store_jsonb(&conn);

        print!("stored results as JSONB using {}\n", &connection_string);
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::path::Path;

use async_trait::async_trait;
use indexmap::IndexMap;
use regex::Regex;
use serde::{Deserialize, Serialize};

use crate::{
    build::BuildStep,
    bundle::{layout::{ios::IOsKbdLayer, IOsTarget, IOsPlatform}, KbdgenBundle},
    util::split_keys,
};

#[derive(Serialize, Deserialize)]
pub struct IosInfo {
    name: String,
    locale: String,
    enter: String,
    space: String,
}

pub fn keyboard_entity_from_string(input: String) -> Option<IosButton> {
    let regex = Regex::new(r"\{(\w+):?([\d | \.]+)?\}").expect("valid regex");
    let captures = regex.captures(input.as_str());

    if let Some(captures) = captures {
        if let Some(id) = captures.get(1) {
            let id = format!("_{}", id.as_str());
            if let Some(width) = captures.get(2) {
                Some(IosButton {
                    id: id,
                    width: width.as_str().parse::<f32>().expect("invalid float value"),
                })
            } else {
                Some(IosButton {
                    id: id,
                    width: 1.0f32,
                })
            }
        } else {
            return None;
        }
    } else {
        return None;
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct IosButton {
    id: String,
    width: f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum IosKeyMapTypes {
    Character(String),
    Button(IosButton),
}

#[derive(Serialize, Deserialize)]
pub struct IosNormalLayer {
    #[serde(flatten)]
    layer: IndexMap<String, Vec<Vec<IosKeyMapTypes>>>,
}

#[derive(Serialize, Deserialize)]
pub struct IosKeyboardDefinitions {
    #[serde(flatten)]
    info: IosInfo,
    longpress: IndexMap<String, Vec<String>>,
    iphone: IosNormalLayer,
    i_pad_9in: IosNormalLayer,
    i_pad_12in: IosNormalLayer,
}

pub fn enum_to_output(layer: &IOsKbdLayer) -> String {
    match layer {
        IOsKbdLayer::Default => "normal",
        IOsKbdLayer::Shift => "shifted",
        IOsKbdLayer::Alt => "alt",
        IOsKbdLayer::AltAndShift => "alt+shift",
        IOsKbdLayer::Symbols1 => "symbols-1",
        IOsKbdLayer::Symbols2 => "symbols-2",
    }
    .to_string()
}

pub fn generate_platform(platform: &IOsPlatform) -> IndexMap<String, Vec<Vec<IosKeyMapTypes>>> {
    let mut layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();
    for (layer_name, layer_key_map) in &platform.layers {
        let layer_name = enum_to_output(layer_name);
        let key_map_rows: Vec<&str> =
            layer_key_map.trim().split("\n").map(|x| x.clone()).collect();
        let mut layer_rows: Vec<Vec<IosKeyMapTypes>> = Vec::new();
        for key_map in key_map_rows {
            let key_map = split_keys(key_map);
            let mut new_key_map: Vec<IosKeyMapTypes> = Vec::new();
            for key in key_map {
                if let Some(keyboard_entity) =
                    keyboard_entity_from_string(key.clone())
                {
                    new_key_map.push(IosKeyMapTypes::Button(keyboard_entity));
                } else {
                    new_key_map.push(IosKeyMapTypes::Character(key));
                }
            }
                layer_rows.push(new_key_map)
        }
        layers.insert(layer_name, layer_rows);
    }
    layers
}

pub struct GenerateIos;

#[async_trait(?Send)]
impl BuildStep for GenerateIos {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let keyboard_definitions_file_path = output_path.join("KeyboardDefinitions.json");

        for (language_tag, layout) in &bundle.layouts {
            let mut longpress: IndexMap<String, Vec<String>> = IndexMap::new();
            let mut iphone_layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();
            let mut i_pad_9in_layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();
            let mut i_pad_12in_layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();

            if let Some(ios_target) = &layout.i_os {
                if let Some(layout_longpress) = &layout.longpress {
                    for (key, value) in layout_longpress {
                        longpress.insert(key.clone(), value.clone());
                    }
                }

                if let Some(primary_platform) = &ios_target.primary {
                    iphone_layers.extend(generate_platform(&primary_platform));
                }
                if let Some(i_pad_9in_platform) = &ios_target.i_pad_9in {
                    i_pad_9in_layers.extend(generate_platform(&i_pad_9in_platform));
                }
                if let Some(i_pad_12in_platform) = &ios_target.i_pad_12in {
                    i_pad_12in_layers.extend(generate_platform(&i_pad_12in_platform));
                }

                if let Some(key_names) = &layout.key_names {
                    std::fs::write(
                        output_path.join(keyboard_definitions_file_path.clone()),
                        serde_json::to_string_pretty(&[IosKeyboardDefinitions {
                            info: IosInfo {
                                name: layout.display_names.get(language_tag).expect("can't evaluate language tag of layout").to_string(),
                                locale: language_tag.to_string(),
                                enter: key_names.r#return.to_string(),
                                space: key_names.space.to_string(),
                            },
                            longpress: longpress,
                            iphone: IosNormalLayer { layer: iphone_layers },
                            i_pad_9in: IosNormalLayer { layer: i_pad_9in_layers },
                            i_pad_12in: IosNormalLayer { layer: i_pad_12in_layers },
                        }])
                        .unwrap(),
                    )
                    .unwrap();
                }

            }
        }
    }
}
<commit_msg>fmt and new regex<commit_after>use std::path::Path;

use async_trait::async_trait;
use indexmap::IndexMap;
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_json;

use crate::{
    build::BuildStep,
    bundle::{
        layout::{ios::IOsKbdLayer, IOsPlatform},
        KbdgenBundle,
    },
    util::split_keys,
};

#[derive(Serialize, Deserialize)]
pub struct IosInfo {
    name: String,
    locale: String,
    enter: String,
    space: String,
}

pub fn keyboard_entity_from_string(input: String) -> Option<IosButton> {
    let regex = Regex::new(r"^\\s\{([^}:]+)(?::(\d+(?:\.\d+)?))?\}$").expect("valid regex");
    let captures = regex.captures(input.as_str());

    if let Some(captures) = captures {
        if let Some(id) = captures.get(1) {
            let id = format!("_{}", id.as_str());
            if let Some(width) = captures.get(2) {
                Some(IosButton {
                    id: id,
                    width: width.as_str().parse::<f32>().expect("invalid float value"),
                })
            } else {
                Some(IosButton {
                    id: id,
                    width: 1.0f32,
                })
            }
        } else {
            return None;
        }
    } else {
        return None;
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct IosButton {
    id: String,
    width: f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum IosKeyMapTypes {
    Character(String),
    Button(IosButton),
}

#[derive(Serialize, Deserialize)]
pub struct IosNormalLayer {
    #[serde(flatten)]
    layer: IndexMap<String, Vec<Vec<IosKeyMapTypes>>>,
}

#[derive(Serialize, Deserialize)]
pub struct IosDeadKeys {
    iphone: IndexMap<String, String>,
    #[serde(rename = "ipad-9in")]
    i_pad_9in: IndexMap<String, String>,
    #[serde(rename = "ipad-12in")]
    i_pad_12in: IndexMap<String, String>,
}

#[derive(Serialize, Deserialize)]
pub struct IosKeyboardDefinitions {
    #[serde(flatten)]
    info: IosInfo,
    longpress: IndexMap<String, Vec<String>>,
    dead_keys: IosDeadKeys,
    transforms: serde_json::value::Value,
    iphone: IosNormalLayer,
    i_pad_9in: IosNormalLayer,
    i_pad_12in: IosNormalLayer,
}

pub fn enum_to_output(layer: &IOsKbdLayer) -> String {
    match layer {
        IOsKbdLayer::Default => "normal",
        IOsKbdLayer::Shift => "shifted",
        IOsKbdLayer::Alt => "alt",
        IOsKbdLayer::AltAndShift => "alt+shift",
        IOsKbdLayer::Symbols1 => "symbols-1",
        IOsKbdLayer::Symbols2 => "symbols-2",
    }
    .to_string()
}

pub fn generate_platform(platform: &IOsPlatform) -> IndexMap<String, Vec<Vec<IosKeyMapTypes>>> {
    let mut layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();
    for (layer_name, layer_key_map) in &platform.layers {
        let layer_name = enum_to_output(layer_name);
        let key_map_rows: Vec<&str> = layer_key_map
            .trim()
            .split("\n")
            .map(|x| x.clone())
            .collect();
        let mut layer_rows: Vec<Vec<IosKeyMapTypes>> = Vec::new();
        for key_map in key_map_rows {
            let key_map = split_keys(key_map);
            let mut new_key_map: Vec<IosKeyMapTypes> = Vec::new();
            for key in key_map {
                if let Some(keyboard_entity) = keyboard_entity_from_string(key.clone()) {
                    new_key_map.push(IosKeyMapTypes::Button(keyboard_entity));
                } else {
                    new_key_map.push(IosKeyMapTypes::Character(key));
                }
            }
            layer_rows.push(new_key_map)
        }
        layers.insert(layer_name, layer_rows);
    }
    layers
}

pub struct GenerateIos;

#[async_trait(?Send)]
impl BuildStep for GenerateIos {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let keyboard_definitions_file_path = output_path.join("KeyboardDefinitions.json");

        for (language_tag, layout) in &bundle.layouts {
            let mut longpress: IndexMap<String, Vec<String>> = IndexMap::new();
            let mut iphone_layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();
            let mut i_pad_9in_layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();
            let mut i_pad_12in_layers: IndexMap<String, Vec<Vec<IosKeyMapTypes>>> = IndexMap::new();

            if let Some(ios_target) = &layout.i_os {
                if let Some(layout_longpress) = &layout.longpress {
                    for (key, value) in layout_longpress {
                        longpress.insert(key.clone(), value.clone());
                    }
                }

                if let Some(primary_platform) = &ios_target.primary {
                    iphone_layers.extend(generate_platform(&primary_platform));
                }
                if let Some(i_pad_9in_platform) = &ios_target.i_pad_9in {
                    i_pad_9in_layers.extend(generate_platform(&i_pad_9in_platform));
                }
                if let Some(i_pad_12in_platform) = &ios_target.i_pad_12in {
                    i_pad_12in_layers.extend(generate_platform(&i_pad_12in_platform));
                }

                if let Some(key_names) = &layout.key_names {
                    std::fs::write(
                        output_path.join(keyboard_definitions_file_path.clone()),
                        serde_json::to_string_pretty(&[IosKeyboardDefinitions {
                            info: IosInfo {
                                name: layout
                                    .display_names
                                    .get(language_tag)
                                    .expect("can't evaluate language tag of layout")
                                    .to_string(),
                                locale: language_tag.to_string(),
                                enter: key_names.r#return.to_string(),
                                space: key_names.space.to_string(),
                            },
                            longpress: longpress,
                            dead_keys: IosDeadKeys {
                                iphone: IndexMap::new(),
                                i_pad_9in: IndexMap::new(),
                                i_pad_12in: IndexMap::new(),
                            },
                            transforms: serde_json::value::Value::Null,
                            iphone: IosNormalLayer {
                                layer: iphone_layers,
                            },
                            i_pad_9in: IosNormalLayer {
                                layer: i_pad_9in_layers,
                            },
                            i_pad_12in: IosNormalLayer {
                                layer: i_pad_12in_layers,
                            },
                        }])
                        .unwrap(),
                    )
                    .unwrap();
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::super::StreamId;
use super::frames::{
    Frame,
    Flag,
    pack_header,
    RawFrame,
    FrameHeader
};

/// An enum representing the flags that a `PingFrame` can have.
/// The integer representation associated to each variant is that flag's
/// bitmask.
///
/// HTTP/2 spec, section 6.
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Copy)]
pub enum NoFlag {
    None = 0x0,
}

impl Flag for NoFlag {
    #[inline]
    fn bitmask(&self) -> u8 {
        *self as u8
    }
}

/// The struct represents the dependency information that can be attached to a stream
/// and sent within HEADERS frame
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub struct StreamDependency {
    /// The ID of the stream that a particular stream depends on
    pub stream_id: StreamId,
    /// The weight for the stream. The value exposed (and set) here is always
    /// in the range [0, 255], instead of [1, 256] so that the value fits
    /// into a `u8`.
    pub weight: u8,
    /// A flag indicating whether the stream dependency is exclusive.
    pub is_exclusive: bool,
}

impl StreamDependency {
    /// Creates a new `StreamDependency` with the given stream Id, weight, and
    /// exclusivity.
    pub fn new(stream_id: StreamId, weight: u8, is_exclusive: bool)
            -> StreamDependency {
        StreamDependency {
            stream_id: stream_id,
            weight: weight,
            is_exclusive: is_exclusive,
        }
    }

    /// Parses the 5-byte length frame
    ///
    /// # Panics
    ///
    /// If the frame is less than 5 bytes, the method will panic
    pub fn parse(buf: &[u8]) -> StreamDependency {
        // The most significant bit of the first byte is the "E" bit indicating
        // whether the dependency is exclusive.
        let is_exclusive = buf[0] & 0x80 != 0;
        let stream_id = {
            // Parse the first 4 bytes into a u32
            let mut id = unpack_octets_4!(buf, 0, u32);
            // and clear the first bit since the stream id is only 31 bits.
            id &= !(1 << 31);
            id
        };

        StreamDependency {
            stream_id: stream_id,
            weight: buf[4],
            is_exclusive: is_exclusive,
        }
    }

    /// Serializes the `StreamDependency` into a 5-byte buffer representing the
    /// dependency description.
    pub fn serialize(&self) -> [u8; 5] {
        let e_bit = if self.is_exclusive {
            1 << 7
        } else {
            0
        };
        [
            (((self.stream_id >> 24) & 0x000000FF) as u8) | e_bit,
            (((self.stream_id >> 16) & 0x000000FF) as u8),
            (((self.stream_id >>  8) & 0x000000FF) as u8),
            (((self.stream_id >>  0) & 0x000000FF) as u8),
            self.weight,
        ]
    }
}

/// A struct representing the PRIORITY frmas of HTTP/2
#[derive(PartialEq)]
#[derive(Debug)]
pub struct PriorityFrame {
    /// The id of the stream with which this frame is associated
    pub stream_id: StreamId,
    /// The stream dependency information
    pub stream_dep: StreamDependency,
}

impl PriorityFrame {
    pub fn new(stream_id: StreamId, stream_dep: StreamDependency)
            -> PriorityFrame {
        PriorityFrame {
            stream_id: stream_id,
            stream_dep: stream_dep,
        }
    }

    /// Returns the length of the payload of the current frame
    /// Priority frame must be 5 octets
    fn payload_len(&self) -> u32 {
        5
    }
}

impl Frame for PriorityFrame {
    /// `Priority` frame does not take a flag
    type FlagType = NoFlag;

    /// Creates a new `PriorityFrame` with the given `RawFrame` (i.e. header and
    /// payload), if possible.
    ///
    /// # Returns
    ///
    /// `None` if a valid `PriorityFrame` cannot be constructed from the given
    /// `RawFrame`. The stream ID *MUST NOT* be 0.
    ///
    /// Otherwise, returns a newly contructed `PriorityFrame`
    fn from_raw(raw_frame: RawFrame) -> Option<PriorityFrame> {
        // Unpack the header
        let (len, frame_type, flags, stream_id) = raw_frame.header;
        // Check that the frame type is correct for this frame implementation
        if frame_type != 0x2 {
            return None;
        }
        // Check that the length given in the header matches the payload
        // if not, soemthing went wrong and we do not consider this as
        // a valid frame.
        if (len as usize) != raw_frame.payload.len() {
            return None;
        }
        // Check that the length of the payload is 5 bytes
        // If not, this is not a valid frame
        if raw_frame.payload.len() != 5 {
            return None;
        }
        // Check that the PRIORITY frame is not associated to stream 0
        // If it is, this is not a valid frame
        if stream_id == 0 {
            return None;
        }
        // Extract the stream dependecy info from the payload
        let stream_dep = StreamDependency::parse(&raw_frame.payload);

        Some(PriorityFrame {
            stream_id: stream_id,
            stream_dep: stream_dep,
        })
    }

    /// `Priority` frame does not set any flags
    fn is_set(&self, flag: NoFlag) -> bool {
        true
    }

    /// Returns the `StreamId` of the stream to which the frame is associated
    ///
    fn get_stream_id(&self) -> StreamId {
        self.stream_id
    }

    /// Returns a `FrameHeader` based on the current state of the `Frame`.
    fn get_header(&self) -> FrameHeader {
        (self.payload_len(), 0x2, 0, self.stream_id)
    }

    /// `PriorityFrame` does not set any flags
    fn set_flag(&mut self, flag: NoFlag) {
    }

    /// Returns a `Vec` with the serialized representation of the frame.
    fn serialize(&self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(self.payload_len() as usize);
        // The header
        buf.extend(pack_header(&self.get_header()).to_vec().into_iter());
        // and then the body
        buf.extend(self.stream_dep.serialize().to_vec().into_iter());

        buf
    }
}

<commit_msg>(feat) Implement tests for Priority Frame<commit_after>use super::super::StreamId;
use super::frames::{
    Frame,
    Flag,
    pack_header,
    RawFrame,
    FrameHeader
};

/// An enum representing the flags that a `PingFrame` can have.
/// The integer representation associated to each variant is that flag's
/// bitmask.
///
/// HTTP/2 spec, section 6.
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Copy)]
pub enum NoFlag {
    None = 0x0,
}

impl Flag for NoFlag {
    #[inline]
    fn bitmask(&self) -> u8 {
        *self as u8
    }
}

/// The struct represents the dependency information that can be attached to a stream
/// and sent within HEADERS frame
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub struct StreamDependency {
    /// The ID of the stream that a particular stream depends on
    pub stream_id: StreamId,
    /// The weight for the stream. The value exposed (and set) here is always
    /// in the range [0, 255], instead of [1, 256] so that the value fits
    /// into a `u8`.
    pub weight: u8,
    /// A flag indicating whether the stream dependency is exclusive.
    pub is_exclusive: bool,
}

impl StreamDependency {
    /// Creates a new `StreamDependency` with the given stream Id, weight, and
    /// exclusivity.
    pub fn new(stream_id: StreamId, weight: u8, is_exclusive: bool)
            -> StreamDependency {
        StreamDependency {
            stream_id: stream_id,
            weight: weight,
            is_exclusive: is_exclusive,
        }
    }

    /// Parses the 5-byte length frame
    ///
    /// # Panics
    ///
    /// If the frame is less than 5 bytes, the method will panic
    pub fn parse(buf: &[u8]) -> StreamDependency {
        // The most significant bit of the first byte is the "E" bit indicating
        // whether the dependency is exclusive.
        let is_exclusive = buf[0] & 0x80 != 0;
        let stream_id = {
            // Parse the first 4 bytes into a u32
            let mut id = unpack_octets_4!(buf, 0, u32);
            // and clear the first bit since the stream id is only 31 bits.
            id &= !(1 << 31);
            id
        };

        StreamDependency {
            stream_id: stream_id,
            weight: buf[4],
            is_exclusive: is_exclusive,
        }
    }

    /// Serializes the `StreamDependency` into a 5-byte buffer representing the
    /// dependency description.
    pub fn serialize(&self) -> [u8; 5] {
        let e_bit = if self.is_exclusive {
            1 << 7
        } else {
            0
        };
        [
            (((self.stream_id >> 24) & 0x000000FF) as u8) | e_bit,
            (((self.stream_id >> 16) & 0x000000FF) as u8),
            (((self.stream_id >>  8) & 0x000000FF) as u8),
            (((self.stream_id >>  0) & 0x000000FF) as u8),
            self.weight,
        ]
    }
}

/// A struct representing the PRIORITY frmas of HTTP/2
#[derive(PartialEq)]
#[derive(Debug)]
pub struct PriorityFrame {
    /// The id of the stream with which this frame is associated
    pub stream_id: StreamId,
    /// The stream dependency information
    pub stream_dep: StreamDependency,
    /// `Priority` frame does not define any flags
    flags: u8,
}

impl PriorityFrame {
    pub fn new(stream_id: StreamId, stream_dep: StreamDependency)
            -> PriorityFrame {
        PriorityFrame {
            stream_id: stream_id,
            stream_dep: stream_dep,
            flags: 0,
        }
    }

    /// Returns the length of the payload of the current frame
    /// Priority frame must be 5 octets
    fn payload_len(&self) -> u32 {
        5
    }
}

impl Frame for PriorityFrame {
    /// `Priority` frame does not take a flag
    type FlagType = NoFlag;

    /// Creates a new `PriorityFrame` with the given `RawFrame` (i.e. header and
    /// payload), if possible.
    ///
    /// # Returns
    ///
    /// `None` if a valid `PriorityFrame` cannot be constructed from the given
    /// `RawFrame`. The stream ID *MUST NOT* be 0.
    ///
    /// Otherwise, returns a newly contructed `PriorityFrame`
    fn from_raw(raw_frame: RawFrame) -> Option<PriorityFrame> {
        // Unpack the header
        let (len, frame_type, flags, stream_id) = raw_frame.header;
        // Check that the frame type is correct for this frame implementation
        if frame_type != 0x2 {
            return None;
        }
        // Check that the length given in the header matches the payload
        // if not, soemthing went wrong and we do not consider this as
        // a valid frame.
        if (len as usize) != raw_frame.payload.len() {
            return None;
        }
        // Check that the length of the payload is 5 bytes
        // If not, this is not a valid frame
        if raw_frame.payload.len() != 5 {
            return None;
        }
        // Check that the PRIORITY frame is not associated to stream 0
        // If it is, this is not a valid frame
        if stream_id == 0 {
            return None;
        }
        // Extract the stream dependecy info from the payload
        let stream_dep = StreamDependency::parse(&raw_frame.payload);

        Some(PriorityFrame {
            stream_id: stream_id,
            stream_dep: stream_dep,
            flags: flags,
        })
    }

    /// `Priority` frame does not set any flags
    fn is_set(&self, flag: NoFlag) -> bool {
        true
    }

    /// Returns the `StreamId` of the stream to which the frame is associated
    ///
    fn get_stream_id(&self) -> StreamId {
        self.stream_id
    }

    /// Returns a `FrameHeader` based on the current state of the `Frame`.
    fn get_header(&self) -> FrameHeader {
        (self.payload_len(), 0x2, 0, self.stream_id)
    }

    /// `PriorityFrame` does not set any flags
    fn set_flag(&mut self, flag: NoFlag) {
    }

    /// Returns a `Vec` with the serialized representation of the frame.
    fn serialize(&self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(self.payload_len() as usize);
        // The header
        buf.extend(pack_header(&self.get_header()).to_vec().into_iter());
        // and then the body
        buf.extend(self.stream_dep.serialize().to_vec().into_iter());

        buf
    }
}

#[cfg(test)]
mod tests {
    use super::super::frames::{Frame, RawFrame, pack_header};
    use super::super::test::{build_test_frame};
    use super::{PriorityFrame, NoFlag, StreamDependency};

    /// Tests that a simple PRIORITY frame is correclty parsed.
    #[test]
    fn test_priority_frame_parse() {
        let dep = StreamDependency::new(0, 5, true);
        let payload = {
            let mut buf: Vec<u8> = Vec::new();
            buf.extend(dep.serialize().to_vec().into_iter());

            buf
        };
        let header = (payload.len() as u32, 0x2, 0, 1);

        let frame = build_test_frame::<PriorityFrame>(&header, &payload);

        assert_eq!(frame.flags, 0);
        assert_eq!(frame.get_stream_id(), 1);
        assert_eq!(frame.stream_dep, dep);
    }

    /// Tests that a HEADERS with stream ID 0 is considered invalid.
    #[test]
    fn test_priority_frame_parse_invalid_stream_id() {
        let dep = StreamDependency::new(0, 5, true);
        let payload = {
            let mut buf: Vec<u8> = Vec::new();
            buf.extend(dep.serialize().to_vec().into_iter());

            buf
        };
        let header = (payload.len() as u32, 0x2, 0, 0);

        let frame: Option<PriorityFrame> = Frame::from_raw(
            RawFrame::with_payload(header, payload));

        assert!(frame.is_none());
    }

    /// Tests that the `HeadersFrame::parse` method considers any frame with
    /// a frame ID other than 1 in the frame header invalid.
    #[test]
    fn priority_frame_parse_invalid_type() {
        let dep = StreamDependency::new(0, 5, true);
        let payload = {
            let mut buf: Vec<u8> = Vec::new();
            buf.extend(dep.serialize().to_vec().into_iter());

            buf
        };
        let header = (payload.len() as u32, 0x1, 0, 1);

        let frame: Option<PriorityFrame> = Frame::from_raw(
            RawFrame::with_payload(header, payload));

        assert!(frame.is_none());
    }

    /// Tests that a HEADERS frame with priority gets correctly serialized.
    #[test]
    fn test_priority_frame_serialize() {
        let dep = StreamDependency::new(0, 5, true);
        let payload = {
            let mut buf: Vec<u8> = Vec::new();
            buf.extend(dep.serialize().to_vec().into_iter());

            buf
        };
        let header = (payload.len() as u32, 0x2, 0, 1);
        let expected = {
            let headers = pack_header(&header);
            let mut res: Vec<u8> = Vec::new();
            res.extend(headers.to_vec().into_iter());
            res.extend(payload.into_iter());

            res
        };
        let frame = PriorityFrame::new(1, dep.clone());

        let actual = frame.serialize();

        assert_eq!(expected, actual);
    }

    /// Tests that a stream dependency structure can be correctly parsed by the
    /// `StreamDependency::parse` method.
    #[test]
    fn test_parse_stream_dependency() {
        {
            let buf = [0, 0, 0, 1, 5];

            let dep = StreamDependency::parse(&buf);

            assert_eq!(dep.stream_id, 1);
            assert_eq!(dep.weight, 5);
            // This one was not exclusive!
            assert!(!dep.is_exclusive)
        }
        {
            // Most significant bit set => is exclusive!
            let buf = [128, 0, 0, 1, 5];

            let dep = StreamDependency::parse(&buf);

            assert_eq!(dep.stream_id, 1);
            assert_eq!(dep.weight, 5);
            // This one was indeed exclusive!
            assert!(dep.is_exclusive)
        }
        {
            // Most significant bit set => is exclusive!
            let buf = [255, 255, 255, 255, 5];

            let dep = StreamDependency::parse(&buf);

            assert_eq!(dep.stream_id, (1 << 31) - 1);
            assert_eq!(dep.weight, 5);
            // This one was indeed exclusive!
            assert!(dep.is_exclusive);
        }
        {
            let buf = [127, 255, 255, 255, 5];

            let dep = StreamDependency::parse(&buf);

            assert_eq!(dep.stream_id, (1 << 31) - 1);
            assert_eq!(dep.weight, 5);
            // This one was not exclusive!
            assert!(!dep.is_exclusive);
        }
    }

    /// Tests that a stream dependency structure can be correctly serialized by
    /// the `StreamDependency::serialize` method.
    #[test]
    fn test_serialize_stream_dependency() {
        {
            let buf = [0, 0, 0, 1, 5];
            let dep = StreamDependency::new(1, 5, false);

            assert_eq!(buf, dep.serialize());
        }
        {
            // Most significant bit set => is exclusive!
            let buf = [128, 0, 0, 1, 5];
            let dep = StreamDependency::new(1, 5, true);

            assert_eq!(buf, dep.serialize());
        }
        {
            // Most significant bit set => is exclusive!
            let buf = [255, 255, 255, 255, 5];
            let dep = StreamDependency::new((1 << 31) - 1, 5, true);

            assert_eq!(buf, dep.serialize());
        }
        {
            let buf = [127, 255, 255, 255, 5];
            let dep = StreamDependency::new((1 << 31) - 1, 5, false);

            assert_eq!(buf, dep.serialize());
        }
    }
}

<|endoftext|>