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

// Internal
use app::App;
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![];
    fn add_sc(sc: &App, n: &str, ret: &mut Vec<String>) {
        let s = format!("\"{name}:{help}\" \\",
            name = n,
            help = sc.p.meta.about.unwrap_or(""));
        if !s.is_empty() {
            ret.push(s);
        }
    }

    // First the subcommands
    for sc in p.subcommands() {
        debugln!("iter;subcommand={}", sc.p.meta.name);
        add_sc(sc, &sc.p.meta.name, &mut ret);
        if let Some(ref v) = sc.p.meta.aliases {
            for alias in v.iter().filter(|&&(_, vis)| vis).map(|&(n, _)| n) {
                add_sc(sc, alias, &mut ret);
            }
        }
    }

    // Then the positional args
    for arg in p.positionals() {
        debugln!("iter;arg={}", arg.b.name);
        let a = format!("\"{name}:{help}\" \\",
            name = arg.b.name.to_ascii_uppercase(),
            help = arg.b.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_of");

    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);
    if sc == p.meta.bin_name.as_ref().unwrap_or(&String::new()) {
        return p;
    }
    &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")
}
<commit_msg>fix(ZSH Completions): escapes square brackets in ZSH completions<commit_after>
// Std
use std::io::Write;
use std::ascii::AsciiExt;

// Internal
use app::App;
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![];
    fn add_sc(sc: &App, n: &str, ret: &mut Vec<String>) {
        let s = format!("\"{name}:{help}\" \\",
            name = n,
            help = sc.p.meta.about.unwrap_or("").replace("[", "\\[").replace("]", "\\]"));
        if !s.is_empty() {
            ret.push(s);
        }
    }

    // First the subcommands
    for sc in p.subcommands() {
        debugln!("iter;subcommand={}", sc.p.meta.name);
        add_sc(sc, &sc.p.meta.name, &mut ret);
        if let Some(ref v) = sc.p.meta.aliases {
            for alias in v.iter().filter(|&&(_, vis)| vis).map(|&(n, _)| n) {
                add_sc(sc, alias, &mut ret);
            }
        }
    }

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

        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_of");

    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);
    if sc == p.meta.bin_name.as_ref().unwrap_or(&String::new()) {
        return p;
    }
    &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("").replace("[", "\\[").replace("]", "\\]");
        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("").replace("[", "\\[").replace("]", "\\]");
        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>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use std::process::Command;
use errors::{DeliveryError, Kind};
use libc::funcs::posix88::unistd;
use std::path::Path;
use std::convert::AsRef;
use std::fs;

pub mod say;
pub mod path_join_many;
pub mod open;

// This will need a windows implementation
pub fn copy_recursive<P: ?Sized>(f: &P, t: &P) -> Result<(), DeliveryError> where P: AsRef<Path> {
    let from = f.as_ref();
    let to = t.as_ref();
    let result = try!(Command::new("cp")
         .arg("-R")
         .arg("-a")
         .arg(from.to_str().unwrap())
         .arg(to.to_str().unwrap())
         .output());
    if !result.status.success() {
        return Err(DeliveryError{kind: Kind::CopyFailed, detail: Some(format!("STDOUT: {}\nSTDERR: {}", String::from_utf8_lossy(&result.stdout), String::from_utf8_lossy(&result.stderr)))});
    }
    Ok(())
}

pub fn remove_recursive<P: ?Sized>(path: &P) -> Result<(), DeliveryError> where P: AsRef<Path> {
    try!(Command::new("rm")
         .arg("-rf")
         .arg(path.as_ref().to_str().unwrap())
         .output());
    Ok(())
}

pub fn mkdir_recursive<P: ?Sized>(path: &P) -> Result<(), DeliveryError> where P: AsRef<Path> {
    try!(fs::create_dir_all(path.as_ref()));
    Ok(())
}

// This will need a windows implementation
pub fn chmod<P: ?Sized>(path: &P, setting: &str) -> Result<(), DeliveryError> where P: AsRef<Path> {
    let result = try!(Command::new("chmod")
         .arg(setting)
         .arg(path.as_ref().to_str().unwrap())
         .output());
    if !result.status.success() {
        return Err(DeliveryError{kind: Kind::ChmodFailed, detail: Some(format!("STDOUT: {}\nSTDERR: {}", String::from_utf8_lossy(&result.stdout), String::from_utf8_lossy(&result.stderr)))});
    }
    Ok(())
}

pub fn privileged_process() -> bool {
    match unsafe { unistd::getuid() } {
        0 => true,
        _ => false
    }
}
<commit_msg>Commit missing compile hack<commit_after>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use std::process::Command;
use errors::{DeliveryError, Kind};
#[cfg(not(target_os = "windows"))]
use libc::funcs::posix88::unistd;
use std::path::Path;
use std::convert::AsRef;
use std::fs;

pub mod say;
pub mod path_join_many;
pub mod open;

// This will need a windows implementation
pub fn copy_recursive<P: ?Sized>(f: &P, t: &P) -> Result<(), DeliveryError> where P: AsRef<Path> {
    let from = f.as_ref();
    let to = t.as_ref();
    let result = try!(Command::new("cp")
         .arg("-R")
         .arg("-a")
         .arg(from.to_str().unwrap())
         .arg(to.to_str().unwrap())
         .output());
    if !result.status.success() {
        return Err(DeliveryError{kind: Kind::CopyFailed, detail: Some(format!("STDOUT: {}\nSTDERR: {}", String::from_utf8_lossy(&result.stdout), String::from_utf8_lossy(&result.stderr)))});
    }
    Ok(())
}

pub fn remove_recursive<P: ?Sized>(path: &P) -> Result<(), DeliveryError> where P: AsRef<Path> {
    try!(Command::new("rm")
         .arg("-rf")
         .arg(path.as_ref().to_str().unwrap())
         .output());
    Ok(())
}

pub fn mkdir_recursive<P: ?Sized>(path: &P) -> Result<(), DeliveryError> where P: AsRef<Path> {
    try!(fs::create_dir_all(path.as_ref()));
    Ok(())
}

// This will need a windows implementation
pub fn chmod<P: ?Sized>(path: &P, setting: &str) -> Result<(), DeliveryError> where P: AsRef<Path> {
    let result = try!(Command::new("chmod")
         .arg(setting)
         .arg(path.as_ref().to_str().unwrap())
         .output());
    if !result.status.success() {
        return Err(DeliveryError{kind: Kind::ChmodFailed, detail: Some(format!("STDOUT: {}\nSTDERR: {}", String::from_utf8_lossy(&result.stdout), String::from_utf8_lossy(&result.stderr)))});
    }
    Ok(())
}

