<commit_before>use syntax_pos::symbol::Symbol;
use back::write::create_informational_target_machine;
use llvm;
use rustc::session::Session;
use rustc::session::config::PrintRequest;
use rustc_target::spec::MergeFunctions;
use libc::c_int;
use std::ffi::CString;
use syntax::feature_gate::UnstableFeatures;

use std::str;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

static POISONED: AtomicBool = AtomicBool::new(false);
static INIT: Once = Once::new();

pub(crate) fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

fn require_inited() {
    INIT.call_once(|| bug!("llvm is not initialized"));
    if POISONED.load(Ordering::SeqCst) {
        bug!("couldn't enable multi-threaded LLVM");
    }
}

unsafe fn configure_llvm(sess: &Session) {
    let n_args = sess.opts.cg.llvm_args.len();
    let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
    let mut llvm_args = Vec::with_capacity(n_args + 1);

    llvm::LLVMRustInstallFatalErrorHandler();

    {
        let mut add = |arg: &str| {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
        };
        add("rustc"); // fake program name
        if sess.time_llvm_passes() { add("-time-passes"); }
        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
        if sess.opts.debugging_opts.disable_instrumentation_preinliner {
            add("-disable-preinline");
        }
        if llvm::LLVMRustIsRustLLVM() {
            match sess.opts.debugging_opts.merge_functions
                  .unwrap_or(sess.target.target.options.merge_functions) {
                MergeFunctions::Disabled |
                MergeFunctions::Trampolines => {}
                MergeFunctions::Aliases => {
                    add("-mergefunc-use-aliases");
                }
            }
        }

        // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes
        // during inlining. Unfortunately these may block other optimizations.
        add("-preserve-alignment-assumptions-during-inlining=false");

        for arg in &sess.opts.cg.llvm_args {
            add(&(*arg));
        }
    }

    llvm::LLVMInitializePasses();

    ::rustc_llvm::initialize_available_targets();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                 llvm_args.as_ptr());
}

// WARNING: the features after applying `to_llvm_feature` must be known
// to LLVM or the feature detection code will walk past the end of the feature
// array, leading to crashes.

const ARM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("aclass", Some("arm_target_feature")),
    ("mclass", Some("arm_target_feature")),
    ("rclass", Some("arm_target_feature")),
    ("dsp", Some("arm_target_feature")),
    ("neon", Some("arm_target_feature")),
    ("v5te", Some("arm_target_feature")),
    ("v6k", Some("arm_target_feature")),
    ("v6t2", Some("arm_target_feature")),
    ("v7", Some("arm_target_feature")),
    ("vfp2", Some("arm_target_feature")),
    ("vfp3", Some("arm_target_feature")),
    ("vfp4", Some("arm_target_feature")),
];

const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp", Some("aarch64_target_feature")),
    ("neon", Some("aarch64_target_feature")),
    ("sve", Some("aarch64_target_feature")),
    ("crc", Some("aarch64_target_feature")),
    ("crypto", Some("aarch64_target_feature")),
    ("ras", Some("aarch64_target_feature")),
    ("lse", Some("aarch64_target_feature")),
    ("rdm", Some("aarch64_target_feature")),
    ("fp16", Some("aarch64_target_feature")),
    ("rcpc", Some("aarch64_target_feature")),
    ("dotprod", Some("aarch64_target_feature")),
    ("v8.1a", Some("aarch64_target_feature")),
    ("v8.2a", Some("aarch64_target_feature")),
    ("v8.3a", Some("aarch64_target_feature")),
];

const X86_WHITELIST: &[(&str, Option<&str>)] = &[
    ("adx", Some("adx_target_feature")),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bw", Some("avx512_target_feature")),
    ("avx512cd", Some("avx512_target_feature")),
    ("avx512dq", Some("avx512_target_feature")),
    ("avx512er", Some("avx512_target_feature")),
    ("avx512f", Some("avx512_target_feature")),
    ("avx512ifma", Some("avx512_target_feature")),
    ("avx512pf", Some("avx512_target_feature")),
    ("avx512vbmi", Some("avx512_target_feature")),
    ("avx512vl", Some("avx512_target_feature")),
    ("avx512vpopcntdq", Some("avx512_target_feature")),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some("cmpxchg16b_target_feature")),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("mmx", Some("mmx_target_feature")),
    ("movbe", Some("movbe_target_feature")),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some("sse4a_target_feature")),
    ("ssse3", None),
    ("tbm", Some("tbm_target_feature")),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[
    ("hvx", Some("hexagon_target_feature")),
    ("hvx-double", Some("hexagon_target_feature")),
];

const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[
    ("altivec", Some("powerpc_target_feature")),
    ("power8-altivec", Some("powerpc_target_feature")),
    ("power9-altivec", Some("powerpc_target_feature")),
    ("power8-vector", Some("powerpc_target_feature")),
    ("power9-vector", Some("powerpc_target_feature")),
    ("vsx", Some("powerpc_target_feature")),
];

const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp64", Some("mips_target_feature")),
    ("msa", Some("mips_target_feature")),
];

const WASM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("simd128", Some("wasm_target_feature")),
    ("atomics", Some("wasm_target_feature")),
];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this
/// iterator!
pub fn all_known_features() -> impl Iterator<Item=(&'static str, Option<&'static str>)> {
    ARM_WHITELIST.iter().cloned()
        .chain(AARCH64_WHITELIST.iter().cloned())
        .chain(X86_WHITELIST.iter().cloned())
        .chain(HEXAGON_WHITELIST.iter().cloned())
        .chain(POWERPC_WHITELIST.iter().cloned())
        .chain(MIPS_WHITELIST.iter().cloned())
        .chain(WASM_WHITELIST.iter().cloned())
}

pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
    let arch = if sess.target.target.arch == "x86_64" {
        "x86"
    } else {
        &*sess.target.target.arch
    };
    match (arch, s) {
        ("x86", "pclmulqdq") => "pclmul",
        ("x86", "rdrand") => "rdrnd",
        ("x86", "bmi1") => "bmi",
        ("x86", "cmpxchg16b") => "cx16",
        ("aarch64", "fp") => "fp-armv8",
        ("aarch64", "fp16") => "fullfp16",
        (_, s) => s,
    }
}

pub fn target_features(sess: &Session) -> Vec<Symbol> {
    let target_machine = create_informational_target_machine(sess, true);
    target_feature_whitelist(sess)
        .iter()
        .filter_map(|&(feature, gate)| {
            if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
                Some(feature)
            } else {
                None
            }
        })
        .filter(|feature| {
            let llvm_feature = to_llvm_feature(sess, feature);
            let cstr = CString::new(llvm_feature).unwrap();
            unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
        })
        .map(|feature| Symbol::intern(feature)).collect()
}

pub fn target_feature_whitelist(sess: &Session)
    -> &'static [(&'static str, Option<&'static str>)]
{
    match &*sess.target.target.arch {
        "arm" => ARM_WHITELIST,
        "aarch64" => AARCH64_WHITELIST,
        "x86" | "x86_64" => X86_WHITELIST,
        "hexagon" => HEXAGON_WHITELIST,
        "mips" | "mips64" => MIPS_WHITELIST,
        "powerpc" | "powerpc64" => POWERPC_WHITELIST,
        // wasm32 on emscripten does not support these target features
        "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST,
        _ => &[],
    }
}

pub fn print_version() {
    // Can be called without initializing LLVM
    unsafe {
        println!("LLVM version: {}.{}",
                 llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
    }
}

pub fn get_major_version() -> u32 {
    unsafe { llvm::LLVMRustVersionMajor() }
}

pub fn print_passes() {
    // Can be called without initializing LLVM
    unsafe { llvm::LLVMRustPrintPasses(); }
}

pub(crate) fn print(req: PrintRequest, sess: &Session) {
    require_inited();
    let tm = create_informational_target_machine(sess, true);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
        }
    }
}

pub fn target_cpu(sess: &Session) -> &str {
    let name = match sess.opts.cg.target_cpu {
        Some(ref s) => &**s,
        None => &*sess.target.target.options.cpu
    };
    if name != "native" {
        return name
    }

    unsafe {
        let mut len = 0;
        let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
        str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
    }
}
<commit_msg>Whitelist the ARM v6 target-feature<commit_after>use syntax_pos::symbol::Symbol;
use back::write::create_informational_target_machine;
use llvm;
use rustc::session::Session;
use rustc::session::config::PrintRequest;
use rustc_target::spec::MergeFunctions;
use libc::c_int;
use std::ffi::CString;
use syntax::feature_gate::UnstableFeatures;

use std::str;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

static POISONED: AtomicBool = AtomicBool::new(false);
static INIT: Once = Once::new();

pub(crate) fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

