<commit_before>
// Std
use std::io::Write;
use std::ascii::AsciiExt;

// Internal
use app::parser::Parser;
use args::{ArgSettings, AnyArg};
use completions;
use INTERNAL_ERROR_MSG;

pub struct ZshGen<'a, 'b>
    where 'a: 'b
{
    p: &'b Parser<'a, 'b>,
}

impl<'a, 'b> ZshGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>) -> Self {
        debugln!("fn=ZshGen::new;");
        ZshGen {
            p: p,
        }
    }

    pub fn generate_to<W: Write>(&self, buf: &mut W) {
        debugln!("fn=generate_to;");
        w!(buf,
            format!("\
#compdef {name}

_{name}() {{
	typeset -A opt_args
	local ret=1

	local context curcontext=\"$curcontext\" state line
    {initial_args}
    {subcommands}
}}

{subcommand_details}

_{name} \"$@\"",
                   name = self.p.meta.bin_name.as_ref().unwrap(),
                   initial_args = get_args_of(self.p),
                   subcommands = get_subcommands_of(self.p),
                   subcommand_details = subcommand_details(self.p)).as_bytes());

    }
}

// Displays the positional args and commands of a subcommand
// (( $+functions[_[bin_name_underscore]_commands] )) ||
// _[bin_name_underscore]_commands() {
// 	local commands; commands=(
// 		'[arg_name]:[arg_help]'
// 	)
// 	_describe -t commands '[bin_name] commands' commands "$@"
//
// Where the following variables are present:
//    [bin_name_underscore]: The full space deliniated bin_name, where spaces have been replaced by
//                           underscore characters
//    [arg_name]: The name of the positional arg or subcommand
//    [arg_help]: The help message of the arg or subcommand
//    [bin_name]: The full space deliniated bin_name
//
// Here's a snippet from rustup:
//
// (( $+functions[_rustup_commands] )) ||
// _rustup_commands() {
// 	local commands; commands=(
// 		'show:Show the active and installed toolchains'
//      'update:Update Rust toolchains'
//      # ... snip for brevity
//      'help:Prints this message or the help of the given subcommand(s)'
// 	)
// 	_describe -t commands 'rustup commands' commands "$@"
//
fn subcommand_details(p: &Parser) -> String {
    debugln!("fn=subcommand_details");
    // First we do ourself
    let mut ret = vec![format!("\
(( $+functions[_{bin_name_underscore}_commands] )) ||
_{bin_name_underscore}_commands() {{
    local commands; commands=(
        {subcommands_and_args}
    )
    _describe -t commands '{bin_name} commands' commands \"$@\"
}}",
            bin_name_underscore = p.meta.bin_name.as_ref().unwrap().replace(" ", "_"),
            bin_name = p.meta.bin_name.as_ref().unwrap(),
            subcommands_and_args = subcommands_and_args_of(p))];

    // Next we start looping through all the children, grandchildren, etc.
    let mut all_subcommands = completions::all_subcommands(p);
    all_subcommands.sort();
    all_subcommands.dedup();
    for &(_, ref bin_name) in &all_subcommands {
        debugln!("iter;bin_name={}", bin_name);
        ret.push(format!("\
(( $+functions[_{bin_name_underscore}_commands] )) ||
_{bin_name_underscore}_commands() {{
    local commands; commands=(
        {subcommands_and_args}
    )
    _describe -t commands '{bin_name} commands' commands \"$@\"
}}",
            bin_name_underscore = bin_name.replace(" ", "_"),
            bin_name = bin_name,
            subcommands_and_args = subcommands_and_args_of(parser_of(p, bin_name))));
    }

    ret.join("\n")
}

// Generates subcommand and positional argument completions in form of
//
// 		'[arg_name]:[arg_help]'
//
// Where:
//    [arg_name]: the argument or subcommand's name
//    [arg_help]: the help message of the argument or subcommand
//
// A snippet from rustup:
// 		'show:Show the active and installed toolchains'
//      'update:Update Rust toolchains'
fn subcommands_and_args_of(p: &Parser) -> String {
    debugln!("fn=subcommands_and_args_of;");
    let mut ret = vec![];

    // Firs the subcommands
    for sc in p.subcommands() {
        debugln!("iter;subcommand={}", sc.p.meta.name);
        let s = format!("\"{name}:{help}\" \\",
            name = sc.p.meta.name,
            help = sc.p.meta.about.unwrap_or(""));
        if !s.is_empty() {
            ret.push(s);
        }
    }

    // Then the positional args
    for arg in p.positionals() {
        debugln!("iter;arg={}", arg.name);
        let a = format!("\"{name}:{help}\" \\",
            name = arg.name.to_ascii_uppercase(),
            help = arg.help.unwrap_or(""));

        if !a.is_empty() {
            ret.push(a);
        }
    }

    ret.join("\n")
}

// Get's the subcommand section of a completion file
// This looks roughly like:
//
// case $state in
// ([bin_name]_args)
//     curcontext=\"${curcontext%:*:*}:[name_hyphen]-command-$words[1]:\"
//     case $line[1] in
//
//         ([name])
//         _arguments -C -s -S \
//             [subcommand_args]
//         && ret=0
//
//         [RECURSIVE_CALLS]
//
//         ;;",
//
//         [repeat]
//
//     esac
// ;;
// esac",
//
// Where the following variables are present:
//    [name] = The subcommand name in the form of "install" for "rustup toolchain install"
//    [bin_name] = The full space deliniated bin_name such as "rustup toolchain install"
//    [name_hyphen] = The full space deliniated bin_name, but replace spaces with hyphens
//    [repeat] = From the same recursive calls, but for all subcommands
//    [subcommand_args] = The same as zsh::get_args_of
fn get_subcommands_of(p: &Parser) -> String {
    debugln!("fn=get_subcommands");

    debug!("Has subcommands...");
    if !p.has_subcommands() {
        sdebugln!("No");
        return String::new();
    } else {
        sdebugln!("Yes");
    }

    let sc_names = completions::subcommands_of(p);

    let mut subcmds = vec![];
    for &(ref name, ref bin_name) in &sc_names {
        let mut v = vec![format!("({})", name)];
        let subcommand_args = get_args_of(parser_of(p, &*bin_name));
        if !subcommand_args.is_empty() {
            v.push(subcommand_args);
        }
        let subcommands = get_subcommands_of(parser_of(p, &*bin_name));
        if !subcommands.is_empty() {
            v.push(subcommands);
        }
        v.push(String::from(";;"));
        subcmds.push(v.join("\n"));
    }

    format!(
"case $state in
    ({name})
        curcontext=\"${{curcontext%:*:*}}:{name_hyphen}-command-$words[1]:\"
        case $line[1] in
            {subcommands}
        esac
    ;;
esac",
        name = p.meta.name,
        name_hyphen = p.meta.bin_name.as_ref().unwrap().replace(" ", "-"),
        subcommands = subcmds.join("\n"))
}

fn parser_of<'a, 'b>(p: &'b Parser<'a, 'b>, sc: &str) -> &'b Parser<'a, 'b> {
    debugln!("fn=parser_of;sc={}", sc);
    &p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG).p
}

// Writes out the args section, which ends up being the flags and opts, and a jump to
// another ZSH function if there are positional args or subcommands.
// The structer works like this:
//    ([conflicting_args]) [multiple] arg [takes_value] [[help]] [: :(possible_values)]
//       ^-- list '-v -h'    ^--'*'          ^--'+'                   ^-- list 'one two three'
//
// An example from the rustup command:
//
// _arguments -C -s -S \
// 		'(-h --help --verbose)-v[Enable verbose output]' \
// 		'(-V -v --version --verbose --help)-h[Prints help information]' \
//      # ... snip for brevity
// 		'1:: :_rustup_commands' \   # <-- displays positional args and subcommands
// 		'*:: :->rustup' \           # <-- displays subcommand args and child subcommands
// 	&& ret=0
//
// The args used for _arguments are as follows:
//    -C: modify the $context internal variable
//    -s: Allow stacking of short args (i.e. -a -b -c => -abc)
//    -S: Do not complete anything after '--' and treat those as argument values
fn get_args_of(p: &Parser) -> String {
    debugln!("fn=get_args_of");
    let mut ret = vec![String::from("_arguments -s -S -C \\")];
    let opts = write_opts_of(p);
    let flags = write_flags_of(p);
    let sc_or_a = if p.has_subcommands() || p.has_positionals() {
            format!("\"1:: :_{name}_commands\" \\",
                name = p.meta.bin_name.as_ref().unwrap().replace(" ", "_"))
        } else {
            String::new()
        };
   let sc = if p.has_subcommands() {
            format!("\"*:: :->{name}\" \\", name = p.meta.name)
        } else {
            String::new()
    };

    if !opts.is_empty() {
        ret.push(opts);
    }
    if !flags.is_empty() {
        ret.push(flags);
    }
    if !sc_or_a.is_empty() {
        ret.push(sc_or_a);
    }
    if !sc.is_empty() {
        ret.push(sc);
    }
    ret.push(String::from("&& ret=0"));

    ret.join("\n")
}