#[cfg(not(target_os = "windows"))]
pub fn privileged_process() -> bool {
    match unsafe { unistd::getuid() } {
        0 => true,
        _ => false
    }
}

#[cfg(target_os = "windows")]
pub fn privileged_process() -> bool {
    true
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::{mpsc, Arc, Mutex};

use input_parser;
use channel::Channel;
use current_state::{self, CurrentState};
use rdispatcher::{Subscribe, SubscribeHandle, DispatchMessage, Broadcast, BroadcastHandle};
use dispatch_type::DispatchType;
use view::View;
use view_data::ViewData;

pub struct DisplayController {
   _input_guard: Option<thread::JoinHandle<()>>,
   _output_guard: Option<thread::JoinHandle<()>>,
   current_state: Arc<Mutex<CurrentState>>,
   subscribe_tx: mpsc::Sender<DispatchMessage<DispatchType>>,
   subscribe_rx: Arc<Mutex<mpsc::Receiver<DispatchMessage<DispatchType>>>>,
   broadcast_tx: Option<mpsc::Sender<DispatchMessage<DispatchType>>>
}

impl DisplayController {
   pub fn new(initial_state: &str) -> DisplayController {
      let initial_state = current_state::new_from_str(&initial_state);
      let (tx, rx) = mpsc::channel::<DispatchMessage<DispatchType>>();

      DisplayController {
         _input_guard: None,
         _output_guard: None,
         current_state: Arc::new(Mutex::new(initial_state)),
         subscribe_rx: Arc::new(Mutex::new(rx)),
         subscribe_tx: tx,
         broadcast_tx: None
      }
   }

   pub fn start(&mut self) {
      // For communicating from and to the view
      let (view_tx, view_rx) = mpsc::channel::<ViewData>();

      self.spawn_view_loop(view_rx);
      self.spawn_print_loop(view_tx);
   }

   fn spawn_view_loop(&self, view_rx: mpsc::Receiver<ViewData>) {
      let broadcast_tx = self.broadcast_tx.clone().expect("Expected broadcaster to be present in display controller");

      thread::spawn(move || {
         let mut view = View::new();
         let on_input = Box::new(move |string: String, channel_id: String| {
            let (payload, dtype) = input_parser::parse(string, channel_id);
            let message = DispatchMessage { payload: payload, dispatch_type: dtype };
            broadcast_tx.send(message).unwrap();
         });
         view.init(on_input, view_rx);
      });
   }

   fn spawn_print_loop(&self, view_tx: mpsc::Sender<ViewData>) {
      let rx = self.subscribe_rx.clone();
      let state = self.current_state.clone();
      let default_channel = self.default_channel();

      thread::spawn(move || {
         let mut view_data = ViewData::new(default_channel.clone());
         loop {
            let message = rx.lock().unwrap().recv().unwrap();
            match message.dispatch_type {
               DispatchType::RawIncomingMessage => {
                  let parsed = state.lock().unwrap().parse_incoming_message(&message.payload).unwrap();
                  // Innefficient clone that saves a clone on the parsed, lesser evil
                  let channel = parsed.channel.clone();
                  // Would be nice if rust could figure out parsed was done and movable after the if
                  if channel.is_some() && channel.unwrap() == view_data.channel {
                     view_data.add_message(parsed)
                  } else {
                     view_data.add_debug(format!("{}", parsed))
                  }
               },
               // DispatchType::UserInput => {
               //    view_data.add_debug(format!("User input: {}", &message.payload))
               // },
               DispatchType::ChangeCurrentChannel => {
                  match state.lock().unwrap().name_to_channel(&message.payload) {
                     Some(channel) => {
                        view_data = ViewData::new(channel.clone());
                        view_data.add_debug(format!("Changed channel to: {}", channel.name))
                     },
                     None => {
                        view_data.add_debug(format!("Channel not found: {}", &message.payload))
                     }
                  }
               },
               DispatchType::ListChannels => {
                  let channel_names = state.lock().unwrap().channel_names();
                  view_data.add_debug(format!("Channels: {}", channel_names.connect(", ")));
               },
               _ => ()
            }
            view_tx.send(view_data.clone()).unwrap();
         }
      });
   }

   fn default_channel(&self) -> Channel {
      self.current_state.lock().unwrap().default_channel()
         .expect("Could not find default channel")
         .clone()
   }
}

impl Subscribe<DispatchType> for DisplayController {
   fn subscribe_handle(&self) -> SubscribeHandle<DispatchType> {
      self.subscribe_tx.clone()
   }
}

impl Broadcast<DispatchType> for DisplayController {
    fn broadcast_handle(&mut self) -> BroadcastHandle<DispatchType> {
        let (tx, rx) = mpsc::channel::<DispatchMessage<DispatchType>>();
        self.broadcast_tx = Some(tx);
        rx
    }
}
<commit_msg>Preserve messages when switching channels<commit_after>use std::thread;
use std::sync::{mpsc, Arc, Mutex};

use input_parser;
use channel::Channel;
use current_state::{self, CurrentState};
use rdispatcher::{Subscribe, SubscribeHandle, DispatchMessage, Broadcast, BroadcastHandle};
use dispatch_type::DispatchType;
use view::View;
use view_data::ViewData;

pub struct DisplayController {
   _input_guard: Option<thread::JoinHandle<()>>,
   _output_guard: Option<thread::JoinHandle<()>>,
   current_state: Arc<Mutex<CurrentState>>,
   subscribe_tx: mpsc::Sender<DispatchMessage<DispatchType>>,
   subscribe_rx: Arc<Mutex<mpsc::Receiver<DispatchMessage<DispatchType>>>>,
   broadcast_tx: Option<mpsc::Sender<DispatchMessage<DispatchType>>>
}

impl DisplayController {
   pub fn new(initial_state: &str) -> DisplayController {
      let initial_state = current_state::new_from_str(&initial_state);
      let (tx, rx) = mpsc::channel::<DispatchMessage<DispatchType>>();

      DisplayController {
         _input_guard: None,
         _output_guard: None,
         current_state: Arc::new(Mutex::new(initial_state)),
         subscribe_rx: Arc::new(Mutex::new(rx)),
         subscribe_tx: tx,
         broadcast_tx: None
      }
   }

   pub fn start(&mut self) {
      // For communicating from and to the view
      let (view_tx, view_rx) = mpsc::channel::<ViewData>();

      self.spawn_view_loop(view_rx);
      self.spawn_print_loop(view_tx);
   }

   fn spawn_view_loop(&self, view_rx: mpsc::Receiver<ViewData>) {
      let broadcast_tx = self.broadcast_tx.clone().expect("Expected broadcaster to be present in display controller");

      thread::spawn(move || {
         let mut view = View::new();
         let on_input = Box::new(move |string: String, channel_id: String| {
            let (payload, dtype) = input_parser::parse(string, channel_id);
            let message = DispatchMessage { payload: payload, dispatch_type: dtype };
            broadcast_tx.send(message).unwrap();
         });
         view.init(on_input, view_rx);
      });
   }

   fn spawn_print_loop(&self, view_tx: mpsc::Sender<ViewData>) {
      let rx = self.subscribe_rx.clone();
      let state = self.current_state.clone();
      let default_channel = self.default_channel();

      thread::spawn(move || {
         let mut all_view_data: Vec<ViewData> = vec![];
         let mut current_view_data = ViewData::new(default_channel.clone());

         loop {
            let message = rx.lock().unwrap().recv().unwrap();
            match message.dispatch_type {
               DispatchType::RawIncomingMessage => {
                  let parsed = state.lock().unwrap().parse_incoming_message(&message.payload).unwrap();

                  match parsed.channel.as_ref() {
                     Some(channel) if channel == &current_view_data.channel => {
                        current_view_data.add_message(parsed.clone())
                     },
                     Some(channel) => {
                        for data in all_view_data.iter_mut() {
                           if &data.channel == channel {
                              data.add_message(parsed.clone());
                              break;
                           }
                        }

                     },
                     None => current_view_data.add_debug(format!("{}", parsed))
                  }
               },
               // DispatchType::UserInput => {
               //    current_view_data.add_debug(format!("User input: {}", &message.payload))
               // },
               DispatchType::ChangeCurrentChannel => {
                  match state.lock().unwrap().name_to_channel(&message.payload) {
                     Some(channel) => {
                        all_view_data.push(current_view_data.clone());
                        match all_view_data.iter().position(|d| &d.channel == channel) {
                           Some(idx) => {
                              current_view_data = all_view_data.remove(idx);
                           },
                           None => {
                              current_view_data = ViewData::new(channel.clone());

                           }
                        }
                        current_view_data.add_debug(format!("Changed channel to: {}", channel.name))
                     },
                     None => {
                        current_view_data.add_debug(format!("Channel not found: {}", &message.payload))
                     }
                  }
               },
               DispatchType::ListChannels => {
                  let channel_names = state.lock().unwrap().channel_names();
                  current_view_data.add_debug(format!("Channels: {}", channel_names.connect(", ")));
               },
               _ => ()
            }
            view_tx.send(current_view_data.clone()).unwrap();
         }
      });
   }

   fn default_channel(&self) -> Channel {
      self.current_state.lock().unwrap().default_channel()
         .expect("Could not find default channel")
         .clone()
   }
}

impl Subscribe<DispatchType> for DisplayController {
   fn subscribe_handle(&self) -> SubscribeHandle<DispatchType> {
      self.subscribe_tx.clone()
   }
}

impl Broadcast<DispatchType> for DisplayController {
    fn broadcast_handle(&mut self) -> BroadcastHandle<DispatchType> {
        let (tx, rx) = mpsc::channel::<DispatchMessage<DispatchType>>();
        self.broadcast_tx = Some(tx);
        rx
    }
}
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
    use properties::longhands::{flex_direction, flex_wrap};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut direction = None;
        let mut wrap = None;
        loop {
            if direction.is_none() {
                if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
                    direction = Some(value);
                    continue
                }
            }
            if wrap.is_none() {
                if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
                    wrap = Some(value);
                    continue
                }
            }
            break
        }

        if direction.is_none() && wrap.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_direction: unwrap_or_initial!(flex_direction, direction),
            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.flex_direction.to_css(dest)?;
            dest.write_str(" ")?;
            self.flex_wrap.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-property">
    use values::specified::Number;

    fn parse_flexibility(input: &mut Parser)
                         -> Result<(Number, Option<Number>),()> {
        let grow = try!(Number::parse_non_negative(input));
        let shrink = input.try(Number::parse_non_negative).ok();
        Ok((grow, shrink))
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut grow = None;
        let mut shrink = None;
        let mut basis = None;

        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(Longhands {
                flex_grow: Number::new(0.0),
                flex_shrink: Number::new(0.0),
                flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
            })
        }
        loop {
            if grow.is_none() {
                if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
                    grow = Some(flex_grow);
                    shrink = flex_shrink;
                    continue
                }
            }
            if basis.is_none() {
                if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                    basis = Some(value);
                    continue
                }
            }
            break
        }

        if grow.is_none() && basis.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_grow: grow.unwrap_or(Number::new(1.0)),
            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
            flex_basis: basis.unwrap_or(longhands::flex_basis::SpecifiedValue::zero()),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.flex_grow.to_css(dest));
            try!(dest.write_str(" "));

            try!(self.flex_shrink.to_css(dest));
            try!(dest.write_str(" "));

            self.flex_basis.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="grid-gap" sub_properties="grid-row-gap grid-column-gap"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-gap"
                    products="gecko">
  use properties::longhands::{grid_row_gap, grid_column_gap};

  pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
      let row_gap = grid_row_gap::parse(context, input)?;
      let column_gap = input.try(|input| grid_column_gap::parse(context, input)).unwrap_or(row_gap.clone());

      Ok(Longhands {
        grid_row_gap: row_gap,
        grid_column_gap: column_gap,
      })
  }

  impl<'a> ToCss for LonghandsToSerialize<'a>  {
      fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
          if self.grid_row_gap == self.grid_column_gap {
            self.grid_row_gap.to_css(dest)
          } else {
            self.grid_row_gap.to_css(dest)?;
            dest.write_str(" ")?;
            self.grid_column_gap.to_css(dest)
          }
      }
  }