fn require_inited() {
    INIT.call_once(|| bug!("llvm is not initialized"));
    if POISONED.load(Ordering::SeqCst) {
        bug!("couldn't enable multi-threaded LLVM");
    }
}

unsafe fn configure_llvm(sess: &Session) {
    let n_args = sess.opts.cg.llvm_args.len();
    let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
    let mut llvm_args = Vec::with_capacity(n_args + 1);

    llvm::LLVMRustInstallFatalErrorHandler();

    {
        let mut add = |arg: &str| {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
        };
        add("rustc"); // fake program name
        if sess.time_llvm_passes() { add("-time-passes"); }
        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
        if sess.opts.debugging_opts.disable_instrumentation_preinliner {
            add("-disable-preinline");
        }
        if llvm::LLVMRustIsRustLLVM() {
            match sess.opts.debugging_opts.merge_functions
                  .unwrap_or(sess.target.target.options.merge_functions) {
                MergeFunctions::Disabled |
                MergeFunctions::Trampolines => {}
                MergeFunctions::Aliases => {
                    add("-mergefunc-use-aliases");
                }
            }
        }

        // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes
        // during inlining. Unfortunately these may block other optimizations.
        add("-preserve-alignment-assumptions-during-inlining=false");

        for arg in &sess.opts.cg.llvm_args {
            add(&(*arg));
        }
    }

    llvm::LLVMInitializePasses();

    ::rustc_llvm::initialize_available_targets();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                 llvm_args.as_ptr());
}

// WARNING: the features after applying `to_llvm_feature` must be known
// to LLVM or the feature detection code will walk past the end of the feature
// array, leading to crashes.

const ARM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("aclass", Some("arm_target_feature")),
    ("mclass", Some("arm_target_feature")),
    ("rclass", Some("arm_target_feature")),
    ("dsp", Some("arm_target_feature")),
    ("neon", Some("arm_target_feature")),
    ("v5te", Some("arm_target_feature")),
    ("v6", Some("arm_target_feature")),
    ("v6k", Some("arm_target_feature")),
    ("v6t2", Some("arm_target_feature")),
    ("v7", Some("arm_target_feature")),
    ("vfp2", Some("arm_target_feature")),
    ("vfp3", Some("arm_target_feature")),
    ("vfp4", Some("arm_target_feature")),
];

const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp", Some("aarch64_target_feature")),
    ("neon", Some("aarch64_target_feature")),
    ("sve", Some("aarch64_target_feature")),
    ("crc", Some("aarch64_target_feature")),
    ("crypto", Some("aarch64_target_feature")),
    ("ras", Some("aarch64_target_feature")),
    ("lse", Some("aarch64_target_feature")),
    ("rdm", Some("aarch64_target_feature")),
    ("fp16", Some("aarch64_target_feature")),
    ("rcpc", Some("aarch64_target_feature")),
    ("dotprod", Some("aarch64_target_feature")),
    ("v8.1a", Some("aarch64_target_feature")),
    ("v8.2a", Some("aarch64_target_feature")),
    ("v8.3a", Some("aarch64_target_feature")),
];

const X86_WHITELIST: &[(&str, Option<&str>)] = &[
    ("adx", Some("adx_target_feature")),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bw", Some("avx512_target_feature")),
    ("avx512cd", Some("avx512_target_feature")),
    ("avx512dq", Some("avx512_target_feature")),
    ("avx512er", Some("avx512_target_feature")),
    ("avx512f", Some("avx512_target_feature")),
    ("avx512ifma", Some("avx512_target_feature")),
    ("avx512pf", Some("avx512_target_feature")),
    ("avx512vbmi", Some("avx512_target_feature")),
    ("avx512vl", Some("avx512_target_feature")),
    ("avx512vpopcntdq", Some("avx512_target_feature")),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some("cmpxchg16b_target_feature")),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("mmx", Some("mmx_target_feature")),
    ("movbe", Some("movbe_target_feature")),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some("sse4a_target_feature")),
    ("ssse3", None),
    ("tbm", Some("tbm_target_feature")),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[
    ("hvx", Some("hexagon_target_feature")),
    ("hvx-double", Some("hexagon_target_feature")),
];

const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[
    ("altivec", Some("powerpc_target_feature")),
    ("power8-altivec", Some("powerpc_target_feature")),
    ("power9-altivec", Some("powerpc_target_feature")),
    ("power8-vector", Some("powerpc_target_feature")),
    ("power9-vector", Some("powerpc_target_feature")),
    ("vsx", Some("powerpc_target_feature")),
];

const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp64", Some("mips_target_feature")),
    ("msa", Some("mips_target_feature")),
];

const WASM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("simd128", Some("wasm_target_feature")),
    ("atomics", Some("wasm_target_feature")),
];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this
/// iterator!
pub fn all_known_features() -> impl Iterator<Item=(&'static str, Option<&'static str>)> {
    ARM_WHITELIST.iter().cloned()
        .chain(AARCH64_WHITELIST.iter().cloned())
        .chain(X86_WHITELIST.iter().cloned())
        .chain(HEXAGON_WHITELIST.iter().cloned())
        .chain(POWERPC_WHITELIST.iter().cloned())
        .chain(MIPS_WHITELIST.iter().cloned())
        .chain(WASM_WHITELIST.iter().cloned())
}

pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
    let arch = if sess.target.target.arch == "x86_64" {
        "x86"
    } else {
        &*sess.target.target.arch
    };
    match (arch, s) {
        ("x86", "pclmulqdq") => "pclmul",
        ("x86", "rdrand") => "rdrnd",
        ("x86", "bmi1") => "bmi",
        ("x86", "cmpxchg16b") => "cx16",
        ("aarch64", "fp") => "fp-armv8",
        ("aarch64", "fp16") => "fullfp16",
        (_, s) => s,
    }
}

pub fn target_features(sess: &Session) -> Vec<Symbol> {
    let target_machine = create_informational_target_machine(sess, true);
    target_feature_whitelist(sess)
        .iter()
        .filter_map(|&(feature, gate)| {
            if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
                Some(feature)
            } else {
                None
            }
        })
        .filter(|feature| {
            let llvm_feature = to_llvm_feature(sess, feature);
            let cstr = CString::new(llvm_feature).unwrap();
            unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
        })
        .map(|feature| Symbol::intern(feature)).collect()
}

pub fn target_feature_whitelist(sess: &Session)
    -> &'static [(&'static str, Option<&'static str>)]
{
    match &*sess.target.target.arch {
        "arm" => ARM_WHITELIST,
        "aarch64" => AARCH64_WHITELIST,
        "x86" | "x86_64" => X86_WHITELIST,
        "hexagon" => HEXAGON_WHITELIST,
        "mips" | "mips64" => MIPS_WHITELIST,
        "powerpc" | "powerpc64" => POWERPC_WHITELIST,
        // wasm32 on emscripten does not support these target features
        "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST,
        _ => &[],
    }
}

pub fn print_version() {
    // Can be called without initializing LLVM
    unsafe {
        println!("LLVM version: {}.{}",
                 llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
    }
}

pub fn get_major_version() -> u32 {
    unsafe { llvm::LLVMRustVersionMajor() }
}

pub fn print_passes() {
    // Can be called without initializing LLVM
    unsafe { llvm::LLVMRustPrintPasses(); }
}

pub(crate) fn print(req: PrintRequest, sess: &Session) {
    require_inited();
    let tm = create_informational_target_machine(sess, true);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
        }
    }
}

