<commit_before>use crate::back::write::create_informational_target_machine;
use crate::llvm;
use libc::c_int;
use rustc_codegen_ssa::target_features::supported_target_features;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
use rustc_session::config::PrintRequest;
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy};
use std::ffi::{CStr, CString};

use std::slice;
use std::str;
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() + sess.target.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();

    fn llvm_arg_to_arg_name(full_arg: &str) -> &str {
        full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("")
    }

    let cg_opts = sess.opts.cg.llvm_args.iter();
    let tg_opts = sess.target.llvm_args.iter();
    let sess_args = cg_opts.chain(tg_opts);

    let user_specified_args: FxHashSet<_> =
        sess_args.clone().map(|s| llvm_arg_to_arg_name(s)).filter(|s| !s.is_empty()).collect();

    {
        // This adds the given argument to LLVM. Unless `force` is true
        // user specified arguments are *not* overridden.
        let mut add = |arg: &str, force: bool| {
            if force || !user_specified_args.contains(llvm_arg_to_arg_name(arg)) {
                let s = CString::new(arg).unwrap();
                llvm_args.push(s.as_ptr());
                llvm_c_strs.push(s);
            }
        };
        // Set the llvm "program name" to make usage and invalid argument messages more clear.
        add("rustc -Cllvm-args=\"...\" with", true);
        if sess.time_llvm_passes() {
            add("-time-passes", false);
        }
        if sess.print_llvm_passes() {
            add("-debug-pass=Structure", false);
        }
        if !sess.opts.debugging_opts.no_generate_arange_section {
            add("-generate-arange-section", false);
        }
        match sess.opts.debugging_opts.merge_functions.unwrap_or(sess.target.merge_functions) {
            MergeFunctions::Disabled | MergeFunctions::Trampolines => {}
            MergeFunctions::Aliases => {
                add("-mergefunc-use-aliases", false);
            }
        }

        if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind {
            add("-enable-emscripten-cxx-exceptions", false);
        }

        // 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", false);

        for arg in sess_args {
            add(&(*arg), true);
        }
    }

    if sess.opts.debugging_opts.llvm_time_trace {
        // time-trace is not thread safe and running it in parallel will cause seg faults.
        if !sess.opts.debugging_opts.no_parallel_llvm {
            bug!("`-Z llvm-time-trace` requires `-Z no-parallel-llvm")
        }

        llvm::LLVMTimeTraceProfilerInitialize();
    }

    llvm::LLVMInitializePasses();

    rustc_llvm::initialize_available_targets();

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

pub fn time_trace_profiler_finish(file_name: &str) {
    unsafe {
        let file_name = CString::new(file_name).unwrap();
        llvm::LLVMTimeTraceProfilerFinish(file_name.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.
// To find a list of LLVM's names, check llvm-project/llvm/include/llvm/Support/*TargetParser.def
// where the * matches the architecture's name
// Beware to not use the llvm github project for this, but check the git submodule
// found in src/llvm-project
// Though note that Rust can also be build with an external precompiled version of LLVM
// which might lead to failures if the oldest tested / supported LLVM version
// doesn't yet support the relevant intrinsics
pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
    let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
    match (arch, s) {
        ("x86", "pclmulqdq") => "pclmul",
        ("x86", "rdrand") => "rdrnd",
        ("x86", "bmi1") => "bmi",
        ("x86", "cmpxchg16b") => "cx16",
        ("x86", "avx512vaes") => "vaes",
        ("x86", "avx512gfni") => "gfni",
        ("x86", "avx512vpclmulqdq") => "vpclmulqdq",
        ("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);
    supported_target_features(sess)
        .iter()
        .filter_map(
            |&(feature, gate)| {
                if sess.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 print_version() {
    let (major, minor, patch) = get_version();
    println!("LLVM version: {}.{}.{}", major, minor, patch);
}

pub fn get_version() -> (u32, u32, u32) {
    // Can be called without initializing LLVM
    unsafe {
        (llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor(), llvm::LLVMRustVersionPatch())
    }
}

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);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
        }
    }
}

fn handle_native(name: &str) -> &str {
    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()
    }
}

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

    handle_native(name)
}

pub fn handle_native_features(sess: &Session) -> Vec<String> {
    match sess.opts.cg.target_cpu {
        Some(ref s) => {
            if s != "native" {
                return vec![];
            }

            let features_string = unsafe {
                let ptr = llvm::LLVMGetHostCPUFeatures();
                let features_string = if !ptr.is_null() {
                    CStr::from_ptr(ptr)
                        .to_str()
                        .unwrap_or_else(|e| {
                            bug!("LLVM returned a non-utf8 features string: {}", e);
                        })
                        .to_owned()
                } else {
                    bug!("could not allocate host CPU features, LLVM returned a `null` string");
                };

                llvm::LLVMDisposeMessage(ptr);

                features_string
            };

            features_string.split(",").map(|s| s.to_owned()).collect()
        }
        None => vec![],
    }
}

pub fn tune_cpu(sess: &Session) -> Option<&str> {
    match sess.opts.debugging_opts.tune_cpu {
        Some(ref s) => Some(handle_native(&**s)),
        None => None,
    }
}
<commit_msg>Use Option::unwrap_or instead of open-coding it<commit_after>use crate::back::write::create_informational_target_machine;
use crate::llvm;
use libc::c_int;
use rustc_codegen_ssa::target_features::supported_target_features;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
use rustc_session::config::PrintRequest;
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy};
use std::ffi::{CStr, CString};

use std::slice;
use std::str;
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() + sess.target.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();

    fn llvm_arg_to_arg_name(full_arg: &str) -> &str {
        full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("")
    }

    let cg_opts = sess.opts.cg.llvm_args.iter();
    let tg_opts = sess.target.llvm_args.iter();
    let sess_args = cg_opts.chain(tg_opts);

    let user_specified_args: FxHashSet<_> =
        sess_args.clone().map(|s| llvm_arg_to_arg_name(s)).filter(|s| !s.is_empty()).collect();

    {
        // This adds the given argument to LLVM. Unless `force` is true
        // user specified arguments are *not* overridden.
        let mut add = |arg: &str, force: bool| {
            if force || !user_specified_args.contains(llvm_arg_to_arg_name(arg)) {
                let s = CString::new(arg).unwrap();
                llvm_args.push(s.as_ptr());
                llvm_c_strs.push(s);
            }
        };
        // Set the llvm "program name" to make usage and invalid argument messages more clear.
        add("rustc -Cllvm-args=\"...\" with", true);
        if sess.time_llvm_passes() {
            add("-time-passes", false);
        }
        if sess.print_llvm_passes() {
            add("-debug-pass=Structure", false);
        }
        if !sess.opts.debugging_opts.no_generate_arange_section {
            add("-generate-arange-section", false);
        }
        match sess.opts.debugging_opts.merge_functions.unwrap_or(sess.target.merge_functions) {
            MergeFunctions::Disabled | MergeFunctions::Trampolines => {}
            MergeFunctions::Aliases => {
                add("-mergefunc-use-aliases", false);
            }
        }

        if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind {
            add("-enable-emscripten-cxx-exceptions", false);
        }

        // 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", false);

        for arg in sess_args {
            add(&(*arg), true);
        }
    }

    if sess.opts.debugging_opts.llvm_time_trace {
        // time-trace is not thread safe and running it in parallel will cause seg faults.
        if !sess.opts.debugging_opts.no_parallel_llvm {
            bug!("`-Z llvm-time-trace` requires `-Z no-parallel-llvm")
        }

        llvm::LLVMTimeTraceProfilerInitialize();
    }

    llvm::LLVMInitializePasses();

    rustc_llvm::initialize_available_targets();

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

pub fn time_trace_profiler_finish(file_name: &str) {
    unsafe {
        let file_name = CString::new(file_name).unwrap();
        llvm::LLVMTimeTraceProfilerFinish(file_name.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.
// To find a list of LLVM's names, check llvm-project/llvm/include/llvm/Support/*TargetParser.def
// where the * matches the architecture's name
// Beware to not use the llvm github project for this, but check the git submodule
// found in src/llvm-project
// Though note that Rust can also be build with an external precompiled version of LLVM
// which might lead to failures if the oldest tested / supported LLVM version
// doesn't yet support the relevant intrinsics
pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
    let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
    match (arch, s) {
        ("x86", "pclmulqdq") => "pclmul",
        ("x86", "rdrand") => "rdrnd",
        ("x86", "bmi1") => "bmi",
        ("x86", "cmpxchg16b") => "cx16",
        ("x86", "avx512vaes") => "vaes",
        ("x86", "avx512gfni") => "gfni",
        ("x86", "avx512vpclmulqdq") => "vpclmulqdq",
        ("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);
    supported_target_features(sess)
        .iter()
        .filter_map(
            |&(feature, gate)| {
                if sess.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 print_version() {
    let (major, minor, patch) = get_version();
    println!("LLVM version: {}.{}.{}", major, minor, patch);
}

pub fn get_version() -> (u32, u32, u32) {
    // Can be called without initializing LLVM
    unsafe {
        (llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor(), llvm::LLVMRustVersionPatch())
    }
}

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);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
        }
    }
}

fn handle_native(name: &str) -> &str {
    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()
    }
}

pub fn target_cpu(sess: &Session) -> &str {
    let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu);
    handle_native(name)
}

pub fn handle_native_features(sess: &Session) -> Vec<String> {
    match sess.opts.cg.target_cpu {
        Some(ref s) => {
            if s != "native" {
                return vec![];
            }

            let features_string = unsafe {
                let ptr = llvm::LLVMGetHostCPUFeatures();
                let features_string = if !ptr.is_null() {
                    CStr::from_ptr(ptr)
                        .to_str()
                        .unwrap_or_else(|e| {
                            bug!("LLVM returned a non-utf8 features string: {}", e);
                        })
                        .to_owned()
                } else {
                    bug!("could not allocate host CPU features, LLVM returned a `null` string");
                };

                llvm::LLVMDisposeMessage(ptr);

                features_string
            };

            features_string.split(",").map(|s| s.to_owned()).collect()
        }
        None => vec![],
    }
}

pub fn tune_cpu(sess: &Session) -> Option<&str> {
    match sess.opts.debugging_opts.tune_cpu {
        Some(ref s) => Some(handle_native(&**s)),
        None => None,
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "alloc")]

extern crate jemallocator;
extern crate nom;

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

use nom::{
  branch::alt,
  bytes::complete::{escaped, tag, take_while},
  character::complete::{alphanumeric1 as alphanumeric, char, one_of},
  combinator::{cut, map, opt, value},
  error::{context, convert_error, ErrorKind, ParseError, VerboseError},
  multi::separated_list,
  number::complete::double,
  sequence::{delimited, preceded, separated_pair, terminated},
  Err, IResult,
};
use std::collections::HashMap;
use std::str;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Boolean(bool),
  Num(f64),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

/// parser combinators are constructed from the bottom up:
/// first we write parsers for the smallest elements (here a space character),
/// then we'll combine them in larger parsers
fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  let chars = " \t\r\n";

  // nom combinators like `take_while` return a function. That function is the
  // parser,to which we can pass the input
  take_while(move |c| chars.contains(c))(i)
}