</%helpers:shorthand>

<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
                    products="gecko" disable_when_testing="True">
    use properties::longhands::align_content;
    use properties::longhands::justify_content;

% if product == "servo":
    impl From<align_content::SpecifiedValue> for justify_content::SpecifiedValue {
        fn from(align: align_content::SpecifiedValue) ->
            justify_content::SpecifiedValue {
            match align {
                align_content::SpecifiedValue::stretch =>
                    justify_content::SpecifiedValue::stretch,
                align_content::SpecifiedValue::flex_start =>
                    justify_content::SpecifiedValue::flex_start,
                align_content::SpecifiedValue::flex_end =>
                    justify_content::SpecifiedValue::flex_end,
                align_content::SpecifiedValue::center =>
                    justify_content::SpecifiedValue::center,
                align_content::SpecifiedValue::space_between =>
                    justify_content::SpecifiedValue::space_between,
                align_content::SpecifiedValue::space_around =>
                    justify_content::SpecifiedValue::space_around,
            }
        }
    }
% endif

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = align_content::parse(context, input)?;
        let justify = input.try(|input| justify_content::parse(context, input))
                           .unwrap_or(justify_content::SpecifiedValue::from(align));

        Ok(Longhands {
            align_content: align,
            justify_content: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_content.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_content.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
                    spec="https://drafts.csswg.org/css-align/#place-self-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::AlignJustifySelf;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignJustifySelf::parse(context, input)?;
        let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());

        Ok(Longhands {
            align_self: align,
            justify_self: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_self.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_self.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
                    spec="https://drafts.csswg.org/css-align/#place-items-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::{AlignItems, JustifyItems};
    use parser::Parse;

    impl From<AlignItems> for JustifyItems {
        fn from(align: AlignItems) -> JustifyItems {
            JustifyItems(align.0)
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignItems::parse(context, input)?;
        let justify = input.try(|input| JustifyItems::parse(context, input))
                           .unwrap_or(JustifyItems::from(align));

        Ok(Longhands {
            align_items: align,
            justify_items: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_items.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_items.to_css(dest)
        }
    }
</%helpers:shorthand>
<commit_msg>Fix serialization when the second value is identical to the first<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
    use properties::longhands::{flex_direction, flex_wrap};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut direction = None;
        let mut wrap = None;
        loop {
            if direction.is_none() {
                if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
                    direction = Some(value);
                    continue
                }
            }
            if wrap.is_none() {
                if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
                    wrap = Some(value);
                    continue
                }
            }
            break
        }

        if direction.is_none() && wrap.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_direction: unwrap_or_initial!(flex_direction, direction),
            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.flex_direction.to_css(dest)?;
            dest.write_str(" ")?;
            self.flex_wrap.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-property">
    use values::specified::Number;

    fn parse_flexibility(input: &mut Parser)
                         -> Result<(Number, Option<Number>),()> {
        let grow = try!(Number::parse_non_negative(input));
        let shrink = input.try(Number::parse_non_negative).ok();
        Ok((grow, shrink))
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut grow = None;
        let mut shrink = None;
        let mut basis = None;

        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(Longhands {
                flex_grow: Number::new(0.0),
                flex_shrink: Number::new(0.0),
                flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
            })
        }
        loop {
            if grow.is_none() {
                if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
                    grow = Some(flex_grow);
                    shrink = flex_shrink;
                    continue
                }
            }
            if basis.is_none() {
                if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                    basis = Some(value);
                    continue
                }
            }
            break
        }

        if grow.is_none() && basis.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_grow: grow.unwrap_or(Number::new(1.0)),
            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
            flex_basis: basis.unwrap_or(longhands::flex_basis::SpecifiedValue::zero()),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.flex_grow.to_css(dest));
            try!(dest.write_str(" "));

            try!(self.flex_shrink.to_css(dest));
            try!(dest.write_str(" "));

            self.flex_basis.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="grid-gap" sub_properties="grid-row-gap grid-column-gap"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-gap"
                    products="gecko">
  use properties::longhands::{grid_row_gap, grid_column_gap};

  pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
      let row_gap = grid_row_gap::parse(context, input)?;
      let column_gap = input.try(|input| grid_column_gap::parse(context, input)).unwrap_or(row_gap.clone());

      Ok(Longhands {
        grid_row_gap: row_gap,
        grid_column_gap: column_gap,
      })
  }

  impl<'a> ToCss for LonghandsToSerialize<'a>  {
      fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
          if self.grid_row_gap == self.grid_column_gap {
            self.grid_row_gap.to_css(dest)
          } else {
            self.grid_row_gap.to_css(dest)?;
            dest.write_str(" ")?;
            self.grid_column_gap.to_css(dest)
          }
      }
  }