pub fn target_cpu(sess: &Session) -> &str {
    let name = match sess.opts.cg.target_cpu {
        Some(ref s) => &**s,
        None => &*sess.target.target.options.cpu
    };
    if name != "native" {
        return name
    }

    unsafe {
        let mut len = 0;
        let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
        str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_int, size_t, strncpy};
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_char;

mod add;
pub use add::add;

mod cmp;
pub use cmp::cmp;

mod denormalize;
pub use denormalize::denormalize;

mod normalize;
pub use normalize::normalize;

#[no_mangle]
pub extern fn rnc_add(dst: *mut c_char, dstlen: size_t, num_l: *const c_char, num_r: *const c_char) -> c_int {
    unsafe {
        let sum = add(CStr::from_ptr(num_l).to_str().unwrap(), CStr::from_ptr(num_r).to_str().unwrap());
        if sum.len() > (dstlen - 1) {
            1
        } else {
            let csum = CString::new(sum).unwrap();
            strncpy(dst, csum.as_ptr() as *const i8, dstlen);
            0
        }
    }
}
<commit_msg>Degenerate sub impl in Rust<commit_after>extern crate libc;

use libc::{c_int, size_t, strncpy};
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_char;

mod add;
pub use add::add;

mod cmp;
pub use cmp::cmp;

mod denormalize;
pub use denormalize::denormalize;

mod normalize;
pub use normalize::normalize;

#[no_mangle]
pub extern fn rnc_add(dst: *mut c_char, dstlen: size_t, num_l: *const c_char, num_r: *const c_char) -> c_int {
    unsafe {
        let sum = add(CStr::from_ptr(num_l).to_str().unwrap(), CStr::from_ptr(num_r).to_str().unwrap());
        if sum.len() > (dstlen - 1) {
            1
        } else {
            let csum = CString::new(sum).unwrap();
            strncpy(dst, csum.as_ptr() as *const i8, dstlen);
            0
        }
    }
}

#[no_mangle]
pub extern fn rnc_sub(dst: *mut c_char, dstlen: size_t, num_l: *const c_char, num_r: *const c_char) -> c_int {
    unsafe {
        let sum = "I".to_string();
        //let sum = add(CStr::from_ptr(num_l).to_str().unwrap(), CStr::from_ptr(num_r).to_str().unwrap());
        //if sum.len() > (dstlen - 1) {
            //1
        //} else {
            let csum = CString::new(sum).unwrap();
            strncpy(dst, csum.as_ptr() as *const i8, dstlen);
            0
        //}
    }
}
<|endoftext|>


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

use std::collections::BTreeMap;
use std::process::exit;
use std::io::Write;
use std::io::stdout;
use std::path::PathBuf;
use std::fs::OpenOptions;

use vobject::vcard::Vcard;
use vobject::write_component;
use toml_query::read::TomlValueReadExt;
use toml::Value;
use uuid::Uuid;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::trace::trace_error;
use libimagerror::trace::trace_error_exit;
use libimagutil::warn_result::WarnResult;
use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;

const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml");

#[cfg(test)]
mod test {
    use toml::Value;
    use super::TEMPLATE;

    const TEMPLATE_WITH_DATA : &'static str = include_str!("../static/new-contact-template-test.toml");

    #[test]
    fn test_validity_template_toml() {
        let _ : Value = ::toml::de::from_str(TEMPLATE).unwrap();
    }

    #[test]
    fn test_validity_template_toml_without_comments() {
        let _ : Value = ::toml::de::from_str(TEMPLATE_WITH_DATA).unwrap();
    }
}

macro_rules! ask_continue {
    { yes => $yes:expr; no => $no:expr } => {
        if ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true)) {
            $yes
        } else {
            $no
        }
    };
}

pub fn create(rt: &Runtime) {
    let scmd         = rt.cli().subcommand_matches("create").unwrap();
    let mut template = String::from(TEMPLATE);

    let (mut dest, location) : (Box<Write>, Option<PathBuf>) = {
        if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) {
            if fl.is_file() {
                error!("File does exist, cannot create/override");
                exit(1);
            } else if fl.is_dir() {
                fl.set_file_name(Uuid::new_v4().hyphenated().to_string());
            }

            let file = OpenOptions::new()
                .write(true)
                .create_new(true)
                .open(fl.clone())
                .map_warn_err_str("Cannot create/open destination File. Stopping.")
                .map_err_trace_exit_unwrap(1);

            (Box::new(file), Some(fl))
        } else {
            (Box::new(stdout()), None)
        }
    };

    loop {
        ::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template)
            .map_warn_err_str("Editing failed.")
            .map_err_trace_exit_unwrap(1);

        if template == TEMPLATE || template.is_empty() {
            error!("No (changed) content in tempfile. Not doing anything.");
            exit(2);
        }

        match ::toml::de::from_str(&template).map(parse_toml_into_vcard) {
            Err(e) => {
                error!("Error parsing template");
                trace_error(&e);
                ask_continue! { yes => continue; no => exit(1) };
            },

            Ok(None)        => continue,
            Ok(Some(vcard)) => {
                debug!("Parsed:\n{:#?}", vcard);

                if template == TEMPLATE || template.is_empty() {
                    if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false)) {
                        exit(1);
                    } else {
                        continue;
                    }
                }

                let vcard_string = write_component(&vcard);
                if let Err(e) = dest.write_all(&vcard_string.as_bytes()) {
                    warn!("Error while writing out vcard content");
                    trace_error_exit(&e, 1);
                }

                break;
            }
        }
    }

    if let Some(location) = location {
        if !scmd.is_present("dont-track") {
            let flags = RefFlags::default()
                .with_content_hashing(true)
                .with_permission_tracking(false);

            RefStore::create(rt.store(), location, flags)
                .map_err_trace_exit_unwrap(1);

            info!("Created entry in store");
        } else {
            info!("Not creating entry in store");
        }
    } else {
        info!("Cannot track stdout-created contact information");
    }

    info!("Ready");
}