/// A nom parser has the following signature:
/// `Input -> IResult<Input, Output, Error>`, with `IResult` defined as:
/// `type IResult<I, O, E = (I, ErrorKind)> = Result<(I, O), Err<E>>;`
///
/// most of the times you can ignore the error type and use the default (but this
/// examples shows custom error types later on!)
///
/// Here we use `&str` as input type, but nom parsers can be generic over
/// the input type, and work directly with `&[u8]` or any other type that
/// implements the required traits.
///
/// Finally, we can see here that the input and output type are both `&str`
/// with the same lifetime tag. This means that the produced value is a subslice
/// of the input data. and there is no allocation needed. This is the main idea
/// behind nom's performance.
fn parse_str<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  escaped(alphanumeric, '\\', one_of("\"n\\"))(i)
}

/// `tag(string)` generates a parser that recognizes the argument string.
///
/// we can combine it with other functions, like `value` that takes another
/// parser, and if that parser returns without an error, returns a given
/// constant value.
///
/// `alt` is another combinator that tries multiple parsers one by one, until
/// one of them succeeds
///
/// `value` is a combinator that returns its value if the inner parser
fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
  // This is a parser that returns `true` if it sees the string "true", and
  // an error otherwise
  let parse_true = value(true, tag("true"))

  // This is a parser that returns `true` if it sees the string "true", and
  // an error otherwise
  let parse_false = value(false, tag("false"));

  // `alt` combines the two parsers. It returns the result of the first
  // successful parser, or an error
  alt((parse_true, parse_false))(input)
}

/// this parser combines the previous `parse_str` parser, that recognizes the
/// interior of a string, with a parse to recognize the double quote character,
/// before the string (using `preceded`) and after the string (using `terminated`).
///
/// `context` and `cut` are related to error management:
/// - `cut` transforms an `Err::Error(e)` in `Err::Failure(e)`, signaling to
/// combinators like  `alt` that they should not try other parsers. We were in the
/// right branch (since we found the `"` character) but encountered an error when
/// parsing the string
/// - `context` lets you add a static string to provide more information in the
/// error chain (to indicate which parser had an error)
fn string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  context("string", preceded(char('\"'), cut(terminated(parse_str, char('\"')))))(i)
}

/// some combinators, like `separated_list` or `many0`, will call a parser repeatedly,
/// accumulating results in a `Vec`, until it encounters an error.
/// If you want more control on the parser application, check out the `iterator`
/// combinator (cf `examples/iterator.rs`)
fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Vec<JsonValue>, E> {
  context(
    "array",
    preceded(
      char('['),
      cut(terminated(
        separated_list(preceded(sp, char(',')), json_value),
        preceded(sp, char(']')),
      )),
    ),
  )(i)
}

fn key_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (&'a str, JsonValue), E> {
  separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), json_value)(i)
}

fn hash<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, HashMap<String, JsonValue>, E> {
  context(
    "map",
    preceded(
      char('{'),
      cut(terminated(
        map(separated_list(preceded(sp, char(',')), key_value), |tuple_vec| {
          tuple_vec.into_iter().map(|(k, v)| (String::from(k), v)).collect()
        }),
        preceded(sp, char('}')),
      )),
    ),
  )(i)
}

/// here, we apply the space parser before trying to parse a value
fn json_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  preceded(
    sp,
    alt((
      map(hash, JsonValue::Object),
      map(array, JsonValue::Array),
      map(string, |s| JsonValue::Str(String::from(s))),
      map(double, JsonValue::Num),
      map(boolean, JsonValue::Boolean),
    )),
  )(i)
}

/// the root element of a JSON parser is either an object or an array
fn root<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  delimited(sp, alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))), opt(sp))(i)
}

fn main() {
  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  } ";

  println!("will try to parse valid JSON data:\n\n**********\n{}\n**********\n", data);

  // this will print:
  // Ok(
  //     (
  //         "",
  //         Object(
  //             {
  //                 "b": Array(
  //                     [
  //                         Str(
  //                             "x",
  //                         ),
  //                         Str(
  //                             "y",
  //                         ),
  //                         Num(
  //                             12.0,
  //                         ),
  //                     ],
  //                 ),
  //                 "c": Object(
  //                     {
  //                         "hello": Str(
  //                             "world",
  //                         ),
  //                     },
  //                 ),
  //                 "a": Num(
  //                     42.0,
  //                 ),
  //             },
  //         ),
  //     ),
  // )
  println!("parsing a valid file:\n{:#?}\n", root::<(&str, ErrorKind)>(data));

  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { 1\"hello\" : \"world\"
  }
  } ";

  println!("will try to parse invalid JSON data:\n\n**********\n{}\n**********\n", data);

  // here we use `(Input, ErrorKind)` as error type, which is used by default
  // if you don't specify it. It contains the position of the error and some
  // info on which parser encountered it.
  // It is fast and small, but does not provide much context.
  //
  // This will print:
  // basic errors - `root::<(&str, ErrorKind)>(data)`:
  // Err(
  //   Failure(
  //       (
  //           "1\"hello\" : \"world\"\n  }\n  } ",
  //           Char,
  //       ),
  //   ),
  // )
  println!(
    "basic errors - `root::<(&str, ErrorKind)>(data)`:\n{:#?}\n",
    root::<(&str, ErrorKind)>(data)
  );

  // nom also provides `the `VerboseError<Input>` type, which will generate a sort
  // of backtrace of the path through the parser, accumulating info on input positions
  // and affected parsers.
  //
  // This will print:
  //
  // parsed verbose: Err(
  //   Failure(
  //       VerboseError {
  //           errors: [
  //               (
  //                   "1\"hello\" : \"world\"\n  }\n  } ",
  //                   Char(
  //                       '}',
  //                   ),
  //               ),
  //               (
  //                   "{ 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //               (
  //                   "{ \"a\"\t: 42,\n  \"b\": [ \"x\", \"y\", 12 ] ,\n  \"c\": { 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //           ],
  //       },
  //   ),
  // )
  println!("parsed verbose: {:#?}", root::<VerboseError<&str>>(data));

  match root::<VerboseError<&str>>(data) {
    Err(Err::Error(e)) | Err(Err::Failure(e)) => {
      // here we use the `convert_error` function, to transform a `VerboseError<&str>`
      // into a printable trace.
      //
      // This will print:
      // verbose errors - `root::<VerboseError>(data)`:
      // 0: at line 2:
      //   "c": { 1"hello" : "world"
      //          ^
      // expected '}', found 1
      //
      // 1: at line 2, in map:
      //   "c": { 1"hello" : "world"
      //        ^
      //
      // 2: at line 0, in map:
      //   { "a" : 42,
      //   ^
      println!("verbose errors - `root::<VerboseError>(data)`:\n{}", convert_error(data, e));
    }
    _ => {}
  }
}
<commit_msg>fix json example compilation<commit_after>#![cfg(feature = "alloc")]

extern crate jemallocator;
extern crate nom;

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

use nom::{
  branch::alt,
  bytes::complete::{escaped, tag, take_while},
  character::complete::{alphanumeric1 as alphanumeric, char, one_of},
  combinator::{cut, map, opt, value},
  error::{context, convert_error, ErrorKind, ParseError, VerboseError},
  multi::separated_list,
  number::complete::double,
  sequence::{delimited, preceded, separated_pair, terminated},
  Err, IResult,
};
use std::collections::HashMap;
use std::str;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Boolean(bool),
  Num(f64),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

/// parser combinators are constructed from the bottom up:
/// first we write parsers for the smallest elements (here a space character),
/// then we'll combine them in larger parsers
fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  let chars = " \t\r\n";

  // nom combinators like `take_while` return a function. That function is the
  // parser,to which we can pass the input
  take_while(move |c| chars.contains(c))(i)
}