</%helpers:shorthand>

<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
                    products="gecko" disable_when_testing="True">
    use properties::longhands::align_content;
    use properties::longhands::justify_content;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = align_content::parse(context, input)?;
        let justify = input.try(|input| justify_content::parse(context, input))
                           .unwrap_or(justify_content::SpecifiedValue::from(align));

        Ok(Longhands {
            align_content: align,
            justify_content: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_content == self.justify_content {
                self.align_content.to_css(dest)
            } else {
                self.justify_content.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_content.to_css(dest)
            }
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
                    spec="https://drafts.csswg.org/css-align/#place-self-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::AlignJustifySelf;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignJustifySelf::parse(context, input)?;
        let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());

        Ok(Longhands {
            align_self: align,
            justify_self: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_self == self.justify_self {
                self.align_self.to_css(dest)
            } else {
                self.align_self.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_self.to_css(dest)
            }
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
                    spec="https://drafts.csswg.org/css-align/#place-items-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::{AlignItems, JustifyItems};
    use parser::Parse;

    impl From<AlignItems> for JustifyItems {
        fn from(align: AlignItems) -> JustifyItems {
            JustifyItems(align.0)
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignItems::parse(context, input)?;
        let justify = input.try(|input| JustifyItems::parse(context, input))
                           .unwrap_or(JustifyItems::from(align));

        Ok(Longhands {
            align_items: align,
            justify_items: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_items.0 == self.justify_items.0 {
                self.align_items.to_css(dest)
            } else {
                self.align_items.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_items.to_css(dest)
            }
        }
    }
</%helpers:shorthand>
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
    use properties::longhands::{flex_direction, flex_wrap};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut direction = None;
        let mut wrap = None;
        loop {
            if direction.is_none() {
                if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
                    direction = Some(value);
                    continue
                }
            }
            if wrap.is_none() {
                if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
                    wrap = Some(value);
                    continue
                }
            }
            break
        }

        if direction.is_none() && wrap.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_direction: unwrap_or_initial!(flex_direction, direction),
            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.flex_direction.to_css(dest)?;
            dest.write_str(" ")?;
            self.flex_wrap.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-property">
    use values::specified::Number;

    fn parse_flexibility(input: &mut Parser)
                         -> Result<(Number, Option<Number>),()> {
        let grow = try!(Number::parse_non_negative(input));
        let shrink = input.try(Number::parse_non_negative).ok();
        Ok((grow, shrink))
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut grow = None;
        let mut shrink = None;
        let mut basis = None;

        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(Longhands {
                flex_grow: Number::new(0.0),
                flex_shrink: Number::new(0.0),
                flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
            })
        }
        loop {
            if grow.is_none() {
                if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
                    grow = Some(flex_grow);
                    shrink = flex_shrink;
                    continue
                }
            }
            if basis.is_none() {
                if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                    basis = Some(value);
                    continue
                }
            }
            break
        }

        if grow.is_none() && basis.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_grow: grow.unwrap_or(Number::new(1.0)),
            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
            flex_basis: basis.unwrap_or(longhands::flex_basis::SpecifiedValue::zero()),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.flex_grow.to_css(dest));
            try!(dest.write_str(" "));

            try!(self.flex_shrink.to_css(dest));
            try!(dest.write_str(" "));

            self.flex_basis.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="grid-gap" sub_properties="grid-row-gap grid-column-gap"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-gap"
                    products="gecko">
  use properties::longhands::{grid_row_gap, grid_column_gap};

  pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
      let row_gap = grid_row_gap::parse(context, input)?;
      let column_gap = input.try(|input| grid_column_gap::parse(context, input)).unwrap_or(row_gap.clone());

      Ok(Longhands {
        grid_row_gap: row_gap,
        grid_column_gap: column_gap,
      })
  }

  impl<'a> ToCss for LonghandsToSerialize<'a>  {
      fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
          if self.grid_row_gap == self.grid_column_gap {
            self.grid_row_gap.to_css(dest)
          } else {
            self.grid_row_gap.to_css(dest)?;
            dest.write_str(" ")?;
            self.grid_column_gap.to_css(dest)
          }
      }
  }