fn parse_toml_into_vcard(toml: Value) -> Option<Vcard> {
    let mut vcard = Vcard::default();

    { // parse name
        debug!("Parsing name");
        let firstname = read_str_from_toml(&toml, "name.first");
        trace!("firstname = {:?}", firstname);

        let lastname  = read_str_from_toml(&toml, "name.last");
        trace!("lastname = {:?}", lastname);

        vcard = vcard.with_name(parameters!(),
            read_str_from_toml(&toml, "name.prefix"),
            firstname.clone(),
            read_str_from_toml(&toml, "name.additional"),
            lastname.clone(),
            read_str_from_toml(&toml, "name.suffix"));

        if let (Some(first), Some(last)) = (firstname, lastname) {
            trace!("Building fullname: '{} {}'", first, last);
            vcard = vcard.with_fullname(format!("{} {}", first, last));
        }
    }

    { // parse nicknames
        debug!("Parsing nicknames");
        match toml.read("nickname").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let nicktype = match read_str_from_toml(element, "type") {
                        None    => BTreeMap::new(),
                        Some(p) => {
                            let mut m = BTreeMap::new();
                            m.insert("TYPE".into(), p);
                            m
                        },
                    };

                    let name = match read_str_from_toml(element, "name") {
                        Some(p) => p,
                        None    => {
                            error!("Key 'nickname.[{}].name' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                    };

                    trace!("nick type = {:?}", nicktype);
                    trace!("name      = {:?}", name);

                    vcard = vcard.with_nickname(nicktype, name);
                }
            },

            Some(&Value::String(ref name)) => {
                vcard = vcard.with_nickname(parameters!(), name.clone());
            }

            Some(_) => {
                error!("Type Error: Expected Array or String at 'nickname'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse organisation
        debug!("Parsing organisation");

        if let Some(orgs) = read_strary_from_toml(&toml, "organisation.name") {
            trace!("orgs = {:?}", orgs);
            vcard = vcard.with_org(orgs);
        } else {
            error!("Key 'organisation.name' missing");
            ask_continue! { yes => return None; no => exit(1) };
        }

        if let Some(title) = read_str_from_toml(&toml, "organisation.title") {
            trace!("title = {:?}", title);
            vcard = vcard.with_title(title);
        }

        if let Some(role) = read_str_from_toml(&toml, "organisation.role") {
            trace!("role = {:?}", role);
            vcard = vcard.with_role(role);
        }
    }

    { // parse phone
        debug!("Parse phone");
        match toml.read("phone").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let phonetype = match read_str_from_toml(element, "type") {
                        Some(p) => p,
                        None => {
                            error!("Key 'phones.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        }
                    };

                    let number = match read_str_from_toml(element, "number") {
                        Some(p) => p,
                        None => {
                            error!("Key 'phones.[{}].number' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        }
                    };

                    trace!("phonetype = {:?}", phonetype);
                    trace!("number    = {:?}", number);

                    vcard = vcard.with_tel(parameters!("TYPE" => phonetype), number);
                }
            },

            Some(_) => {
                error!("Expected Array at 'phones'.");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse address
        debug!("Parsing address");
        match toml.read("addresses").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let adrtype  = match read_str_from_toml(element, "type") {
                        None => {
                            error!("Key 'adresses.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    };
                    trace!("adrtype = {:?}", adrtype);

                    let bx       = read_str_from_toml(element, "box");
                    let extended = read_str_from_toml(element, "extended");
                    let street   = read_str_from_toml(element, "street");
                    let code     = read_str_from_toml(element, "code");
                    let city     = read_str_from_toml(element, "city");
                    let region   = read_str_from_toml(element, "region");
                    let country  = read_str_from_toml(element, "country");

                    trace!("bx       = {:?}", bx);
                    trace!("extended = {:?}", extended);
                    trace!("street   = {:?}", street);
                    trace!("code     = {:?}", code);
                    trace!("city     = {:?}", city);
                    trace!("region   = {:?}", region);
                    trace!("country  = {:?}", country);

                    vcard = vcard.with_adr(
                        parameters!("TYPE" => adrtype),
                        bx, extended, street, code, city, region, country
                    );
                }
            },

            Some(_) => {
                error!("Type Error: Expected Array at 'addresses'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse email
        debug!("Parsing email");
        match toml.read("email").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let mailtype  = match read_str_from_toml(element, "type") {
                        None => {
                            error!("Error: 'email.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    }; // TODO: Unused, because unsupported by vobject

                    let mail = match read_str_from_toml(element, "addr") {
                        None => {
                            error!("Error: 'email.[{}].addr' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    };

                    trace!("mailtype = {:?} (UNUSED)", mailtype);
                    trace!("mail     = {:?}", mail);

                    vcard = vcard.with_email(mail);
                }
            },

            Some(_) => {
                error!("Type Error: Expected Array at 'email'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse others
        debug!("Parsing others");
        if let Some(categories) = read_strary_from_toml(&toml, "others.categories") {
            vcard = vcard.with_categories(categories);
        } else {
            debug!("No categories");
        }

        if let Some(webpage) = read_str_from_toml(&toml, "others.webpage") {
            vcard = vcard.with_url(webpage);
        } else {
            debug!("No webpage");
        }

        if let Some(note) = read_str_from_toml(&toml, "others.note") {
            vcard = vcard.with_note(note);
        } else {
            debug!("No note");
        }

    }

    Some(vcard)
}

fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>> {
    match toml.read(path).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
        Ok(Some(&Value::Array(ref vec))) => {
            let mut v = Vec::new();
            for elem in vec {
                match *elem {
                    Value::String(ref s) => v.push(s.clone()),
                    _ => {
                        error!("Type Error: '{}' must be Array<String>", path);
                        return None
                    },
                }
            }

            Some(v)
        }
        Ok(Some(&Value::String(ref s))) => {
            warn!("Having String, wanting Array<String> ... going to auto-fix");
            Some(vec![s.clone()])
        },
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be Array<String>", path);
            None
        },
        Ok(None) => None,
        Err(_) => None,
    }
}

fn read_str_from_toml(toml: &Value, path: &'static str) -> Option<String> {
    let v = toml.read(path)
        .map_warn_err_str(&format!("Failed to read value at '{}'", path));

    match v {
        Ok(Some(&Value::String(ref s))) => Some(s.clone()),
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be String", path);
            None
        },
        Ok(None) => {
            error!("Expected '{}' to be present, but is not.", path);
            None
        },
        Err(e) => {
            trace_error(&e);
            None
        }
    }
}

#[cfg(test)]
mod test_entry {
    // TODO
}

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

use std::collections::BTreeMap;
use std::process::exit;
use std::io::Write;
use std::io::stdout;
use std::path::PathBuf;
use std::fs::OpenOptions;

use vobject::vcard::Vcard;
use vobject::write_component;
use toml_query::read::TomlValueReadExt;
use toml::Value;
use uuid::Uuid;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::trace::trace_error;
use libimagerror::trace::trace_error_exit;
use libimagutil::warn_result::WarnResult;
use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;

const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml");

#[cfg(test)]
mod test {
    use toml::Value;
    use super::TEMPLATE;

    const TEMPLATE_WITH_DATA : &'static str = include_str!("../static/new-contact-template-test.toml");

    #[test]
    fn test_validity_template_toml() {
        let _ : Value = ::toml::de::from_str(TEMPLATE).unwrap();
    }

    #[test]
    fn test_validity_template_toml_without_comments() {
        let _ : Value = ::toml::de::from_str(TEMPLATE_WITH_DATA).unwrap();
    }
}

macro_rules! ask_continue {
    { yes => $yes:expr; no => $no:expr } => {
        if ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true)) {
            $yes
        } else {
            $no
        }
    };
}

pub fn create(rt: &Runtime) {
    let scmd         = rt.cli().subcommand_matches("create").unwrap();
    let mut template = String::from(TEMPLATE);

    let (mut dest, location) : (Box<Write>, Option<PathBuf>) = {
        if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) {
            if fl.is_file() {
                error!("File does exist, cannot create/override");
                exit(1);
            } else if fl.is_dir() {
                fl.set_file_name(Uuid::new_v4().hyphenated().to_string());
            }

            let file = OpenOptions::new()
                .write(true)
                .create_new(true)
                .open(fl.clone())
                .map_warn_err_str("Cannot create/open destination File. Stopping.")
                .map_err_trace_exit_unwrap(1);

            (Box::new(file), Some(fl))
        } else {
            (Box::new(stdout()), None)
        }
    };

    loop {
        ::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template)
            .map_warn_err_str("Editing failed.")
            .map_err_trace_exit_unwrap(1);

        if template == TEMPLATE || template.is_empty() {
            error!("No (changed) content in tempfile. Not doing anything.");
            exit(2);
        }

        match ::toml::de::from_str(&template).map(parse_toml_into_vcard) {
            Err(e) => {
                error!("Error parsing template");
                trace_error(&e);
                ask_continue! { yes => continue; no => exit(1) };
            },

            Ok(None)        => continue,
            Ok(Some(vcard)) => {
                debug!("Parsed:\n{:#?}", vcard);

                if template == TEMPLATE || template.is_empty() {
                    if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false)) {
                        exit(1);
                    } else {
                        continue;
                    }
                }

                let vcard_string = write_component(&vcard);
                if let Err(e) = dest.write_all(&vcard_string.as_bytes()) {
                    warn!("Error while writing out vcard content");
                    trace_error_exit(&e, 1);
                }

                break;
            }
        }
    }

    if let Some(location) = location {
        if !scmd.is_present("dont-track") {
            let flags = RefFlags::default()
                .with_content_hashing(true)
                .with_permission_tracking(false);

            RefStore::create(rt.store(), location, flags)
                .map_err_trace_exit_unwrap(1);

            info!("Created entry in store");
        } else {
            info!("Not creating entry in store");
        }
    } else {
        info!("Cannot track stdout-created contact information");
    }

    info!("Ready");
}

fn parse_toml_into_vcard(toml: Value) -> Option<Vcard> {
    let mut vcard = Vcard::default();

    { // parse name
        debug!("Parsing name");
        let firstname = read_str_from_toml(&toml, "name.first");
        trace!("firstname = {:?}", firstname);

        let lastname  = read_str_from_toml(&toml, "name.last");
        trace!("lastname = {:?}", lastname);

        vcard = vcard.with_name(parameters!(),
            read_str_from_toml(&toml, "name.prefix"),
            firstname.clone(),
            read_str_from_toml(&toml, "name.additional"),
            lastname.clone(),
            read_str_from_toml(&toml, "name.suffix"));

        if let (Some(first), Some(last)) = (firstname, lastname) {
            trace!("Building fullname: '{} {}'", first, last);
            vcard = vcard.with_fullname(format!("{} {}", first, last));
        }
    }

    { // parse personal
        debug!("Parsing person information");
        let birthday = read_str_from_toml(&toml, "person.birthday");
        trace!("birthday = {:?}", birthday);

        if let Some(bday) = birthday {
            vcard = vcard.with_bday(parameters!(), bday);
        }
    }

    { // parse nicknames
        debug!("Parsing nicknames");
        match toml.read("nickname").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let nicktype = match read_str_from_toml(element, "type") {
                        None    => BTreeMap::new(),
                        Some(p) => {
                            let mut m = BTreeMap::new();
                            m.insert("TYPE".into(), p);
                            m
                        },
                    };

                    let name = match read_str_from_toml(element, "name") {
                        Some(p) => p,
                        None    => {
                            error!("Key 'nickname.[{}].name' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                    };

                    trace!("nick type = {:?}", nicktype);
                    trace!("name      = {:?}", name);

                    vcard = vcard.with_nickname(nicktype, name);
                }
            },

            Some(&Value::String(ref name)) => {
                vcard = vcard.with_nickname(parameters!(), name.clone());
            }

            Some(_) => {
                error!("Type Error: Expected Array or String at 'nickname'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse organisation
        debug!("Parsing organisation");

        if let Some(orgs) = read_strary_from_toml(&toml, "organisation.name") {
            trace!("orgs = {:?}", orgs);
            vcard = vcard.with_org(orgs);
        } else {
            error!("Key 'organisation.name' missing");
            ask_continue! { yes => return None; no => exit(1) };
        }

        if let Some(title) = read_str_from_toml(&toml, "organisation.title") {
            trace!("title = {:?}", title);
            vcard = vcard.with_title(title);
        }

        if let Some(role) = read_str_from_toml(&toml, "organisation.role") {
            trace!("role = {:?}", role);
            vcard = vcard.with_role(role);
        }
    }

    { // parse phone
        debug!("Parse phone");
        match toml.read("phone").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let phonetype = match read_str_from_toml(element, "type") {
                        Some(p) => p,
                        None => {
                            error!("Key 'phones.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        }
                    };

                    let number = match read_str_from_toml(element, "number") {
                        Some(p) => p,
                        None => {
                            error!("Key 'phones.[{}].number' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        }
                    };

                    trace!("phonetype = {:?}", phonetype);
                    trace!("number    = {:?}", number);

                    vcard = vcard.with_tel(parameters!("TYPE" => phonetype), number);
                }
            },

            Some(_) => {
                error!("Expected Array at 'phones'.");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse address
        debug!("Parsing address");
        match toml.read("addresses").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let adrtype  = match read_str_from_toml(element, "type") {
                        None => {
                            error!("Key 'adresses.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    };
                    trace!("adrtype = {:?}", adrtype);

                    let bx       = read_str_from_toml(element, "box");
                    let extended = read_str_from_toml(element, "extended");
                    let street   = read_str_from_toml(element, "street");
                    let code     = read_str_from_toml(element, "code");
                    let city     = read_str_from_toml(element, "city");
                    let region   = read_str_from_toml(element, "region");
                    let country  = read_str_from_toml(element, "country");

                    trace!("bx       = {:?}", bx);
                    trace!("extended = {:?}", extended);
                    trace!("street   = {:?}", street);
                    trace!("code     = {:?}", code);
                    trace!("city     = {:?}", city);
                    trace!("region   = {:?}", region);
                    trace!("country  = {:?}", country);

                    vcard = vcard.with_adr(
                        parameters!("TYPE" => adrtype),
                        bx, extended, street, code, city, region, country
                    );
                }
            },

            Some(_) => {
                error!("Type Error: Expected Array at 'addresses'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse email
        debug!("Parsing email");
        match toml.read("email").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let mailtype  = match read_str_from_toml(element, "type") {
                        None => {
                            error!("Error: 'email.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    }; // TODO: Unused, because unsupported by vobject

                    let mail = match read_str_from_toml(element, "addr") {
                        None => {
                            error!("Error: 'email.[{}].addr' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    };

                    trace!("mailtype = {:?} (UNUSED)", mailtype);
                    trace!("mail     = {:?}", mail);

                    vcard = vcard.with_email(mail);
                }
            },

            Some(_) => {
                error!("Type Error: Expected Array at 'email'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse others
        debug!("Parsing others");
        if let Some(categories) = read_strary_from_toml(&toml, "others.categories") {
            vcard = vcard.with_categories(categories);
        } else {
            debug!("No categories");
        }

        if let Some(webpage) = read_str_from_toml(&toml, "others.webpage") {
            vcard = vcard.with_url(webpage);
        } else {
            debug!("No webpage");
        }

        if let Some(note) = read_str_from_toml(&toml, "others.note") {
            vcard = vcard.with_note(note);
        } else {
            debug!("No note");
        }

    }

    Some(vcard)
}

fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>> {
    match toml.read(path).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
        Ok(Some(&Value::Array(ref vec))) => {
            let mut v = Vec::new();
            for elem in vec {
                match *elem {
                    Value::String(ref s) => v.push(s.clone()),
                    _ => {
                        error!("Type Error: '{}' must be Array<String>", path);
                        return None
                    },
                }
            }

            Some(v)
        }
        Ok(Some(&Value::String(ref s))) => {
            warn!("Having String, wanting Array<String> ... going to auto-fix");
            Some(vec![s.clone()])
        },
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be Array<String>", path);
            None
        },
        Ok(None) => None,
        Err(_) => None,
    }
}

fn read_str_from_toml(toml: &Value, path: &'static str) -> Option<String> {
    let v = toml.read(path)
        .map_warn_err_str(&format!("Failed to read value at '{}'", path));

    match v {
        Ok(Some(&Value::String(ref s))) => Some(s.clone()),
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be String", path);
            None
        },
        Ok(None) => {
            error!("Expected '{}' to be present, but is not.", path);
            None
        },
        Err(e) => {
            trace_error(&e);
            None
        }
    }
}

#[cfg(test)]
mod test_entry {
    // TODO
}

<|endoftext|>


<commit_before>use crate::error::{ParseError, ParseErrorKind, ParseErrorKind::*};
use std::fmt;
use std::iter;
use std::str::{self, FromStr};

/// A cfg expression.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum CfgExpr {
    Not(Box<CfgExpr>),
    All(Vec<CfgExpr>),
    Any(Vec<CfgExpr>),
    Value(Cfg),
}

/// A cfg value.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum Cfg {
    /// A named cfg value, like `unix`.
    Name(String),
    /// A key/value cfg pair, like `target_os = "linux"`.
    KeyPair(String, String),
}

#[derive(PartialEq)]
enum Token<'a> {
    LeftParen,
    RightParen,
    Ident(&'a str),
    Comma,
    Equals,
    String(&'a str),
}

#[derive(Clone)]
struct Tokenizer<'a> {
    s: iter::Peekable<str::CharIndices<'a>>,
    orig: &'a str,
}

struct Parser<'a> {
    t: Tokenizer<'a>,
}

impl Cfg {
    pub(crate) fn validate_as_target(&self) -> Result<(), ParseErrorKind> {
        match self {
            Cfg::Name(name) => match name.as_str() {
                "unix" | "windows" => Ok(()),
                _ => Err(InvalidCfgName(name.to_string())),
            },
            Cfg::KeyPair(name, _) => match name.as_str() {
                "target_arch"
                | "target_feature"
                | "target_os"
                | "target_family"
                | "target_env"
                | "target_endian"
                | "target_pointer_width"
                | "target_vendor" => Ok(()),
                _ => Err(InvalidCfgKey(name.to_string())),
            },
        }
    }
}

impl FromStr for Cfg {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Cfg, Self::Err> {
        let mut p = Parser::new(s);
        let e = p.cfg()?;
        if let Some(rest) = p.rest() {
            return Err(ParseError::new(
                p.t.orig,
                UnterminatedExpression(rest.to_string()),
            ));
        }
        Ok(e)
    }
}

impl fmt::Display for Cfg {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Cfg::Name(ref s) => s.fmt(f),
            Cfg::KeyPair(ref k, ref v) => write!(f, "{} = \"{}\"", k, v),
        }
    }
}

impl CfgExpr {
    /// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
    pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
        if key.starts_with("cfg(") && key.ends_with(')') {
            let cfg = &key[4..key.len() - 1];

            CfgExpr::from_str(cfg)
                .ok()
                .map(|ce| ce.matches(target_cfg))
                .unwrap_or(false)
        } else {
            false
        }
    }

    pub fn matches(&self, cfg: &[Cfg]) -> bool {
        match *self {
            CfgExpr::Not(ref e) => !e.matches(cfg),
            CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg)),
            CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg)),
            CfgExpr::Value(ref e) => cfg.contains(e),
        }
    }

    pub(crate) fn validate_as_target(&self) -> Result<(), ParseErrorKind> {
        match *self {
            CfgExpr::Not(ref e) => e.validate_as_target()?,
            CfgExpr::All(ref e) | CfgExpr::Any(ref e) => {
                for e in e {
                    e.validate_as_target()?;
                }
            }
            CfgExpr::Value(ref e) => e.validate_as_target()?,
        }
        Ok(())
    }
}

impl FromStr for CfgExpr {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<CfgExpr, Self::Err> {
        let mut p = Parser::new(s);
        let e = p.expr()?;
        if let Some(rest) = p.rest() {
            return Err(ParseError::new(
                p.t.orig,
                UnterminatedExpression(rest.to_string()),
            ));
        }
        Ok(e)
    }
}

impl fmt::Display for CfgExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            CfgExpr::Not(ref e) => write!(f, "not({})", e),
            CfgExpr::All(ref e) => write!(f, "all({})", CommaSep(e)),
            CfgExpr::Any(ref e) => write!(f, "any({})", CommaSep(e)),
            CfgExpr::Value(ref e) => write!(f, "{}", e),
        }
    }
}

struct CommaSep<'a, T>(&'a [T]);

impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (i, v) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{}", v)?;
        }
        Ok(())
    }
}

impl<'a> Parser<'a> {
    fn new(s: &'a str) -> Parser<'a> {
        Parser {
            t: Tokenizer {
                s: s.char_indices().peekable(),
                orig: s,
            },
        }
    }

    fn expr(&mut self) -> Result<CfgExpr, ParseError> {
        match self.peek() {
            Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => {
                self.t.next();
                let mut e = Vec::new();
                self.eat(&Token::LeftParen)?;
                while !self.r#try(&Token::RightParen) {
                    e.push(self.expr()?);
                    if !self.r#try(&Token::Comma) {
                        self.eat(&Token::RightParen)?;
                        break;
                    }
                }
                if op == "all" {
                    Ok(CfgExpr::All(e))
                } else {
                    Ok(CfgExpr::Any(e))
                }
            }
            Some(Ok(Token::Ident("not"))) => {
                self.t.next();
                self.eat(&Token::LeftParen)?;
                let e = self.expr()?;
                self.eat(&Token::RightParen)?;
                Ok(CfgExpr::Not(Box::new(e)))
            }
            Some(Ok(..)) => self.cfg().map(CfgExpr::Value),
            Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
            None => Err(ParseError::new(
                self.t.orig,
                IncompleteExpr("start of a cfg expression"),
            )),
        }
    }

    fn cfg(&mut self) -> Result<Cfg, ParseError> {
        match self.t.next() {
            Some(Ok(Token::Ident(name))) => {
                let e = if self.r#try(&Token::Equals) {
                    let val = match self.t.next() {
                        Some(Ok(Token::String(s))) => s,
                        Some(Ok(t)) => {
                            return Err(ParseError::new(
                                self.t.orig,
                                UnexpectedToken {
                                    expected: "a string",
                                    found: t.classify(),
                                },
                            ))
                        }
                        Some(Err(e)) => return Err(e),
                        None => {
                            return Err(ParseError::new(self.t.orig, IncompleteExpr("a string")))
                        }
                    };
                    Cfg::KeyPair(name.to_string(), val.to_string())
                } else {
                    Cfg::Name(name.to_string())
                };
                Ok(e)
            }
            Some(Ok(t)) => Err(ParseError::new(
                self.t.orig,
                UnexpectedToken {
                    expected: "identifier",
                    found: t.classify(),
                },
            )),
            Some(Err(e)) => Err(e),
            None => Err(ParseError::new(self.t.orig, IncompleteExpr("identifier"))),
        }
    }

    fn peek(&mut self) -> Option<Result<Token<'a>, ParseError>> {
        self.t.clone().next()
    }

    fn r#try(&mut self, token: &Token<'a>) -> bool {
        match self.peek() {
            Some(Ok(ref t)) if token == t => {}
            _ => return false,
        }
        self.t.next();
        true
    }

    fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> {
        match self.t.next() {
            Some(Ok(ref t)) if token == t => Ok(()),
            Some(Ok(t)) => Err(ParseError::new(
                self.t.orig,
                UnexpectedToken {
                    expected: token.classify(),
                    found: t.classify(),
                },
            )),
            Some(Err(e)) => Err(e),
            None => Err(ParseError::new(
                self.t.orig,
                IncompleteExpr(token.classify()),
            )),
        }
    }

    /// Returns the rest of the input from the current location.
    fn rest(&self) -> Option<&str> {
        let mut s = self.t.s.clone();
        loop {
            match s.next() {
                Some((_, ' ')) => {}
                Some((start, _ch)) => return Some(&self.t.orig[start..]),
                None => return None,
            }
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Token<'a>, ParseError>;

    fn next(&mut self) -> Option<Result<Token<'a>, ParseError>> {
        loop {
            match self.s.next() {
                Some((_, ' ')) => {}
                Some((_, '(')) => return Some(Ok(Token::LeftParen)),
                Some((_, ')')) => return Some(Ok(Token::RightParen)),
                Some((_, ',')) => return Some(Ok(Token::Comma)),
                Some((_, '=')) => return Some(Ok(Token::Equals)),
                Some((start, '"')) => {
                    while let Some((end, ch)) = self.s.next() {
                        if ch == '"' {
                            return Some(Ok(Token::String(&self.orig[start + 1..end])));
                        }
                    }
                    return Some(Err(ParseError::new(self.orig, UnterminatedString)));
                }
                Some((start, ch)) if is_ident_start(ch) => {
                    while let Some(&(end, ch)) = self.s.peek() {
                        if !is_ident_rest(ch) {
                            return Some(Ok(Token::Ident(&self.orig[start..end])));
                        } else {
                            self.s.next();
                        }
                    }
                    return Some(Ok(Token::Ident(&self.orig[start..])));
                }
                Some((_, ch)) => {
                    return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));
                }
                None => return None,
            }
        }
    }
}