/// A nom parser has the following signature:
/// `Input -> IResult<Input, Output, Error>`, with `IResult` defined as:
/// `type IResult<I, O, E = (I, ErrorKind)> = Result<(I, O), Err<E>>;`
///
/// most of the times you can ignore the error type and use the default (but this
/// examples shows custom error types later on!)
///
/// Here we use `&str` as input type, but nom parsers can be generic over
/// the input type, and work directly with `&[u8]` or any other type that
/// implements the required traits.
///
/// Finally, we can see here that the input and output type are both `&str`
/// with the same lifetime tag. This means that the produced value is a subslice
/// of the input data. and there is no allocation needed. This is the main idea
/// behind nom's performance.
fn parse_str<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  escaped(alphanumeric, '\\', one_of("\"n\\"))(i)
}

/// `tag(string)` generates a parser that recognizes the argument string.
///
/// we can combine it with other functions, like `value` that takes another
/// parser, and if that parser returns without an error, returns a given
/// constant value.
///
/// `alt` is another combinator that tries multiple parsers one by one, until
/// one of them succeeds
///
/// `value` is a combinator that returns its value if the inner parser
fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
  // This is a parser that returns `true` if it sees the string "true", and
  // an error otherwise
  let parse_true = value(true, tag("true"));

  // This is a parser that returns `true` if it sees the string "true", and
  // an error otherwise
  let parse_false = value(false, tag("false"));

  // `alt` combines the two parsers. It returns the result of the first
  // successful parser, or an error
  alt((parse_true, parse_false))(input)
}

/// this parser combines the previous `parse_str` parser, that recognizes the
/// interior of a string, with a parse to recognize the double quote character,
/// before the string (using `preceded`) and after the string (using `terminated`).
///
/// `context` and `cut` are related to error management:
/// - `cut` transforms an `Err::Error(e)` in `Err::Failure(e)`, signaling to
/// combinators like  `alt` that they should not try other parsers. We were in the
/// right branch (since we found the `"` character) but encountered an error when
/// parsing the string
/// - `context` lets you add a static string to provide more information in the
/// error chain (to indicate which parser had an error)
fn string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  context("string", preceded(char('\"'), cut(terminated(parse_str, char('\"')))))(i)
}

/// some combinators, like `separated_list` or `many0`, will call a parser repeatedly,
/// accumulating results in a `Vec`, until it encounters an error.
/// If you want more control on the parser application, check out the `iterator`
/// combinator (cf `examples/iterator.rs`)
fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Vec<JsonValue>, E> {
  context(
    "array",
    preceded(
      char('['),
      cut(terminated(
        separated_list(preceded(sp, char(',')), json_value),
        preceded(sp, char(']')),
      )),
    ),
  )(i)
}

fn key_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (&'a str, JsonValue), E> {
  separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), json_value)(i)
}

fn hash<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, HashMap<String, JsonValue>, E> {
  context(
    "map",
    preceded(
      char('{'),
      cut(terminated(
        map(separated_list(preceded(sp, char(',')), key_value), |tuple_vec| {
          tuple_vec.into_iter().map(|(k, v)| (String::from(k), v)).collect()
        }),
        preceded(sp, char('}')),
      )),
    ),
  )(i)
}

/// here, we apply the space parser before trying to parse a value
fn json_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  preceded(
    sp,
    alt((
      map(hash, JsonValue::Object),
      map(array, JsonValue::Array),
      map(string, |s| JsonValue::Str(String::from(s))),
      map(double, JsonValue::Num),
      map(boolean, JsonValue::Boolean),
    )),
  )(i)
}

/// the root element of a JSON parser is either an object or an array
fn root<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  delimited(sp, alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))), opt(sp))(i)
}

fn main() {
  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  } ";

  println!("will try to parse valid JSON data:\n\n**********\n{}\n**********\n", data);

  // this will print:
  // Ok(
  //     (
  //         "",
  //         Object(
  //             {
  //                 "b": Array(
  //                     [
  //                         Str(
  //                             "x",
  //                         ),
  //                         Str(
  //                             "y",
  //                         ),
  //                         Num(
  //                             12.0,
  //                         ),
  //                     ],
  //                 ),
  //                 "c": Object(
  //                     {
  //                         "hello": Str(
  //                             "world",
  //                         ),
  //                     },
  //                 ),
  //                 "a": Num(
  //                     42.0,
  //                 ),
  //             },
  //         ),
  //     ),
  // )
  println!("parsing a valid file:\n{:#?}\n", root::<(&str, ErrorKind)>(data));

  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { 1\"hello\" : \"world\"
  }
  } ";

  println!("will try to parse invalid JSON data:\n\n**********\n{}\n**********\n", data);

  // here we use `(Input, ErrorKind)` as error type, which is used by default
  // if you don't specify it. It contains the position of the error and some
  // info on which parser encountered it.
  // It is fast and small, but does not provide much context.
  //
  // This will print:
  // basic errors - `root::<(&str, ErrorKind)>(data)`:
  // Err(
  //   Failure(
  //       (
  //           "1\"hello\" : \"world\"\n  }\n  } ",
  //           Char,
  //       ),
  //   ),
  // )
  println!(
    "basic errors - `root::<(&str, ErrorKind)>(data)`:\n{:#?}\n",
    root::<(&str, ErrorKind)>(data)
  );

  // nom also provides `the `VerboseError<Input>` type, which will generate a sort
  // of backtrace of the path through the parser, accumulating info on input positions
  // and affected parsers.
  //
  // This will print:
  //
  // parsed verbose: Err(
  //   Failure(
  //       VerboseError {
  //           errors: [
  //               (
  //                   "1\"hello\" : \"world\"\n  }\n  } ",
  //                   Char(
  //                       '}',
  //                   ),
  //               ),
  //               (
  //                   "{ 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //               (
  //                   "{ \"a\"\t: 42,\n  \"b\": [ \"x\", \"y\", 12 ] ,\n  \"c\": { 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //           ],
  //       },
  //   ),
  // )
  println!("parsed verbose: {:#?}", root::<VerboseError<&str>>(data));

  match root::<VerboseError<&str>>(data) {
    Err(Err::Error(e)) | Err(Err::Failure(e)) => {
      // here we use the `convert_error` function, to transform a `VerboseError<&str>`
      // into a printable trace.
      //
      // This will print:
      // verbose errors - `root::<VerboseError>(data)`:
      // 0: at line 2:
      //   "c": { 1"hello" : "world"
      //          ^
      // expected '}', found 1
      //
      // 1: at line 2, in map:
      //   "c": { 1"hello" : "world"
      //        ^
      //
      // 2: at line 0, in map:
      //   { "a" : 42,
      //   ^
      println!("verbose errors - `root::<VerboseError>(data)`:\n{}", convert_error(data, e));
    }
    _ => {}
  }
}
<|endoftext|>


<commit_before>#![cfg(feature = "alloc")]

extern crate nom;
extern crate jemallocator;

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

use nom::{
  branch::alt,
  bytes::complete::{escaped, tag, take_while},
  character::complete::{alphanumeric1 as alphanumeric, char, one_of},
  combinator::{map, opt, cut},
  error::{context, convert_error, ErrorKind, ParseError,VerboseError},
  multi::separated_list,
  number::complete::double,
  sequence::{delimited, preceded, separated_pair, terminated},
  Err, IResult,
};
use std::collections::HashMap;
use std::str;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Boolean(bool),
  Num(f64),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

/// parser combinators are constructed from the bottom up:
/// first we write parsers for the smallest elements (here a space character),
/// then we'll combine them in larger parsers
fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  let chars = " \t\r\n";

  // nom combinators like `take_while` return a function. That function is the
  // parser,to which we can pass the input
  take_while(move |c| chars.contains(c))(i)
}

/// A nom parser has the following signature:
/// `Input -> IResult<Input, Output, Error>`, with `IResult` defined as:
/// `type IResult<I, O, E = (I, ErrorKind)> = Result<(I, O), Err<E>>;`
///
/// most of the times you can ignore the error type and use the default (but this
/// examples shows custom error types later on!)
///
/// Here we use `&str` as input type, but nom parsers can be generic over
/// the input type, and work directly with `&[u8]` or any other type that
/// implements the required traits.
///
/// Finally, we can see here that the input and output type are both `&str`
/// with the same lifetime tag. This means that the produced value is a subslice
/// of the input data. and there is no allocation needed. This is the main idea
/// behind nom's performance.
fn parse_str<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  escaped(alphanumeric, '\\', one_of("\"n\\"))(i)
}

/// `tag(string)` generates a parser that recognizes the argument string.
///
/// we can combine it with other functions, like `map` that takes the result
/// of another parser, and applies a function over it (`map` itself generates
/// a new parser`.
///
/// `alt` is another combinator that tries multiple parsers one by one, until
/// one of them succeeds
fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
  alt((
      map(tag("false"), |_| false),
      map(tag("true"), |_| true)
  ))(input)
}

/// this parser combines the previous `parse_str` parser, that recognizes the
/// interior of a string, with a parse to recognize the double quote character,
/// before the string (using `preceded`) and after the string (using `terminated`).
///
/// `context` and `cut` are related to error management:
/// - `cut` transforms an `Err::Error(e)` in `Err::Failure(e)`, signaling to
/// combinators like  `alt` that they should not try other parsers. We were in the
/// right branch (since we found the `"` character) but encountered an error when
/// parsing the string
/// - `context` lets you add a static string to provide more information in the
/// error chain (to indicate which parser had an error)
fn string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  context("string",
    preceded(
      char('\"'),
      cut(terminated(
          parse_str,
          char('\"')
  ))))(i)
}