</%helpers:shorthand>

<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
                    products="gecko">
    use properties::longhands::align_content;
    use properties::longhands::justify_content;

% if product == "servo":
    impl From<align_content::SpecifiedValue> for justify_content::SpecifiedValue {
        fn from(align: align_content::SpecifiedValue) ->
            justify_content::SpecifiedValue {
            match align {
                align_content::SpecifiedValue::stretch =>
                    justify_content::SpecifiedValue::stretch,
                align_content::SpecifiedValue::flex_start =>
                    justify_content::SpecifiedValue::flex_start,
                align_content::SpecifiedValue::flex_end =>
                    justify_content::SpecifiedValue::flex_end,
                align_content::SpecifiedValue::center =>
                    justify_content::SpecifiedValue::center,
                align_content::SpecifiedValue::space_between =>
                    justify_content::SpecifiedValue::space_between,
                align_content::SpecifiedValue::space_around =>
                    justify_content::SpecifiedValue::space_around,
            }
        }
    }
% endif

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = align_content::parse(context, input)?;
        let justify = input.try(|input| justify_content::parse(context, input))
                           .unwrap_or(justify_content::SpecifiedValue::from(align));

        Ok(Longhands {
            align_content: align,
            justify_content: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_content.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_content.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
                    spec="https://drafts.csswg.org/css-align/#place-self-property"
                    products="gecko">
    use values::specified::align::AlignJustifySelf;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignJustifySelf::parse(context, input)?;
        let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());

        Ok(Longhands {
            align_self: align,
            justify_self: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_self.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_self.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
                    spec="https://drafts.csswg.org/css-align/#place-items-property"
                    products="gecko">
    use values::specified::align::{AlignItems, JustifyItems};
    use parser::Parse;

    impl From<AlignItems> for JustifyItems {
        fn from(align: AlignItems) -> JustifyItems {
            JustifyItems(align.0)
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignItems::parse(context, input)?;
        let justify = input.try(|input| JustifyItems::parse(context, input))
                           .unwrap_or(JustifyItems::from(align));

        Ok(Longhands {
            align_items: align,
            justify_items: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_items.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_items.to_css(dest)
        }
    }
</%helpers:shorthand>
<commit_msg>Mark disable_when_testing<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
    use properties::longhands::{flex_direction, flex_wrap};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut direction = None;
        let mut wrap = None;
        loop {
            if direction.is_none() {
                if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
                    direction = Some(value);
                    continue
                }
            }
            if wrap.is_none() {
                if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
                    wrap = Some(value);
                    continue
                }
            }
            break
        }

        if direction.is_none() && wrap.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_direction: unwrap_or_initial!(flex_direction, direction),
            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.flex_direction.to_css(dest)?;
            dest.write_str(" ")?;
            self.flex_wrap.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-property">
    use values::specified::Number;

    fn parse_flexibility(input: &mut Parser)
                         -> Result<(Number, Option<Number>),()> {
        let grow = try!(Number::parse_non_negative(input));
        let shrink = input.try(Number::parse_non_negative).ok();
        Ok((grow, shrink))
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut grow = None;
        let mut shrink = None;
        let mut basis = None;

        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(Longhands {
                flex_grow: Number::new(0.0),
                flex_shrink: Number::new(0.0),
                flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
            })
        }
        loop {
            if grow.is_none() {
                if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
                    grow = Some(flex_grow);
                    shrink = flex_shrink;
                    continue
                }
            }
            if basis.is_none() {
                if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                    basis = Some(value);
                    continue
                }
            }
            break
        }

        if grow.is_none() && basis.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_grow: grow.unwrap_or(Number::new(1.0)),
            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
            flex_basis: basis.unwrap_or(longhands::flex_basis::SpecifiedValue::zero()),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.flex_grow.to_css(dest));
            try!(dest.write_str(" "));

            try!(self.flex_shrink.to_css(dest));
            try!(dest.write_str(" "));

            self.flex_basis.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="grid-gap" sub_properties="grid-row-gap grid-column-gap"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-gap"
                    products="gecko">
  use properties::longhands::{grid_row_gap, grid_column_gap};

  pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
      let row_gap = grid_row_gap::parse(context, input)?;
      let column_gap = input.try(|input| grid_column_gap::parse(context, input)).unwrap_or(row_gap.clone());

      Ok(Longhands {
        grid_row_gap: row_gap,
        grid_column_gap: column_gap,
      })
  }

  impl<'a> ToCss for LonghandsToSerialize<'a>  {
      fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
          if self.grid_row_gap == self.grid_column_gap {
            self.grid_row_gap.to_css(dest)
          } else {
            self.grid_row_gap.to_css(dest)?;
            dest.write_str(" ")?;
            self.grid_column_gap.to_css(dest)
          }
      }
  }

