<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate toml;

use file_lines::FileLines;
use lists::{SeparatorTactic, ListTactic};
use std::io::Write;

macro_rules! configuration_option_enum{
    ($e:ident: $( $x:ident ),+ $(,)*) => {
        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
        pub enum $e {
            $( $x ),+
        }

        impl_enum_decodable!($e, $( $x ),+);
    }
}

configuration_option_enum! { NewlineStyle:
    Windows, // \r\n
    Unix, // \n
    Native, // \r\n in Windows, \n on other platforms
}

configuration_option_enum! { BraceStyle:
    AlwaysNextLine,
    PreferSameLine,
    // Prefer same line except where there is a where clause, in which case force
    // the brace to the next line.
    SameLineWhere,
}

configuration_option_enum! { ControlBraceStyle:
    // K&R/Stroustrup style, Rust community default
    AlwaysSameLine,
    // Allman style
    AlwaysNextLine,
}

configuration_option_enum! { ElseIfBraceStyle:
    // K&R style, Rust community default
    AlwaysSameLine,
    // Stroustrup style
    ClosingNextLine,
    // Allman style
    AlwaysNextLine,
}

// How to indent a function's return type.
configuration_option_enum! { ReturnIndent:
    // Aligned with the arguments
    WithArgs,
    // Aligned with the where clause
    WithWhereClause,
}

// How to style a struct literal.
configuration_option_enum! { StructLitStyle:
    // First line on the same line as the opening brace, all lines aligned with
    // the first line.
    Visual,
    // First line is on a new line and all lines align with block indent.
    Block,
    // FIXME Maybe we should also have an option to align types.
}

// How to style fn args.
configuration_option_enum! { FnArgLayoutStyle:
    // First line on the same line as the opening brace, all lines aligned with
    // the first line.
    Visual,
    // Put args on one line if they fit, or start a new line with block indent.
    Block,
    // First line is on a new line and all lines align with block indent.
    BlockAlways,
}

configuration_option_enum! { BlockIndentStyle:
    // Same level as parent.
    Inherit,
    // One level deeper than parent.
    Tabbed,
    // Aligned with block open.
    Visual,
}

configuration_option_enum! { Density:
    // Fit as much on one line as possible.
    Compressed,
    // Use more lines.
    Tall,
    // Try to compress if the body is empty.
    CompressedIfEmpty,
    // Place every item on a separate line.
    Vertical,
}

configuration_option_enum! { TypeDensity:
    // No spaces around "=" and "+"
    Compressed,
    // Spaces around " = " and " + "
    Wide,
}

impl Density {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            Density::Compressed => ListTactic::Mixed,
            Density::Tall |
            Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
            Density::Vertical => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { LicensePolicy:
    // Do not place license text at top of files
    NoLicense,
    // Use the text in "license" field as the license
    TextLicense,
    // Use a text file as the license text
    FileLicense,
}

configuration_option_enum! { MultilineStyle:
    // Use horizontal layout if it fits in one line, fall back to vertical
    PreferSingle,
    // Use vertical layout
    ForceMulti,
}

impl MultilineStyle {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
            MultilineStyle::ForceMulti => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { ReportTactic:
    Always,
    Unnumbered,
    Never,
}

configuration_option_enum! { WriteMode:
    // Backs the original file up and overwrites the original.
    Replace,
    // Overwrites original file without backup.
    Overwrite,
    // Writes the output to stdout.
    Display,
    // Writes the diff to stdout.
    Diff,
    // Displays how much of the input file was processed
    Coverage,
    // Unfancy stdout
    Plain,
    // Outputs a checkstyle XML file.
    Checkstyle,
}

/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
    /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
    /// pipe-separated list of variants; for other types it returns "<type>".
    fn doc_hint() -> String;
}

impl ConfigType for bool {
    fn doc_hint() -> String {
        String::from("<boolean>")
    }
}

impl ConfigType for usize {
    fn doc_hint() -> String {
        String::from("<unsigned integer>")
    }
}

impl ConfigType for isize {
    fn doc_hint() -> String {
        String::from("<signed integer>")
    }
}

impl ConfigType for String {
    fn doc_hint() -> String {
        String::from("<string>")
    }
}

impl ConfigType for FileLines {
    fn doc_hint() -> String {
        String::from("<json>")
    }
}

pub struct ConfigHelpItem {
    option_name: &'static str,
    doc_string: &'static str,
    variant_names: String,
    default: &'static str,
}

impl ConfigHelpItem {
    pub fn option_name(&self) -> &'static str {
        self.option_name
    }

    pub fn doc_string(&self) -> &'static str {
        self.doc_string
    }

    pub fn variant_names(&self) -> &String {
        &self.variant_names
    }

    pub fn default(&self) -> &'static str {
        self.default
    }
}

macro_rules! create_config {
    ($($i:ident: $ty:ty, $def:expr, $( $dstring:expr ),+ );+ $(;)*) => (
        #[derive(RustcDecodable, Clone)]
        pub struct Config {
            $(pub $i: $ty),+
        }

        // Just like the Config struct but with each property wrapped
        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
        // specity all properties of `Config`.
        // We first parse into `ParsedConfig`, then create a default `Config`
        // and overwrite the properties with corresponding values from `ParsedConfig`
        #[derive(RustcDecodable, Clone)]
        pub struct ParsedConfig {
            $(pub $i: Option<$ty>),+
        }

        impl Config {

            fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
            $(
                if let Some(val) = parsed.$i {
                    self.$i = val;
                }
            )+
                self
            }

            pub fn from_toml(toml: &str) -> Config {
                let parsed = toml.parse().expect("Could not parse TOML");
                let parsed_config:ParsedConfig = match toml::decode(parsed) {
                    Some(decoded) => decoded,
                    None => {
                        msg!("Decoding config file failed. Config:\n{}", toml);
                        let parsed: toml::Value = toml.parse().expect("Could not parse TOML");
                        msg!("\n\nParsed:\n{:?}", parsed);
                        panic!();
                    }
                };
                Config::default().fill_from_parsed_config(parsed_config)
            }

            pub fn override_value(&mut self, key: &str, val: &str) {
                match key {
                    $(
                        stringify!($i) => {
                            self.$i = val.parse::<$ty>()
                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
                                                 stringify!($i),
                                                 val,
                                                 stringify!($ty)));
                        }
                    )+
                    _ => panic!("Unknown config key in override: {}", key)
                }
            }

            pub fn print_docs() {
                use std::cmp;
                let max = 0;
                $( let max = cmp::max(max, stringify!($i).len()+1); )+
                let mut space_str = String::with_capacity(max);
                for _ in 0..max {
                    space_str.push(' ');
                }
                println!("Configuration Options:");
                $(
                    let name_raw = stringify!($i);
                    let mut name_out = String::with_capacity(max);
                    for _ in name_raw.len()..max-1 {
                        name_out.push(' ')
                    }
                    name_out.push_str(name_raw);
                    name_out.push(' ');
                    println!("{}{} Default: {:?}",
                             name_out,
                             <$ty>::doc_hint(),
                             $def);
                    $(
                        println!("{}{}", space_str, $dstring);
                    )+
                    println!("");
                )+
            }
        }

        // Template for the default configuration
        impl Default for Config {
            fn default() -> Config {
                Config {
                    $(
                        $i: $def,
                    )+
                }
            }
        }
    )
}

create_config! {
    verbose: bool, false, "Use verbose output";
    skip_children: bool, false, "Don't reformat out of line modules";
    file_lines: FileLines, FileLines::all(),
        "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
         via the --file-lines option";
    max_width: usize, 100, "Maximum width of each line";
    ideal_width: usize, 80, "Ideal width of each line";
    tab_spaces: usize, 4, "Number of spaces per tab";
    fn_call_width: usize, 60,
        "Maximum width of the args of a function call before falling back to vertical formatting";
    struct_lit_width: usize, 16,
        "Maximum width in the body of a struct lit before falling back to vertical formatting";
    struct_variant_width: usize, 35,
        "Maximum width in the body of a struct variant before falling back to vertical formatting";
    force_explicit_abi: bool, true, "Always print the abi for extern items";
    newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
    fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
    item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
    else_if_brace_style: ElseIfBraceStyle, ElseIfBraceStyle::AlwaysSameLine,
        "Brace style for if, else if, and else constructs";
    control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
        "Brace style for match, loop, for, and while constructs";
    impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
    fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
    fn_single_line: bool, false, "Put single-expression functions on a single line";
    fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
        "Location of return type in function declaration";
    fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
    fn_args_density: Density, Density::Tall, "Argument density in functions";
    fn_args_layout: FnArgLayoutStyle, FnArgLayoutStyle::Visual, "Layout of function arguments";
    fn_arg_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indent on function arguments";
    type_punctuation_density: TypeDensity, TypeDensity::Wide,
        "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
    // Should we at least try to put the where clause on the same line as the rest of the
    // function decl?
    where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
    // Visual will be treated like Tabbed
    where_indent: BlockIndentStyle, BlockIndentStyle::Tabbed, "Indentation of a where clause";
    where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
    where_pred_indent: BlockIndentStyle, BlockIndentStyle::Visual,
        "Indentation style of a where predicate";
    where_trailing_comma: bool, false, "Put a trailing comma on where clauses";
    generics_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indentation of generics";
    struct_trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
        "If there is a trailing comma on structs";
    struct_lit_trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
        "If there is a trailing comma on literal structs";
    struct_lit_style: StructLitStyle, StructLitStyle::Block, "Style of struct definition";
    struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
        "Multiline style on literal structs";
    enum_trailing_comma: bool, true, "Put a trailing comma on enum declarations";
    report_todo: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of TODO in source file comments";
    report_fixme: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of FIXME in source file comments";
    chain_base_indent: BlockIndentStyle, BlockIndentStyle::Tabbed, "Indent on chain base";
    chain_indent: BlockIndentStyle, BlockIndentStyle::Tabbed, "Indentation of chain";
    chains_overflow_last: bool, true, "Allow last call in method chain to break the line";
    reorder_imports: bool, false, "Reorder import statements alphabetically";
    reorder_imported_names: bool, false,
        "Reorder lists of names in import statements alphabetically";
    single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
                                                expressions. A value of zero means always break \
                                                if-else expressions.";
    format_strings: bool, true, "Format string literals where necessary";
    force_format_strings: bool, false, "Always format string literals";
    take_source_hints: bool, true, "Retain some formatting characteristics from the source code";
    hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
    wrap_comments: bool, false, "Break comments to fit on the line";
    normalize_comments: bool, true, "Convert /* */ comments to // comments where possible";
    wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
    match_block_trailing_comma: bool, false,
        "Put a trailing comma after a block based match arm (non-block arms are not affected)";
    match_wildcard_trailing_comma: bool, true, "Put a trailing comma after a wildcard arm";
    closure_block_indent_threshold: isize, 5, "How many lines a closure must have before it is \
                                               block indented. -1 means never use block indent.";
    space_before_type_annotation: bool, false,
        "Leave a space before the colon in a type annotation";
    space_after_type_annotation_colon: bool, true,
        "Leave a space after the colon in a type annotation";
    space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
    space_after_bound_colon: bool, true,
        "Leave a space after the colon in a trait or lifetime bound";
    spaces_around_ranges: bool, false, "Put spaces around the  .. and ... range operators";
    spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
    spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
    spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
    use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
    write_mode: WriteMode, WriteMode::Replace,
        "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
    condense_wildcard_suffices: bool, false, "Replace strings of _ wildcards by a single .. in
        tuple patterns"
}
<commit_msg>Prevent an ugly linebreak in --config-helps description of condense_wildcard_suffices<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate toml;