/// some combinators, like `separated_list` or `many0`, will call a parser repeatedly,
/// accumulating results in a `Vec`, until it encounters an error.
/// If you want more control on the parser application, check out the `iterator`
/// combinator (cf `examples/iterator.rs`)
fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Vec<JsonValue>, E> {
  context(
    "array",
    preceded(char('['),
    cut(terminated(
      separated_list(preceded(sp, char(',')), value),
      preceded(sp, char(']'))))
  ))(i)
}

fn key_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (&'a str, JsonValue), E> {
separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), value)(i)
}

fn hash<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, HashMap<String, JsonValue>, E> {
  context(
    "map",
    preceded(char('{'),
    cut(terminated(
      map(
        separated_list(preceded(sp, char(',')), key_value),
        |tuple_vec| {
          tuple_vec.into_iter().map(|(k, v)| (String::from(k), v)).collect()
      }),
      preceded(sp, char('}')),
    ))
  ))(i)
}

/// here, we apply the space parser before trying to parse a value
fn value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  preceded(
    sp,
    alt((
      map(hash, JsonValue::Object),
      map(array, JsonValue::Array),
      map(string, |s| JsonValue::Str(String::from(s))),
      map(double, JsonValue::Num),
      map(boolean, JsonValue::Boolean),
    )),
  )(i)
}

/// the root element of a JSON parser is either an object or an array
fn root<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  delimited(
    sp,
    alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))),
    opt(sp),
  )(i)
}

fn main() {
  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  } ";

  println!("will try to parse valid JSON data:\n\n**********\n{}\n**********\n", data);

  // this will print:
  // Ok(
  //     (
  //         "",
  //         Object(
  //             {
  //                 "b": Array(
  //                     [
  //                         Str(
  //                             "x",
  //                         ),
  //                         Str(
  //                             "y",
  //                         ),
  //                         Num(
  //                             12.0,
  //                         ),
  //                     ],
  //                 ),
  //                 "c": Object(
  //                     {
  //                         "hello": Str(
  //                             "world",
  //                         ),
  //                     },
  //                 ),
  //                 "a": Num(
  //                     42.0,
  //                 ),
  //             },
  //         ),
  //     ),
  // )
  println!(
    "parsing a valid file:\n{:#?}\n",
    root::<(&str, ErrorKind)>(data)
  );

  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { 1\"hello\" : \"world\"
  }
  } ";

  println!("will try to parse invalid JSON data:\n\n**********\n{}\n**********\n", data);

  // here we use `(Input, ErrorKind)` as error type, which is used by default
  // if you don't specify it. It contains the position of the error and some
  // info on which parser encountered it.
  // It is fast and small, but does not provide much context.
  //
  // This will print:
  // basic errors - `root::<(&str, ErrorKind)>(data)`:
  // Err(
  //   Failure(
  //       (
  //           "1\"hello\" : \"world\"\n  }\n  } ",
  //           Char,
  //       ),
  //   ),
  // )
  println!(
    "basic errors - `root::<(&str, ErrorKind)>(data)`:\n{:#?}\n",
    root::<(&str, ErrorKind)>(data)
  );

  // nom also provides `the `VerboseError<Input>` type, which will generate a sort
  // of backtrace of the path through the parser, accumulating info on input positions
  // and affected parsers.
  //
  // This will print:
  //
  // parsed verbose: Err(
  //   Failure(
  //       VerboseError {
  //           errors: [
  //               (
  //                   "1\"hello\" : \"world\"\n  }\n  } ",
  //                   Char(
  //                       '}',
  //                   ),
  //               ),
  //               (
  //                   "{ 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //               (
  //                   "{ \"a\"\t: 42,\n  \"b\": [ \"x\", \"y\", 12 ] ,\n  \"c\": { 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //           ],
  //       },
  //   ),
  // )
  println!("parsed verbose: {:#?}", root::<VerboseError<&str>>(data));

  match root::<VerboseError<&str>>(data) {
    Err(Err::Error(e)) | Err(Err::Failure(e)) => {

      // here we use the `convert_error` function, to transform a `VerboseError<&str>`
      // into a printable trace.
      //
      // This will print:
      // verbose errors - `root::<VerboseError>(data)`:
      // 0: at line 2:
      //   "c": { 1"hello" : "world"
      //          ^
      // expected '}', found 1
      //
      // 1: at line 2, in map:
      //   "c": { 1"hello" : "world"
      //        ^
      //
      // 2: at line 0, in map:
      //   { "a" : 42,
      //   ^
      println!("verbose errors - `root::<VerboseError>(data)`:\n{}", convert_error(data, e));
    }
    _ => {},
  }
}
<commit_msg>Simplify `boolean` in json.rs example<commit_after>#![cfg(feature = "alloc")]

extern crate nom;
extern crate jemallocator;

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

use nom::{
  branch::alt,
  bytes::complete::{escaped, tag, take_while},
  character::complete::{alphanumeric1 as alphanumeric, char, one_of},
  combinator::{map, opt, cut, value},
  error::{context, convert_error, ErrorKind, ParseError,VerboseError},
  multi::separated_list,
  number::complete::double,
  sequence::{delimited, preceded, separated_pair, terminated},
  Err, IResult,
};
use std::collections::HashMap;
use std::str;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Boolean(bool),
  Num(f64),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

/// parser combinators are constructed from the bottom up:
/// first we write parsers for the smallest elements (here a space character),
/// then we'll combine them in larger parsers
fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  let chars = " \t\r\n";

  // nom combinators like `take_while` return a function. That function is the
  // parser,to which we can pass the input
  take_while(move |c| chars.contains(c))(i)
}

/// A nom parser has the following signature:
/// `Input -> IResult<Input, Output, Error>`, with `IResult` defined as:
/// `type IResult<I, O, E = (I, ErrorKind)> = Result<(I, O), Err<E>>;`
///
/// most of the times you can ignore the error type and use the default (but this
/// examples shows custom error types later on!)
///
/// Here we use `&str` as input type, but nom parsers can be generic over
/// the input type, and work directly with `&[u8]` or any other type that
/// implements the required traits.
///
/// Finally, we can see here that the input and output type are both `&str`
/// with the same lifetime tag. This means that the produced value is a subslice
/// of the input data. and there is no allocation needed. This is the main idea
/// behind nom's performance.
fn parse_str<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  escaped(alphanumeric, '\\', one_of("\"n\\"))(i)
}

/// `tag(string)` generates a parser that recognizes the argument string.
///
/// we can combine it with other functions, like `map` that takes the result
/// of another parser, and applies a function over it (`map` itself generates
/// a new parser`.
///
/// `alt` is another combinator that tries multiple parsers one by one, until
/// one of them succeeds
fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
  alt((
      value(false, tag("false")),
      value(true, tag("true")),
  ))(input)
}

/// this parser combines the previous `parse_str` parser, that recognizes the
/// interior of a string, with a parse to recognize the double quote character,
/// before the string (using `preceded`) and after the string (using `terminated`).
///
/// `context` and `cut` are related to error management:
/// - `cut` transforms an `Err::Error(e)` in `Err::Failure(e)`, signaling to
/// combinators like  `alt` that they should not try other parsers. We were in the
/// right branch (since we found the `"` character) but encountered an error when
/// parsing the string
/// - `context` lets you add a static string to provide more information in the
/// error chain (to indicate which parser had an error)
fn string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
  context("string",
    preceded(
      char('\"'),
      cut(terminated(
          parse_str,
          char('\"')
  ))))(i)
}

/// some combinators, like `separated_list` or `many0`, will call a parser repeatedly,
/// accumulating results in a `Vec`, until it encounters an error.
/// If you want more control on the parser application, check out the `iterator`
/// combinator (cf `examples/iterator.rs`)
fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Vec<JsonValue>, E> {
  context(
    "array",
    preceded(char('['),
    cut(terminated(
      separated_list(preceded(sp, char(',')), value),
      preceded(sp, char(']'))))
  ))(i)
}

fn key_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (&'a str, JsonValue), E> {
separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), value)(i)
}

fn hash<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, HashMap<String, JsonValue>, E> {
  context(
    "map",
    preceded(char('{'),
    cut(terminated(
      map(
        separated_list(preceded(sp, char(',')), key_value),
        |tuple_vec| {
          tuple_vec.into_iter().map(|(k, v)| (String::from(k), v)).collect()
      }),
      preceded(sp, char('}')),
    ))
  ))(i)
}

/// here, we apply the space parser before trying to parse a value
fn value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  preceded(
    sp,
    alt((
      map(hash, JsonValue::Object),
      map(array, JsonValue::Array),
      map(string, |s| JsonValue::Str(String::from(s))),
      map(double, JsonValue::Num),
      map(boolean, JsonValue::Boolean),
    )),
  )(i)
}

/// the root element of a JSON parser is either an object or an array
fn root<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> {
  delimited(
    sp,
    alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))),
    opt(sp),
  )(i)
}