fn write_opts_of(p: &Parser) -> String {
    debugln!("fn=write_opts_of;");
    let mut ret = vec![];
    for o in p.opts() {
        debugln!("iter;o={}", o.name());
        let help = o.help().unwrap_or("");
        let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
        conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };

        let multiple = if o.is_set(ArgSettings::Multiple) { "*" } else { "" };
        let pv = if let Some(pv_vec) = o.possible_vals() {
                format!(": :({})", pv_vec.join(" "))
        } else {
            String::new()
        };
        if let Some(short) = o.short() {
            let s = format!("\"{conflicts}{multiple}-{arg}+[{help}]{possible_values}\" \\",
                conflicts = conflicts,
                multiple = multiple,
                arg = short,
                possible_values = pv,
                help = help);

            ret.push(s);
            debugln!("Wrote...{}", &*s);
        }
        if let Some(long) = o.long() {
            let l = format!("\"{conflicts}{multiple}--{arg}+[{help}]{possible_values}\" \\",
                conflicts = conflicts,
                multiple = multiple,
                arg = long,
                possible_values = pv,
                help = help);

            ret.push(l);
            debugln!("Wrote...{}", &*l);
        }
    }

    ret.join("\n")
}

fn write_flags_of(p: &Parser) -> String {
    debugln!("fn=write_flags_of;");
    let mut ret = vec![];
    for f in p.flags() {
        debugln!("iter;f={}", f.name());
        let help = f.help().unwrap_or("");
        let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
        conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };

        let multiple = if f.is_set(ArgSettings::Multiple) { "*" } else { "" };
        if let Some(short) = f.short() {
            let s = format!("\"{conflicts}{multiple}-{arg}[{help}]\" \\",
                multiple = multiple,
                conflicts = conflicts,
                arg = short,
                help = help);

            ret.push(s);
            debugln!("Wrote...{}", &*s);
        }

        if let Some(long) = f.long() {
            let l = format!("\"{conflicts}{multiple}--{arg}[{help}]\" \\",
                conflicts = conflicts,
                multiple = multiple,
                arg = long,
                help = help);

            ret.push(l);
            debugln!("Wrote...{}", &*l);
        }
    }

    ret.join("\n")
}<commit_msg>fix(debug): fixes the debug feature (#716)<commit_after>
// Std
use std::io::Write;
use std::ascii::AsciiExt;

// Internal
use app::parser::Parser;
use args::{ArgSettings, AnyArg};
use completions;
use INTERNAL_ERROR_MSG;

pub struct ZshGen<'a, 'b>
    where 'a: 'b
{
    p: &'b Parser<'a, 'b>,
}

impl<'a, 'b> ZshGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>) -> Self {
        debugln!("fn=ZshGen::new;");
        ZshGen {
            p: p,
        }
    }

    pub fn generate_to<W: Write>(&self, buf: &mut W) {
        debugln!("fn=generate_to;");
        w!(buf,
            format!("\
#compdef {name}

_{name}() {{
	typeset -A opt_args
	local ret=1

	local context curcontext=\"$curcontext\" state line
    {initial_args}
    {subcommands}
}}

{subcommand_details}

_{name} \"$@\"",
                   name = self.p.meta.bin_name.as_ref().unwrap(),
                   initial_args = get_args_of(self.p),
                   subcommands = get_subcommands_of(self.p),
                   subcommand_details = subcommand_details(self.p)).as_bytes());

    }
}

// Displays the positional args and commands of a subcommand
// (( $+functions[_[bin_name_underscore]_commands] )) ||
// _[bin_name_underscore]_commands() {
// 	local commands; commands=(
// 		'[arg_name]:[arg_help]'
// 	)
// 	_describe -t commands '[bin_name] commands' commands "$@"
//
// Where the following variables are present:
//    [bin_name_underscore]: The full space deliniated bin_name, where spaces have been replaced by
//                           underscore characters
//    [arg_name]: The name of the positional arg or subcommand
//    [arg_help]: The help message of the arg or subcommand
//    [bin_name]: The full space deliniated bin_name
//
// Here's a snippet from rustup:
//
// (( $+functions[_rustup_commands] )) ||
// _rustup_commands() {
// 	local commands; commands=(
// 		'show:Show the active and installed toolchains'
//      'update:Update Rust toolchains'
//      # ... snip for brevity
//      'help:Prints this message or the help of the given subcommand(s)'
// 	)
// 	_describe -t commands 'rustup commands' commands "$@"
//
fn subcommand_details(p: &Parser) -> String {
    debugln!("fn=subcommand_details");
    // First we do ourself
    let mut ret = vec![format!("\
(( $+functions[_{bin_name_underscore}_commands] )) ||
_{bin_name_underscore}_commands() {{
    local commands; commands=(
        {subcommands_and_args}
    )
    _describe -t commands '{bin_name} commands' commands \"$@\"
}}",
            bin_name_underscore = p.meta.bin_name.as_ref().unwrap().replace(" ", "_"),
            bin_name = p.meta.bin_name.as_ref().unwrap(),
            subcommands_and_args = subcommands_and_args_of(p))];

    // Next we start looping through all the children, grandchildren, etc.
    let mut all_subcommands = completions::all_subcommands(p);
    all_subcommands.sort();
    all_subcommands.dedup();
    for &(_, ref bin_name) in &all_subcommands {
        debugln!("iter;bin_name={}", bin_name);
        ret.push(format!("\
(( $+functions[_{bin_name_underscore}_commands] )) ||
_{bin_name_underscore}_commands() {{
    local commands; commands=(
        {subcommands_and_args}
    )
    _describe -t commands '{bin_name} commands' commands \"$@\"
}}",
            bin_name_underscore = bin_name.replace(" ", "_"),
            bin_name = bin_name,
            subcommands_and_args = subcommands_and_args_of(parser_of(p, bin_name))));
    }

    ret.join("\n")
}

// Generates subcommand and positional argument completions in form of
//
// 		'[arg_name]:[arg_help]'
//
// Where:
//    [arg_name]: the argument or subcommand's name
//    [arg_help]: the help message of the argument or subcommand
//
// A snippet from rustup:
// 		'show:Show the active and installed toolchains'
//      'update:Update Rust toolchains'
fn subcommands_and_args_of(p: &Parser) -> String {
    debugln!("fn=subcommands_and_args_of;");
    let mut ret = vec![];

    // Firs the subcommands
    for sc in p.subcommands() {
        debugln!("iter;subcommand={}", sc.p.meta.name);
        let s = format!("\"{name}:{help}\" \\",
            name = sc.p.meta.name,
            help = sc.p.meta.about.unwrap_or(""));
        if !s.is_empty() {
            ret.push(s);
        }
    }

    // Then the positional args
    for arg in p.positionals() {
        debugln!("iter;arg={}", arg.name);
        let a = format!("\"{name}:{help}\" \\",
            name = arg.name.to_ascii_uppercase(),
            help = arg.help.unwrap_or(""));

        if !a.is_empty() {
            ret.push(a);
        }
    }

    ret.join("\n")
}