use file_lines::FileLines;
use lists::{SeparatorTactic, ListTactic};
use std::io::Write;

macro_rules! configuration_option_enum{
    ($e:ident: $( $x:ident ),+ $(,)*) => {
        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
        pub enum $e {
            $( $x ),+
        }

        impl_enum_decodable!($e, $( $x ),+);
    }
}

configuration_option_enum! { NewlineStyle:
    Windows, // \r\n
    Unix, // \n
    Native, // \r\n in Windows, \n on other platforms
}

configuration_option_enum! { BraceStyle:
    AlwaysNextLine,
    PreferSameLine,
    // Prefer same line except where there is a where clause, in which case force
    // the brace to the next line.
    SameLineWhere,
}

configuration_option_enum! { ControlBraceStyle:
    // K&R/Stroustrup style, Rust community default
    AlwaysSameLine,
    // Allman style
    AlwaysNextLine,
}

configuration_option_enum! { ElseIfBraceStyle:
    // K&R style, Rust community default
    AlwaysSameLine,
    // Stroustrup style
    ClosingNextLine,
    // Allman style
    AlwaysNextLine,
}

// How to indent a function's return type.
configuration_option_enum! { ReturnIndent:
    // Aligned with the arguments
    WithArgs,
    // Aligned with the where clause
    WithWhereClause,
}

// How to style a struct literal.
configuration_option_enum! { StructLitStyle:
    // First line on the same line as the opening brace, all lines aligned with
    // the first line.
    Visual,
    // First line is on a new line and all lines align with block indent.
    Block,
    // FIXME Maybe we should also have an option to align types.
}

// How to style fn args.
configuration_option_enum! { FnArgLayoutStyle:
    // First line on the same line as the opening brace, all lines aligned with
    // the first line.
    Visual,
    // Put args on one line if they fit, or start a new line with block indent.
    Block,
    // First line is on a new line and all lines align with block indent.
    BlockAlways,
}

configuration_option_enum! { BlockIndentStyle:
    // Same level as parent.
    Inherit,
    // One level deeper than parent.
    Tabbed,
    // Aligned with block open.
    Visual,
}

configuration_option_enum! { Density:
    // Fit as much on one line as possible.
    Compressed,
    // Use more lines.
    Tall,
    // Try to compress if the body is empty.
    CompressedIfEmpty,
    // Place every item on a separate line.
    Vertical,
}

configuration_option_enum! { TypeDensity:
    // No spaces around "=" and "+"
    Compressed,
    // Spaces around " = " and " + "
    Wide,
}

impl Density {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            Density::Compressed => ListTactic::Mixed,
            Density::Tall |
            Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
            Density::Vertical => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { LicensePolicy:
    // Do not place license text at top of files
    NoLicense,
    // Use the text in "license" field as the license
    TextLicense,
    // Use a text file as the license text
    FileLicense,
}

configuration_option_enum! { MultilineStyle:
    // Use horizontal layout if it fits in one line, fall back to vertical
    PreferSingle,
    // Use vertical layout
    ForceMulti,
}

impl MultilineStyle {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
            MultilineStyle::ForceMulti => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { ReportTactic:
    Always,
    Unnumbered,
    Never,
}

configuration_option_enum! { WriteMode:
    // Backs the original file up and overwrites the original.
    Replace,
    // Overwrites original file without backup.
    Overwrite,
    // Writes the output to stdout.
    Display,
    // Writes the diff to stdout.
    Diff,
    // Displays how much of the input file was processed
    Coverage,
    // Unfancy stdout
    Plain,
    // Outputs a checkstyle XML file.
    Checkstyle,
}

/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
    /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
    /// pipe-separated list of variants; for other types it returns "<type>".
    fn doc_hint() -> String;
}

impl ConfigType for bool {
    fn doc_hint() -> String {
        String::from("<boolean>")
    }
}

impl ConfigType for usize {
    fn doc_hint() -> String {
        String::from("<unsigned integer>")
    }
}

impl ConfigType for isize {
    fn doc_hint() -> String {
        String::from("<signed integer>")
    }
}

impl ConfigType for String {
    fn doc_hint() -> String {
        String::from("<string>")
    }
}

impl ConfigType for FileLines {
    fn doc_hint() -> String {
        String::from("<json>")
    }
}

pub struct ConfigHelpItem {
    option_name: &'static str,
    doc_string: &'static str,
    variant_names: String,
    default: &'static str,
}

impl ConfigHelpItem {
    pub fn option_name(&self) -> &'static str {
        self.option_name
    }

    pub fn doc_string(&self) -> &'static str {
        self.doc_string
    }

    pub fn variant_names(&self) -> &String {
        &self.variant_names
    }

    pub fn default(&self) -> &'static str {
        self.default
    }
}

macro_rules! create_config {
    ($($i:ident: $ty:ty, $def:expr, $( $dstring:expr ),+ );+ $(;)*) => (
        #[derive(RustcDecodable, Clone)]
        pub struct Config {
            $(pub $i: $ty),+
        }

        // Just like the Config struct but with each property wrapped
        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
        // specity all properties of `Config`.
        // We first parse into `ParsedConfig`, then create a default `Config`
        // and overwrite the properties with corresponding values from `ParsedConfig`
        #[derive(RustcDecodable, Clone)]
        pub struct ParsedConfig {
            $(pub $i: Option<$ty>),+
        }

        impl Config {

            fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
            $(
                if let Some(val) = parsed.$i {
                    self.$i = val;
                }
            )+
                self
            }

            pub fn from_toml(toml: &str) -> Config {
                let parsed = toml.parse().expect("Could not parse TOML");
                let parsed_config:ParsedConfig = match toml::decode(parsed) {
                    Some(decoded) => decoded,
                    None => {
                        msg!("Decoding config file failed. Config:\n{}", toml);
                        let parsed: toml::Value = toml.parse().expect("Could not parse TOML");
                        msg!("\n\nParsed:\n{:?}", parsed);
                        panic!();
                    }
                };
                Config::default().fill_from_parsed_config(parsed_config)
            }

            pub fn override_value(&mut self, key: &str, val: &str) {
                match key {
                    $(
                        stringify!($i) => {
                            self.$i = val.parse::<$ty>()
                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
                                                 stringify!($i),
                                                 val,
                                                 stringify!($ty)));
                        }
                    )+
                    _ => panic!("Unknown config key in override: {}", key)
                }
            }

            pub fn print_docs() {
                use std::cmp;
                let max = 0;
                $( let max = cmp::max(max, stringify!($i).len()+1); )+
                let mut space_str = String::with_capacity(max);
                for _ in 0..max {
                    space_str.push(' ');
                }
                println!("Configuration Options:");
                $(
                    let name_raw = stringify!($i);
                    let mut name_out = String::with_capacity(max);
                    for _ in name_raw.len()..max-1 {
                        name_out.push(' ')
                    }
                    name_out.push_str(name_raw);
                    name_out.push(' ');
                    println!("{}{} Default: {:?}",
                             name_out,
                             <$ty>::doc_hint(),
                             $def);
                    $(
                        println!("{}{}", space_str, $dstring);
                    )+
                    println!("");
                )+
            }
        }

        // Template for the default configuration
        impl Default for Config {
            fn default() -> Config {
                Config {
                    $(
                        $i: $def,
                    )+
                }
            }
        }
    )
}