fn main() {
  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  } ";

  println!("will try to parse valid JSON data:\n\n**********\n{}\n**********\n", data);

  // this will print:
  // Ok(
  //     (
  //         "",
  //         Object(
  //             {
  //                 "b": Array(
  //                     [
  //                         Str(
  //                             "x",
  //                         ),
  //                         Str(
  //                             "y",
  //                         ),
  //                         Num(
  //                             12.0,
  //                         ),
  //                     ],
  //                 ),
  //                 "c": Object(
  //                     {
  //                         "hello": Str(
  //                             "world",
  //                         ),
  //                     },
  //                 ),
  //                 "a": Num(
  //                     42.0,
  //                 ),
  //             },
  //         ),
  //     ),
  // )
  println!(
    "parsing a valid file:\n{:#?}\n",
    root::<(&str, ErrorKind)>(data)
  );

  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { 1\"hello\" : \"world\"
  }
  } ";

  println!("will try to parse invalid JSON data:\n\n**********\n{}\n**********\n", data);

  // here we use `(Input, ErrorKind)` as error type, which is used by default
  // if you don't specify it. It contains the position of the error and some
  // info on which parser encountered it.
  // It is fast and small, but does not provide much context.
  //
  // This will print:
  // basic errors - `root::<(&str, ErrorKind)>(data)`:
  // Err(
  //   Failure(
  //       (
  //           "1\"hello\" : \"world\"\n  }\n  } ",
  //           Char,
  //       ),
  //   ),
  // )
  println!(
    "basic errors - `root::<(&str, ErrorKind)>(data)`:\n{:#?}\n",
    root::<(&str, ErrorKind)>(data)
  );

  // nom also provides `the `VerboseError<Input>` type, which will generate a sort
  // of backtrace of the path through the parser, accumulating info on input positions
  // and affected parsers.
  //
  // This will print:
  //
  // parsed verbose: Err(
  //   Failure(
  //       VerboseError {
  //           errors: [
  //               (
  //                   "1\"hello\" : \"world\"\n  }\n  } ",
  //                   Char(
  //                       '}',
  //                   ),
  //               ),
  //               (
  //                   "{ 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //               (
  //                   "{ \"a\"\t: 42,\n  \"b\": [ \"x\", \"y\", 12 ] ,\n  \"c\": { 1\"hello\" : \"world\"\n  }\n  } ",
  //                   Context(
  //                       "map",
  //                   ),
  //               ),
  //           ],
  //       },
  //   ),
  // )
  println!("parsed verbose: {:#?}", root::<VerboseError<&str>>(data));

  match root::<VerboseError<&str>>(data) {
    Err(Err::Error(e)) | Err(Err::Failure(e)) => {

      // here we use the `convert_error` function, to transform a `VerboseError<&str>`
      // into a printable trace.
      //
      // This will print:
      // verbose errors - `root::<VerboseError>(data)`:
      // 0: at line 2:
      //   "c": { 1"hello" : "world"
      //          ^
      // expected '}', found 1
      //
      // 1: at line 2, in map:
      //   "c": { 1"hello" : "world"
      //        ^
      //
      // 2: at line 0, in map:
      //   { "a" : 42,
      //   ^
      println!("verbose errors - `root::<VerboseError>(data)`:\n{}", convert_error(data, e));
    }
    _ => {},
  }
}
<|endoftext|>


<commit_before>use crate::{error::Result,
            RegistryType};
use failure::SyncFailure;
use habitat_core::{package::{FullyQualifiedPackageIdent,
                             Identifiable},
                   ChannelIdent};
use handlebars::Handlebars;
use serde::Serialize;

const DEFAULT_IMAGE_NAME_TEMPLATE: &str = "{{pkg_origin}}/{{pkg_name}}";
const VERSION_RELEASE_TAG_TEMPLATE: &str = "{{pkg_version}}-{{pkg_release}}";
const VERSION_TAG_TEMPLATE: &str = "{{pkg_version}}";

/// Helper macro to mark all Handlebars templating calls that are not based
/// on user input, and can thus be safely unwrapped.
macro_rules! safe {
    ($render_result:expr) => {
        $render_result.expect("We are in control of all inputs to this rendering, and thus the \
                           result should always be safe to unwrap")
    };
}

/// An image naming policy.
///
/// This is a value struct which captures the naming and tagging intentions for an image.
#[derive(Debug, Default)]
pub struct Naming {
    /// An optional custom image name which would override a computed default value.
    custom_image_name:   Option<String>,
    /// Whether or not to tag the image with a latest value.
    latest_tag:          bool,
    /// Whether or not to tag the image with a value containing a version from a Package
    /// Identifier.
    version_tag:         bool,
    /// Whether or not to tag the image with a value containing a version and release from a
    /// Package Identifier.
    version_release_tag: bool,
    /// An optional custom tag value for the image.
    custom_tag:          Option<String>,

    // TODO (CM): I don't think either of these really belongs to this
    // Naming type

    // TODO (CM): really, url and type are conceptually part of a
    // single new type.
    /// A URL to a custom Docker registry to publish to. This will be used as part of every tag
    /// before pushing.
    pub registry_url:  Option<String>, // TODO (CM): Option<Url>
    /// The type of registry we're publishing to. Ex: Amazon, Docker, Google, Azure.
    pub registry_type: RegistryType,
}

// TODO (CM): must validate custom names and tags w/r/t tag spec
// https://docs.docker.com/engine/reference/commandline/tag/
//
// An image name is made up of slash-separated name components,
// optionally prefixed by a registry hostname. The hostname must
// comply with standard DNS rules, but may not contain underscores. If
// a hostname is present, it may optionally be followed by a port
// number in the format :8080. If not present, the command uses
// Docker’s public registry located at registry-1.docker.io by
// default. Name components may contain lowercase letters, digits and
// separators. A separator is defined as a period, one or two
// underscores, or one or more dashes. A name component may not start
// or end with a separator.
//
// A tag name must be valid ASCII and may contain lowercase and
// uppercase letters, digits, underscores, periods and dashes. A tag
// name may not start with a period or a dash and may contain a
// maximum of 128 characters.

impl Naming {
    /// Creates a `Naming` from cli arguments.
    pub fn new_from_cli_matches(m: &clap::ArgMatches) -> Self {
        let registry_type =
            clap::value_t!(m.value_of("REGISTRY_TYPE"), RegistryType).unwrap_or_default();

        // TODO (CM): If registry_type is Docker, we must set this to
        // dockerhub. Otherwise, it MUST be present, because of how
        // clap is set up.
        let registry_url = m.value_of("REGISTRY_URL").map(ToString::to_string);

        Naming { custom_image_name: m.value_of("IMAGE_NAME").map(ToString::to_string),
                 latest_tag: !m.is_present("NO_TAG_LATEST"),
                 version_tag: !m.is_present("NO_TAG_VERSION"),
                 version_release_tag: !m.is_present("NO_TAG_VERSION_RELEASE"),
                 custom_tag: m.value_of("TAG_CUSTOM").map(ToString::to_string),
                 registry_url,
                 registry_type }
    }

    // TODO (CM): I am skeptical of use of "channel" in any container
    // identifier, since that is not anything inherent to the package
    // we are containerizing.

    /// Return the image name, along with a (possibly empty) vector of
    /// additional tags.
    pub fn image_identifiers(&self,
                             ident: &FullyQualifiedPackageIdent,
                             channel: &ChannelIdent)
                             -> Result<(String, Vec<String>)> {
        let context = Self::rendering_context(ident, channel);

        let name = self.image_name(&context)?;
        let tags = vec![self.latest_tag(),
                        self.version_tag(&context),
                        self.version_release_tag(&context),
                        self.custom_tag(&context)?].into_iter()
                                                   .filter_map(|e| e)
                                                   .collect();
        Ok((name, tags))
    }

    ////////////////////////////////////////////////////////////////////////

    fn image_name<S>(&self, context: &S) -> Result<String>
        where S: Serialize
    {
        let image_name = if let Some(ref template) = self.custom_image_name {
            Self::render(&template, &context)?
        } else {
            safe!(Self::render(DEFAULT_IMAGE_NAME_TEMPLATE, &context))
        };

        // TODO (CM): perhaps we should prepend the registry URL to
        // tags only if we going to push (and at the time we are
        // pushing, not here)
        let image_name = match self.registry_url {
            Some(ref url) => format!("{}/{}", url, image_name),
            None => image_name,
        };

        Ok(image_name.to_lowercase())
    }

    fn version_release_tag<S>(&self, context: &S) -> Option<String>
        where S: Serialize
    {
        if self.version_release_tag {
            Some(safe!(Self::render(VERSION_RELEASE_TAG_TEMPLATE, &context)))
        } else {
            None
        }
    }

    fn version_tag<S>(&self, context: &S) -> Option<String>
        where S: Serialize
    {
        if self.version_tag {
            Some(safe!(Self::render(VERSION_TAG_TEMPLATE, &context)))
        } else {
            None
        }
    }

    // TODO (CM): not sure how useful this is, since I think "latest"
    // is *always* created.
    fn latest_tag(&self) -> Option<String> {
        if self.latest_tag {
            Some("latest".to_string())
        } else {
            None
        }
    }

    fn custom_tag<S>(&self, context: &S) -> Result<Option<String>>
        where S: Serialize
    {
        if let Some(ref custom_tag_template) = self.custom_tag {
            Ok(Some(Self::render(custom_tag_template, &context)?))
        } else {
            Ok(None)
        }
    }

    // TODO (CM): need to generate better error cases for this... if a
    // user inputs invalid input, the results can be cryptic:
    //
    // For instance, a template of "{{" give the error
    //
    //   ✗✗✗ Template "Unnamed template" line 1, col 3: invalid
    //   handlebars syntax.
    //
    // Not terribly useful, as there's no indication of what the
    // offending input is.
    //
    // We might want to pass more context to this render call (so
    // users can know which template was the offender)
    fn render<S>(template: &str, context: &S) -> Result<String>
        where S: Serialize
    {
        Ok(Handlebars::new().template_render(template, context)
                            .map_err(SyncFailure::new)
                            .map(|s| s.to_lowercase())?)
    }