// Get's the subcommand section of a completion file
// This looks roughly like:
//
// case $state in
// ([bin_name]_args)
//     curcontext=\"${curcontext%:*:*}:[name_hyphen]-command-$words[1]:\"
//     case $line[1] in
//
//         ([name])
//         _arguments -C -s -S \
//             [subcommand_args]
//         && ret=0
//
//         [RECURSIVE_CALLS]
//
//         ;;",
//
//         [repeat]
//
//     esac
// ;;
// esac",
//
// Where the following variables are present:
//    [name] = The subcommand name in the form of "install" for "rustup toolchain install"
//    [bin_name] = The full space deliniated bin_name such as "rustup toolchain install"
//    [name_hyphen] = The full space deliniated bin_name, but replace spaces with hyphens
//    [repeat] = From the same recursive calls, but for all subcommands
//    [subcommand_args] = The same as zsh::get_args_of
fn get_subcommands_of(p: &Parser) -> String {
    debugln!("fn=get_subcommands");

    debug!("Has subcommands...");
    if !p.has_subcommands() {
        sdebugln!("No");
        return String::new();
    } else {
        sdebugln!("Yes");
    }

    let sc_names = completions::subcommands_of(p);

    let mut subcmds = vec![];
    for &(ref name, ref bin_name) in &sc_names {
        let mut v = vec![format!("({})", name)];
        let subcommand_args = get_args_of(parser_of(p, &*bin_name));
        if !subcommand_args.is_empty() {
            v.push(subcommand_args);
        }
        let subcommands = get_subcommands_of(parser_of(p, &*bin_name));
        if !subcommands.is_empty() {
            v.push(subcommands);
        }
        v.push(String::from(";;"));
        subcmds.push(v.join("\n"));
    }

    format!(
"case $state in
    ({name})
        curcontext=\"${{curcontext%:*:*}}:{name_hyphen}-command-$words[1]:\"
        case $line[1] in
            {subcommands}
        esac
    ;;
esac",
        name = p.meta.name,
        name_hyphen = p.meta.bin_name.as_ref().unwrap().replace(" ", "-"),
        subcommands = subcmds.join("\n"))
}

fn parser_of<'a, 'b>(p: &'b Parser<'a, 'b>, sc: &str) -> &'b Parser<'a, 'b> {
    debugln!("fn=parser_of;sc={}", sc);
    &p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG).p
}

// Writes out the args section, which ends up being the flags and opts, and a jump to
// another ZSH function if there are positional args or subcommands.
// The structer works like this:
//    ([conflicting_args]) [multiple] arg [takes_value] [[help]] [: :(possible_values)]
//       ^-- list '-v -h'    ^--'*'          ^--'+'                   ^-- list 'one two three'
//
// An example from the rustup command:
//
// _arguments -C -s -S \
// 		'(-h --help --verbose)-v[Enable verbose output]' \
// 		'(-V -v --version --verbose --help)-h[Prints help information]' \
//      # ... snip for brevity
// 		'1:: :_rustup_commands' \   # <-- displays positional args and subcommands
// 		'*:: :->rustup' \           # <-- displays subcommand args and child subcommands
// 	&& ret=0
//
// The args used for _arguments are as follows:
//    -C: modify the $context internal variable
//    -s: Allow stacking of short args (i.e. -a -b -c => -abc)
//    -S: Do not complete anything after '--' and treat those as argument values
fn get_args_of(p: &Parser) -> String {
    debugln!("fn=get_args_of");
    let mut ret = vec![String::from("_arguments -s -S -C \\")];
    let opts = write_opts_of(p);
    let flags = write_flags_of(p);
    let sc_or_a = if p.has_subcommands() || p.has_positionals() {
            format!("\"1:: :_{name}_commands\" \\",
                name = p.meta.bin_name.as_ref().unwrap().replace(" ", "_"))
        } else {
            String::new()
        };
   let sc = if p.has_subcommands() {
            format!("\"*:: :->{name}\" \\", name = p.meta.name)
        } else {
            String::new()
    };

    if !opts.is_empty() {
        ret.push(opts);
    }
    if !flags.is_empty() {
        ret.push(flags);
    }
    if !sc_or_a.is_empty() {
        ret.push(sc_or_a);
    }
    if !sc.is_empty() {
        ret.push(sc);
    }
    ret.push(String::from("&& ret=0"));

    ret.join("\n")
}

fn write_opts_of(p: &Parser) -> String {
    debugln!("fn=write_opts_of;");
    let mut ret = vec![];
    for o in p.opts() {
        debugln!("iter;o={}", o.name());
        let help = o.help().unwrap_or("");
        let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
        conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };

        let multiple = if o.is_set(ArgSettings::Multiple) { "*" } else { "" };
        let pv = if let Some(pv_vec) = o.possible_vals() {
                format!(": :({})", pv_vec.join(" "))
        } else {
            String::new()
        };
        if let Some(short) = o.short() {
            let s = format!("\"{conflicts}{multiple}-{arg}+[{help}]{possible_values}\" \\",
                conflicts = conflicts,
                multiple = multiple,
                arg = short,
                possible_values = pv,
                help = help);

            debugln!("Wrote...{}", &*s);
            ret.push(s);
        }
        if let Some(long) = o.long() {
            let l = format!("\"{conflicts}{multiple}--{arg}+[{help}]{possible_values}\" \\",
                conflicts = conflicts,
                multiple = multiple,
                arg = long,
                possible_values = pv,
                help = help);

            debugln!("Wrote...{}", &*l);
            ret.push(l);
        }
    }

    ret.join("\n")
}

fn write_flags_of(p: &Parser) -> String {
    debugln!("fn=write_flags_of;");
    let mut ret = vec![];
    for f in p.flags() {
        debugln!("iter;f={}", f.name());
        let help = f.help().unwrap_or("");
        let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
        conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };

        let multiple = if f.is_set(ArgSettings::Multiple) { "*" } else { "" };
        if let Some(short) = f.short() {
            let s = format!("\"{conflicts}{multiple}-{arg}[{help}]\" \\",
                multiple = multiple,
                conflicts = conflicts,
                arg = short,
                help = help);

            debugln!("Wrote...{}", &*s);
            ret.push(s);
        }

        if let Some(long) = f.long() {
            let l = format!("\"{conflicts}{multiple}--{arg}[{help}]\" \\",
                conflicts = conflicts,
                multiple = multiple,
                arg = long,
                help = help);

            debugln!("Wrote...{}", &*l);
            ret.push(l);
        }
    }

    ret.join("\n")
}<|endoftext|>


<commit_before>use super::*;
use redox::*;

impl Editor {
    pub fn exec(&mut self, Inst(n, cmd): Inst) {
        use super::Mode::*;
        use super::PrimitiveMode::*;
        use super::CommandMode::*;
        match self.cursor().mode {
            Command(Normal) => match cmd {
                'i' => {
                    self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                        InsertOptions {
                            mode: InsertMode::Insert,
                        }));

                },
                'h' => self.left(n as usize),
                'j' => self.down(n as usize),
                'k' => self.up(n as usize),
                'l' => self.right(n as usize),
                'J' => self.down(15),
                'K' => self.up(15),
                'x' => self.delete(),
                'X' => {
                    self.previous();
                    self.delete();
                },
                '$' => self.cursor_mut().x = self.text[self.y()].len(),
                '0' => self.cursor_mut().x = 0,
                'r' => {
                    if let EventOption::Key(k) = self.window.poll()
                                                 .unwrap_or(Event::new())
                                                 .to_option() {
                        let x = self.x();
                        let y = self.y();
                        self.text[y][x] = k.character;
                    }
                },
                ' ' => self.next(),
                _ => {},
            },
            Primitive(Insert(_)) => {
                self.insert(cmd);
            },
        }
    }
}
<commit_msg>Fix r command<commit_after>use super::*;
use redox::*;

impl Editor {
    pub fn exec(&mut self, Inst(n, cmd): Inst) {
        use super::Mode::*;
        use super::PrimitiveMode::*;
        use super::CommandMode::*;
        match self.cursor().mode {
            Command(Normal) => match cmd {
                'i' => {
                    self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                        InsertOptions {
                            mode: InsertMode::Insert,
                        }));

                },
                'h' => self.left(n as usize),
                'j' => self.down(n as usize),
                'k' => self.up(n as usize),
                'l' => self.right(n as usize),
                'J' => self.down(15),
                'K' => self.up(15),
                'x' => self.delete(),
                'X' => {
                    self.previous();
                    self.delete();
                },
                '$' => self.cursor_mut().x = self.text[self.y()].len(),
                '0' => self.cursor_mut().x = 0,
                'r' => {
                    loop {
                        if let EventOption::Key(k) = self.window.poll()
                                                     .unwrap_or(Event::new())
                                                     .to_option() {
                            if k.pressed {
                                let x = self.x();
                                let y = self.y();
                                self.text[y][x] = k.character;
                                break;
                            }
                        }
                    }
                },
                ' ' => self.next(),
                _ => {},
            },
            Primitive(Insert(_)) => {
                self.insert(cmd);
            },
        }
    }
}
<|endoftext|>