create_config! {
    verbose: bool, false, "Use verbose output";
    skip_children: bool, false, "Don't reformat out of line modules";
    file_lines: FileLines, FileLines::all(),
        "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
         via the --file-lines option";
    max_width: usize, 100, "Maximum width of each line";
    ideal_width: usize, 80, "Ideal width of each line";
    tab_spaces: usize, 4, "Number of spaces per tab";
    fn_call_width: usize, 60,
        "Maximum width of the args of a function call before falling back to vertical formatting";
    struct_lit_width: usize, 16,
        "Maximum width in the body of a struct lit before falling back to vertical formatting";
    struct_variant_width: usize, 35,
        "Maximum width in the body of a struct variant before falling back to vertical formatting";
    force_explicit_abi: bool, true, "Always print the abi for extern items";
    newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
    fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
    item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
    else_if_brace_style: ElseIfBraceStyle, ElseIfBraceStyle::AlwaysSameLine,
        "Brace style for if, else if, and else constructs";
    control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
        "Brace style for match, loop, for, and while constructs";
    impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
    fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
    fn_single_line: bool, false, "Put single-expression functions on a single line";
    fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
        "Location of return type in function declaration";
    fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
    fn_args_density: Density, Density::Tall, "Argument density in functions";
    fn_args_layout: FnArgLayoutStyle, FnArgLayoutStyle::Visual, "Layout of function arguments";
    fn_arg_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indent on function arguments";
    type_punctuation_density: TypeDensity, TypeDensity::Wide,
        "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
    // Should we at least try to put the where clause on the same line as the rest of the
    // function decl?
    where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
    // Visual will be treated like Tabbed
    where_indent: BlockIndentStyle, BlockIndentStyle::Tabbed, "Indentation of a where clause";
    where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
    where_pred_indent: BlockIndentStyle, BlockIndentStyle::Visual,
        "Indentation style of a where predicate";
    where_trailing_comma: bool, false, "Put a trailing comma on where clauses";
    generics_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indentation of generics";
    struct_trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
        "If there is a trailing comma on structs";
    struct_lit_trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
        "If there is a trailing comma on literal structs";
    struct_lit_style: StructLitStyle, StructLitStyle::Block, "Style of struct definition";
    struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
        "Multiline style on literal structs";
    enum_trailing_comma: bool, true, "Put a trailing comma on enum declarations";
    report_todo: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of TODO in source file comments";
    report_fixme: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of FIXME in source file comments";
    chain_base_indent: BlockIndentStyle, BlockIndentStyle::Tabbed, "Indent on chain base";
    chain_indent: BlockIndentStyle, BlockIndentStyle::Tabbed, "Indentation of chain";
    chains_overflow_last: bool, true, "Allow last call in method chain to break the line";
    reorder_imports: bool, false, "Reorder import statements alphabetically";
    reorder_imported_names: bool, false,
        "Reorder lists of names in import statements alphabetically";
    single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
                                                expressions. A value of zero means always break \
                                                if-else expressions.";
    format_strings: bool, true, "Format string literals where necessary";
    force_format_strings: bool, false, "Always format string literals";
    take_source_hints: bool, true, "Retain some formatting characteristics from the source code";
    hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
    wrap_comments: bool, false, "Break comments to fit on the line";
    normalize_comments: bool, true, "Convert /* */ comments to // comments where possible";
    wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
    match_block_trailing_comma: bool, false,
        "Put a trailing comma after a block based match arm (non-block arms are not affected)";
    match_wildcard_trailing_comma: bool, true, "Put a trailing comma after a wildcard arm";
    closure_block_indent_threshold: isize, 5, "How many lines a closure must have before it is \
                                               block indented. -1 means never use block indent.";
    space_before_type_annotation: bool, false,
        "Leave a space before the colon in a type annotation";
    space_after_type_annotation_colon: bool, true,
        "Leave a space after the colon in a type annotation";
    space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
    space_after_bound_colon: bool, true,
        "Leave a space after the colon in a trait or lifetime bound";
    spaces_around_ranges: bool, false, "Put spaces around the  .. and ... range operators";
    spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
    spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
    spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
    use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
    write_mode: WriteMode, WriteMode::Replace,
        "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
    condense_wildcard_suffices: bool, false, "Replace strings of _ wildcards by a single .. in \
                                              tuple patterns"
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::fs;
use std::io::Write;
use clap::{App, Arg};
use tempfile::{NamedTempFile, NamedTempFileOptions};
use ct_result::{CtResult, CtError, OkOr};
use cmd::Cmd;

#[derive(Debug)]
pub enum SourceFile {
    /// the C++ source file given as argument
    /// to `cpp-typecheck`
    FromArg {
        cpp_file: PathBuf
    },

    /// a header file was given as argument and a
    /// source file in the same directory was found
    FromHeader {
        header_file: PathBuf,
        cpp_file: PathBuf
    },

    /// a header file was given as argument and no
    /// source file could be found, so a temporary
    /// one was created that just includes the header,
    /// a compiler command was created by taking the
    /// arguments of an other source file in the same
    /// directory
    FromHeaderWithTmpSource {
        header_file: PathBuf,
        cpp_file: NamedTempFile,
        command: Cmd
    }
}

impl SourceFile {
    pub fn origin_path(&self) -> &Path {
        match *self {
            SourceFile::FromArg { ref cpp_file } => cpp_file.as_path(),
            SourceFile::FromHeader { ref header_file, .. } => header_file.as_path(),
            SourceFile::FromHeaderWithTmpSource { ref header_file, .. } => header_file.as_path()
        }
    }
}

/// the configuration used to run `cpp-typecheck`
#[derive(Debug)]
pub struct Config {
    /// use this compiler for the type checking instead
    /// of the one specified in the database
    pub compiler: Option<String>,

    /// the C++ source file to type check
    pub source_file: SourceFile,

    /// the clang compilation databases to look up
    /// the C++ source file
    pub db_files: Vec<PathBuf>,

    /// forces the lookup in the database without considering
    /// the command cache
    pub no_cache: bool,

    /// forces the recaching of the command by doing the lookup
    /// in the database
    pub force_recache: bool
}

impl Config {
   pub fn from_command_args() -> CtResult<Config> {
       let matches = try!(App::new("cpp-typecheck")
           .about("Type check a C++ source file with a clang compilation database")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg(Arg::with_name("compiler")
                .short("c")
                .long("compiler")
                .value_names(&["PATH"])
                .help("Use this compiler for the type checking instead of the one specified in the database")
                .takes_value(true))
           .arg(Arg::with_name("no-cache")
                .short("n")
                .long("no-cache")
                .help("Forces the lookup in the database without considering the command cache"))
           .arg(Arg::with_name("force-recache")
                .short("f")
                .long("force-recache")
                .help("Forces the recaching of the command by doing the lookup in the database"))
           .arg(Arg::with_name("SOURCE-FILE")
               .help("The C++ source file to type check")
               .required(true)
               .index(1))
           .arg(Arg::with_name("CLANG-DB")
               .help("The clang compilation database")
               .index(2)
               .multiple(true))
           .get_matches_safe());

       let src_file = PathBuf::from(try!(matches.value_of("SOURCE-FILE").ok_or("Missing C++ source file!")));
       try!(src_file.is_absolute().ok_or(format!("C++ source file '{}' has to have an absolute path!",
                                         src_file.display())));

       let db_files: Vec<PathBuf> = {
           if let Some(values) = matches.values_of("CLANG-DB") {
               values.map(PathBuf::from).collect()
           } else {
               let dir = try!(src_file.parent()
                  .ok_or(format!("Couldn't get directory of source file '{}'!", src_file.display())));

               vec![try!(find_db(&dir))]
           }
       };

       try!((! db_files.is_empty()).ok_or("Missing clang compilation database!"));

       let source_file = try!(get_source_file(&src_file, &db_files));

       Ok(Config {
           compiler: matches.value_of("compiler").map(String::from),
           source_file: source_file,
           db_files: db_files,
           no_cache: matches.is_present("no-cache"),
           force_recache: matches.is_present("force-recache")
       })
   }
}

/// Searches for a `compile_commands.json` file starting at `start_dir` and continuing the search
/// upwards the directory tree until the file is found.
fn find_db(start_dir: &Path) -> CtResult<PathBuf> {
    let mut dir = start_dir.to_path_buf();
    loop {
        if let Ok(files) = fs::read_dir(&dir) {
            for file in files {
                if let Ok(file) = file {
                    let path = file.path();
                    if path.is_file() {
                        if let Some("compile_commands.json") = path.file_name().and_then(|s| s.to_str()) {
                            return Ok(path);
                        }
                    }
                }
            }
        }

        try!(dir.pop().ok_or(format!("Couldn't find 'compile_commands.json' starting at directory '{}'!",
                                     start_dir.display())));
    }
}

fn get_source_file(src_file: &Path, db_files: &[PathBuf]) -> CtResult<SourceFile> {
    let is_header_file = if let Some(ext) = src_file.extension() {
        ext == "h"
            || ext == "hpp"
            || ext == "hh"
            || ext == "H"
            || ext == "HPP"
            || ext == "HH"
    } else {
        // no extension, assume a header file
        true
    };

    if ! is_header_file {
        return Ok(SourceFile::FromArg { cpp_file: src_file.to_path_buf() });
    }

    let cpp_exts = ["cpp", "cxx", "cc", "c", "CPP", "CXX", "CC", "C"];
    // search for C++ source file in the same directory with same
    // name as the header file
    {
        let mut cpp_file: Option<PathBuf> = None;
        for cpp_ext in &cpp_exts {
            let file = src_file.with_extension(cpp_ext);
            if file.is_file() {
                cpp_file = Some(file);
                break;
            }
        }

        if let Some(cpp_file) = cpp_file {
            return Ok(SourceFile::FromHeader {
                header_file: src_file.to_path_buf(),
                cpp_file: cpp_file
            });
        }
    }

    // search for any other C++ source file in the same directory
    // as the header file, take its compiler command and replace the
    // source file with a temporary created source file that only
    // includes the header file
    {
        let src_dir = try!(src_file.parent()
            .ok_or(format!("Couldn't get directory of source file '{}'!", src_file.display())));

        for file in try!(src_dir.read_dir()) {
            if let Ok(file) = file {
                let file = file.path();
                if ! file.is_file() {
                    continue;
                }

                if ! is_cpp_source_file(&file) {
                    continue;
                }

                if let Ok(cmd) = Cmd::from_databases(&file, db_files) {
                    let mut cpp_file = try!(NamedTempFileOptions::new()
                        .prefix("cpp-typecheck-")
                        .suffix(".cpp")
                        .create());

                    try!(cpp_file.write_fmt(format_args!("#include \"{}\"\n", src_file.display())));
                    let cmd = cmd.replace_cpp_file(&cpp_file.path());

                    return Ok(SourceFile::FromHeaderWithTmpSource {
                        header_file: src_file.to_path_buf(),
                        cpp_file: cpp_file,
                        command: cmd
                    });
                }
            }
        }
    }

    Err(CtError::from(format!("Couldn't find C++ source file for header '{}'!", src_file.display())))
}

fn is_cpp_source_file(file: &Path) -> bool {
    if let Some(ext) = file.extension() {
        let ext = ext.to_string_lossy();
        let cpp_exts = ["cpp", "cxx", "c"];
        for cpp_ext in &cpp_exts {
            if ext == *cpp_ext {
                return true;
            }
        }
    }

    return false;
}
<commit_msg>Simplify header/source extension checking<commit_after>use std::path::{Path, PathBuf};
use std::fs;
use std::io::Write;
use clap::{App, Arg};
use tempfile::{NamedTempFile, NamedTempFileOptions};
use ct_result::{CtResult, CtError, OkOr};
use cmd::Cmd;

#[derive(Debug)]
pub enum SourceFile {
    /// the C++ source file given as argument
    /// to `cpp-typecheck`
    FromArg {
        cpp_file: PathBuf
    },

    /// a header file was given as argument and a
    /// source file in the same directory was found
    FromHeader {
        header_file: PathBuf,
        cpp_file: PathBuf
    },

    /// a header file was given as argument and no
    /// source file could be found, so a temporary
    /// one was created that just includes the header,
    /// a compiler command was created by taking the
    /// arguments of an other source file in the same
    /// directory
    FromHeaderWithTmpSource {
        header_file: PathBuf,
        cpp_file: NamedTempFile,
        command: Cmd
    }
}

impl SourceFile {
    pub fn origin_path(&self) -> &Path {
        match *self {
            SourceFile::FromArg { ref cpp_file } => cpp_file.as_path(),
            SourceFile::FromHeader { ref header_file, .. } => header_file.as_path(),
            SourceFile::FromHeaderWithTmpSource { ref header_file, .. } => header_file.as_path()
        }
    }
}

/// the configuration used to run `cpp-typecheck`
#[derive(Debug)]
pub struct Config {
    /// use this compiler for the type checking instead
    /// of the one specified in the database
    pub compiler: Option<String>,

    /// the C++ source file to type check
    pub source_file: SourceFile,

    /// the clang compilation databases to look up
    /// the C++ source file
    pub db_files: Vec<PathBuf>,

    /// forces the lookup in the database without considering
    /// the command cache
    pub no_cache: bool,

    /// forces the recaching of the command by doing the lookup
    /// in the database
    pub force_recache: bool
}

impl Config {
   pub fn from_command_args() -> CtResult<Config> {
       let matches = try!(App::new("cpp-typecheck")
           .about("Type check a C++ source file with a clang compilation database")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg(Arg::with_name("compiler")
                .short("c")
                .long("compiler")
                .value_names(&["PATH"])
                .help("Use this compiler for the type checking instead of the one specified in the database")
                .takes_value(true))
           .arg(Arg::with_name("no-cache")
                .short("n")
                .long("no-cache")
                .help("Forces the lookup in the database without considering the command cache"))
           .arg(Arg::with_name("force-recache")
                .short("f")
                .long("force-recache")
                .help("Forces the recaching of the command by doing the lookup in the database"))
           .arg(Arg::with_name("SOURCE-FILE")
               .help("The C++ source file to type check")
               .required(true)
               .index(1))
           .arg(Arg::with_name("CLANG-DB")
               .help("The clang compilation database")
               .index(2)
               .multiple(true))
           .get_matches_safe());

       let src_file = PathBuf::from(try!(matches.value_of("SOURCE-FILE").ok_or("Missing C++ source file!")));
       try!(src_file.is_absolute().ok_or(format!("C++ source file '{}' has to have an absolute path!",
                                         src_file.display())));

       let db_files: Vec<PathBuf> = {
           if let Some(values) = matches.values_of("CLANG-DB") {
               values.map(PathBuf::from).collect()
           } else {
               let dir = try!(src_file.parent()
                  .ok_or(format!("Couldn't get directory of source file '{}'!", src_file.display())));

               vec![try!(find_db(&dir))]
           }
       };

       try!((! db_files.is_empty()).ok_or("Missing clang compilation database!"));

       let source_file = try!(get_source_file(&src_file, &db_files));

       Ok(Config {
           compiler: matches.value_of("compiler").map(String::from),
           source_file: source_file,
           db_files: db_files,
           no_cache: matches.is_present("no-cache"),
           force_recache: matches.is_present("force-recache")
       })
   }
}

/// Searches for a `compile_commands.json` file starting at `start_dir` and continuing the search
/// upwards the directory tree until the file is found.
fn find_db(start_dir: &Path) -> CtResult<PathBuf> {
    let mut dir = start_dir.to_path_buf();
    loop {
        if let Ok(files) = fs::read_dir(&dir) {
            for file in files {
                if let Ok(file) = file {
                    let path = file.path();
                    if path.is_file() {
                        if let Some("compile_commands.json") = path.file_name().and_then(|s| s.to_str()) {
                            return Ok(path);
                        }
                    }
                }
            }
        }

        try!(dir.pop().ok_or(format!("Couldn't find 'compile_commands.json' starting at directory '{}'!",
                                     start_dir.display())));
    }
}

fn get_source_file(src_file: &Path, db_files: &[PathBuf]) -> CtResult<SourceFile> {
    let is_header_file = if let Some(ext) = src_file.extension() {
        HEADER_EXTENSIONS.iter().any(|he| *he == ext)
    } else {
        // no extension, assume a header file
        true
    };

    if ! is_header_file {
        return Ok(SourceFile::FromArg { cpp_file: src_file.to_path_buf() });
    }

    // search for C++ source file in the same directory with same
    // name as the header file
    {
        let mut cpp_file: Option<PathBuf> = None;
        for cpp_ext in &SOURCE_EXTENSIONS {
            let file = src_file.with_extension(cpp_ext);
            if file.is_file() {
                cpp_file = Some(file);
                break;
            }
        }

        if let Some(cpp_file) = cpp_file {
            return Ok(SourceFile::FromHeader {
                header_file: src_file.to_path_buf(),
                cpp_file: cpp_file
            });
        }
    }

    // search for any other C++ source file in the same directory
    // as the header file, take its compiler command and replace the
    // source file with a temporary created source file that only
    // includes the header file
    {
        let src_dir = try!(src_file.parent()
            .ok_or(format!("Couldn't get directory of source file '{}'!", src_file.display())));

        for file in try!(src_dir.read_dir()) {
            if let Ok(file) = file {
                let file = file.path();
                if ! file.is_file() {
                    continue;
                }

                if ! is_cpp_source_file(&file) {
                    continue;
                }

                if let Ok(cmd) = Cmd::from_databases(&file, db_files) {
                    let mut cpp_file = try!(NamedTempFileOptions::new()
                        .prefix("cpp-typecheck-")
                        .suffix(".cpp")
                        .create());

                    try!(cpp_file.write_fmt(format_args!("#include \"{}\"\n", src_file.display())));
                    let cmd = cmd.replace_cpp_file(&cpp_file.path());

                    return Ok(SourceFile::FromHeaderWithTmpSource {
                        header_file: src_file.to_path_buf(),
                        cpp_file: cpp_file,
                        command: cmd
                    });
                }
            }
        }
    }

    Err(CtError::from(format!("Couldn't find C++ source file for header '{}'!", src_file.display())))
}

fn is_cpp_source_file(file: &Path) -> bool {
    if let Some(ext) = file.extension() {
        let ext = ext.to_string_lossy();
        SOURCE_EXTENSIONS.iter().any(|se| *se == ext)
    }
    else {
        false
    }
}

static SOURCE_EXTENSIONS: [&'static str; 10] = ["cpp", "cxx", "cc", "c++", "c", "CPP", "CXX", "CC", "C", "C++"];
static HEADER_EXTENSIONS: [&'static str; 10] = ["h", "hpp", "hxx", "hh", "h++", "H", "HPP", "HXX", "HH", "H++"];
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;
extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;

use std::path::Path;

mod generator;
mod parser;
mod path;

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let ast = syn::parse_derive_input(&input.to_string()).unwrap();
    match ast.body {
        syn::Body::Struct(ref data) => data,
        _ => panic!("#[derive(Template)] can only be used with structs"),
    };
    build_template(&ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `template()` attribute to get the template
/// metadata, then fetches the source from the filesystem. The source is
/// parsed, and the parse tree is fed to the code generator. Will print
/// the parse tree and/or generated source according to the `print` key's
/// value as passed to the `template()` attribute.
fn build_template(ast: &syn::DeriveInput) -> String {
    let meta = get_template_meta(ast);
    let path = path::find_template_from_path(&meta.path, None);
    let src = path::get_template_source(&path);
    let nodes = parser::parse(&src);
    if meta.print == Print::Ast || meta.print == Print::All {
        println!("{:?}", nodes);
    }
    let code = generator::generate(ast, Path::new(&meta.path), nodes);
    if meta.print == Print::Code || meta.print == Print::All {
        println!("{}", code);
    }
    code
}

// Returns a `TemplateMeta` based on the `template()` attribute data found
// in the parsed struct or enum. Will panic if it does not find the required
// template path, or if the `print` key has an unexpected value.
fn get_template_meta<'a>(ast: &'a syn::DeriveInput) -> TemplateMeta<'a> {
    let attr = ast.attrs.iter().find(|a| a.name() == "template");
    if attr.is_none() {
        let msg = format!("'template' attribute not found on struct '{}'",
                          ast.ident.as_ref());
        panic!(msg);
    }

    let attr = attr.unwrap();
    let mut path = None;
    let mut print = Print::None;
    if let syn::MetaItem::List(_, ref inner) = attr.value {
        for nm_item in inner {
            if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                    match key.as_ref() {
                        "path" => if let syn::Lit::Str(ref s, _) = *val {
                            path = Some(s.as_ref());
                        } else {
                            panic!("template path must be string literal");
                        },
                        "print" => if let syn::Lit::Str(ref s, _) = *val {
                            print = s.into();
                        } else {
                            panic!("print value must be string literal");
                        },
                        _ => { panic!("unsupported annotation key found") }
                    }
                }
            }
        }
    }
    if path.is_none() {
        panic!("template path not found in struct attributes");
    }
    TemplateMeta { path: path.unwrap(), print: print }
}