    fn rendering_context(ident: &FullyQualifiedPackageIdent,
                         channel: &ChannelIdent)
                         -> impl Serialize {
        json!({
            "pkg_origin": ident.origin(),
            "pkg_name": ident.name(),
            "pkg_version": ident.version(),
            "pkg_release": ident.release(),
            "channel": channel,
        })
    }
}

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

    fn ident() -> FullyQualifiedPackageIdent { "core/foo/1.2.3/20200430153200".parse().unwrap() }

    fn context() -> impl Serialize {
        let ident = ident();
        let channel = ChannelIdent::default();
        Naming::rendering_context(&ident, &channel)
    }

    #[test]
    fn default_naming_policy() {
        let naming = Naming::default();
        let context = context();

        assert!(naming.latest_tag().is_none());
        assert!(naming.custom_tag(&context).unwrap().is_none());
        assert!(naming.version_tag(&context).is_none());
        assert!(naming.version_release_tag(&context).is_none());

        assert_eq!(naming.image_name(&context).unwrap(), "core/foo");
    }

    #[test]
    fn latest_tag() {
        let mut naming = Naming::default();
        naming.latest_tag = true;
        assert_eq!(naming.latest_tag().unwrap(), "latest");
    }

    #[test]
    fn version_tag() {
        let mut naming = Naming::default();
        naming.version_tag = true;

        let context = context();
        assert_eq!(naming.version_tag(&context).unwrap(), "1.2.3");
    }

    #[test]
    fn version_release_tag() {
        let mut naming = Naming::default();
        naming.version_release_tag = true;

        let context = context();
        assert_eq!(naming.version_release_tag(&context).unwrap(),
                   "1.2.3-20200430153200");
    }

    #[test]
    fn image_name_with_registry_url() {
        let mut naming = Naming::default();
        // TODO (CM): IMPLEMENTATION QUIRK
        // Registry type has no bearing on this! Fix it!
        naming.registry_url = Some(String::from("registry.mycompany.com:8080/v1"));

        let context = context();

        let name = naming.image_name(&context).unwrap();
        assert_eq!(name, "registry.mycompany.com:8080/v1/core/foo");
    }

    #[test]
    fn custom_image_names() {
        let context = context();

        // Template, Expected Result
        //
        // A "Some" expected result is something you expect to successfully
        // pass. A "None", on the other hand, is something you expect
        // to throw an error.
        let inputs = vec![

            // Valid inputs
            ("monkeys", Some("monkeys")),
            ("{{pkg_name}}", Some("foo")),
            ("{{pkg_origin}}-{{pkg_name}}", Some("core-foo")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}", Some("core-foo-1.2.3")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}",
             Some("core-foo-1.2.3-20200430153200")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}-{{channel}}",
             Some("core-foo-1.2.3-20200430153200-stable")),
            ("super-{{pkg_origin}}-wacky-{{pkg_name}}-funtime-{{pkg_version}}-container-{{pkg_release}}-party-{{channel}}-ohemgee",
             Some("super-core-wacky-foo-funtime-1.2.3-container-20200430153200-party-stable-ohemgee")),

            // Invalid inputs
            ("{{" , None),
            // ("{{not_a_valid_variable}}-{{pkg_name}}", None),
            // ("", None),
            // more examples of things that violate the tagging spec
        ];

        for (template, expected) in inputs {
            let mut naming = Naming::default();

            let template = String::from(template);
            naming.custom_image_name = Some(template.clone());
            let actual_name = naming.image_name(&context);

            if let Some(expected_name) = expected {
                // expected success
                assert!(actual_name.is_ok());
                let actual_name = actual_name.unwrap();
                assert_eq!(actual_name, expected_name,
                           "Expected template '{}' to generate image name '{}', but it generated \
                            '{}'",
                           template, expected_name, actual_name);
            } else {
                // expected failure
                assert!(actual_name.is_err(),
                        "Expected template '{}' to fail to generate an image name, but it \
                         generated '{}'",
                        template,
                        actual_name.unwrap());
            }
        }
    }

    // TODO (CM): there is a bunch of duplication here :(

    #[test]
    fn custom_tag_names() {
        let context = context();

        // Template, Expected Result
        //
        // A "Some" expected result is something you expect to successfully
        // pass. A "None", on the other hand, is something you expect
        // to throw an error.
        let inputs = vec![

            // Valid inputs
            ("monkeys", Some("monkeys")),
            ("{{pkg_name}}", Some("foo")),
            ("{{pkg_origin}}-{{pkg_name}}", Some("core-foo")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}", Some("core-foo-1.2.3")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}",
             Some("core-foo-1.2.3-20200430153200")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}-{{channel}}",
             Some("core-foo-1.2.3-20200430153200-stable")),
            ("super-{{pkg_origin}}-wacky-{{pkg_name}}-funtime-{{pkg_version}}-container-{{pkg_release}}-party-{{channel}}-ohemgee",
             Some("super-core-wacky-foo-funtime-1.2.3-container-20200430153200-party-stable-ohemgee")),

            // Invalid inputs
            ("{{" , None),
            // ("{{not_a_valid_variable}}-{{pkg_name}}", None),
            // ("", None),
            // more examples of things that violate the tagging spec
        ];

        for (template, expected) in inputs {
            let mut naming = Naming::default();

            let template = String::from(template);
            naming.custom_tag = Some(template.clone());
            let actual_tag = naming.custom_tag(&context);

            if let Some(expected_tag) = expected {
                // expected success
                assert!(actual_tag.is_ok());
                let actual_tag = actual_tag.unwrap().unwrap(); // Result<Option<String>>
                assert_eq!(actual_tag, expected_tag,
                           "Expected template '{}' to generate tag '{}', but it generated '{}'",
                           template, expected_tag, actual_tag);
            } else {
                // expected failure
                assert!(actual_tag.is_err(),
                        "Expected template '{}' to fail to generate a tag, but it generated '{}'",
                        template,
                        actual_tag.unwrap().unwrap());
            }
        }
    }

    #[test]
    fn default_image_identifiers() {
        let naming = Naming::default();
        let ident = ident();
        let channel = ChannelIdent::default();

        let (name, tags) = naming.image_identifiers(&ident, &channel).unwrap();

        assert_eq!(name, "core/foo");
        assert!(tags.is_empty());
    }

    #[test]
    fn all_the_image_identifiers() {
        let naming = Naming { custom_image_name:   Some(String::from("my-nifty/{{pkg_name}}")),
                              latest_tag:          true,
                              version_tag:         true,
                              version_release_tag: true,
                              custom_tag:          Some(String::from("new-hotness")),
                              registry_url:        Some(String::from("registry.mycompany.com:\
                                                                      8080/v1")),
                              registry_type:       RegistryType::Docker, };

        let ident = ident();
        let channel = ChannelIdent::default();

        let (name, tags) = naming.image_identifiers(&ident, &channel).unwrap();

        assert_eq!(name, "registry.mycompany.com:8080/v1/my-nifty/foo");
        assert_eq!(tags,
                   ["latest", "1.2.3", "1.2.3-20200430153200", "new-hotness"]);
    }
}
<commit_msg>Rename Naming fields for clarity<commit_after>use crate::{error::Result,
            RegistryType};
use failure::SyncFailure;
use habitat_core::{package::{FullyQualifiedPackageIdent,
                             Identifiable},
                   ChannelIdent};
use handlebars::Handlebars;
use serde::Serialize;

const DEFAULT_IMAGE_NAME_TEMPLATE: &str = "{{pkg_origin}}/{{pkg_name}}";
const VERSION_RELEASE_TAG_TEMPLATE: &str = "{{pkg_version}}-{{pkg_release}}";
const VERSION_TAG_TEMPLATE: &str = "{{pkg_version}}";

/// Helper macro to mark all Handlebars templating calls that are not based
/// on user input, and can thus be safely unwrapped.
macro_rules! safe {
    ($render_result:expr) => {
        $render_result.expect("We are in control of all inputs to this rendering, and thus the \
                           result should always be safe to unwrap")
    };
}

/// An image naming policy.
///
/// This is a value struct which captures the naming and tagging intentions for an image.
#[derive(Debug, Default)]
pub struct Naming {
    /// An optional custom image name which would override a computed default value.
    custom_image_name_template:  Option<String>,
    /// Whether or not to tag the image with a latest value.
    include_latest_tag:          bool,
    /// Whether or not to tag the image with a value containing a version from a Package
    /// Identifier.
    include_version_tag:         bool,
    /// Whether or not to tag the image with a value containing a version and release from a
    /// Package Identifier.
    include_version_release_tag: bool,
    /// An optional custom tag value for the image.
    custom_tag_template:         Option<String>,

    // TODO (CM): I don't think either of these really belongs to this
    // Naming type

    // TODO (CM): really, url and type are conceptually part of a
    // single new type.
    /// A URL to a custom Docker registry to publish to. This will be used as part of every tag
    /// before pushing.
    pub registry_url:  Option<String>, // TODO (CM): Option<Url>
    /// The type of registry we're publishing to. Ex: Amazon, Docker, Google, Azure.
    pub registry_type: RegistryType,
}