fn is_ident_start(ch: char) -> bool {
    ch == '_' || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')
}

fn is_ident_rest(ch: char) -> bool {
    is_ident_start(ch) || ('0' <= ch && ch <= '9')
}

impl<'a> Token<'a> {
    fn classify(&self) -> &'static str {
        match *self {
            Token::LeftParen => "`(`",
            Token::RightParen => "`)`",
            Token::Ident(..) => "an identifier",
            Token::Comma => "`,`",
            Token::Equals => "`=`",
            Token::String(..) => "a string",
        }
    }
}

#[test]
fn cfg_validate_as_target() {
    fn p(s: &str) -> CfgExpr {
        s.parse().unwrap()
    }

    assert!(p("unix").validate_as_target().is_ok());
    assert!(p("windows").validate_as_target().is_ok());
    assert!(p("any(not(unix), windows)").validate_as_target().is_ok());

    assert!(p("target_arch = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_feature = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_os = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_family = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_env = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_endian = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_pointer_width = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_vendor = \"abc\"").validate_as_target().is_ok());

    assert!(p("debug_assertions").validate_as_target().is_err());
    assert!(p("foo").validate_as_target().is_err());
    assert!(p("any(not(debug_assertions), windows)").validate_as_target().is_err());

    assert!(p("feature = \"abc\"").validate_as_target().is_err());
    assert!(p("bar = \"def\"").validate_as_target().is_err());
    assert!(p("any(not(feature = \"def\"))").validate_as_target().is_err());
}
<commit_msg>Fix test formatting<commit_after>use crate::error::{ParseError, ParseErrorKind, ParseErrorKind::*};
use std::fmt;
use std::iter;
use std::str::{self, FromStr};