// Holds metadata for the template, based on the `template()` attribute.
struct TemplateMeta<'a> {
    path: &'a str,
    print: Print,
}

#[derive(PartialEq)]
enum Print {
    All,
    Ast,
    Code,
    None,
}

impl<'a> From<&'a String> for Print {
    fn from(s: &'a String) -> Print {
        use Print::*;
        match s.as_ref() {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}
<commit_msg>Support specifying template source as an attribute<commit_after>#[macro_use]
extern crate nom;
extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;

use std::borrow::Cow;
use std::path::PathBuf;

mod generator;
mod parser;
mod path;

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let ast = syn::parse_derive_input(&input.to_string()).unwrap();
    match ast.body {
        syn::Body::Struct(ref data) => data,
        _ => panic!("#[derive(Template)] can only be used with structs"),
    };
    build_template(&ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `template()` attribute to get the template
/// metadata, then fetches the source from the filesystem. The source is
/// parsed, and the parse tree is fed to the code generator. Will print
/// the parse tree and/or generated source according to the `print` key's
/// value as passed to the `template()` attribute.
fn build_template(ast: &syn::DeriveInput) -> String {
    let meta = get_template_meta(ast);
    let (path, src) = match meta.source {
        Source::Source(s) => (PathBuf::new(), Cow::Borrowed(s)),
        Source::Path(s) => {
            let path = path::find_template_from_path(&s, None);
            let src = path::get_template_source(&path);
            (path, Cow::Owned(src))
        },
    };
    let nodes = parser::parse(src.as_ref());
    if meta.print == Print::Ast || meta.print == Print::All {
        println!("{:?}", nodes);
    }
    let code = generator::generate(ast, &path, nodes);
    if meta.print == Print::Code || meta.print == Print::All {
        println!("{}", code);
    }
    code
}

// Returns a `TemplateMeta` based on the `template()` attribute data found
// in the parsed struct or enum. Will panic if it does not find the required
// template path, or if the `print` key has an unexpected value.
fn get_template_meta<'a>(ast: &'a syn::DeriveInput) -> TemplateMeta<'a> {
    let attr = ast.attrs.iter().find(|a| a.name() == "template");
    if attr.is_none() {
        let msg = format!("'template' attribute not found on struct '{}'",
                          ast.ident.as_ref());
        panic!(msg);
    }

    let attr = attr.unwrap();
    let mut source = None;
    let mut print = Print::None;
    if let syn::MetaItem::List(_, ref inner) = attr.value {
        for nm_item in inner {
            if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                    match key.as_ref() {
                        "path" => if let syn::Lit::Str(ref s, _) = *val {
                            source = Some(Source::Path(s.as_ref()));
                        } else {
                            panic!("template path must be string literal");
                        },
                        "source" => if let syn::Lit::Str(ref s, _) = *val {
                            source = Some(Source::Source(s.as_ref()));
                        } else {
                            panic!("template source must be string literal");
                        },
                        "print" => if let syn::Lit::Str(ref s, _) = *val {
                            print = s.into();
                        } else {
                            panic!("print value must be string literal");
                        },
                        _ => { panic!("unsupported annotation key found") }
                    }
                }
            }
        }
    }

    match source {
        Some(s) => TemplateMeta { source: s, print },
        None => panic!("template path or source not found in struct attributes"),
    }
}