<commit_before>use core::arch::asm;
use libtock_platform::{syscall_class, RawSyscalls, Register};

unsafe impl RawSyscalls for crate::TockSyscalls {
    unsafe fn yield1([Register(r0)]: [Register; 1]) {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::yield1
        unsafe {
            #[cfg(all(
                target_arch = "arm",
                target_feature = "v7",
                target_feature = "thumb-mode",
                target_os = "none"
            ))]
            asm!("svc 0",
                 inlateout("r0") r0 => _, // a1
                 lateout("r1") _,         // a2
                 lateout("r2") _,         // a3
                 lateout("r3") _,         // a4
                 // r4-r8 are callee-saved.
                 // r9 is platform-specific. We don't use it in libtock_runtime,
                 // so it is either unused or used as a callee-saved register.
                 // r10 and r11 are callee-saved.
                 lateout("r12") _, // ip
                 // r13 is the stack pointer and must be restored by the callee.
                 lateout("r14") _, // lr
                 // r15 is the program counter.
            );

            // It seems that target_feature = "v6" is set for arm v7
            // so we explicitly check that we are not compiling it.
            #[cfg(all(
                target_arch = "arm",
                target_feature = "v6",
                not(target_feature = "v7"),
                target_feature = "thumb-mode",
                target_os = "none"
            ))]
            asm!("svc 0",
                 inlateout("r0") r0 => _, // a1
                 lateout("r1") _,         // a2
                 lateout("r2") _,         // a3
                 lateout("r3") _,         // a4
                 // r4-r8 are callee-saved.
                 // r9 is platform-specific. We don't use it in libtock_runtime,
                 // so it is either unused or used as a callee-saved register.
                 // r10 and r11 are callee-saved.

                 // r13 is the stack pointer and must be restored by the callee.
                 // r15 is the program counter.

                //  clobber_abi("C"), // ip (r12), lr (r14)
            );
        }
    }

    unsafe fn yield2([Register(r0), Register(r1)]: [Register; 2]) {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::yield2
        unsafe {
            #[cfg(all(
                target_arch = "arm",
                target_feature = "v7",
                target_feature = "thumb-mode",
                target_os = "none"
            ))]
            asm!("svc 0",
                 inlateout("r0") r0 => _, // a1
                 inlateout("r1") r1 => _, // a2
                 lateout("r2") _,         // a3
                 lateout("r3") _,         // a4
                 // r4-r8 are callee-saved.
                 // r9 is platform-specific. We don't use it in libtock_runtime,
                 // so it is either unused or used as a callee-saved register.
                 // r10 and r11 are callee-saved.

                 lateout("r12") _, // ip
                 // r13 is the stack pointer and must be restored by the callee.
                 lateout("r14") _, // lr
                 // r15 is the program counter.
            );

            // It seems that target_feature = "v6" is set for arm v7
            // so we explicitly check that we are not compiling it.
            #[cfg(all(
                target_arch = "arm",
                target_feature = "v6",
                not(target_feature = "v7"),
                target_feature = "thumb-mode",
                target_os = "none"
            ))]
            asm!("svc 0",
                 inlateout("r0") r0 => _, // a1
                 inlateout("r1") r1 => _, // a2
                 lateout("r2") _,         // a3
                 lateout("r3") _,         // a4
                 // r4-r8 are callee-saved.
                 // r9 is platform-specific. We don't use it in libtock_runtime,
                 // so it is either unused or used as a callee-saved register.
                 // r10 and r11 are callee-saved.

                 // r13 is the stack pointer and must be restored by the callee.
                 // r15 is the program counter.

                 clobber_abi("C"), // ip (r12), lr (r14)
            );
        }
    }

    unsafe fn syscall1<const CLASS: usize>([Register(mut r0)]: [Register; 1]) -> [Register; 2] {
        let r1;
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::syscall1
        unsafe {
            // Syscall class 5 is Memop, the only syscall class that syscall1
            // supports.
            asm!("svc 5",
                 inlateout("r0") r0,
                 lateout("r1") r1,
                 options(preserves_flags, nostack, nomem),
            );
        }
        [Register(r0), Register(r1)]
    }

    unsafe fn syscall2<const CLASS: usize>(
        [Register(mut r0), Register(mut r1)]: [Register; 2],
    ) -> [Register; 2] {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::syscall2
        unsafe {
            // TODO: Replace this match statement with a `const` operand when
            // asm_const [1] is stabilized, or redesign RawSyscalls to not need
            // this match statement.
            //
            // [1] https://github.com/rust-lang/rust/issues/93332
            match CLASS {
                syscall_class::MEMOP => asm!("svc 5",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     options(preserves_flags, nostack, nomem)
                ),
                syscall_class::EXIT => asm!("svc 6",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     options(preserves_flags, nostack, nomem)
                ),
                _ => unreachable!(),
            }
        }
        [Register(r0), Register(r1)]
    }

    unsafe fn syscall4<const CLASS: usize>(
        [Register(mut r0), Register(mut r1), Register(mut r2), Register(mut r3)]: [Register; 4],
    ) -> [Register; 4] {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::syscall4
        unsafe {
            // TODO: Replace this match statement with a `const` operand when
            // asm_const [1] is stabilized, or redesign RawSyscalls to not need
            // this match statement.
            //
            // [1] https://github.com/rust-lang/rust/issues/93332
            match CLASS {
                syscall_class::SUBSCRIBE => asm!("svc 1",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                syscall_class::COMMAND => asm!("svc 2",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                syscall_class::ALLOW_RW => asm!("svc 3",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                syscall_class::ALLOW_RO => asm!("svc 4",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                _ => unreachable!(),
            }
        }
        [Register(r0), Register(r1), Register(r2), Register(r3)]
    }
}
<commit_msg>use clobber_abi<commit_after>use core::arch::asm;
use libtock_platform::{syscall_class, RawSyscalls, Register};

unsafe impl RawSyscalls for crate::TockSyscalls {
    unsafe fn yield1([Register(r0)]: [Register; 1]) {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::yield1
        // the use of `clobber_abi` allows us this to run on both Thumb-1 and Thumb-2
        unsafe {
            asm!("svc 0",
                 inlateout("r0") r0 => _, // a1
                 lateout("r1") _,         // a2
                 lateout("r2") _,         // a3
                 lateout("r3") _,         // a4
                 // r4-r8 are callee-saved.
                 // r9 is platform-specific. We don't use it in libtock_runtime,
                 // so it is either unused or used as a callee-saved register.
                 // r10 and r11 are callee-saved.

                 // r13 is the stack pointer and must be restored by the callee.
                 // r15 is the program counter.

                 clobber_abi("C"), // ip (r12), lr (r14)
            );
        }
    }

    unsafe fn yield2([Register(r0), Register(r1)]: [Register; 2]) {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::yield2
        // the use of `clobber_abi` allows us this to run on both Thumb-1 and Thumb-2
        unsafe {
            asm!("svc 0",
                 inlateout("r0") r0 => _, // a1
                 inlateout("r1") r1 => _, // a2
                 lateout("r2") _,         // a3
                 lateout("r3") _,         // a4
                 // r4-r8 are callee-saved.
                 // r9 is platform-specific. We don't use it in libtock_runtime,
                 // so it is either unused or used as a callee-saved register.
                 // r10 and r11 are callee-saved.

                 // r13 is the stack pointer and must be restored by the callee.
                 // r15 is the program counter.

                 clobber_abi("C"), // ip (r12), lr (r14)
            );
        }
    }

    unsafe fn syscall1<const CLASS: usize>([Register(mut r0)]: [Register; 1]) -> [Register; 2] {
        let r1;
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::syscall1
        unsafe {
            // Syscall class 5 is Memop, the only syscall class that syscall1
            // supports.
            asm!("svc 5",
                 inlateout("r0") r0,
                 lateout("r1") r1,
                 options(preserves_flags, nostack, nomem),
            );
        }
        [Register(r0), Register(r1)]
    }

    unsafe fn syscall2<const CLASS: usize>(
        [Register(mut r0), Register(mut r1)]: [Register; 2],
    ) -> [Register; 2] {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::syscall2
        unsafe {
            // TODO: Replace this match statement with a `const` operand when
            // asm_const [1] is stabilized, or redesign RawSyscalls to not need
            // this match statement.
            //
            // [1] https://github.com/rust-lang/rust/issues/93332
            match CLASS {
                syscall_class::MEMOP => asm!("svc 5",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     options(preserves_flags, nostack, nomem)
                ),
                syscall_class::EXIT => asm!("svc 6",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     options(preserves_flags, nostack, nomem)
                ),
                _ => unreachable!(),
            }
        }
        [Register(r0), Register(r1)]
    }

    unsafe fn syscall4<const CLASS: usize>(
        [Register(mut r0), Register(mut r1), Register(mut r2), Register(mut r3)]: [Register; 4],
    ) -> [Register; 4] {
        // Safety: This matches the invariants required by the documentation on
        // RawSyscalls::syscall4
        unsafe {
            // TODO: Replace this match statement with a `const` operand when
            // asm_const [1] is stabilized, or redesign RawSyscalls to not need
            // this match statement.
            //
            // [1] https://github.com/rust-lang/rust/issues/93332
            match CLASS {
                syscall_class::SUBSCRIBE => asm!("svc 1",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                syscall_class::COMMAND => asm!("svc 2",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                syscall_class::ALLOW_RW => asm!("svc 3",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                syscall_class::ALLOW_RO => asm!("svc 4",
                     inlateout("r0") r0,
                     inlateout("r1") r1,
                     inlateout("r2") r2,
                     inlateout("r3") r3,
                     options(preserves_flags, nostack),
                ),
                _ => unreachable!(),
            }
        }
        [Register(r0), Register(r1), Register(r2), Register(r3)]
    }
}
<|endoftext|>


<commit_before>use super::tree;
use super::entropy;

use std::fmt::Display as Display;
use std::fmt::Debug as Debug;

pub fn create_tree<C:PartialEq, T>(dataset:&Vec<(T, C)>,
                         rules:&Vec<Box<Fn(T)->bool>>,
                         gain:Box<Fn(&Vec<(T,C)>, &C)->f64>)
                         -> Option<Box<tree::Node<T>>> {

    //let entropy = entropy::entropy2(dataset);

    None
}
<commit_msg>src/junk/minamoto/id3.rs: id3 up to scoring (not finished)<commit_after>use super::tree;
use std::rc::Rc;

pub fn create_tree<C:PartialEq, T>(dataset:&Vec<Rc<(T, C)>>,
                         rules:&Vec<Rc<Fn(&T)->bool>>,
                         gain:Box<Fn(&Vec<Rc<(T,C)>>, &C)->f64>)
                         -> Option<Box<tree::Node<T>>> {

    let classes: Vec<&C> = dataset.iter().map(|x| {&x.1}).collect();
    let mut score = Vec::new();

    for r in rules {
        let mut right = Rc::new(Vec::new());
        let mut left = Rc::new(Vec::new());
        for d in dataset {
            if r(&d.0) {
                Rc::get_mut(&mut left).unwrap().push(d.clone())
            }
            else {
                Rc::get_mut(&mut right).unwrap().push(d.clone());
            }
        }
        for &c in &classes {
            score.push((Rc::new(r), Rc::new(c), gain(&left, &c), left.clone(), gain(&right, &c), right.clone()));
        }
    }

    None
}
<|endoftext|>


<commit_before>//! Read configurations files.

#![deny(clippy::missing_docs_in_private_items)]

use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
use rustc_span::source_map;
use source_map::Span;
use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::{env, fmt, fs, io};

/// Gets the configuration file from arguments.
pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
    for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
        if arg.has_name(sym!(conf_file)) {
            return match arg.kind {
                MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
                MetaItemKind::NameValue(ref value) => {
                    if let LitKind::Str(ref file, _) = value.kind {
                        Ok(Some(file.to_string().into()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    }
                },
            };
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    /// An I/O error.
    Io(io::Error),
    /// Not valid toml or doesn't fit the expected config format
    Toml(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(err) => err.fmt(f),
            Self::Toml(err) => err.fmt(f),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

/// Vec of errors that might be collected during config toml parsing
static ERRORS: SyncLazy<Mutex<Vec<Error>>> = SyncLazy::new(|| Mutex::new(Vec::new()));

macro_rules! define_Conf {
    ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
        mod helpers {
            use serde::Deserialize;
            /// Type used to store lint configuration.
            #[derive(Deserialize)]
            #[serde(rename_all = "kebab-case", deny_unknown_fields)]
            pub struct Conf {
                $(
                    #[$doc]
                    #[serde(default = $config_str)]
                    #[serde(with = $config_str)]
                    pub $config: $Ty,
                )+
                #[allow(dead_code)]
                #[serde(default)]
                third_party: Option<::toml::Value>,
            }

            $(
                mod $config {
                    use serde::Deserialize;
                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
                        use super::super::{ERRORS, Error};

                        Ok(
                            <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
                                ERRORS
                                    .lock()
                                    .expect("no threading here")
                                    .push(Error::Toml(e.to_string()));
                                super::$config()
                            })
                        )
                    }
                }

                #[must_use]
                fn $config() -> $Ty {
                    let x = $default;
                    x
                }
            )+
        }
    };
}

pub use self::helpers::Conf;
define_Conf! {
    /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports
    (msrv, "msrv": Option<String>, None),
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
    (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
    (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents, "doc_valid_idents": Vec<String>, [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
        "NaN", "NaNs",
        "OAuth", "GraphQL",
        "OCaml",
        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
        "TensorFlow",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
        "CamelCase",
    ].iter().map(ToString::to_string).collect()),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold, "type_complexity_threshold": u64, 250),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
    /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack, "too_large_for_stack": u64, 200),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
    (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
    (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
    (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
    /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
    (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256),
    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
    (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
    (array_size_threshold, "array_size_threshold": u64, 512_000),
    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
    (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
    (max_trait_bounds, "max_trait_bounds": u64, 3),
    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
    (max_struct_bools, "max_struct_bools": u64, 3),
    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
    (max_fn_params_bools, "max_fn_params_bools": u64, 3),
    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
    (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
    /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
    (disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
    /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
    (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
}

impl Default for Conf {
    #[must_use]
    fn default() -> Self {
        toml::from_str("").expect("we never error on empty config files")
    }
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
    // If neither of those exist, use ".".
    let mut current = env::var_os("CLIPPY_CONF_DIR")
        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
        .map_or_else(|| PathBuf::from("."), PathBuf::from);
    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
                _ => {},
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Produces a `Conf` filled with the default values and forwards the errors
///
/// Used internally for convenience
fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
    (Conf::default(), errors)
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> (Conf, Vec<Error>) {
    let content = match fs::read_to_string(path) {
        Ok(content) => content,
        Err(err) => return default(vec![err.into()]),
    };

    assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
    match toml::from_str(&content) {
        Ok(toml) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);

            let toml_ref: &Conf = &toml;

            let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;

            if cyc_field.is_some() {
                let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
                errors.push(Error::Toml(cyc_err));
            }

            (toml, errors)
        },
        Err(e) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
            errors.push(Error::Toml(e.to_string()));

            default(errors)
        },
    }
}
<commit_msg>add more lints to msrv docs<commit_after>//! Read configurations files.

#![deny(clippy::missing_docs_in_private_items)]

use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
use rustc_span::source_map;
use source_map::Span;
use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::{env, fmt, fs, io};

/// Gets the configuration file from arguments.
pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
    for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
        if arg.has_name(sym!(conf_file)) {
            return match arg.kind {
                MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
                MetaItemKind::NameValue(ref value) => {
                    if let LitKind::Str(ref file, _) = value.kind {
                        Ok(Some(file.to_string().into()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    }
                },
            };
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    /// An I/O error.
    Io(io::Error),
    /// Not valid toml or doesn't fit the expected config format
    Toml(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(err) => err.fmt(f),
            Self::Toml(err) => err.fmt(f),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

/// Vec of errors that might be collected during config toml parsing
static ERRORS: SyncLazy<Mutex<Vec<Error>>> = SyncLazy::new(|| Mutex::new(Vec::new()));

macro_rules! define_Conf {
    ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
        mod helpers {
            use serde::Deserialize;
            /// Type used to store lint configuration.
            #[derive(Deserialize)]
            #[serde(rename_all = "kebab-case", deny_unknown_fields)]
            pub struct Conf {
                $(
                    #[$doc]
                    #[serde(default = $config_str)]
                    #[serde(with = $config_str)]
                    pub $config: $Ty,
                )+
                #[allow(dead_code)]
                #[serde(default)]
                third_party: Option<::toml::Value>,
            }

            $(
                mod $config {
                    use serde::Deserialize;
                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
                        use super::super::{ERRORS, Error};

                        Ok(
                            <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
                                ERRORS
                                    .lock()
                                    .expect("no threading here")
                                    .push(Error::Toml(e.to_string()));
                                super::$config()
                            })
                        )
                    }
                }

                #[must_use]
                fn $config() -> $Ty {
                    let x = $default;
                    x
                }
            )+
        }
    };
}

pub use self::helpers::Conf;
define_Conf! {
    /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports
    (msrv, "msrv": Option<String>, None),
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
    (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
    (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents, "doc_valid_idents": Vec<String>, [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
        "NaN", "NaNs",
        "OAuth", "GraphQL",
        "OCaml",
        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
        "TensorFlow",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
        "CamelCase",
    ].iter().map(ToString::to_string).collect()),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold, "type_complexity_threshold": u64, 250),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
    /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack, "too_large_for_stack": u64, 200),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
    (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
    (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
    (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
    /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
    (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256),
    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
    (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
    (array_size_threshold, "array_size_threshold": u64, 512_000),
    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
    (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
    (max_trait_bounds, "max_trait_bounds": u64, 3),
    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
    (max_struct_bools, "max_struct_bools": u64, 3),
    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
    (max_fn_params_bools, "max_fn_params_bools": u64, 3),
    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
    (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
    /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
    (disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
    /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
    (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
}

impl Default for Conf {
    #[must_use]
    fn default() -> Self {
        toml::from_str("").expect("we never error on empty config files")
    }
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
    // If neither of those exist, use ".".
    let mut current = env::var_os("CLIPPY_CONF_DIR")
        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
        .map_or_else(|| PathBuf::from("."), PathBuf::from);
    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
                _ => {},
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Produces a `Conf` filled with the default values and forwards the errors
///
/// Used internally for convenience
fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
    (Conf::default(), errors)
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> (Conf, Vec<Error>) {
    let content = match fs::read_to_string(path) {
        Ok(content) => content,
        Err(err) => return default(vec![err.into()]),
    };

    assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
    match toml::from_str(&content) {
        Ok(toml) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);

            let toml_ref: &Conf = &toml;

            let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;

            if cyc_field.is_some() {
                let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
                errors.push(Error::Toml(cyc_err));
            }

            (toml, errors)
        },
        Err(e) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
            errors.push(Error::Toml(e.to_string()));

            default(errors)
        },
    }
}
<|endoftext|>


<commit_before>use super::{
    child_properties::ChildProperties,
    constants::Constants,
    derives::Derives,
    functions::Functions,
    members::Members,
    properties::Properties,
    signals::{Signal, Signals},
};
use crate::{
    analysis::{conversion_type, ref_mode},
    config::{
        error::TomlHelper,
        parsable::{Parsable, Parse},
    },
    library::{self, Library, TypeId, MAIN_NAMESPACE},
    version::Version,
};
use log::warn;
use std::{collections::BTreeMap, str::FromStr};
use toml::Value;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GStatus {
    Manual,
    Generate,
    Ignore,
}

impl GStatus {
    pub fn ignored(self) -> bool {
        self == GStatus::Ignore
    }
    pub fn need_generate(self) -> bool {
        self == GStatus::Generate
    }
}

impl Default for GStatus {
    fn default() -> GStatus {
        GStatus::Ignore
    }
}

impl FromStr for GStatus {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(GStatus::Manual),
            "generate" => Ok(GStatus::Generate),
            "ignore" => Ok(GStatus::Ignore),
            e => Err(format!("Wrong object status: \"{}\"", e)),
        }
    }
}

/// Info about `GObject` descendant
#[derive(Clone, Debug)]
pub struct GObject {
    pub name: String,
    pub functions: Functions,
    pub constants: Constants,
    pub signals: Signals,
    pub members: Members,
    pub properties: Properties,
    pub derives: Option<Derives>,
    pub status: GStatus,
    pub module_name: Option<String>,
    pub version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub type_id: Option<TypeId>,
    pub final_type: Option<bool>,
    pub trait_name: Option<String>,
    pub child_properties: Option<ChildProperties>,
    pub concurrency: library::Concurrency,
    pub ref_mode: Option<ref_mode::RefMode>,
    pub must_use: bool,
    pub conversion_type: Option<conversion_type::ConversionType>,
    pub use_boxed_functions: bool,
    pub generate_display_trait: bool,
    pub manual_traits: Vec<String>,
    pub align: Option<u32>,
    pub generate_builder: bool,
    pub init_function_expression: Option<String>,
    pub clear_function_expression: Option<String>,
}

impl Default for GObject {
    fn default() -> GObject {
        GObject {
            name: "Default".into(),
            functions: Functions::new(),
            constants: Constants::new(),
            signals: Signals::new(),
            members: Members::new(),
            properties: Properties::new(),
            derives: None,
            status: Default::default(),
            module_name: None,
            version: None,
            cfg_condition: None,
            type_id: None,
            final_type: None,
            trait_name: None,
            child_properties: None,
            concurrency: Default::default(),
            ref_mode: None,
            must_use: false,
            conversion_type: None,
            use_boxed_functions: false,
            generate_display_trait: true,
            manual_traits: Vec::default(),
            align: None,
            generate_builder: false,
            init_function_expression: None,
            clear_function_expression: None,
        }
    }
}

//TODO: ?change to HashMap<String, GStatus>
pub type GObjects = BTreeMap<String, GObject>;

pub fn parse_toml(
    toml_objects: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) -> GObjects {
    let mut objects = GObjects::new();
    for toml_object in toml_objects.as_array().unwrap() {
        let gobject = parse_object(toml_object, concurrency, generate_display_trait);
        objects.insert(gobject.name.clone(), gobject);
    }
    objects
}

fn ref_mode_from_str(ref_mode: &str) -> Option<ref_mode::RefMode> {
    use crate::analysis::ref_mode::RefMode::*;

    match ref_mode {
        "none" => Some(None),
        "ref" => Some(ByRef),
        "ref-mut" => Some(ByRefMut),
        "ref-immut" => Some(ByRefImmut),
        "ref-fake" => Some(ByRefFake),
        _ => Option::None,
    }
}

fn conversion_type_from_str(conversion_type: &str) -> Option<conversion_type::ConversionType> {
    use crate::analysis::conversion_type::ConversionType::*;

    match conversion_type {
        "direct" => Some(Direct),
        "scalar" => Some(Scalar),
        "pointer" => Some(Pointer),
        "borrow" => Some(Borrow),
        "unknown" => Some(Unknown),
        _ => None,
    }
}

fn parse_object(
    toml_object: &Value,
    concurrency: library::Concurrency,
    default_generate_display_trait: bool,
) -> GObject {
    let name: String = toml_object
        .lookup("name")
        .expect("Object name not defined")
        .as_str()
        .unwrap()
        .into();
    // Also checks for ChildProperties
    toml_object.check_unwanted(
        &[
            "name",
            "status",
            "function",
            "constant",
            "signal",
            "member",
            "property",
            "derive",
            "module_name",
            "version",
            "concurrency",
            "ref_mode",
            "conversion_type",
            "child_prop",
            "child_name",
            "child_type",
            "final_type",
            "trait",
            "trait_name",
            "cfg_condition",
            "must_use",
            "use_boxed_functions",
            "generate_display_trait",
            "manual_traits",
            "align",
            "generate_builder",
            "init_function_expression",
            "clear_function_expression",
        ],
        &format!("object {}", name),
    );

    let status = match toml_object.lookup("status") {
        Some(value) => {
            GStatus::from_str(value.as_str().unwrap()).unwrap_or_else(|_| Default::default())
        }
        None => Default::default(),
    };

    let constants = Constants::parse(toml_object.lookup("constant"), &name);
    let functions = Functions::parse(toml_object.lookup("function"), &name);
    let signals = {
        let mut v = Vec::new();
        if let Some(configs) = toml_object.lookup("signal").and_then(Value::as_array) {
            for config in configs {
                if let Some(item) = Signal::parse(config, &name, concurrency) {
                    v.push(item);
                }
            }
        }

        v
    };
    let members = Members::parse(toml_object.lookup("member"), &name);
    let properties = Properties::parse(toml_object.lookup("property"), &name);
    let derives = if let Some(derives) = toml_object.lookup("derive") {
        Some(Derives::parse(Some(derives), &name))
    } else {
        None
    };
    let module_name = toml_object
        .lookup("module_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let version = toml_object
        .lookup("version")
        .and_then(Value::as_str)
        .and_then(|s| s.parse().ok());
    let cfg_condition = toml_object
        .lookup("cfg_condition")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let generate_trait = toml_object.lookup("trait").and_then(Value::as_bool);
    let final_type = toml_object
        .lookup("final_type")
        .and_then(Value::as_bool)
        .or_else(|| generate_trait.map(|t| !t));
    let trait_name = toml_object
        .lookup("trait_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let concurrency = toml_object
        .lookup("concurrency")
        .and_then(Value::as_str)
        .and_then(|v| v.parse().ok())
        .unwrap_or(concurrency);
    let ref_mode = toml_object
        .lookup("ref_mode")
        .and_then(Value::as_str)
        .and_then(ref_mode_from_str);
    let conversion_type = toml_object
        .lookup("conversion_type")
        .and_then(Value::as_str)
        .and_then(conversion_type_from_str);
    let child_properties = ChildProperties::parse(toml_object, &name);
    let must_use = toml_object
        .lookup("must_use")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let use_boxed_functions = toml_object
        .lookup("use_boxed_functions")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let generate_display_trait = toml_object
        .lookup("generate_display_trait")
        .and_then(Value::as_bool)
        .unwrap_or(default_generate_display_trait);
    let manual_traits = toml_object
        .lookup_vec("manual_traits", "IGNORED ERROR")
        .map(|v| {
            v.iter()
                .filter_map(|v| v.as_str().map(String::from))
                .collect()
        })
        .unwrap_or_else(|_| Vec::new());
    let align = toml_object
        .lookup("align")
        .and_then(Value::as_integer)
        .and_then(|v| {
            if v.count_ones() != 1 || v > i64::from(u32::max_value()) || v < 0 {
                warn!(
                    "`align` configuration must be a power of two of type u32, found {}",
                    v
                );
                None
            } else {
                Some(v as u32)
            }
        });
    let generate_builder = toml_object
        .lookup("generate_builder")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let init_function_expression = toml_object
        .lookup("init_function_expression")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let clear_function_expression = toml_object
        .lookup("clear_function_expression")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);

    if (init_function_expression.is_some() && clear_function_expression.is_none())
        || (init_function_expression.is_none() && clear_function_expression.is_some())
    {
        panic!(
            "`init_function_expression` and `clear_function_expression` both have to be provided"
        );
    }

    if status != GStatus::Manual && ref_mode.is_some() {
        warn!("ref_mode configuration used for non-manual object {}", name);
    }

    if status != GStatus::Manual && conversion_type.is_some() {
        warn!(
            "conversion_type configuration used for non-manual object {}",
            name
        );
    }

    if generate_trait.is_some() {
        warn!(
            "`trait` configuration is deprecated and replaced by `final_type` for object {}",
            name
        );
    }

    GObject {
        name,
        functions,
        constants,
        signals,
        members,
        properties,
        derives,
        status,
        module_name,
        version,
        cfg_condition,
        type_id: None,
        final_type,
        trait_name,
        child_properties,
        concurrency,
        ref_mode,
        must_use,
        conversion_type,
        use_boxed_functions,
        generate_display_trait,
        manual_traits,
        align,
        generate_builder,
        init_function_expression,
        clear_function_expression,
    }
}

pub fn parse_status_shorthands(
    objects: &mut GObjects,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    use self::GStatus::*;
    for &status in &[Manual, Generate, Ignore] {
        parse_status_shorthand(objects, status, toml, concurrency, generate_display_trait);
    }
}

fn parse_status_shorthand(
    objects: &mut GObjects,
    status: GStatus,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    let name = format!("options.{:?}", status).to_ascii_lowercase();
    if let Some(a) = toml.lookup(&name).map(|a| a.as_array().unwrap()) {
        for name_ in a.iter().map(|s| s.as_str().unwrap()) {
            match objects.get(name_) {
                None => {
                    objects.insert(
                        name_.into(),
                        GObject {
                            name: name_.into(),
                            status,
                            concurrency,
                            generate_display_trait,
                            ..Default::default()
                        },
                    );
                }
                Some(_) => panic!("Bad name in {}: {} already defined", name, name_),
            }
        }
    }
}

pub fn parse_builders(objects: &mut GObjects, toml: &Value) {
    let option_name = "options.builders";
    let suffix = "Builder";
    if let Some(a) = toml.lookup(option_name).map(|a| a.as_array().unwrap()) {
        for name in a.iter().map(|s| s.as_str().unwrap()) {
            // Support both object name and builder name
            let obj_name = if name.ends_with(suffix) {
                &name[..name.len() - suffix.len()]
            } else {
                name
            };
            match objects.get_mut(obj_name) {
                Some(obj) => obj.generate_builder = true,
                None => panic!("Bad name in {}: object {} not defined", option_name, name),
            }
        }
    }
}

pub fn resolve_type_ids(objects: &mut GObjects, library: &Library) {
    let ns = library.namespace(MAIN_NAMESPACE);
    let global_functions_name = format!("{}.*", ns.name);

    for (name, object) in objects.iter_mut() {
        let type_id = library.find_type(0, name);
        if type_id.is_none() && name != &global_functions_name {
            warn!("Configured object `{}` missing from the library", name);
        }
        object.type_id = type_id;
    }
}
<commit_msg>config: Teach reader about new builder_postprocess attribute<commit_after>use super::{
    child_properties::ChildProperties,
    constants::Constants,
    derives::Derives,
    functions::Functions,
    members::Members,
    properties::Properties,
    signals::{Signal, Signals},
};
use crate::{
    analysis::{conversion_type, ref_mode},
    config::{
        error::TomlHelper,
        parsable::{Parsable, Parse},
    },
    library::{self, Library, TypeId, MAIN_NAMESPACE},
    version::Version,
};
use log::warn;
use std::{collections::BTreeMap, str::FromStr};
use toml::Value;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GStatus {
    Manual,
    Generate,
    Ignore,
}

impl GStatus {
    pub fn ignored(self) -> bool {
        self == GStatus::Ignore
    }
    pub fn need_generate(self) -> bool {
        self == GStatus::Generate
    }
}

impl Default for GStatus {
    fn default() -> GStatus {
        GStatus::Ignore
    }
}

impl FromStr for GStatus {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(GStatus::Manual),
            "generate" => Ok(GStatus::Generate),
            "ignore" => Ok(GStatus::Ignore),
            e => Err(format!("Wrong object status: \"{}\"", e)),
        }
    }
}

/// Info about `GObject` descendant
#[derive(Clone, Debug)]
pub struct GObject {
    pub name: String,
    pub functions: Functions,
    pub constants: Constants,
    pub signals: Signals,
    pub members: Members,
    pub properties: Properties,
    pub derives: Option<Derives>,
    pub status: GStatus,
    pub module_name: Option<String>,
    pub version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub type_id: Option<TypeId>,
    pub final_type: Option<bool>,
    pub trait_name: Option<String>,
    pub child_properties: Option<ChildProperties>,
    pub concurrency: library::Concurrency,
    pub ref_mode: Option<ref_mode::RefMode>,
    pub must_use: bool,
    pub conversion_type: Option<conversion_type::ConversionType>,
    pub use_boxed_functions: bool,
    pub generate_display_trait: bool,
    pub manual_traits: Vec<String>,
    pub align: Option<u32>,
    pub generate_builder: bool,
    pub builder_postprocess: Option<String>,
    pub init_function_expression: Option<String>,
    pub clear_function_expression: Option<String>,
}

impl Default for GObject {
    fn default() -> GObject {
        GObject {
            name: "Default".into(),
            functions: Functions::new(),
            constants: Constants::new(),
            signals: Signals::new(),
            members: Members::new(),
            properties: Properties::new(),
            derives: None,
            status: Default::default(),
            module_name: None,
            version: None,
            cfg_condition: None,
            type_id: None,
            final_type: None,
            trait_name: None,
            child_properties: None,
            concurrency: Default::default(),
            ref_mode: None,
            must_use: false,
            conversion_type: None,
            use_boxed_functions: false,
            generate_display_trait: true,
            manual_traits: Vec::default(),
            align: None,
            generate_builder: false,
            builder_postprocess: None,
            init_function_expression: None,
            clear_function_expression: None,
        }
    }
}

//TODO: ?change to HashMap<String, GStatus>
pub type GObjects = BTreeMap<String, GObject>;

pub fn parse_toml(
    toml_objects: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) -> GObjects {
    let mut objects = GObjects::new();
    for toml_object in toml_objects.as_array().unwrap() {
        let gobject = parse_object(toml_object, concurrency, generate_display_trait);
        objects.insert(gobject.name.clone(), gobject);
    }
    objects
}

fn ref_mode_from_str(ref_mode: &str) -> Option<ref_mode::RefMode> {
    use crate::analysis::ref_mode::RefMode::*;

    match ref_mode {
        "none" => Some(None),
        "ref" => Some(ByRef),
        "ref-mut" => Some(ByRefMut),
        "ref-immut" => Some(ByRefImmut),
        "ref-fake" => Some(ByRefFake),
        _ => Option::None,
    }
}

fn conversion_type_from_str(conversion_type: &str) -> Option<conversion_type::ConversionType> {
    use crate::analysis::conversion_type::ConversionType::*;

    match conversion_type {
        "direct" => Some(Direct),
        "scalar" => Some(Scalar),
        "pointer" => Some(Pointer),
        "borrow" => Some(Borrow),
        "unknown" => Some(Unknown),
        _ => None,
    }
}

fn parse_object(
    toml_object: &Value,
    concurrency: library::Concurrency,
    default_generate_display_trait: bool,
) -> GObject {
    let name: String = toml_object
        .lookup("name")
        .expect("Object name not defined")
        .as_str()
        .unwrap()
        .into();
    // Also checks for ChildProperties
    toml_object.check_unwanted(
        &[
            "name",
            "status",
            "function",
            "constant",
            "signal",
            "member",
            "property",
            "derive",
            "module_name",
            "version",
            "concurrency",
            "ref_mode",
            "conversion_type",
            "child_prop",
            "child_name",
            "child_type",
            "final_type",
            "trait",
            "trait_name",
            "cfg_condition",
            "must_use",
            "use_boxed_functions",
            "generate_display_trait",
            "manual_traits",
            "align",
            "generate_builder",
            "builder_postprocess",
            "init_function_expression",
            "clear_function_expression",
        ],
        &format!("object {}", name),
    );

    let status = match toml_object.lookup("status") {
        Some(value) => {
            GStatus::from_str(value.as_str().unwrap()).unwrap_or_else(|_| Default::default())
        }
        None => Default::default(),
    };

    let constants = Constants::parse(toml_object.lookup("constant"), &name);
    let functions = Functions::parse(toml_object.lookup("function"), &name);
    let signals = {
        let mut v = Vec::new();
        if let Some(configs) = toml_object.lookup("signal").and_then(Value::as_array) {
            for config in configs {
                if let Some(item) = Signal::parse(config, &name, concurrency) {
                    v.push(item);
                }
            }
        }

        v
    };
    let members = Members::parse(toml_object.lookup("member"), &name);
    let properties = Properties::parse(toml_object.lookup("property"), &name);
    let derives = if let Some(derives) = toml_object.lookup("derive") {
        Some(Derives::parse(Some(derives), &name))
    } else {
        None
    };
    let module_name = toml_object
        .lookup("module_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let version = toml_object
        .lookup("version")
        .and_then(Value::as_str)
        .and_then(|s| s.parse().ok());
    let cfg_condition = toml_object
        .lookup("cfg_condition")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let generate_trait = toml_object.lookup("trait").and_then(Value::as_bool);
    let final_type = toml_object
        .lookup("final_type")
        .and_then(Value::as_bool)
        .or_else(|| generate_trait.map(|t| !t));
    let trait_name = toml_object
        .lookup("trait_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let concurrency = toml_object
        .lookup("concurrency")
        .and_then(Value::as_str)
        .and_then(|v| v.parse().ok())
        .unwrap_or(concurrency);
    let ref_mode = toml_object
        .lookup("ref_mode")
        .and_then(Value::as_str)
        .and_then(ref_mode_from_str);
    let conversion_type = toml_object
        .lookup("conversion_type")
        .and_then(Value::as_str)
        .and_then(conversion_type_from_str);
    let child_properties = ChildProperties::parse(toml_object, &name);
    let must_use = toml_object
        .lookup("must_use")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let use_boxed_functions = toml_object
        .lookup("use_boxed_functions")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let generate_display_trait = toml_object
        .lookup("generate_display_trait")
        .and_then(Value::as_bool)
        .unwrap_or(default_generate_display_trait);
    let manual_traits = toml_object
        .lookup_vec("manual_traits", "IGNORED ERROR")
        .map(|v| {
            v.iter()
                .filter_map(|v| v.as_str().map(String::from))
                .collect()
        })
        .unwrap_or_else(|_| Vec::new());
    let align = toml_object
        .lookup("align")
        .and_then(Value::as_integer)
        .and_then(|v| {
            if v.count_ones() != 1 || v > i64::from(u32::max_value()) || v < 0 {
                warn!(
                    "`align` configuration must be a power of two of type u32, found {}",
                    v
                );
                None
            } else {
                Some(v as u32)
            }
        });
    let generate_builder = toml_object
        .lookup("generate_builder")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let builder_postprocess = toml_object
        .lookup("builder_postprocess")
        .and_then(Value::as_str)
        .map(String::from);
    let init_function_expression = toml_object
        .lookup("init_function_expression")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let clear_function_expression = toml_object
        .lookup("clear_function_expression")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);

    if (init_function_expression.is_some() && clear_function_expression.is_none())
        || (init_function_expression.is_none() && clear_function_expression.is_some())
    {
        panic!(
            "`init_function_expression` and `clear_function_expression` both have to be provided"
        );
    }

    if status != GStatus::Manual && ref_mode.is_some() {
        warn!("ref_mode configuration used for non-manual object {}", name);
    }

    if status != GStatus::Manual && conversion_type.is_some() {
        warn!(
            "conversion_type configuration used for non-manual object {}",
            name
        );
    }

    if generate_trait.is_some() {
        warn!(
            "`trait` configuration is deprecated and replaced by `final_type` for object {}",
            name
        );
    }

    GObject {
        name,
        functions,
        constants,
        signals,
        members,
        properties,
        derives,
        status,
        module_name,
        version,
        cfg_condition,
        type_id: None,
        final_type,
        trait_name,
        child_properties,
        concurrency,
        ref_mode,
        must_use,
        conversion_type,
        use_boxed_functions,
        generate_display_trait,
        manual_traits,
        align,
        generate_builder,
        builder_postprocess,
        init_function_expression,
        clear_function_expression,
    }
}

pub fn parse_status_shorthands(
    objects: &mut GObjects,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    use self::GStatus::*;
    for &status in &[Manual, Generate, Ignore] {
        parse_status_shorthand(objects, status, toml, concurrency, generate_display_trait);
    }
}

fn parse_status_shorthand(
    objects: &mut GObjects,
    status: GStatus,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    let name = format!("options.{:?}", status).to_ascii_lowercase();
    if let Some(a) = toml.lookup(&name).map(|a| a.as_array().unwrap()) {
        for name_ in a.iter().map(|s| s.as_str().unwrap()) {
            match objects.get(name_) {
                None => {
                    objects.insert(
                        name_.into(),
                        GObject {
                            name: name_.into(),
                            status,
                            concurrency,
                            generate_display_trait,
                            ..Default::default()
                        },
                    );
                }
                Some(_) => panic!("Bad name in {}: {} already defined", name, name_),
            }
        }
    }
}

pub fn parse_builders(objects: &mut GObjects, toml: &Value) {
    let option_name = "options.builders";
    let suffix = "Builder";
    if let Some(a) = toml.lookup(option_name).map(|a| a.as_array().unwrap()) {
        for name in a.iter().map(|s| s.as_str().unwrap()) {
            // Support both object name and builder name
            let obj_name = if name.ends_with(suffix) {
                &name[..name.len() - suffix.len()]
            } else {
                name
            };
            match objects.get_mut(obj_name) {
                Some(obj) => obj.generate_builder = true,
                None => panic!("Bad name in {}: object {} not defined", option_name, name),
            }
        }
    }
}

pub fn resolve_type_ids(objects: &mut GObjects, library: &Library) {
    let ns = library.namespace(MAIN_NAMESPACE);
    let global_functions_name = format!("{}.*", ns.name);

    for (name, object) in objects.iter_mut() {
        let type_id = library.find_type(0, name);
        if type_id.is_none() && name != &global_functions_name {
            warn!("Configured object `{}` missing from the library", name);
        }
        object.type_id = type_id;
    }
}
<|endoftext|>