/// A cfg expression.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum CfgExpr {
    Not(Box<CfgExpr>),
    All(Vec<CfgExpr>),
    Any(Vec<CfgExpr>),
    Value(Cfg),
}

/// A cfg value.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum Cfg {
    /// A named cfg value, like `unix`.
    Name(String),
    /// A key/value cfg pair, like `target_os = "linux"`.
    KeyPair(String, String),
}

#[derive(PartialEq)]
enum Token<'a> {
    LeftParen,
    RightParen,
    Ident(&'a str),
    Comma,
    Equals,
    String(&'a str),
}

#[derive(Clone)]
struct Tokenizer<'a> {
    s: iter::Peekable<str::CharIndices<'a>>,
    orig: &'a str,
}

struct Parser<'a> {
    t: Tokenizer<'a>,
}

impl Cfg {
    pub(crate) fn validate_as_target(&self) -> Result<(), ParseErrorKind> {
        match self {
            Cfg::Name(name) => match name.as_str() {
                "unix" | "windows" => Ok(()),
                _ => Err(InvalidCfgName(name.to_string())),
            },
            Cfg::KeyPair(name, _) => match name.as_str() {
                "target_arch"
                | "target_feature"
                | "target_os"
                | "target_family"
                | "target_env"
                | "target_endian"
                | "target_pointer_width"
                | "target_vendor" => Ok(()),
                _ => Err(InvalidCfgKey(name.to_string())),
            },
        }
    }
}

impl FromStr for Cfg {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Cfg, Self::Err> {
        let mut p = Parser::new(s);
        let e = p.cfg()?;
        if let Some(rest) = p.rest() {
            return Err(ParseError::new(
                p.t.orig,
                UnterminatedExpression(rest.to_string()),
            ));
        }
        Ok(e)
    }
}

impl fmt::Display for Cfg {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Cfg::Name(ref s) => s.fmt(f),
            Cfg::KeyPair(ref k, ref v) => write!(f, "{} = \"{}\"", k, v),
        }
    }
}

impl CfgExpr {
    /// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
    pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
        if key.starts_with("cfg(") && key.ends_with(')') {
            let cfg = &key[4..key.len() - 1];

            CfgExpr::from_str(cfg)
                .ok()
                .map(|ce| ce.matches(target_cfg))
                .unwrap_or(false)
        } else {
            false
        }
    }

    pub fn matches(&self, cfg: &[Cfg]) -> bool {
        match *self {
            CfgExpr::Not(ref e) => !e.matches(cfg),
            CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg)),
            CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg)),
            CfgExpr::Value(ref e) => cfg.contains(e),
        }
    }

    pub(crate) fn validate_as_target(&self) -> Result<(), ParseErrorKind> {
        match *self {
            CfgExpr::Not(ref e) => e.validate_as_target()?,
            CfgExpr::All(ref e) | CfgExpr::Any(ref e) => {
                for e in e {
                    e.validate_as_target()?;
                }
            }
            CfgExpr::Value(ref e) => e.validate_as_target()?,
        }
        Ok(())
    }
}

impl FromStr for CfgExpr {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<CfgExpr, Self::Err> {
        let mut p = Parser::new(s);
        let e = p.expr()?;
        if let Some(rest) = p.rest() {
            return Err(ParseError::new(
                p.t.orig,
                UnterminatedExpression(rest.to_string()),
            ));
        }
        Ok(e)
    }
}