// Holds metadata for the template, based on the `template()` attribute.
struct TemplateMeta<'a> {
    source: Source<'a>,
    print: Print,
}

enum Source<'a> {
    Path(&'a str),
    Source(&'a str),
}

#[derive(PartialEq)]
enum Print {
    All,
    Ast,
    Code,
    None,
}

impl<'a> From<&'a String> for Print {
    fn from(s: &'a String) -> Print {
        use Print::*;
        match s.as_ref() {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}
<|endoftext|>


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

//! Just some stubs so that you can link against this library in place of the
//! Chromium version of CEF. If you call these functions you will assuredly
//! crash.

macro_rules! stub(
    ($name:ident) => (
        #[no_mangle]
        pub extern "C" fn $name() {
            println!("CEF stub function called: {}", stringify!($name));
            unsafe {
                ::std::intrinsics::abort()
            }
        }
    )
);

stub!(cef_add_cross_origin_whitelist_entry);
stub!(cef_add_web_plugin_directory);
stub!(cef_add_web_plugin_path);
stub!(cef_begin_tracing);
stub!(cef_clear_cross_origin_whitelist);
stub!(cef_clear_scheme_handler_factories);
stub!(cef_create_url);
stub!(cef_end_tracing);
stub!(cef_force_web_plugin_shutdown);
stub!(cef_get_current_platform_thread_handle);
stub!(cef_get_current_platform_thread_id);
stub!(cef_get_extensions_for_mime_type);
stub!(cef_get_geolocation);
stub!(cef_get_mime_type);
stub!(cef_get_path);
stub!(cef_is_web_plugin_unstable);
stub!(cef_launch_process);
stub!(cef_now_from_system_trace_time);
stub!(cef_parse_url);
stub!(cef_post_delayed_task);
stub!(cef_post_task);
stub!(cef_refresh_web_plugins);
stub!(cef_register_extension);
stub!(cef_register_scheme_handler_factory);
stub!(cef_register_web_plugin_crash);
stub!(cef_remove_cross_origin_whitelist_entry);
stub!(cef_remove_web_plugin_path);
stub!(cef_set_osmodal_loop);
stub!(cef_string_utf16_to_wide);
stub!(cef_string_wide_to_utf16);
stub!(cef_unregister_internal_web_plugin);
stub!(cef_visit_web_plugin_info);

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

//! Just some stubs so that you can link against this library in place of the
//! Chromium version of CEF. If you call these functions you will assuredly
//! crash.

macro_rules! stub(
    ($name:ident) => (
        #[no_mangle]
        pub extern "C" fn $name() {
            println!("CEF stub function called: {}", stringify!($name));
            unsafe {
                ::std::intrinsics::abort()
            }
        }
    )
);

stub!(cef_add_cross_origin_whitelist_entry);
stub!(cef_add_web_plugin_directory);
stub!(cef_add_web_plugin_path);
stub!(cef_begin_tracing);
stub!(cef_clear_cross_origin_whitelist);
stub!(cef_clear_scheme_handler_factories);
stub!(cef_create_url);
stub!(cef_end_tracing);
stub!(cef_force_web_plugin_shutdown);
stub!(cef_get_current_platform_thread_handle);
stub!(cef_get_current_platform_thread_id);
stub!(cef_get_extensions_for_mime_type);
stub!(cef_get_geolocation);
stub!(cef_get_mime_type);
stub!(cef_get_path);
stub!(cef_is_web_plugin_unstable);
stub!(cef_launch_process);
stub!(cef_now_from_system_trace_time);
stub!(cef_parse_url);
stub!(cef_post_delayed_task);
stub!(cef_post_task);
stub!(cef_refresh_web_plugins);
stub!(cef_register_extension);
stub!(cef_register_scheme_handler_factory);
stub!(cef_register_web_plugin_crash);
stub!(cef_remove_cross_origin_whitelist_entry);
stub!(cef_remove_web_plugin_path);
stub!(cef_set_osmodal_loop);
stub!(cef_string_utf16_to_wide);
stub!(cef_string_wide_to_utf16);
stub!(cef_unregister_internal_web_plugin);
stub!(cef_visit_web_plugin_info);

//from skia
stub!(gluCheckExtension);
<|endoftext|>


<commit_before>//! Serde `Deserializer` module

use crate::{
    de::escape::EscapedDeserializer,
    de::{DeEvent, Deserializer, XmlRead, INNER_VALUE, UNFLATTEN_PREFIX},
    errors::serialize::DeError,
    events::attributes::IterState,
    events::BytesStart,
};
use serde::de::{self, DeserializeSeed, IntoDeserializer};
use std::borrow::Cow;
use std::ops::Range;

/// Defines a source that should be used to deserialize a value in the next call
/// to [`next_value_seed()`](de::MapAccess::next_value_seed)
#[derive(Debug, PartialEq)]
enum ValueSource {
    /// Source are not specified, because [`next_key_seed()`] not yet called.
    /// This is an initial state and state after deserializing value
    /// (after call of [`next_value_seed()`]).
    ///
    /// Attempt to call [`next_value_seed()`] while accessor in this state would
    /// return a [`DeError::KeyNotRead`] error.
    ///
    /// [`next_key_seed()`]: de::MapAccess::next_key_seed
    /// [`next_value_seed()`]: de::MapAccess::next_value_seed
    Unknown,
    /// `next_key_seed` checked the attributes list and find it is not exhausted yet.
    /// Next call to the `next_value_seed` will deserialize type from the attribute value
    Attribute(Range<usize>),
    /// The same as `InnerValue`
    Nested,
    /// Value should be deserialized from the text content of the XML node:
    ///
    /// ```xml
    /// <...>text content for field value<...>
    /// ```
    InnerValue,
}

/// A deserializer for `Attributes`
pub(crate) struct MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Tag -- owner of attributes
    start: BytesStart<'de>,
    de: &'a mut Deserializer<'de, R>,
    /// State of the iterator over attributes. Contains the next position in the
    /// inner `start` slice, from which next attribute should be parsed.
    iter: IterState,
    /// Current state of the accessor that determines what next call to API
    /// methods should return.
    source: ValueSource,
    /// list of fields yet to unflatten (defined as starting with $unflatten=)
    unflatten_fields: Vec<&'static [u8]>,
}

impl<'de, 'a, R> MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Create a new MapAccess
    pub fn new(
        de: &'a mut Deserializer<'de, R>,
        start: BytesStart<'de>,
        fields: &[&'static str],
    ) -> Result<Self, DeError> {
        Ok(MapAccess {
            de,
            start,
            iter: IterState::new(0, false),
            source: ValueSource::Unknown,
            unflatten_fields: fields
                .iter()
                .filter(|f| f.starts_with(UNFLATTEN_PREFIX))
                .map(|f| f.as_bytes())
                .collect(),
        })
    }
}

impl<'de, 'a, R> de::MapAccess<'de> for MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    type Error = DeError;

    fn next_key_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<Option<K::Value>, Self::Error> {
        debug_assert_eq!(self.source, ValueSource::Unknown);

        // FIXME: There error positions counted from end of tag name - need global position
        let slice = self.start.attributes_raw();
        let decoder = self.de.reader.decoder();
        let has_value_field = self.de.has_value_field;

        if let Some(a) = self.iter.next(slice).transpose()? {
            // try getting map from attributes (key= "value")
            let (key, value) = a.into();
            self.source = ValueSource::Attribute(value.unwrap_or_default());
            seed.deserialize(EscapedDeserializer::new(
                Cow::Borrowed(&slice[key]),
                decoder,
                false,
            ))
            .map(Some)
        } else {
            // try getting from events (<key>value</key>)
            match self.de.peek()? {
                DeEvent::Text(_) | DeEvent::CData(_) => {
                    self.source = ValueSource::InnerValue;
                    // Deserialize `key` from special attribute name which means
                    // that value should be taken from the text content of the
                    // XML node
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                // Used to deserialize collections of enums, like:
                // <root>
                //
                //
                //   <C/>
                // </root>
                //
                // into
                //
                // enum Enum { A, B, С }
                // struct Root {
                //     #[serde(rename = "$value")]
                //     items: Vec<Enum>,
                // }
                // TODO: This should be handled by #[serde(flatten)]
                // See https://github.com/serde-rs/serde/issues/1905
                DeEvent::Start(_) if has_value_field => {
                    self.source = ValueSource::InnerValue;
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                DeEvent::Start(e) => {
                    let key = if let Some(p) = self
                        .unflatten_fields
                        .iter()
                        .position(|f| e.name() == &f[UNFLATTEN_PREFIX.len()..])
                    {
                        // Used to deserialize elements, like:
                        // <root>
                        //   <xxx>test</xxx>
                        // </root>
                        //
                        // into
                        //
                        // struct Root {
                        //     #[serde(rename = "$unflatten=xxx")]
                        //     xxx: String,
                        // }
                        self.source = ValueSource::InnerValue;
                        seed.deserialize(self.unflatten_fields.remove(p).into_deserializer())
                    } else {
                        let name = Cow::Borrowed(e.local_name());
                        self.source = ValueSource::Nested;
                        seed.deserialize(EscapedDeserializer::new(name, decoder, false))
                    };
                    key.map(Some)
                }
                _ => Ok(None),
            }
        }
    }

    fn next_value_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<K::Value, Self::Error> {
        match std::mem::replace(&mut self.source, ValueSource::Unknown) {
            ValueSource::Attribute(value) => {
                let slice = self.start.attributes_raw();
                let decoder = self.de.reader.decoder();

                seed.deserialize(EscapedDeserializer::new(
                    Cow::Borrowed(&slice[value]),
                    decoder,
                    true,
                ))
            }
            ValueSource::Nested | ValueSource::InnerValue => seed.deserialize(&mut *self.de),
            ValueSource::Unknown => Err(DeError::KeyNotRead),
        }
    }
}
<commit_msg>Rename `InnerValue` to `Text` because this name is better reflects purpose of that enum variant<commit_after>//! Serde `Deserializer` module

use crate::{
    de::escape::EscapedDeserializer,
    de::{DeEvent, Deserializer, XmlRead, INNER_VALUE, UNFLATTEN_PREFIX},
    errors::serialize::DeError,
    events::attributes::IterState,
    events::BytesStart,
};
use serde::de::{self, DeserializeSeed, IntoDeserializer};
use std::borrow::Cow;
use std::ops::Range;

/// Defines a source that should be used to deserialize a value in the next call
/// to [`next_value_seed()`](de::MapAccess::next_value_seed)
#[derive(Debug, PartialEq)]
enum ValueSource {
    /// Source are not specified, because [`next_key_seed()`] not yet called.
    /// This is an initial state and state after deserializing value
    /// (after call of [`next_value_seed()`]).
    ///
    /// Attempt to call [`next_value_seed()`] while accessor in this state would
    /// return a [`DeError::KeyNotRead`] error.
    ///
    /// [`next_key_seed()`]: de::MapAccess::next_key_seed
    /// [`next_value_seed()`]: de::MapAccess::next_value_seed
    Unknown,
    /// `next_key_seed` checked the attributes list and find it is not exhausted yet.
    /// Next call to the `next_value_seed` will deserialize type from the attribute value
    Attribute(Range<usize>),
    /// The same as `Text`
    Nested,
    /// Value should be deserialized from the text content of the XML node, which
    /// represented or by an ordinary text node, or by a CDATA node:
    ///
    /// ```xml
    /// <...>text content for field value<...>
    /// ```
    /// ```xml
    /// <any-tag>
    ///     <key><![CDATA[cdata content]]></key>
    /// <!--              ^^^^^^^^^^^^^ - this will be used to deserialize a map value -->
    /// </any-tag>
    /// ```
    Text,
}

/// A deserializer for `Attributes`
pub(crate) struct MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Tag -- owner of attributes
    start: BytesStart<'de>,
    de: &'a mut Deserializer<'de, R>,
    /// State of the iterator over attributes. Contains the next position in the
    /// inner `start` slice, from which next attribute should be parsed.
    iter: IterState,
    /// Current state of the accessor that determines what next call to API
    /// methods should return.
    source: ValueSource,
    /// list of fields yet to unflatten (defined as starting with $unflatten=)
    unflatten_fields: Vec<&'static [u8]>,
}

impl<'de, 'a, R> MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Create a new MapAccess
    pub fn new(
        de: &'a mut Deserializer<'de, R>,
        start: BytesStart<'de>,
        fields: &[&'static str],
    ) -> Result<Self, DeError> {
        Ok(MapAccess {
            de,
            start,
            iter: IterState::new(0, false),
            source: ValueSource::Unknown,
            unflatten_fields: fields
                .iter()
                .filter(|f| f.starts_with(UNFLATTEN_PREFIX))
                .map(|f| f.as_bytes())
                .collect(),
        })
    }
}

impl<'de, 'a, R> de::MapAccess<'de> for MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    type Error = DeError;

    fn next_key_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<Option<K::Value>, Self::Error> {
        debug_assert_eq!(self.source, ValueSource::Unknown);

        // FIXME: There error positions counted from end of tag name - need global position
        let slice = self.start.attributes_raw();
        let decoder = self.de.reader.decoder();
        let has_value_field = self.de.has_value_field;

        if let Some(a) = self.iter.next(slice).transpose()? {
            // try getting map from attributes (key= "value")
            let (key, value) = a.into();
            self.source = ValueSource::Attribute(value.unwrap_or_default());
            seed.deserialize(EscapedDeserializer::new(
                Cow::Borrowed(&slice[key]),
                decoder,
                false,
            ))
            .map(Some)
        } else {
            // try getting from events (<key>value</key>)
            match self.de.peek()? {
                DeEvent::Text(_) | DeEvent::CData(_) => {
                    self.source = ValueSource::Text;
                    // Deserialize `key` from special attribute name which means
                    // that value should be taken from the text content of the
                    // XML node
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                // Used to deserialize collections of enums, like:
                // <root>
                //
                //
                //   <C/>
                // </root>
                //
                // into
                //
                // enum Enum { A, B, С }
                // struct Root {
                //     #[serde(rename = "$value")]
                //     items: Vec<Enum>,
                // }
                // TODO: This should be handled by #[serde(flatten)]
                // See https://github.com/serde-rs/serde/issues/1905
                DeEvent::Start(_) if has_value_field => {
                    self.source = ValueSource::Text;
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                DeEvent::Start(e) => {
                    let key = if let Some(p) = self
                        .unflatten_fields
                        .iter()
                        .position(|f| e.name() == &f[UNFLATTEN_PREFIX.len()..])
                    {
                        // Used to deserialize elements, like:
                        // <root>
                        //   <xxx>test</xxx>
                        // </root>
                        //
                        // into
                        //
                        // struct Root {
                        //     #[serde(rename = "$unflatten=xxx")]
                        //     xxx: String,
                        // }
                        self.source = ValueSource::Text;
                        seed.deserialize(self.unflatten_fields.remove(p).into_deserializer())
                    } else {
                        let name = Cow::Borrowed(e.local_name());
                        self.source = ValueSource::Nested;
                        seed.deserialize(EscapedDeserializer::new(name, decoder, false))
                    };
                    key.map(Some)
                }
                _ => Ok(None),
            }
        }
    }

    fn next_value_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<K::Value, Self::Error> {
        match std::mem::replace(&mut self.source, ValueSource::Unknown) {
            ValueSource::Attribute(value) => {
                let slice = self.start.attributes_raw();
                let decoder = self.de.reader.decoder();

                seed.deserialize(EscapedDeserializer::new(
                    Cow::Borrowed(&slice[value]),
                    decoder,
                    true,
                ))
            }
            ValueSource::Nested | ValueSource::Text => seed.deserialize(&mut *self.de),
            ValueSource::Unknown => Err(DeError::KeyNotRead),
        }
    }
}
<|endoftext|>


<commit_before>//! Serde `Deserializer` module

use crate::{
    de::escape::EscapedDeserializer,
    de::{DeEvent, Deserializer, XmlRead, INNER_VALUE, UNFLATTEN_PREFIX},
    errors::serialize::DeError,
    events::attributes::IterState,
    events::BytesStart,
};
use serde::de::{self, DeserializeSeed, IntoDeserializer};
use std::borrow::Cow;
use std::ops::Range;

/// Representing state of the `MapAccess` accessor.
enum State {
    /// `next_key_seed` not yet called. This is initial state and state after deserializing
    /// value (calling `next_value_seed`).
    Empty,
    /// `next_key_seed` checked the attributes list and find it is not exhausted yet.
    /// Next call to the `next_value_seed` will deserialize type from the attribute value
    Attribute(Range<usize>),
    /// The same as `InnerValue`
    Nested,
    /// Value should be deserialized from the text content of the XML node:
    ///
    /// ```xml
    /// <...>text content for field value<...>
    /// ```
    InnerValue,
}

/// A deserializer for `Attributes`
pub(crate) struct MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Tag -- owner of attributes
    start: BytesStart<'de>,
    de: &'a mut Deserializer<'de, R>,
    /// State of the iterator over attributes. Contains the next position in the
    /// inner `start` slice, from which next attribute should be parsed.
    iter: IterState,
    /// Current state of the accessor that determines what next call to API
    /// methods should return.
    state: State,
    /// list of fields yet to unflatten (defined as starting with $unflatten=)
    unflatten_fields: Vec<&'static [u8]>,
}

impl<'de, 'a, R> MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Create a new MapAccess
    pub fn new(
        de: &'a mut Deserializer<'de, R>,
        start: BytesStart<'de>,
        fields: &[&'static str],
    ) -> Result<Self, DeError> {
        Ok(MapAccess {
            de,
            start,
            iter: IterState::new(0, false),
            state: State::Empty,
            unflatten_fields: fields
                .iter()
                .filter(|f| f.starts_with(UNFLATTEN_PREFIX))
                .map(|f| f.as_bytes())
                .collect(),
        })
    }
}

impl<'de, 'a, R> de::MapAccess<'de> for MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    type Error = DeError;

    fn next_key_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<Option<K::Value>, Self::Error> {
        // FIXME: There error positions counted from end of tag name - need global position
        let slice = self.start.attributes_raw();
        let decoder = self.de.reader.decoder();
        let has_value_field = self.de.has_value_field;

        if let Some(a) = self.iter.next(slice).transpose()? {
            // try getting map from attributes (key= "value")
            let (key, value) = a.into();
            self.state = State::Attribute(value.unwrap_or_default());
            seed.deserialize(EscapedDeserializer::new(
                Cow::Borrowed(&slice[key]),
                decoder,
                false,
            ))
            .map(Some)
        } else {
            // try getting from events (<key>value</key>)
            match self.de.peek()? {
                DeEvent::Text(_) | DeEvent::CData(_) => {
                    self.state = State::InnerValue;
                    // Deserialize `key` from special attribute name which means
                    // that value should be taken from the text content of the
                    // XML node
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                // Used to deserialize collections of enums, like:
                // <root>
                //
                //
                //   <C/>
                // </root>
                //
                // into
                //
                // enum Enum { A, B, С }
                // struct Root {
                //     #[serde(rename = "$value")]
                //     items: Vec<Enum>,
                // }
                // TODO: This should be handled by #[serde(flatten)]
                // See https://github.com/serde-rs/serde/issues/1905
                DeEvent::Start(_) if has_value_field => {
                    self.state = State::InnerValue;
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                DeEvent::Start(e) => {
                    let key = if let Some(p) = self
                        .unflatten_fields
                        .iter()
                        .position(|f| e.name() == &f[UNFLATTEN_PREFIX.len()..])
                    {
                        // Used to deserialize elements, like:
                        // <root>
                        //   <xxx>test</xxx>
                        // </root>
                        //
                        // into
                        //
                        // struct Root {
                        //     #[serde(rename = "$unflatten=xxx")]
                        //     xxx: String,
                        // }
                        self.state = State::InnerValue;
                        seed.deserialize(self.unflatten_fields.remove(p).into_deserializer())
                    } else {
                        let name = Cow::Borrowed(e.local_name());
                        self.state = State::Nested;
                        seed.deserialize(EscapedDeserializer::new(name, decoder, false))
                    };
                    key.map(Some)
                }
                _ => Ok(None),
            }
        }
    }

    fn next_value_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<K::Value, Self::Error> {
        match std::mem::replace(&mut self.state, State::Empty) {
            State::Attribute(value) => {
                let slice = self.start.attributes_raw();
                let decoder = self.de.reader.decoder();

                seed.deserialize(EscapedDeserializer::new(
                    Cow::Borrowed(&slice[value]),
                    decoder,
                    true,
                ))
            }
            State::Nested | State::InnerValue => seed.deserialize(&mut *self.de),
            State::Empty => Err(DeError::KeyNotRead),
        }
    }
}
<commit_msg>Rename enumeration to name that better reflects its purpose and add a debug guard<commit_after>//! Serde `Deserializer` module

use crate::{
    de::escape::EscapedDeserializer,
    de::{DeEvent, Deserializer, XmlRead, INNER_VALUE, UNFLATTEN_PREFIX},
    errors::serialize::DeError,
    events::attributes::IterState,
    events::BytesStart,
};
use serde::de::{self, DeserializeSeed, IntoDeserializer};
use std::borrow::Cow;
use std::ops::Range;

/// Defines a source that should be used to deserialize a value in the next call
/// to [`next_value_seed()`](de::MapAccess::next_value_seed)
#[derive(Debug, PartialEq)]
enum ValueSource {
    /// Source are not specified, because [`next_key_seed()`] not yet called.
    /// This is an initial state and state after deserializing value
    /// (after call of [`next_value_seed()`]).
    ///
    /// Attempt to call [`next_value_seed()`] while accessor in this state would
    /// return a [`DeError::KeyNotRead`] error.
    ///
    /// [`next_key_seed()`]: de::MapAccess::next_key_seed
    /// [`next_value_seed()`]: de::MapAccess::next_value_seed
    Unknown,
    /// `next_key_seed` checked the attributes list and find it is not exhausted yet.
    /// Next call to the `next_value_seed` will deserialize type from the attribute value
    Attribute(Range<usize>),
    /// The same as `InnerValue`
    Nested,
    /// Value should be deserialized from the text content of the XML node:
    ///
    /// ```xml
    /// <...>text content for field value<...>
    /// ```
    InnerValue,
}

/// A deserializer for `Attributes`
pub(crate) struct MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Tag -- owner of attributes
    start: BytesStart<'de>,
    de: &'a mut Deserializer<'de, R>,
    /// State of the iterator over attributes. Contains the next position in the
    /// inner `start` slice, from which next attribute should be parsed.
    iter: IterState,
    /// Current state of the accessor that determines what next call to API
    /// methods should return.
    source: ValueSource,
    /// list of fields yet to unflatten (defined as starting with $unflatten=)
    unflatten_fields: Vec<&'static [u8]>,
}

impl<'de, 'a, R> MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    /// Create a new MapAccess
    pub fn new(
        de: &'a mut Deserializer<'de, R>,
        start: BytesStart<'de>,
        fields: &[&'static str],
    ) -> Result<Self, DeError> {
        Ok(MapAccess {
            de,
            start,
            iter: IterState::new(0, false),
            source: ValueSource::Unknown,
            unflatten_fields: fields
                .iter()
                .filter(|f| f.starts_with(UNFLATTEN_PREFIX))
                .map(|f| f.as_bytes())
                .collect(),
        })
    }
}

impl<'de, 'a, R> de::MapAccess<'de> for MapAccess<'de, 'a, R>
where
    R: XmlRead<'de>,
{
    type Error = DeError;

    fn next_key_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<Option<K::Value>, Self::Error> {
        debug_assert_eq!(self.source, ValueSource::Unknown);

        // FIXME: There error positions counted from end of tag name - need global position
        let slice = self.start.attributes_raw();
        let decoder = self.de.reader.decoder();
        let has_value_field = self.de.has_value_field;

        if let Some(a) = self.iter.next(slice).transpose()? {
            // try getting map from attributes (key= "value")
            let (key, value) = a.into();
            self.source = ValueSource::Attribute(value.unwrap_or_default());
            seed.deserialize(EscapedDeserializer::new(
                Cow::Borrowed(&slice[key]),
                decoder,
                false,
            ))
            .map(Some)
        } else {
            // try getting from events (<key>value</key>)
            match self.de.peek()? {
                DeEvent::Text(_) | DeEvent::CData(_) => {
                    self.source = ValueSource::InnerValue;
                    // Deserialize `key` from special attribute name which means
                    // that value should be taken from the text content of the
                    // XML node
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                // Used to deserialize collections of enums, like:
                // <root>
                //
                //
                //   <C/>
                // </root>
                //
                // into
                //
                // enum Enum { A, B, С }
                // struct Root {
                //     #[serde(rename = "$value")]
                //     items: Vec<Enum>,
                // }
                // TODO: This should be handled by #[serde(flatten)]
                // See https://github.com/serde-rs/serde/issues/1905
                DeEvent::Start(_) if has_value_field => {
                    self.source = ValueSource::InnerValue;
                    seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                }
                DeEvent::Start(e) => {
                    let key = if let Some(p) = self
                        .unflatten_fields
                        .iter()
                        .position(|f| e.name() == &f[UNFLATTEN_PREFIX.len()..])
                    {
                        // Used to deserialize elements, like:
                        // <root>
                        //   <xxx>test</xxx>
                        // </root>
                        //
                        // into
                        //
                        // struct Root {
                        //     #[serde(rename = "$unflatten=xxx")]
                        //     xxx: String,
                        // }
                        self.source = ValueSource::InnerValue;
                        seed.deserialize(self.unflatten_fields.remove(p).into_deserializer())
                    } else {
                        let name = Cow::Borrowed(e.local_name());
                        self.source = ValueSource::Nested;
                        seed.deserialize(EscapedDeserializer::new(name, decoder, false))
                    };
                    key.map(Some)
                }
                _ => Ok(None),
            }
        }
    }

    fn next_value_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<K::Value, Self::Error> {
        match std::mem::replace(&mut self.source, ValueSource::Unknown) {
            ValueSource::Attribute(value) => {
                let slice = self.start.attributes_raw();
                let decoder = self.de.reader.decoder();

                seed.deserialize(EscapedDeserializer::new(
                    Cow::Borrowed(&slice[value]),
                    decoder,
                    true,
                ))
            }
            ValueSource::Nested | ValueSource::InnerValue => seed.deserialize(&mut *self.de),
            ValueSource::Unknown => Err(DeError::KeyNotRead),
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::mem;
use std::process::{Output, Command, Stdio, ExitStatus};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};

use wait_timeout::ChildExt;

pub struct Container {
    id: String,
}

impl Container {
    pub fn new(cmd: &str,
               args: &[String],
               env: &[(String, String)],
               name: &str) -> io::Result<Container> {
        let out = try!(run(Command::new("docker")
                                   .arg("create")
                                   .arg("--cap-drop=ALL")
                                   .arg("--memory=128m")
                                   .arg("--net=none")
                                   .arg("--pids-limit=20")
                                   .arg("--security-opt=no-new-privileges")
                                   .arg("--interactive")
                                   .args(&env.iter().map(|&(ref k, ref v)| format!("--env={}={}", k, v)).collect::<Vec<_>>())
                                   .arg(name)
                                   .arg(cmd)
                                   .stderr(Stdio::inherit())
                                   .args(args)));
        let stdout = String::from_utf8_lossy(&out.stdout);
        Ok(Container {
            id: stdout.trim().to_string(),
        })
    }

    pub fn run(&self,
               input: &[u8],
               timeout: Duration)
               -> io::Result<(ExitStatus, Vec<u8>, bool)> {
        let mut cmd = Command::new("docker");
        cmd.arg("start")
           .arg("--attach")
           .arg("--interactive")
           .arg(&self.id)
           .stdin(Stdio::piped())
           .stdout(Stdio::piped())
           .stderr(Stdio::piped());
        debug!("attaching with {:?}", cmd);
        let start = Instant::now();
        let mut cmd = try!(cmd.spawn());
        try!(cmd.stdin.take().unwrap().write_all(input));
        debug!("input written, now waiting");

        let mut stdout = cmd.stdout.take().unwrap();
        let mut stderr = cmd.stderr.take().unwrap();
        let sink = Arc::new(Mutex::new(Vec::new()));
        let sink2 = sink.clone();
        let stdout = thread::spawn(move || append(&sink2, &mut stdout));
        let sink2 = sink.clone();
        let stderr = thread::spawn(move || append(&sink2, &mut stderr));

        let (status, timeout) = match try!(cmd.wait_timeout(timeout)) {
            Some(status) => {
                debug!("finished before timeout");
                // TODO: document this
                (unsafe { mem::transmute(status) }, false)
            }
            None => {
                debug!("timeout, going to kill");
                try!(run(Command::new("docker").arg("kill").arg(&self.id)));
                (try!(cmd.wait()), true)
            }
        };
        stdout.join().unwrap();
        stderr.join().unwrap();
        debug!("timing: {:?}", start.elapsed());
        let mut lock = sink.lock().unwrap();
        let output = mem::replace(&mut *lock, Vec::new());
        debug!("status: {}", status);
        debug!("output: {}", String::from_utf8_lossy(&output));
        Ok((status, output, timeout))
    }
}

fn append(into: &Mutex<Vec<u8>>, from: &mut Read) {
    let mut buf = [0; 1024];
    while let Ok(amt) = from.read(&mut buf) {
        if amt == 0 {
            break
        }
        into.lock().unwrap().extend_from_slice(&buf[..amt]);
    }
}

impl Drop for Container {
    fn drop(&mut self) {
        run(Command::new("docker")
                    .arg("rm")
                    .arg("--force")
                    .arg(&self.id)).unwrap();
    }
}

fn run(cmd: &mut Command) -> io::Result<Output> {
    debug!("spawning: {:?}", cmd);
    let start = Instant::now();
    let out = try!(cmd.output());
    debug!("done in: {:?}", start.elapsed());
    debug!("output: {:?}", out);
    if !out.status.success() {
        let msg = format!("process failed: {:?}\n{:?}", cmd, out);
        return Err(io::Error::new(io::ErrorKind::Other, msg))
    }
    Ok(out)
}
<commit_msg>Don't log enormous outputs<commit_after>use std::io;
use std::io::prelude::*;
use std::mem;
use std::process::{Output, Command, Stdio, ExitStatus};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};

use wait_timeout::ChildExt;

pub struct Container {
    id: String,
}

impl Container {
    pub fn new(cmd: &str,
               args: &[String],
               env: &[(String, String)],
               name: &str) -> io::Result<Container> {
        let out = try!(run(Command::new("docker")
                                   .arg("create")
                                   .arg("--cap-drop=ALL")
                                   .arg("--memory=128m")
                                   .arg("--net=none")
                                   .arg("--pids-limit=20")
                                   .arg("--security-opt=no-new-privileges")
                                   .arg("--interactive")
                                   .args(&env.iter().map(|&(ref k, ref v)| format!("--env={}={}", k, v)).collect::<Vec<_>>())
                                   .arg(name)
                                   .arg(cmd)
                                   .stderr(Stdio::inherit())
                                   .args(args)));
        let stdout = String::from_utf8_lossy(&out.stdout);
        Ok(Container {
            id: stdout.trim().to_string(),
        })
    }

    pub fn run(&self,
               input: &[u8],
               timeout: Duration)
               -> io::Result<(ExitStatus, Vec<u8>, bool)> {
        let mut cmd = Command::new("docker");
        cmd.arg("start")
           .arg("--attach")
           .arg("--interactive")
           .arg(&self.id)
           .stdin(Stdio::piped())
           .stdout(Stdio::piped())
           .stderr(Stdio::piped());
        debug!("attaching with {:?}", cmd);
        let start = Instant::now();
        let mut cmd = try!(cmd.spawn());
        try!(cmd.stdin.take().unwrap().write_all(input));
        debug!("input written, now waiting");

        let mut stdout = cmd.stdout.take().unwrap();
        let mut stderr = cmd.stderr.take().unwrap();
        let sink = Arc::new(Mutex::new(Vec::new()));
        let sink2 = sink.clone();
        let stdout = thread::spawn(move || append(&sink2, &mut stdout));
        let sink2 = sink.clone();
        let stderr = thread::spawn(move || append(&sink2, &mut stderr));

        let (status, timeout) = match try!(cmd.wait_timeout(timeout)) {
            Some(status) => {
                debug!("finished before timeout");
                // TODO: document this
                (unsafe { mem::transmute(status) }, false)
            }
            None => {
                debug!("timeout, going to kill");
                try!(run(Command::new("docker").arg("kill").arg(&self.id)));
                (try!(cmd.wait()), true)
            }
        };
        stdout.join().unwrap();
        stderr.join().unwrap();
        debug!("timing: {:?}", start.elapsed());
        let mut lock = sink.lock().unwrap();
        let output = mem::replace(&mut *lock, Vec::new());
        debug!("status: {}", status);
        {
            let output_lossy = String::from_utf8_lossy(&output);
            if output_lossy.len() < 1024 {
                debug!("output: {}", output_lossy);
            } else {
                let s = output_lossy.chars().take(1024).collect::<String>();
                debug!("output (truncated): {}...", s);
            }
        }
        Ok((status, output, timeout))
    }
}

fn append(into: &Mutex<Vec<u8>>, from: &mut Read) {
    let mut buf = [0; 1024];
    while let Ok(amt) = from.read(&mut buf) {
        if amt == 0 {
            break
        }
        into.lock().unwrap().extend_from_slice(&buf[..amt]);
    }
}

impl Drop for Container {
    fn drop(&mut self) {
        run(Command::new("docker")
                    .arg("rm")
                    .arg("--force")
                    .arg(&self.id)).unwrap();
    }
}

fn run(cmd: &mut Command) -> io::Result<Output> {
    debug!("spawning: {:?}", cmd);
    let start = Instant::now();
    let out = try!(cmd.output());
    debug!("done in: {:?}", start.elapsed());
    debug!("output: {:?}", out);
    if !out.status.success() {
        let msg = format!("process failed: {:?}\n{:?}", cmd, out);
        return Err(io::Error::new(io::ErrorKind::Other, msg))
    }
    Ok(out)
}
<|endoftext|>