// TODO (CM): must validate custom names and tags w/r/t tag spec
// https://docs.docker.com/engine/reference/commandline/tag/
//
// An image name is made up of slash-separated name components,
// optionally prefixed by a registry hostname. The hostname must
// comply with standard DNS rules, but may not contain underscores. If
// a hostname is present, it may optionally be followed by a port
// number in the format :8080. If not present, the command uses
// Docker’s public registry located at registry-1.docker.io by
// default. Name components may contain lowercase letters, digits and
// separators. A separator is defined as a period, one or two
// underscores, or one or more dashes. A name component may not start
// or end with a separator.
//
// A tag name must be valid ASCII and may contain lowercase and
// uppercase letters, digits, underscores, periods and dashes. A tag
// name may not start with a period or a dash and may contain a
// maximum of 128 characters.

impl Naming {
    /// Creates a `Naming` from cli arguments.
    pub fn new_from_cli_matches(m: &clap::ArgMatches) -> Self {
        let registry_type =
            clap::value_t!(m.value_of("REGISTRY_TYPE"), RegistryType).unwrap_or_default();

        // TODO (CM): If registry_type is Docker, we must set this to
        // dockerhub. Otherwise, it MUST be present, because of how
        // clap is set up.
        let registry_url = m.value_of("REGISTRY_URL").map(ToString::to_string);

        Naming { custom_image_name_template: m.value_of("IMAGE_NAME").map(ToString::to_string),
                 include_latest_tag: !m.is_present("NO_TAG_LATEST"),
                 include_version_tag: !m.is_present("NO_TAG_VERSION"),
                 include_version_release_tag: !m.is_present("NO_TAG_VERSION_RELEASE"),
                 custom_tag_template: m.value_of("TAG_CUSTOM").map(ToString::to_string),
                 registry_url,
                 registry_type }
    }

    // TODO (CM): I am skeptical of use of "channel" in any container
    // identifier, since that is not anything inherent to the package
    // we are containerizing.

    /// Return the image name, along with a (possibly empty) vector of
    /// additional tags.
    pub fn image_identifiers(&self,
                             ident: &FullyQualifiedPackageIdent,
                             channel: &ChannelIdent)
                             -> Result<(String, Vec<String>)> {
        let context = Self::rendering_context(ident, channel);

        let name = self.image_name(&context)?;
        let tags = vec![self.latest_tag(),
                        self.version_tag(&context),
                        self.version_release_tag(&context),
                        self.custom_tag(&context)?].into_iter()
                                                   .filter_map(|e| e)
                                                   .collect();
        Ok((name, tags))
    }

    ////////////////////////////////////////////////////////////////////////

    fn image_name<S>(&self, context: &S) -> Result<String>
        where S: Serialize
    {
        let image_name = if let Some(ref template) = self.custom_image_name_template {
            Self::render(&template, &context)?
        } else {
            safe!(Self::render(DEFAULT_IMAGE_NAME_TEMPLATE, &context))
        };

        // TODO (CM): perhaps we should prepend the registry URL to
        // tags only if we going to push (and at the time we are
        // pushing, not here)
        let image_name = match self.registry_url {
            Some(ref url) => format!("{}/{}", url, image_name),
            None => image_name,
        };

        Ok(image_name.to_lowercase())
    }

    fn version_release_tag<S>(&self, context: &S) -> Option<String>
        where S: Serialize
    {
        if self.include_version_release_tag {
            Some(safe!(Self::render(VERSION_RELEASE_TAG_TEMPLATE, &context)))
        } else {
            None
        }
    }

    fn version_tag<S>(&self, context: &S) -> Option<String>
        where S: Serialize
    {
        if self.include_version_tag {
            Some(safe!(Self::render(VERSION_TAG_TEMPLATE, &context)))
        } else {
            None
        }
    }

    // TODO (CM): not sure how useful this is, since I think "latest"
    // is *always* created.
    fn latest_tag(&self) -> Option<String> {
        if self.include_latest_tag {
            Some("latest".to_string())
        } else {
            None
        }
    }

    fn custom_tag<S>(&self, context: &S) -> Result<Option<String>>
        where S: Serialize
    {
        if let Some(ref custom_tag_template) = self.custom_tag_template {
            Ok(Some(Self::render(custom_tag_template, &context)?))
        } else {
            Ok(None)
        }
    }

    // TODO (CM): need to generate better error cases for this... if a
    // user inputs invalid input, the results can be cryptic:
    //
    // For instance, a template of "{{" give the error
    //
    //   ✗✗✗ Template "Unnamed template" line 1, col 3: invalid
    //   handlebars syntax.
    //
    // Not terribly useful, as there's no indication of what the
    // offending input is.
    //
    // We might want to pass more context to this render call (so
    // users can know which template was the offender)
    fn render<S>(template: &str, context: &S) -> Result<String>
        where S: Serialize
    {
        Ok(Handlebars::new().template_render(template, context)
                            .map_err(SyncFailure::new)
                            .map(|s| s.to_lowercase())?)
    }