impl fmt::Display for CfgExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            CfgExpr::Not(ref e) => write!(f, "not({})", e),
            CfgExpr::All(ref e) => write!(f, "all({})", CommaSep(e)),
            CfgExpr::Any(ref e) => write!(f, "any({})", CommaSep(e)),
            CfgExpr::Value(ref e) => write!(f, "{}", e),
        }
    }
}

struct CommaSep<'a, T>(&'a [T]);

impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (i, v) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{}", v)?;
        }
        Ok(())
    }
}

impl<'a> Parser<'a> {
    fn new(s: &'a str) -> Parser<'a> {
        Parser {
            t: Tokenizer {
                s: s.char_indices().peekable(),
                orig: s,
            },
        }
    }

    fn expr(&mut self) -> Result<CfgExpr, ParseError> {
        match self.peek() {
            Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => {
                self.t.next();
                let mut e = Vec::new();
                self.eat(&Token::LeftParen)?;
                while !self.r#try(&Token::RightParen) {
                    e.push(self.expr()?);
                    if !self.r#try(&Token::Comma) {
                        self.eat(&Token::RightParen)?;
                        break;
                    }
                }
                if op == "all" {
                    Ok(CfgExpr::All(e))
                } else {
                    Ok(CfgExpr::Any(e))
                }
            }
            Some(Ok(Token::Ident("not"))) => {
                self.t.next();
                self.eat(&Token::LeftParen)?;
                let e = self.expr()?;
                self.eat(&Token::RightParen)?;
                Ok(CfgExpr::Not(Box::new(e)))
            }
            Some(Ok(..)) => self.cfg().map(CfgExpr::Value),
            Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
            None => Err(ParseError::new(
                self.t.orig,
                IncompleteExpr("start of a cfg expression"),
            )),
        }
    }

    fn cfg(&mut self) -> Result<Cfg, ParseError> {
        match self.t.next() {
            Some(Ok(Token::Ident(name))) => {
                let e = if self.r#try(&Token::Equals) {
                    let val = match self.t.next() {
                        Some(Ok(Token::String(s))) => s,
                        Some(Ok(t)) => {
                            return Err(ParseError::new(
                                self.t.orig,
                                UnexpectedToken {
                                    expected: "a string",
                                    found: t.classify(),
                                },
                            ))
                        }
                        Some(Err(e)) => return Err(e),
                        None => {
                            return Err(ParseError::new(self.t.orig, IncompleteExpr("a string")))
                        }
                    };
                    Cfg::KeyPair(name.to_string(), val.to_string())
                } else {
                    Cfg::Name(name.to_string())
                };
                Ok(e)
            }
            Some(Ok(t)) => Err(ParseError::new(
                self.t.orig,
                UnexpectedToken {
                    expected: "identifier",
                    found: t.classify(),
                },
            )),
            Some(Err(e)) => Err(e),
            None => Err(ParseError::new(self.t.orig, IncompleteExpr("identifier"))),
        }
    }

    fn peek(&mut self) -> Option<Result<Token<'a>, ParseError>> {
        self.t.clone().next()
    }

    fn r#try(&mut self, token: &Token<'a>) -> bool {
        match self.peek() {
            Some(Ok(ref t)) if token == t => {}
            _ => return false,
        }
        self.t.next();
        true
    }

    fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> {
        match self.t.next() {
            Some(Ok(ref t)) if token == t => Ok(()),
            Some(Ok(t)) => Err(ParseError::new(
                self.t.orig,
                UnexpectedToken {
                    expected: token.classify(),
                    found: t.classify(),
                },
            )),
            Some(Err(e)) => Err(e),
            None => Err(ParseError::new(
                self.t.orig,
                IncompleteExpr(token.classify()),
            )),
        }
    }

    /// Returns the rest of the input from the current location.
    fn rest(&self) -> Option<&str> {
        let mut s = self.t.s.clone();
        loop {
            match s.next() {
                Some((_, ' ')) => {}
                Some((start, _ch)) => return Some(&self.t.orig[start..]),
                None => return None,
            }
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Token<'a>, ParseError>;

    fn next(&mut self) -> Option<Result<Token<'a>, ParseError>> {
        loop {
            match self.s.next() {
                Some((_, ' ')) => {}
                Some((_, '(')) => return Some(Ok(Token::LeftParen)),
                Some((_, ')')) => return Some(Ok(Token::RightParen)),
                Some((_, ',')) => return Some(Ok(Token::Comma)),
                Some((_, '=')) => return Some(Ok(Token::Equals)),
                Some((start, '"')) => {
                    while let Some((end, ch)) = self.s.next() {
                        if ch == '"' {
                            return Some(Ok(Token::String(&self.orig[start + 1..end])));
                        }
                    }
                    return Some(Err(ParseError::new(self.orig, UnterminatedString)));
                }
                Some((start, ch)) if is_ident_start(ch) => {
                    while let Some(&(end, ch)) = self.s.peek() {
                        if !is_ident_rest(ch) {
                            return Some(Ok(Token::Ident(&self.orig[start..end])));
                        } else {
                            self.s.next();
                        }
                    }
                    return Some(Ok(Token::Ident(&self.orig[start..])));
                }
                Some((_, ch)) => {
                    return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));
                }
                None => return None,
            }
        }
    }
}

fn is_ident_start(ch: char) -> bool {
    ch == '_' || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')
}

fn is_ident_rest(ch: char) -> bool {
    is_ident_start(ch) || ('0' <= ch && ch <= '9')
}

impl<'a> Token<'a> {
    fn classify(&self) -> &'static str {
        match *self {
            Token::LeftParen => "`(`",
            Token::RightParen => "`)`",
            Token::Ident(..) => "an identifier",
            Token::Comma => "`,`",
            Token::Equals => "`=`",
            Token::String(..) => "a string",
        }
    }
}

#[test]
fn cfg_validate_as_target() {
    fn p(s: &str) -> CfgExpr {
        s.parse().unwrap()
    }

    assert!(p("unix").validate_as_target().is_ok());
    assert!(p("windows").validate_as_target().is_ok());
    assert!(p("any(not(unix), windows)").validate_as_target().is_ok());

    assert!(p("target_arch = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_feature = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_os = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_family = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_env = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_endian = \"abc\"").validate_as_target().is_ok());
    assert!(p("target_pointer_width = \"abc\"")
        .validate_as_target()
        .is_ok());
    assert!(p("target_vendor = \"abc\"").validate_as_target().is_ok());

    assert!(p("debug_assertions").validate_as_target().is_err());
    assert!(p("foo").validate_as_target().is_err());
    assert!(p("any(not(debug_assertions), windows)")
        .validate_as_target()
        .is_err());

    assert!(p("feature = \"abc\"").validate_as_target().is_err());
    assert!(p("bar = \"def\"").validate_as_target().is_err());
    assert!(p("any(not(feature = \"def\"))")
        .validate_as_target()
        .is_err());
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here
use either::Either;

use hir_def::{
    child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId,
    LocationCtx, ModuleId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_syntax::{
    ast::{self, AstNode, NameOwner},
    match_ast, SyntaxNode,
};

use crate::{
    db::{AstDatabase, DefDatabase, HirDatabase},
    Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
    MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, Union,
};

pub trait FromSource: Sized {
    type Ast;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self>;
}

impl FromSource for Struct {
    type Ast = ast::StructDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Struct { id })
    }
}
impl FromSource for Union {
    type Ast = ast::UnionDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Union { id })
    }
}
impl FromSource for Enum {
    type Ast = ast::EnumDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Enum { id })
    }
}
impl FromSource for Trait {
    type Ast = ast::TraitDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Trait { id })
    }
}
impl FromSource for Function {
    type Ast = ast::FnDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
            Container::Trait(it) => it.id.child_from_source(db, src),
            Container::ImplBlock(it) => it.id.child_from_source(db, src),
            Container::Module(it) => it.id.child_from_source(db, src),
        }
        .map(Function::from)
    }
}

impl FromSource for Const {
    type Ast = ast::ConstDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
            Container::Trait(it) => it.id.child_from_source(db, src),
            Container::ImplBlock(it) => it.id.child_from_source(db, src),
            Container::Module(it) => it.id.child_from_source(db, src),
        }
        .map(Const::from)
    }
}
impl FromSource for Static {
    type Ast = ast::StaticDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
            Container::Module(it) => it.id.child_from_source(db, src).map(Static::from),
            Container::Trait(_) | Container::ImplBlock(_) => None,
        }
    }
}

impl FromSource for TypeAlias {
    type Ast = ast::TypeAliasDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
            Container::Trait(it) => it.id.child_from_source(db, src),
            Container::ImplBlock(it) => it.id.child_from_source(db, src),
            Container::Module(it) => it.id.child_from_source(db, src),
        }
        .map(TypeAlias::from)
    }
}

impl FromSource for MacroDef {
    type Ast = ast::MacroCall;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
        let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
        let krate = module.krate().crate_id();