</%helpers:shorthand>

<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
                    products="gecko" disable_when_testing="True">
    use properties::longhands::align_content;
    use properties::longhands::justify_content;

% if product == "servo":
    impl From<align_content::SpecifiedValue> for justify_content::SpecifiedValue {
        fn from(align: align_content::SpecifiedValue) ->
            justify_content::SpecifiedValue {
            match align {
                align_content::SpecifiedValue::stretch =>
                    justify_content::SpecifiedValue::stretch,
                align_content::SpecifiedValue::flex_start =>
                    justify_content::SpecifiedValue::flex_start,
                align_content::SpecifiedValue::flex_end =>
                    justify_content::SpecifiedValue::flex_end,
                align_content::SpecifiedValue::center =>
                    justify_content::SpecifiedValue::center,
                align_content::SpecifiedValue::space_between =>
                    justify_content::SpecifiedValue::space_between,
                align_content::SpecifiedValue::space_around =>
                    justify_content::SpecifiedValue::space_around,
            }
        }
    }
% endif

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = align_content::parse(context, input)?;
        let justify = input.try(|input| justify_content::parse(context, input))
                           .unwrap_or(justify_content::SpecifiedValue::from(align));

        Ok(Longhands {
            align_content: align,
            justify_content: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_content.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_content.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
                    spec="https://drafts.csswg.org/css-align/#place-self-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::AlignJustifySelf;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignJustifySelf::parse(context, input)?;
        let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());

        Ok(Longhands {
            align_self: align,
            justify_self: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_self.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_self.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
                    spec="https://drafts.csswg.org/css-align/#place-items-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::{AlignItems, JustifyItems};
    use parser::Parse;

    impl From<AlignItems> for JustifyItems {
        fn from(align: AlignItems) -> JustifyItems {
            JustifyItems(align.0)
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignItems::parse(context, input)?;
        let justify = input.try(|input| JustifyItems::parse(context, input))
                           .unwrap_or(JustifyItems::from(align));

        Ok(Longhands {
            align_items: align,
            justify_items: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_items.to_css(dest)?;
            dest.write_str(" ")?;
            self.justify_items.to_css(dest)
        }
    }
</%helpers:shorthand>
<|endoftext|>


<commit_before>//! [PUT /_matrix/app/v1/transactions/{txnId}](https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid)

use ruma_api::ruma_api;
use ruma_events::AnyRoomEvent;
use ruma_serde::Raw;

ruma_api! {
    metadata: {
        description: "This API is called by the homeserver when it wants to push an event (or batch of events) to the application service.",
        method: PUT,
        name: "push_events",
        path: "/_matrix/app/v1/transactions/:txn_id",
        rate_limited: false,
        authentication: QueryOnlyAccessToken,
    }

    request: {
        /// The transaction ID for this set of events.
        ///
        /// Homeservers generate these IDs and they are used to ensure idempotency of results.
        #[ruma_api(path)]
        pub txn_id: &'a str,

        /// A list of events.
        pub events: &'a [Raw<AnyRoomEvent>],
    }

    #[derive(Default)]
    response: {}
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given transaction ID and list of events.
    pub fn new(txn_id: &'a str, events: &'a [Raw<AnyRoomEvent>]) -> Self {
        Self { txn_id, events }
    }
}

impl IncomingRequest {
    /// Creates an `IncomingRequest` with the given transaction ID and list of events.
    pub fn new(txn_id: String, events: Vec<Raw<AnyRoomEvent>>) -> IncomingRequest {
        IncomingRequest { txn_id, events }
    }

    /// Consumes the `IncomingRequest` and tries to convert it to a `sync_events::Response`
    ///
    /// This is a helper conversion in cases where it's easier to work with `sync_events::Response`
    /// instead of the original `push_events::IncomingRequest`. It puts all events with a `room_id`
    /// into the `JoinedRoom`'s `timeline`. The rationale behind that is that incoming Appservice
    /// transactions from the homeserver are not necessarily bound to a specific user but can cover
    /// a multitude of namespaces, and as such the Appservice basically only "observes joined
    /// rooms".
    ///
    /// Note: Currently homeservers only push PDUs to appservices, no EDUs. There's the open
    /// [MSC2409] regarding supporting EDUs in the future, though it seems to be planned to put
    /// EDUs into a different JSON key than `events` to stay backwards compatible.
    ///
    /// [MSC2409]: https://github.com/matrix-org/matrix-doc/pull/2409
    #[cfg(feature = "helper")]
    pub fn try_into_sync_response(
        self,
        next_batch: impl Into<String>,
    ) -> serde_json::Result<ruma_client_api::r0::sync::sync_events::Response> {
        use ruma_client_api::r0::sync::sync_events;
        use ruma_identifiers::RoomId;
        use serde::Deserialize;
        use tracing::warn;

        #[derive(Debug, Deserialize)]
        struct EventDeHelper {
            room_id: Option<Box<RoomId>>,
        }

        let mut response = sync_events::Response::new(next_batch.into());

        for raw_event in self.events {
            let helper = raw_event.deserialize_as::<EventDeHelper>()?;
            let event_json = Raw::into_json(raw_event);

            if let Some(room_id) = helper.room_id {
                let join = response.rooms.join.entry(room_id).or_default();
                join.timeline.events.push(Raw::from_json(event_json));
            } else {
                warn!("Event without room_id: {}", event_json);
            }
        }

        Ok(response)
    }
}

impl Response {
    /// Creates an empty `Response`.
    pub fn new() -> Self {
        Self {}
    }
}

#[cfg(feature = "helper")]
#[cfg(test)]
mod helper_tests {
    use super::{AnyRoomEvent, IncomingRequest, Raw};
    use ruma_client_api::r0::sync::sync_events;
    use ruma_identifiers::room_id;
    use serde_json::json;

    #[test]
    fn convert_incoming_request_to_sync_response() {
        let txn_id = "any_txn_id".to_owned();
        let state_event: AnyRoomEvent = serde_json::from_value(json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!roomid:room.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        }))
        .unwrap();
        let message_event: AnyRoomEvent = serde_json::from_value(json!({
            "type": "m.room.message",
            "event_id": "$143273582443PhrSn:example.com",
            "origin_server_ts": 1,
            "room_id": "!roomid:room.com",
            "sender": "@user:example.com",
            "content": {
                "body": "test",
                "msgtype": "m.audio",
                "url": "mxc://example.com/AuDi0",
            }
        }))
        .unwrap();

        let events = vec![Raw::new(&state_event).unwrap(), Raw::new(&message_event).unwrap()];
        let incoming_request = IncomingRequest { txn_id: txn_id.clone(), events };

        let response: sync_events::Response =
            incoming_request.try_into_sync_response(txn_id).unwrap();

        let response_rooms_join =
            response.rooms.join.get(room_id!("!roomid:room.com")).expect("joined room response");

        assert_eq!(response_rooms_join.timeline.events.len(), 2);
    }
}

#[cfg(feature = "server")]
#[cfg(test)]
mod tests {
    use ruma_api::{exports::http, OutgoingRequest, SendAccessToken};
    use ruma_events::AnyRoomEvent;
    use ruma_serde::Raw;
    use serde_json::json;

    use super::Request;

    #[test]
    fn decode_request_contains_events_field() {
        let dummy_event: AnyRoomEvent = serde_json::from_value(json!({
            "type": "m.room.message",
            "event_id": "$143273582443PhrSn:example.com",
            "origin_server_ts": 1,
            "room_id": "!roomid:room.com",
            "sender": "@user:example.com",
            "content": {
                "body": "test",
                "msgtype": "m.text",
            },
        }))
        .unwrap();
        let dummy_event = Raw::new(&dummy_event).unwrap();
        let events = vec![dummy_event];

        let req: http::Request<Vec<u8>> = Request { events: &events, txn_id: "any_txn_id" }
            .try_into_http_request(
                "https://homeserver.tld",
                SendAccessToken::IfRequired("auth_tok"),
            )
            .unwrap();
        let json_body: serde_json::Value = serde_json::from_slice(req.body()).unwrap();

        assert_eq!(
            1,
            json_body.as_object().unwrap().get("events").unwrap().as_array().unwrap().len()
        );
    }
}
<commit_msg>appservice-api: Remove pointless import<commit_after>//! [PUT /_matrix/app/v1/transactions/{txnId}](https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid)

use ruma_api::ruma_api;
use ruma_events::AnyRoomEvent;
use ruma_serde::Raw;

ruma_api! {
    metadata: {
        description: "This API is called by the homeserver when it wants to push an event (or batch of events) to the application service.",
        method: PUT,
        name: "push_events",
        path: "/_matrix/app/v1/transactions/:txn_id",
        rate_limited: false,
        authentication: QueryOnlyAccessToken,
    }

    request: {
        /// The transaction ID for this set of events.
        ///
        /// Homeservers generate these IDs and they are used to ensure idempotency of results.
        #[ruma_api(path)]
        pub txn_id: &'a str,

        /// A list of events.
        pub events: &'a [Raw<AnyRoomEvent>],
    }

    #[derive(Default)]
    response: {}
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given transaction ID and list of events.
    pub fn new(txn_id: &'a str, events: &'a [Raw<AnyRoomEvent>]) -> Self {
        Self { txn_id, events }
    }
}

impl IncomingRequest {
    /// Creates an `IncomingRequest` with the given transaction ID and list of events.
    pub fn new(txn_id: String, events: Vec<Raw<AnyRoomEvent>>) -> IncomingRequest {
        IncomingRequest { txn_id, events }
    }

    /// Consumes the `IncomingRequest` and tries to convert it to a `sync_events::Response`
    ///
    /// This is a helper conversion in cases where it's easier to work with `sync_events::Response`
    /// instead of the original `push_events::IncomingRequest`. It puts all events with a `room_id`
    /// into the `JoinedRoom`'s `timeline`. The rationale behind that is that incoming Appservice
    /// transactions from the homeserver are not necessarily bound to a specific user but can cover
    /// a multitude of namespaces, and as such the Appservice basically only "observes joined
    /// rooms".
    ///
    /// Note: Currently homeservers only push PDUs to appservices, no EDUs. There's the open
    /// [MSC2409] regarding supporting EDUs in the future, though it seems to be planned to put
    /// EDUs into a different JSON key than `events` to stay backwards compatible.
    ///
    /// [MSC2409]: https://github.com/matrix-org/matrix-doc/pull/2409
    #[cfg(feature = "helper")]
    pub fn try_into_sync_response(
        self,
        next_batch: impl Into<String>,
    ) -> serde_json::Result<ruma_client_api::r0::sync::sync_events::Response> {
        use ruma_client_api::r0::sync::sync_events;
        use ruma_identifiers::RoomId;
        use serde::Deserialize;
        use tracing::warn;

        #[derive(Debug, Deserialize)]
        struct EventDeHelper {
            room_id: Option<Box<RoomId>>,
        }

        let mut response = sync_events::Response::new(next_batch.into());

        for raw_event in self.events {
            let helper = raw_event.deserialize_as::<EventDeHelper>()?;
            let event_json = Raw::into_json(raw_event);

            if let Some(room_id) = helper.room_id {
                let join = response.rooms.join.entry(room_id).or_default();
                join.timeline.events.push(Raw::from_json(event_json));
            } else {
                warn!("Event without room_id: {}", event_json);
            }
        }

        Ok(response)
    }
}

impl Response {
    /// Creates an empty `Response`.
    pub fn new() -> Self {
        Self {}
    }
}

#[cfg(feature = "helper")]
#[cfg(test)]
mod helper_tests {
    use super::{AnyRoomEvent, IncomingRequest, Raw};
    use ruma_client_api::r0::sync::sync_events;
    use ruma_identifiers::room_id;
    use serde_json::json;

    #[test]
    fn convert_incoming_request_to_sync_response() {
        let txn_id = "any_txn_id".to_owned();
        let state_event: AnyRoomEvent = serde_json::from_value(json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!roomid:room.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        }))
        .unwrap();
        let message_event: AnyRoomEvent = serde_json::from_value(json!({
            "type": "m.room.message",
            "event_id": "$143273582443PhrSn:example.com",
            "origin_server_ts": 1,
            "room_id": "!roomid:room.com",
            "sender": "@user:example.com",
            "content": {
                "body": "test",
                "msgtype": "m.audio",
                "url": "mxc://example.com/AuDi0",
            }
        }))
        .unwrap();

        let events = vec![Raw::new(&state_event).unwrap(), Raw::new(&message_event).unwrap()];
        let incoming_request = IncomingRequest { txn_id: txn_id.clone(), events };

        let response: sync_events::Response =
            incoming_request.try_into_sync_response(txn_id).unwrap();

        let response_rooms_join =
            response.rooms.join.get(room_id!("!roomid:room.com")).expect("joined room response");

        assert_eq!(response_rooms_join.timeline.events.len(), 2);
    }
}

#[cfg(feature = "server")]
#[cfg(test)]
mod tests {
    use ruma_api::{OutgoingRequest, SendAccessToken};
    use ruma_events::AnyRoomEvent;
    use ruma_serde::Raw;
    use serde_json::json;

    use super::Request;

    #[test]
    fn decode_request_contains_events_field() {
        let dummy_event: AnyRoomEvent = serde_json::from_value(json!({
            "type": "m.room.message",
            "event_id": "$143273582443PhrSn:example.com",
            "origin_server_ts": 1,
            "room_id": "!roomid:room.com",
            "sender": "@user:example.com",
            "content": {
                "body": "test",
                "msgtype": "m.text",
            },
        }))
        .unwrap();
        let dummy_event = Raw::new(&dummy_event).unwrap();
        let events = vec![dummy_event];

        let req = Request { events: &events, txn_id: "any_txn_id" }
            .try_into_http_request::<Vec<u8>>(
                "https://homeserver.tld",
                SendAccessToken::IfRequired("auth_tok"),
            )
            .unwrap();
        let json_body: serde_json::Value = serde_json::from_slice(req.body()).unwrap();

        assert_eq!(
            1,
            json_body.as_object().unwrap().get("events").unwrap().as_array().unwrap().len()
        );
    }
}
<|endoftext|>


<commit_before>use backend::Backend;
use query_builder::AstPass;
use query_source::QuerySource;
use result::QueryResult;

/// Types which can be passed to
/// [`update.set`](/diesel/query_builder/struct.IncompleteUpdateStatement.html#method.set).
///
/// ### Deriving
///
/// This trait can be automatically derived using `diesel_codegen` by adding
/// `#[derive(AsChangeset)]` to your struct.  Structs which derive this trait
/// must be annotated with `#[table_name = "something"]`. If the field name of
/// your struct differs from the name of the column, you can annotate the field
/// with `#[column_name = "some_column_name"]`.
///
/// By default, any `Option` fields on the struct are skipped if their value is
/// `None`. If you would like to assign `NULL` to the field instead, you can
/// annotate your struct with `#[changeset_options(treat_none_as_null =
/// "true")]`.
pub trait AsChangeset {
    type Target: QuerySource;
    type Changeset;

    fn as_changeset(self) -> Self::Changeset;
}

/// Apps should not need to concern themselves with this trait.
pub trait Changeset<DB: Backend> {
    fn is_noop(&self) -> bool;
    fn walk_ast(&self, pass: AstPass<DB>) -> QueryResult<()>;
}

impl<T: AsChangeset> AsChangeset for Option<T> {
    type Target = T::Target;
    type Changeset = Option<T::Changeset>;

    fn as_changeset(self) -> Self::Changeset {
        self.map(|v| v.as_changeset())
    }
}

impl<T: Changeset<DB>, DB: Backend> Changeset<DB> for Option<T> {
    fn is_noop(&self) -> bool {
        self.is_none()
    }

    fn walk_ast(&self, out: AstPass<DB>) -> QueryResult<()> {
        match *self {
            Some(ref c) => c.walk_ast(out),
            None => Ok(()),
        }
    }
}
<commit_msg>Document all items on `AsChangeset` and `Changeset`<commit_after>use backend::Backend;
use query_builder::AstPass;
use query_source::QuerySource;
use result::QueryResult;

/// Types which can be passed to
/// [`update.set`](/diesel/query_builder/struct.IncompleteUpdateStatement.html#method.set).
///
/// ### Deriving
///
/// This trait can be automatically derived using `diesel_codegen` by adding
/// `#[derive(AsChangeset)]` to your struct.  Structs which derive this trait
/// must be annotated with `#[table_name = "something"]`. If the field name of
/// your struct differs from the name of the column, you can annotate the field
/// with `#[column_name = "some_column_name"]`.
///
/// By default, any `Option` fields on the struct are skipped if their value is
/// `None`. If you would like to assign `NULL` to the field instead, you can
/// annotate your struct with `#[changeset_options(treat_none_as_null =
/// "true")]`.
pub trait AsChangeset {
    /// The table which `Self::Changeset` will be updating
    type Target: QuerySource;

    /// The update statement this type represents
    type Changeset;

    /// Convert `self` into the actual update statement being executed
    fn as_changeset(self) -> Self::Changeset;
}

/// Apps should not need to concern themselves with this trait.
pub trait Changeset<DB: Backend> {
    /// Does this changeset actually include any changes?
    fn is_noop(&self) -> bool;

    /// See [`QueryFragment#walk_ast`]
    ///
    /// [`QueryFragment#walk_ast`]: trait.QueryFragment.html#tymethod.walk_ast
    fn walk_ast(&self, pass: AstPass<DB>) -> QueryResult<()>;
}

impl<T: AsChangeset> AsChangeset for Option<T> {
    type Target = T::Target;
    type Changeset = Option<T::Changeset>;

    fn as_changeset(self) -> Self::Changeset {
        self.map(|v| v.as_changeset())
    }
}

impl<T: Changeset<DB>, DB: Backend> Changeset<DB> for Option<T> {
    fn is_noop(&self) -> bool {
        self.is_none()
    }

    fn walk_ast(&self, out: AstPass<DB>) -> QueryResult<()> {
        match *self {
            Some(ref c) => c.walk_ast(out),
            None => Ok(()),
        }
    }
}
<|endoftext|>


<commit_before>//! Shows a user-friendly compiler error on incompatible selected features.

#[allow(unused_macros)]
macro_rules! hide_from_rustfmt {
    ($mod:item) => {
        $mod
    };
}

#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
hide_from_rustfmt! {
    mod error;
}
<commit_msg>De Morgan the features check<commit_after>//! Shows a user-friendly compiler error on incompatible selected features.

#[allow(unused_macros)]
macro_rules! hide_from_rustfmt {
    ($mod:item) => {
        $mod
    };
}

#[cfg(not(any(feature = "std", feature = "alloc")))]
hide_from_rustfmt! {
    mod error;
}
<|endoftext|>