    fn rendering_context(ident: &FullyQualifiedPackageIdent,
                         channel: &ChannelIdent)
                         -> impl Serialize {
        json!({
            "pkg_origin": ident.origin(),
            "pkg_name": ident.name(),
            "pkg_version": ident.version(),
            "pkg_release": ident.release(),
            "channel": channel,
        })
    }
}

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

    fn ident() -> FullyQualifiedPackageIdent { "core/foo/1.2.3/20200430153200".parse().unwrap() }

    fn context() -> impl Serialize {
        let ident = ident();
        let channel = ChannelIdent::default();
        Naming::rendering_context(&ident, &channel)
    }

    #[test]
    fn default_naming_policy() {
        let naming = Naming::default();
        let context = context();

        assert!(naming.latest_tag().is_none());
        assert!(naming.custom_tag(&context).unwrap().is_none());
        assert!(naming.version_tag(&context).is_none());
        assert!(naming.version_release_tag(&context).is_none());

        assert_eq!(naming.image_name(&context).unwrap(), "core/foo");
    }

    #[test]
    fn latest_tag() {
        let mut naming = Naming::default();
        naming.include_latest_tag = true;
        assert_eq!(naming.latest_tag().unwrap(), "latest");
    }

    #[test]
    fn version_tag() {
        let mut naming = Naming::default();
        naming.include_version_tag = true;

        let context = context();
        assert_eq!(naming.version_tag(&context).unwrap(), "1.2.3");
    }

    #[test]
    fn version_release_tag() {
        let mut naming = Naming::default();
        naming.include_version_release_tag = true;

        let context = context();
        assert_eq!(naming.version_release_tag(&context).unwrap(),
                   "1.2.3-20200430153200");
    }

    #[test]
    fn image_name_with_registry_url() {
        let mut naming = Naming::default();
        // TODO (CM): IMPLEMENTATION QUIRK
        // Registry type has no bearing on this! Fix it!
        naming.registry_url = Some(String::from("registry.mycompany.com:8080/v1"));

        let context = context();

        let name = naming.image_name(&context).unwrap();
        assert_eq!(name, "registry.mycompany.com:8080/v1/core/foo");
    }

    #[test]
    fn custom_image_names() {
        let context = context();

        // Template, Expected Result
        //
        // A "Some" expected result is something you expect to successfully
        // pass. A "None", on the other hand, is something you expect
        // to throw an error.
        let inputs = vec![

            // Valid inputs
            ("monkeys", Some("monkeys")),
            ("{{pkg_name}}", Some("foo")),
            ("{{pkg_origin}}-{{pkg_name}}", Some("core-foo")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}", Some("core-foo-1.2.3")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}",
             Some("core-foo-1.2.3-20200430153200")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}-{{channel}}",
             Some("core-foo-1.2.3-20200430153200-stable")),
            ("super-{{pkg_origin}}-wacky-{{pkg_name}}-funtime-{{pkg_version}}-container-{{pkg_release}}-party-{{channel}}-ohemgee",
             Some("super-core-wacky-foo-funtime-1.2.3-container-20200430153200-party-stable-ohemgee")),

            // Invalid inputs
            ("{{" , None),
            // ("{{not_a_valid_variable}}-{{pkg_name}}", None),
            // ("", None),
            // more examples of things that violate the tagging spec
        ];

        for (template, expected) in inputs {
            let mut naming = Naming::default();

            let template = String::from(template);
            naming.custom_image_name_template = Some(template.clone());
            let actual_name = naming.image_name(&context);

            if let Some(expected_name) = expected {
                // expected success
                assert!(actual_name.is_ok());
                let actual_name = actual_name.unwrap();
                assert_eq!(actual_name, expected_name,
                           "Expected template '{}' to generate image name '{}', but it generated \
                            '{}'",
                           template, expected_name, actual_name);
            } else {
                // expected failure
                assert!(actual_name.is_err(),
                        "Expected template '{}' to fail to generate an image name, but it \
                         generated '{}'",
                        template,
                        actual_name.unwrap());
            }
        }
    }

    // TODO (CM): there is a bunch of duplication here :(

    #[test]
    fn custom_tag_names() {
        let context = context();

        // Template, Expected Result
        //
        // A "Some" expected result is something you expect to successfully
        // pass. A "None", on the other hand, is something you expect
        // to throw an error.
        let inputs = vec![

            // Valid inputs
            ("monkeys", Some("monkeys")),
            ("{{pkg_name}}", Some("foo")),
            ("{{pkg_origin}}-{{pkg_name}}", Some("core-foo")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}", Some("core-foo-1.2.3")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}",
             Some("core-foo-1.2.3-20200430153200")),
            ("{{pkg_origin}}-{{pkg_name}}-{{pkg_version}}-{{pkg_release}}-{{channel}}",
             Some("core-foo-1.2.3-20200430153200-stable")),
            ("super-{{pkg_origin}}-wacky-{{pkg_name}}-funtime-{{pkg_version}}-container-{{pkg_release}}-party-{{channel}}-ohemgee",
             Some("super-core-wacky-foo-funtime-1.2.3-container-20200430153200-party-stable-ohemgee")),

            // Invalid inputs
            ("{{" , None),
            // ("{{not_a_valid_variable}}-{{pkg_name}}", None),
            // ("", None),
            // more examples of things that violate the tagging spec
        ];

        for (template, expected) in inputs {
            let mut naming = Naming::default();

            let template = String::from(template);
            naming.custom_tag_template = Some(template.clone());
            let actual_tag = naming.custom_tag(&context);

            if let Some(expected_tag) = expected {
                // expected success
                assert!(actual_tag.is_ok());
                let actual_tag = actual_tag.unwrap().unwrap(); // Result<Option<String>>
                assert_eq!(actual_tag, expected_tag,
                           "Expected template '{}' to generate tag '{}', but it generated '{}'",
                           template, expected_tag, actual_tag);
            } else {
                // expected failure
                assert!(actual_tag.is_err(),
                        "Expected template '{}' to fail to generate a tag, but it generated '{}'",
                        template,
                        actual_tag.unwrap().unwrap());
            }
        }
    }

    #[test]
    fn default_image_identifiers() {
        let naming = Naming::default();
        let ident = ident();
        let channel = ChannelIdent::default();

        let (name, tags) = naming.image_identifiers(&ident, &channel).unwrap();

        assert_eq!(name, "core/foo");
        assert!(tags.is_empty());
    }

    #[test]
    fn all_the_image_identifiers() {
        let naming = Naming { custom_image_name_template:
                                  Some(String::from("my-nifty/{{pkg_name}}")),
                              include_latest_tag:          true,
                              include_version_tag:         true,
                              include_version_release_tag: true,
                              custom_tag_template:         Some(String::from("new-hotness")),
                              registry_url:
                                  Some(String::from("registry.mycompany.com:8080/v1")),
                              registry_type:               RegistryType::Docker, };

        let ident = ident();
        let channel = ChannelIdent::default();

        let (name, tags) = naming.image_identifiers(&ident, &channel).unwrap();

        assert_eq!(name, "registry.mycompany.com:8080/v1/my-nifty/foo");
        assert_eq!(tags,
                   ["latest", "1.2.3", "1.2.3-20200430153200", "new-hotness"]);
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let min_y_index = _min_y_index(poly);
      let max_y_index = _max_y_index(poly);

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn _min_y_index(poly: &Poly) -> usize {
   let mut max_after = false;

   let (first, rest) = poly.points.split_first().unwrap();

   let mut min_y = first.y;
   let mut min_index: usize = 0;

   for (index, point) in rest.iter().enumerate() {
      if point.y > min_y {
         if max_after {
            return min_index;
         }
      } else {
         max_after = true;
         min_index = index + 1;
         min_y = point.y;
      }
   }

   min_index
}

fn _max_y_index(poly: &Poly) -> usize {
   let mut min_after = false;

   let (first, rest) = poly.points.split_first().unwrap();

   let mut max_y = first.y;
   let mut max_index: usize = 0;

   for (index, point) in rest.iter().enumerate() {
      if point.y < max_y {
         if min_after {
            return max_index;
         }
      } else {
         min_after = true;
         max_index = index + 1;
         max_y = point.y;
      }
   }

   max_index
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<commit_msg>Moving the min and max y index calculation functions as methods of Poly<commit_after>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

impl Poly {
   fn min_y_index(&self) -> usize {
      let mut max_after = false;

      let (first, rest) = self.points.split_first().unwrap();

      let mut min_y = first.y;
      let mut min_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y > min_y {
            if max_after {
               return min_index;
            }
         } else {
            max_after = true;
            min_index = index + 1;
            min_y = point.y;
         }
      }

      min_index
   }

   fn max_y_index(&self) -> usize {
      let mut min_after = false;

      let (first, rest) = self.points.split_first().unwrap();

      let mut max_y = first.y;
      let mut max_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y < max_y {
            if min_after {
               return max_index;
            }
         } else {
            min_after = true;
            max_index = index + 1;
            max_y = point.y;
         }
      }

      max_index
   }
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let min_y_index = poly.min_y_index();
      let max_y_index = poly.max_y_index();

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<|endoftext|>


<commit_before>extern crate epub_builder;

use epub_builder::EpubBuilder;
use epub_builder::Result;
use epub_builder::ZipLibrary;
use epub_builder::EpubContent;
use epub_builder::TocElement;

use std::io;
use std::io::Write;

// Try to print Zip file to stdout
fn run() -> Result<()> {
    // Some dummy content to fill our books
    let dummy_content = "Dummy content. This should be valid XHTML if you want a valid EPUB!";
    let dummy_cover = "Not really a PNG image";
    let dummy_css = "body { background-color: pink }";

    // Create a new EpubBuilder using the zip library
    EpubBuilder::new(ZipLibrary::new())?
    // Set some metadata
        .metadata("author", "Joan Doe")?
        .metadata("title", "Dummy Book")?
    // Set the stylesheet (create a "stylesheet.css" file in EPUB that is used by some generated files)
        .stylesheet(dummy_css.as_bytes())?
    // Add a resource that is not part of the linear document structure
        .add_resource("cover.png", dummy_cover.as_bytes(), "image/png")?
    // Add a chapter
        .add_content(EpubContent::new("chapter_1.xhtml", dummy_content.as_bytes())
                     .title("Chapter 1"))?
    // Add a second chapter; this one has more toc information about its internal structure
        .add_content(EpubContent::new("chapter_2.xhtml", dummy_content.as_bytes())
                     .title("Chapter 2")
                     .child(TocElement::new("chapter_2.xhtml#1", "Chapter 2, section 1")))?
    // Add a section. Since its level is set to 2, it will be attached to the previous chapter.
        .add_content(EpubContent::new("section.xhtml", dummy_content.as_bytes())
                     .title("Chapter 2, section 2")
                     .level(2))?
    // Add a chapter without a title, which will thus not appear in the TOC.
        .add_content(EpubContent::new("notes.xhtml", dummy_content.as_bytes()))?
    // Generate a toc inside of the document, that will be part of the linear structure.
        .inline_toc()
    // Finally, write the EPUB file to stdout
        .generate(&mut io::stdout())?;
    Ok(())
}

fn main() {
    match run() {
        Ok(_) => writeln!(&mut io::stderr(), "Successfully wrote epub document to stdout!").unwrap(),
        Err(err) => writeln!(&mut io::stderr(), "Error: {}", err).unwrap(),
    };
}
<commit_msg>Update example/test.rs<commit_after>extern crate epub_builder;

use epub_builder::EpubBuilder;
use epub_builder::Result;
use epub_builder::ZipLibrary;
use epub_builder::EpubContent;
use epub_builder::ReferenceType;
use epub_builder::TocElement;

use std::io;
use std::io::Write;

// Try to print Zip file to stdout
fn run() -> Result<()> {
    // Some dummy content to fill our books
    let dummy_content = "Dummy content. This should be valid XHTML if you want a valid EPUB!";
    let dummy_image = "Not really a PNG image";
    let dummy_css = "body { background-color: pink }";

    // Create a new EpubBuilder using the zip library
    EpubBuilder::new(ZipLibrary::new())?
    // Set some metadata
        .metadata("author", "Joan Doe")?
        .metadata("title", "Dummy Book")?
    // Set the stylesheet (create a "stylesheet.css" file in EPUB that is used by some generated files)
        .stylesheet(dummy_css.as_bytes())?
    // Add a image cover file
        .add_cover_image("cover.png", dummy_image.as_bytes(), "image/png")?
    // Add a resource that is not part of the linear document structure
        .add_resource("some_image.png", dummy_image.as_bytes(), "image/png")?
    // Add a cover page
        .add_content(EpubContent::new("cover.xhtml", dummy_content.as_bytes())
                     .title("Cover")
                     .reftype(ReferenceType::Cover))?
    // Add a title page
        .add_content(EpubContent::new("title.xhtml", dummy_content.as_bytes())
                     .title("Title")
                     .reftype(ReferenceType::TitlePage))?
    // Add a chapter
        .add_content(EpubContent::new("chapter_1.xhtml", dummy_content.as_bytes())
                     .title("Chapter 1"))?
    // Add a second chapter; this one has more toc information about its internal structure
        .add_content(EpubContent::new("chapter_2.xhtml", dummy_content.as_bytes())
                     .title("Chapter 2")
                     .child(TocElement::new("chapter_2.xhtml#1", "Chapter 2, section 1")))?
    // Add a section. Since its level is set to 2, it will be attached to the previous chapter.
        .add_content(EpubContent::new("section.xhtml", dummy_content.as_bytes())
                     .title("Chapter 2, section 2")
                     .level(2))?
    // Add a chapter without a title, which will thus not appear in the TOC.
        .add_content(EpubContent::new("notes.xhtml", dummy_content.as_bytes()))?
    // Generate a toc inside of the document, that will be part of the linear structure.
        .inline_toc()
    // Finally, write the EPUB file to stdout
        .generate(&mut io::stdout())?;
    Ok(())
}

fn main() {
    match run() {
        Ok(_) => writeln!(&mut io::stderr(), "Successfully wrote epub document to stdout!").unwrap(),
        Err(err) => writeln!(&mut io::stderr(), "Error: {}", err).unwrap(),
    };
}
<|endoftext|>