        let ast_id = AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value));

        let id: MacroDefId = MacroDefId { krate, ast_id, kind };
        Some(MacroDef { id })
    }
}

impl FromSource for ImplBlock {
    type Ast = ast::ImplBlock;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(ImplBlock { id })
    }
}

impl FromSource for EnumVariant {
    type Ast = ast::EnumVariant;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let parent_enum = src.value.parent_enum();
        let src_enum = InFile { file_id: src.file_id, value: parent_enum };
        let parent_enum = Enum::from_source(db, src_enum)?;
        parent_enum.id.child_from_source(db, src).map(EnumVariant::from)
    }
}

impl FromSource for StructField {
    type Ast = FieldSource;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let variant_id: VariantId = match src.value {
            FieldSource::Named(ref field) => {
                let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = Struct::from_source(db, src)?;
                def.id.into()
            }
            FieldSource::Pos(ref field) => {
                let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = EnumVariant::from_source(db, src)?;
                EnumVariantId::from(def).into()
            }
        };
        let src = src.map(|field_source| match field_source {
            FieldSource::Pos(it) => Either::Left(it),
            FieldSource::Named(it) => Either::Right(it),
        });
        variant_id.child_from_source(db, src).map(StructField::from)
    }
}

impl Local {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = db.body_with_source_map(parent.into());
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent, pat_id })
    }
}

impl Module {
    pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
        let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);

        let parent_module = match parent_declaration {
            Some(parent_declaration) => {
                let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
                Module::from_declaration(db, src_parent)
            }
            None => {
                let source_file = db.parse(src.file_id.original_file(db)).tree();
                let src_parent =
                    InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
                Module::from_definition(db, src_parent)
            }
        }?;

        let child_name = src.value.name()?;
        parent_module.child(db, &child_name.as_name())
    }

    pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
        match src.value {
            ModuleSource::Module(ref module) => {
                assert!(!module.has_semi());
                return Module::from_declaration(
                    db,
                    InFile { file_id: src.file_id, value: module.clone() },
                );
            }
            ModuleSource::SourceFile(_) => (),
        };

        let original_file = src.file_id.original_file(db);

        let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(original_file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }
}

fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: InFile<N>) -> Option<DEF>
where
    N: AstNode,
    DEF: AstItemDef<N>,
{
    let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
    let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
    let ctx = LocationCtx::new(db, module.id, src.file_id);
    let items = db.ast_id_map(src.file_id);
    let item_id = items.ast_id(&src.value);
    Some(DEF::from_ast_id(ctx, item_id))
}

enum Container {
    Trait(Trait),
    ImplBlock(ImplBlock),
    Module(Module),
}

impl Container {
    fn find(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<Container> {
        // FIXME: this doesn't try to handle nested declarations
        for container in src.value.ancestors() {
            let res = match_ast! {
                match container {
                    ast::TraitDef(it) => {
                        let c = Trait::from_source(db, src.with_value(it))?;
                        Container::Trait(c)
                    },
                    ast::ImplBlock(it) => {
                        let c = ImplBlock::from_source(db, src.with_value(it))?;
                        Container::ImplBlock(c)
                     },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(db, src);
        let c = Module::from_definition(db, src.with_value(module_source))?;
        Some(Container::Module(c))
    }
}
<commit_msg>Reduce copy-paste<commit_after>//! FIXME: write short doc here
use either::Either;

use hir_def::{
    child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, ImplId,
    LocationCtx, ModuleId, TraitId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_syntax::{
    ast::{self, AstNode, NameOwner},
    match_ast, SyntaxNode,
};

use crate::{
    db::{AstDatabase, DefDatabase, HirDatabase},
    Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
    MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, Union,
};

pub trait FromSource: Sized {
    type Ast;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self>;
}

impl FromSource for Struct {
    type Ast = ast::StructDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Struct { id })
    }
}
impl FromSource for Union {
    type Ast = ast::UnionDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Union { id })
    }
}
impl FromSource for Enum {
    type Ast = ast::EnumDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Enum { id })
    }
}
impl FromSource for Trait {
    type Ast = ast::TraitDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Trait { id })
    }
}
impl FromSource for Function {
    type Ast = ast::FnDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?
            .child_from_source(db, src)
            .map(Function::from)
    }
}

impl FromSource for Const {
    type Ast = ast::ConstDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?
            .child_from_source(db, src)
            .map(Const::from)
    }
}
impl FromSource for Static {
    type Ast = ast::StaticDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
            Container::Module(it) => it.id.child_from_source(db, src).map(Static::from),
            Container::Trait(_) | Container::ImplBlock(_) => None,
        }
    }
}

impl FromSource for TypeAlias {
    type Ast = ast::TypeAliasDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?
            .child_from_source(db, src)
            .map(TypeAlias::from)
    }
}

impl FromSource for MacroDef {
    type Ast = ast::MacroCall;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
        let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
        let krate = module.krate().crate_id();

        let ast_id = AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value));

        let id: MacroDefId = MacroDefId { krate, ast_id, kind };
        Some(MacroDef { id })
    }
}

impl FromSource for ImplBlock {
    type Ast = ast::ImplBlock;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(ImplBlock { id })
    }
}

impl FromSource for EnumVariant {
    type Ast = ast::EnumVariant;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let parent_enum = src.value.parent_enum();
        let src_enum = InFile { file_id: src.file_id, value: parent_enum };
        let parent_enum = Enum::from_source(db, src_enum)?;
        parent_enum.id.child_from_source(db, src).map(EnumVariant::from)
    }
}

impl FromSource for StructField {
    type Ast = FieldSource;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let variant_id: VariantId = match src.value {
            FieldSource::Named(ref field) => {
                let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = Struct::from_source(db, src)?;
                def.id.into()
            }
            FieldSource::Pos(ref field) => {
                let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = EnumVariant::from_source(db, src)?;
                EnumVariantId::from(def).into()
            }
        };
        let src = src.map(|field_source| match field_source {
            FieldSource::Pos(it) => Either::Left(it),
            FieldSource::Named(it) => Either::Right(it),
        });
        variant_id.child_from_source(db, src).map(StructField::from)
    }
}

impl Local {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = db.body_with_source_map(parent.into());
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent, pat_id })
    }
}

impl Module {
    pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
        let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);

        let parent_module = match parent_declaration {
            Some(parent_declaration) => {
                let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
                Module::from_declaration(db, src_parent)
            }
            None => {
                let source_file = db.parse(src.file_id.original_file(db)).tree();
                let src_parent =
                    InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
                Module::from_definition(db, src_parent)
            }
        }?;

        let child_name = src.value.name()?;
        parent_module.child(db, &child_name.as_name())
    }

    pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
        match src.value {
            ModuleSource::Module(ref module) => {
                assert!(!module.has_semi());
                return Module::from_declaration(
                    db,
                    InFile { file_id: src.file_id, value: module.clone() },
                );
            }
            ModuleSource::SourceFile(_) => (),
        };

        let original_file = src.file_id.original_file(db);

        let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(original_file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }
}

fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: InFile<N>) -> Option<DEF>
where
    N: AstNode,
    DEF: AstItemDef<N>,
{
    let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
    let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
    let ctx = LocationCtx::new(db, module.id, src.file_id);
    let items = db.ast_id_map(src.file_id);
    let item_id = items.ast_id(&src.value);
    Some(DEF::from_ast_id(ctx, item_id))
}

enum Container {
    Trait(Trait),
    ImplBlock(ImplBlock),
    Module(Module),
}

impl Container {
    fn find(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<Container> {
        // FIXME: this doesn't try to handle nested declarations
        for container in src.value.ancestors() {
            let res = match_ast! {
                match container {
                    ast::TraitDef(it) => {
                        let c = Trait::from_source(db, src.with_value(it))?;
                        Container::Trait(c)
                    },
                    ast::ImplBlock(it) => {
                        let c = ImplBlock::from_source(db, src.with_value(it))?;
                        Container::ImplBlock(c)
                     },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(db, src);
        let c = Module::from_definition(db, src.with_value(module_source))?;
        Some(Container::Module(c))
    }
}

impl<CHILD, SOURCE> ChildFromSource<CHILD, SOURCE> for Container
where
    TraitId: ChildFromSource<CHILD, SOURCE>,
    ImplId: ChildFromSource<CHILD, SOURCE>,
    ModuleId: ChildFromSource<CHILD, SOURCE>,
{
    fn child_from_source(
        &self,
        db: &impl DefDatabase,
        child_source: InFile<SOURCE>,
    ) -> Option<CHILD> {
        match self {
            Container::Trait(it) => it.id.child_from_source(db, child_source),
            Container::ImplBlock(it) => it.id.child_from_source(db, child_source),
            Container::Module(it) => it.id.child_from_source(db, child_source),
        }
    }
}
<|endoftext|>