<commit_before>extern crate bincode;
use clap::{App, Arg, ArgMatches, SubCommand};
use flate2::{read, write, Compression};
use log_domain::LogDomain;
use std::{fs::File,
          io::{stdin, stdout, Read}};
use rustomata::grammars::lcfrs::from_discodop::DiscoDopGrammar;
use rustomata::grammars::{lcfrs::{csparsing::{CSRepresentation, DebugResult},
                                  Lcfrs},
                          pmcfg::negra::{to_negra, DumpMode, noparse}};
use rustomata::util::Capacity;

pub fn get_sub_command(name: &str) -> App {
    SubCommand::with_name(name)
        .about("Chomsky-Schützenberger representation of LCFRS")
        .subcommand(
            SubCommand::with_name("extract")
                .about(
                    "Reads a grammar from stdin and prints an object
                                       that Chomsky-Schützenberger-represents the grammar.",
                )
                .arg(
                    Arg::with_name("disco-grammar")
                        .short("d")
                        .long("disco")
                        .takes_value(false)
                        .help("Use a grammar extracted by disco-dop.")
                ).arg(
                    Arg::with_name("disco-lexer")
                        .short("l")
                        .long("lexer")
                        .takes_value(true)
                        .help("Provide the lexer file of a disco-dop grammar.")
                ).arg(
                    Arg::with_name("gzipped")
                        .short("z")
                        .long("zipped")
                        .takes_value(false)
                        .help("if the provided grammar file is Gzipped (default for disco-dop grammars)")
                        .conflicts_with("ungzipped")
                ).arg(
                    Arg::with_name("ungzipped")
                        .long("unzipped")
                        .takes_value(false)
                        .help("if the provided grammar file is not Gzipped (default for raw grammars)")
                        .conflicts_with("gzipped")
                ).arg(
                    Arg::with_name("grammar")
                        .index(1)
                        .required(false)
                        .help("Grammar file. Reads from stdin if not provided.")
                )
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about(
                    "Reads a list of words from stdin and parses them
                                       using the CS representation of a grammar.",
                )
                .arg(
                    Arg::with_name("csfile")
                        .required(true)
                        .index(1)
                        .help("The file that contains the CS representation of a grammar."),
                )
                .arg(
                    Arg::with_name("k")
                        .short("k")
                        .takes_value(true)
                        .help("Puts the `k` in k-best parsing."),
                )
                .arg(
                    Arg::with_name("beam")
                        .short("b")
                        .long("beam")
                        .takes_value(true)
                        .help("Beam with during search."),
                )
                .arg(
                    Arg::with_name("candidates")
                        .short("c")
                        .long("candidates")
                        .takes_value(true)
                        .help("Maximum number of candidates to enumerate."),
                )
                .arg(
                    Arg::with_name("with-lines")
                        .short("l")
                        .long("with-lines")
                        .help("The first symbol denotes the id of the parse tree."),
                )
                .arg(
                    Arg::with_name("with-pos")
                        .short("p")
                        .long("with-pos")
                        .help("Parses the sentence with already annotated POS tags."),
                )
                .arg(
                    Arg::with_name("debugmode")
                        .short("d")
                        .long("debug")
                        .help("Prints debug information instead of parse trees."),
                )
                .arg(
                    Arg::with_name("fallback")
                        .short("f")
                        .long("with-fallback")
                        .help("Will output an incorrect parse tree if the parse does not find a correct one."),
                ),
        )
}

pub fn handle_sub_matches(submatches: &ArgMatches) {
    match submatches.subcommand() {
        ("extract", Some(params)) => {
            let mut grammar_string = String::new();
            if let Some(path) = params.value_of("grammar") {
                if (params.is_present("gzipped") || params.is_present("disco-grammar")) && !params.is_present("ungzipped") {
                    read::GzDecoder::new(File::open(path).expect("Could not open grammar file."))
                        .read_to_string(&mut grammar_string)
                        .expect("Could not read the provided grammar file.");
                } else {
                    File::open(path).expect("Could not open grammar file.")
                        .read_to_string(&mut grammar_string)
                        .expect("Could not read the provided grammar file.");
                }
            } else {
                if (params.is_present("gzipped") || params.is_present("disco-grammar")) && !params.is_present("ungzipped") {
                    read::GzDecoder::new(stdin())
                        .read_to_string(&mut grammar_string)
                        .expect("Could not read the provided grammar file.");
                } else {
                    stdin().read_to_string(&mut grammar_string)
                           .expect("Could not read the provided grammar file.");
                }
            }

            let gmr: Lcfrs<String, String, LogDomain<f64>> = if params.is_present("disco-grammar") {
                let dgmr: DiscoDopGrammar<_, _, _> = grammar_string.parse().expect("Could not parse grammar.");
                if params.is_present("disco-lexer") {
                    let mut lexer_string = String::new();
                    if (params.is_present("gzipped") || params.is_present("disco-grammar")) && !params.is_present("ungzipped") {
                        read::GzDecoder::new(File::open(params.value_of("disco-lexer").expect("Missing lexer file"))
                                                .expect("Could not open lexer file."))
                            .read_to_string(&mut lexer_string)
                            .expect("Could not read the provided lexer file.");
                    } else {
                        File::open(params.value_of("disco-lexer").expect("Missing lexer file"))
                            .expect("Could not open lexer file.")
                            .read_to_string(&mut lexer_string)
                            .expect("Could not read lexer. Be sure to provide the Gzipped lexer file as input.");
                    }
                    dgmr.with_lexer(lexer_string.parse().expect("Could not parse lexer file.")).into()
                } else {
                    dgmr.with_default_lexer().into()
                }
            } else {
                grammar_string
                    .parse()
                    .expect("Could not decode the grammar provided via stdin.")
            };

            bincode::serialize_into(
                &mut write::GzEncoder::new(stdout(), Compression::best()),
                &CSRepresentation::new(gmr),
                bincode::Infinite,
            ).unwrap()
        }

        ("parse", Some(params)) => {
            let mut word_strings = String::new();
            stdin()
                .read_to_string(&mut word_strings)
                .expect("Please pass a string to parse using stdin or using the `--word` option.");
            let k: usize = match params.value_of("k") {
                Some(n) => n.parse::<usize>().unwrap(),
                None => 1usize,
            };

            let beam: Capacity = match params.value_of("beam") {
                Some(b) => b.parse().unwrap(),
                None => Capacity::Infinite,
            };
            let candidates: Capacity = match params.value_of("candidates") {
                Some(c) => c.parse().unwrap(),
                None => Capacity::Infinite,
            };

            let csfile = File::open(params.value_of("csfile").unwrap()).unwrap();

            let csrep: CSRepresentation<String, String, LogDomain<f64>> =
                bincode::deserialize_from(&mut read::GzDecoder::new(csfile), bincode::Infinite)
                    .unwrap();

            for (i, sentence) in word_strings.lines().enumerate() {
                let (i, words) = split_line(sentence, params.is_present("with-lines"), i);
                let (words, negra_mode) = split_pos(words, params.is_present("with-pos"));

                if params.is_present("debugmode") {
                    let tuple = csrep.debug(words.as_slice(), beam, candidates);
                    eprint!("{} {} {} {} ", tuple.0, tuple.1, tuple.2, tuple.3);
                    match tuple.4 {
                        DebugResult::Parse(t, n) => {
                            eprintln!("parse {}", n);
                            println!("{}", to_negra(&t, i, negra_mode));
                        },
                        DebugResult::Fallback(t, n) => {
                            eprintln!("fallback {}", n);
                            println!("{}", to_negra(&t, i, negra_mode));
                        },
                        DebugResult::Noparse => {
                            eprintln!("noparse 0");
                            println!("{}", noparse(&words, i, negra_mode));
                        },
                    }
                } else {
                    let mut found_trees = false;
                    let (iterator, fallback) = csrep.generate(words.as_slice(), beam, candidates);
                    for derivation in iterator.take(k) {
                        found_trees = true;
                        println!(
                            "{}",
                            to_negra(
                                &derivation
                                    .into_iter()
                                    .map(|(k, v)| (k, v.clone()))
                                    .collect(),
                                i,
                                negra_mode.clone()
                            )
                        );
                    }
                    if !found_trees && params.is_present("fallback") {
                        if let Some(tree) = fallback {
                            println!("{}", to_negra(&tree, i, negra_mode));
                        } else {
                            println!("{}", noparse(&words, i, negra_mode));
                        }
                    } else if !found_trees {
                        println!("{}", noparse(&words, i, negra_mode));
                    }
                }
            }
        }
        _ => (),
    }
}

fn split_line<'a>(line: &'a str, with_line_number: bool, default_line_number: usize) -> (usize, impl Iterator<Item=&'a str> + 'a) {
    let mut word_iterator = line.split_whitespace();

    let line_number = if with_line_number {
        word_iterator.next()
                     .expect("missing line number")
                     .parse()
                     .expect("invalid line numer")
    } else {
        default_line_number
    };

    (line_number, word_iterator)
}

fn split_pos<'a>(words: impl Iterator<Item=&'a str> + 'a, with_pos: bool) -> (Vec<String>, DumpMode<String>) {
    if with_pos {
        let (pos, ws) = words.map(
            |wp| {
                let mut it = wp.rsplitn(2, '/');
                ( it.next().unwrap().to_string(),
                  it.next().expect("missing pos annotation").to_string()
                )
            }
        ).unzip();
        (pos, DumpMode::FromPos(ws))
    } else {
        (words.map(|s| s.to_string()).collect(), DumpMode::Default)
    }
}<commit_msg>src/bin/csparsing.rs: handle gzipped grammars less copy-pasty<commit_after>extern crate bincode;
use clap::{App, Arg, ArgMatches, SubCommand};
use flate2::{read, write, Compression};
use log_domain::LogDomain;
use std::{fs::File,
          io::{stdin, stdout, Read}};
use rustomata::grammars::lcfrs::from_discodop::DiscoDopGrammar;
use rustomata::grammars::{lcfrs::{csparsing::{CSRepresentation, DebugResult},
                                  Lcfrs},
                          pmcfg::negra::{to_negra, DumpMode, noparse}};
use rustomata::util::Capacity;

pub fn get_sub_command(name: &str) -> App {
    SubCommand::with_name(name)
        .about("Chomsky-Schützenberger representation of LCFRS")
        .subcommand(
            SubCommand::with_name("extract")
                .about(
                    "Reads a grammar from stdin and prints an object
                                       that Chomsky-Schützenberger-represents the grammar.",
                )
                .arg(
                    Arg::with_name("disco-grammar")
                        .short("d")
                        .long("disco")
                        .takes_value(false)
                        .help("Use a grammar extracted by disco-dop.")
                ).arg(
                    Arg::with_name("disco-lexer")
                        .short("l")
                        .long("lexer")
                        .takes_value(true)
                        .help("Provide the lexer file of a disco-dop grammar.")
                ).arg(
                    Arg::with_name("gzipped")
                        .short("z")
                        .long("zipped")
                        .takes_value(false)
                        .help("if the provided grammar file is Gzipped (default for disco-dop grammars)")
                        .conflicts_with("ungzipped")
                ).arg(
                    Arg::with_name("ungzipped")
                        .long("unzipped")
                        .takes_value(false)
                        .help("if the provided grammar file is not Gzipped (default for raw grammars)")
                        .conflicts_with("gzipped")
                ).arg(
                    Arg::with_name("grammar")
                        .index(1)
                        .required(false)
                        .help("Grammar file. Reads from stdin if not provided.")
                )
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about(
                    "Reads a list of words from stdin and parses them
                                       using the CS representation of a grammar.",
                )
                .arg(
                    Arg::with_name("csfile")
                        .required(true)
                        .index(1)
                        .help("The file that contains the CS representation of a grammar."),
                )
                .arg(
                    Arg::with_name("k")
                        .short("k")
                        .takes_value(true)
                        .help("Puts the `k` in k-best parsing."),
                )
                .arg(
                    Arg::with_name("beam")
                        .short("b")
                        .long("beam")
                        .takes_value(true)
                        .help("Beam with during search."),
                )
                .arg(
                    Arg::with_name("candidates")
                        .short("c")
                        .long("candidates")
                        .takes_value(true)
                        .help("Maximum number of candidates to enumerate."),
                )
                .arg(
                    Arg::with_name("with-lines")
                        .short("l")
                        .long("with-lines")
                        .help("The first symbol denotes the id of the parse tree."),
                )
                .arg(
                    Arg::with_name("with-pos")
                        .short("p")
                        .long("with-pos")
                        .help("Parses the sentence with already annotated POS tags."),
                )
                .arg(
                    Arg::with_name("debugmode")
                        .short("d")
                        .long("debug")
                        .help("Prints debug information instead of parse trees."),
                )
                .arg(
                    Arg::with_name("fallback")
                        .short("f")
                        .long("with-fallback")
                        .help("Will output an incorrect parse tree if the parse does not find a correct one."),
                ),
        )
}

enum FileReader<R: Read> {
    Plain(R),
    GZipped(R)
}

impl<R: Read> FileReader<R> {
    fn new(file: R, zipped: bool) -> Self {
        if zipped { FileReader::GZipped(file) }
        else { FileReader::Plain(file) }
    }

    fn read(self) -> Result<String, ::std::io::Error> {
        let mut s = String::new();
        match self {
            FileReader::Plain(mut f) => {
                f.read_to_string(&mut s)?;
            },
            FileReader::GZipped(f) => {
                read::GzDecoder::new(f).read_to_string(&mut s)?;
            }
        }
        Ok(s)
    }
}

pub fn handle_sub_matches(submatches: &ArgMatches) {
    match submatches.subcommand() {
        ("extract", Some(params)) => {
            let grammar_is_gzipped = (params.is_present("gzipped") || params.is_present("disco-grammar")) && !params.is_present("ungzipped");
            let grammar_string = if let Some(path) = params.value_of("grammar") {
                FileReader::new(File::open(path).expect("could not open grammar file"), grammar_is_gzipped).read().expect("could not read grammar file")
            } else {
                FileReader::new(stdin(), grammar_is_gzipped).read().expect("could not read grammar file")
            };

            let gmr: Lcfrs<String, String, LogDomain<f64>> = if params.is_present("disco-grammar") {
                let dgmr: DiscoDopGrammar<_, _, _> = grammar_string.parse().expect("Could not parse grammar.");
                if params.is_present("disco-lexer") {
                    let lexer = File::open(params.value_of("disco-lexer").expect("Missing lexer file as argument")).expect("could not open lexer file");
                    let mut lexer_string = FileReader::new(lexer, grammar_is_gzipped).read().expect("could not read lexer file");
                    dgmr.with_lexer(lexer_string.parse().expect("Could not parse lexer file.")).into()
                } else {
                    dgmr.with_default_lexer().into()
                }
            } else {
                grammar_string
                    .parse()
                    .expect("Could not decode the grammar provided via stdin.")
            };

            bincode::serialize_into(
                &mut write::GzEncoder::new(stdout(), Compression::best()),
                &CSRepresentation::new(gmr),
                bincode::Infinite,
            ).unwrap()
        }

        ("parse", Some(params)) => {
            let mut word_strings = String::new();
            stdin()
                .read_to_string(&mut word_strings)
                .expect("Please pass a string to parse using stdin or using the `--word` option.");
            let k: usize = match params.value_of("k") {
                Some(n) => n.parse::<usize>().unwrap(),
                None => 1usize,
            };

            let beam: Capacity = match params.value_of("beam") {
                Some(b) => b.parse().unwrap(),
                None => Capacity::Infinite,
            };
            let candidates: Capacity = match params.value_of("candidates") {
                Some(c) => c.parse().unwrap(),
                None => Capacity::Infinite,
            };

            let csfile = File::open(params.value_of("csfile").unwrap()).unwrap();

            let csrep: CSRepresentation<String, String, LogDomain<f64>> =
                bincode::deserialize_from(&mut read::GzDecoder::new(csfile), bincode::Infinite)
                    .unwrap();

            for (i, sentence) in word_strings.lines().enumerate() {
                let (i, words) = split_line(sentence, params.is_present("with-lines"), i);
                let (words, negra_mode) = split_pos(words, params.is_present("with-pos"));

                if params.is_present("debugmode") {
                    let tuple = csrep.debug(words.as_slice(), beam, candidates);
                    eprint!("{} {} {} {} ", tuple.0, tuple.1, tuple.2, tuple.3);
                    match tuple.4 {
                        DebugResult::Parse(t, n) => {
                            eprintln!("parse {}", n);
                            println!("{}", to_negra(&t, i, negra_mode));
                        },
                        DebugResult::Fallback(t, n) => {
                            eprintln!("fallback {}", n);
                            println!("{}", to_negra(&t, i, negra_mode));
                        },
                        DebugResult::Noparse => {
                            eprintln!("noparse 0");
                            println!("{}", noparse(&words, i, negra_mode));
                        },
                    }
                } else {
                    let mut found_trees = false;
                    let (iterator, fallback) = csrep.generate(words.as_slice(), beam, candidates);
                    for derivation in iterator.take(k) {
                        found_trees = true;
                        println!(
                            "{}",
                            to_negra(
                                &derivation
                                    .into_iter()
                                    .map(|(k, v)| (k, v.clone()))
                                    .collect(),
                                i,
                                negra_mode.clone()
                            )
                        );
                    }
                    if !found_trees && params.is_present("fallback") {
                        if let Some(tree) = fallback {
                            println!("{}", to_negra(&tree, i, negra_mode));
                        } else {
                            println!("{}", noparse(&words, i, negra_mode));
                        }
                    } else if !found_trees {
                        println!("{}", noparse(&words, i, negra_mode));
                    }
                }
            }
        }
        _ => (),
    }
}

fn split_line<'a>(line: &'a str, with_line_number: bool, default_line_number: usize) -> (usize, impl Iterator<Item=&'a str> + 'a) {
    let mut word_iterator = line.split_whitespace();

    let line_number = if with_line_number {
        word_iterator.next()
                     .expect("missing line number")
                     .parse()
                     .expect("invalid line numer")
    } else {
        default_line_number
    };

    (line_number, word_iterator)
}

fn split_pos<'a>(words: impl Iterator<Item=&'a str> + 'a, with_pos: bool) -> (Vec<String>, DumpMode<String>) {
    if with_pos {
        let (pos, ws) = words.map(
            |wp| {
                let mut it = wp.rsplitn(2, '/');
                ( it.next().unwrap().to_string(),
                  it.next().expect("missing pos annotation").to_string()
                )
            }
        ).unzip();
        (pos, DumpMode::FromPos(ws))
    } else {
        (words.map(|s| s.to_string()).collect(), DumpMode::Default)
    }
}<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

extern crate clap;

use helpers::*;

use crowbook::{Result, Book, BookOptions, InfoLevel, set_lang};
use clap::ArgMatches;
use std::process::exit;
use std::fs::File;
use std::io;
use std::env;

/// Render a book to specific format
fn render_format(book: &mut Book, matches: &ArgMatches, format: &str) -> ! {
    if let Some(file) = matches.value_of("output") {
        match format {
            "epub" => book.options.set("output.epub", file).unwrap(),
            "tex" => book.options.set("output.tex", file).unwrap(),
            "html" => book.options.set("output.html", file).unwrap(),
            "pdf" => book.options.set("output.pdf", file).unwrap(),
            "odt" => book.options.set("output.odt", file).unwrap(),
            "proofread.html" => book.options.set("output.proofread.html", file).unwrap(),
            "proofread.pdf" => book.options.set("output.proofread.pdf", file).unwrap(),
            "proofread.tex" => book.options.set("output.proofead.tex", file).unwrap(),
            "proofread.hml_dir" => book.options.set("output.proofread.html_dir", file).unwrap(),
            _ => unreachable!(),
        };
    }

    let option = match format {
        "epub" => book.options.get_path("output.epub"),
        "tex" => book.options.get_path("output.tex"),
        "html" => book.options.get_path("output.html"),
        "pdf" => book.options.get_path("output.pdf"),
        "odt" => book.options.get_path("output.odt"),
        "proofread.html" => book.options.get_path("output.proofread.html"),
        "proofread.html_dir" => book.options.get_path("output.proofread.html_dir"),
        "proofread.pdf" => book.options.get_path("output.proofread.pdf"),
        "proofread.tex" => book.options.get_path("output.proofread.tex"),
        _ => unreachable!(),
    };
    let result = match option {
        Err(_) => {
            book.render_format_to(format, &mut io::stdout())
        }
        Ok(file) => {
            if let Ok(mut f) = File::create(&file) {
                book.render_format_to(format, &mut f)
            } else {
                print_error(&lformat!("Could not create file '{}'", file));
            }
        }
    };
    match result {
        Err(err) => print_error(&format!("{}", err)),
        Ok(_) => {
            exit(0);
        }
    }
}

pub fn try_main() -> Result<()> {
    let lang = get_lang()
        .or_else(|| {
            match env::var("LANG") {
                Ok(val) => {
                    Some(val)
                },
                Err(_) => None,
            }
        });
    if let Some(val) = lang {
        if val.starts_with("fr") {
            set_lang("fr");
        } else {
            set_lang("en");
        }
    }

    let (matches, help, version) = create_matches();

    if matches.is_present("list-options") {
        println!("{}", BookOptions::description(false));
        exit(0);
    }

    if matches.is_present("list-options-md") {
        println!("{}", BookOptions::description(true));
        exit(0);
    }

    if matches.is_present("print-template") {
        let template = matches.value_of("print-template").unwrap();
        let mut book = Book::new();
        set_book_options(&mut book, &matches);
        let result = book.get_template(template.as_ref());
        match result {
            Ok(s) => {
                println!("{}", s);
                exit(0);
            }
            Err(_) => print_error(&lformat!("{} is not a valid template name.", template)),
        }
    }

    if matches.is_present("help") {
        println!("{}", help);
        exit(0);
    }

    if matches.is_present("version") {
        println!("{}", version);
        exit(0);
    }

    if matches.is_present("create") {
        create_book(&matches);
    }

    if !matches.is_present("BOOK") {
        print_error(&lformat!("You must pass the file of a book configuration \
                               file.\n\n{}\n\nFor more information try --help.",
                              matches.usage()));
    }

    // ok to unwrap since clap checks it's there
    let s = matches.value_of("BOOK").unwrap();
    let verbosity = if matches.is_present("debug") {
        InfoLevel::Debug
    } else if matches.is_present("verbose") {
        InfoLevel::Warning
    } else if matches.is_present("quiet") {
        InfoLevel::Quiet
    } else {
        InfoLevel::Info
    };

    let mut book = Book::new();
    book.set_verbosity(verbosity)
        .set_options(&get_book_options(&matches));

    if matches.is_present("single") {
        book.load_markdown_file(s)?;
    } else {
        book.load_file(s)?;
    }

    set_book_options(&mut book, &matches);
    if let Some(format) = matches.value_of("to") {
        render_format(&mut book, &matches, format);
    } else {
        book.render_all();
    }

    Ok(())
}

pub fn real_main() {
    match try_main() {
        Err(err) => print_error(&format!("{}", err)),
        Ok(_) => (),
    }
}
<commit_msg>Use stdout if --output is set to '-'<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

extern crate clap;

use helpers::*;

use crowbook::{Result, Book, BookOptions, InfoLevel, set_lang};
use clap::ArgMatches;
use std::process::exit;
use std::fs::File;
use std::io;
use std::env;

/// Render a book to specific format
fn render_format(book: &mut Book, matches: &ArgMatches, format: &str) -> ! {
    let mut key = String::from("output.");
    key.push_str(format);
    let mut stdout = false;

    if let Some(file) = matches.value_of("output") {
        if file == "-" {
            stdout = true;
        } else {
            book.options.set(&key, file).unwrap();
        }
    }

    let res = book.options.get_path(&key);

    let result = match (res, stdout) {
        (Err(_), _) |
        (_, true)
        => {
            book.render_format_to(format, &mut io::stdout())
        }
        (Ok(file), false) => {
            if let Ok(mut f) = File::create(&file) {
                book.render_format_to(format, &mut f)
            } else {
                print_error(&lformat!("Could not create file '{}'", file));
            }
        }
    };

    match result {
        Err(err) => print_error(&format!("{}", err)),
        Ok(_) => {
            exit(0);
        }
    }
}

pub fn try_main() -> Result<()> {
    let lang = get_lang()
        .or_else(|| {
            match env::var("LANG") {
                Ok(val) => {
                    Some(val)
                },
                Err(_) => None,
            }
        });
    if let Some(val) = lang {
        if val.starts_with("fr") {
            set_lang("fr");
        } else {
            set_lang("en");
        }
    }

    let (matches, help, version) = create_matches();

    if matches.is_present("list-options") {
        println!("{}", BookOptions::description(false));
        exit(0);
    }

    if matches.is_present("list-options-md") {
        println!("{}", BookOptions::description(true));
        exit(0);
    }

    if matches.is_present("print-template") {
        let template = matches.value_of("print-template").unwrap();
        let mut book = Book::new();
        set_book_options(&mut book, &matches);
        let result = book.get_template(template.as_ref());
        match result {
            Ok(s) => {
                println!("{}", s);
                exit(0);
            }
            Err(_) => print_error(&lformat!("{} is not a valid template name.", template)),
        }
    }

    if matches.is_present("help") {
        println!("{}", help);
        exit(0);
    }

    if matches.is_present("version") {
        println!("{}", version);
        exit(0);
    }

    if matches.is_present("create") {
        create_book(&matches);
    }

    if !matches.is_present("BOOK") {
        print_error(&lformat!("You must pass the file of a book configuration \
                               file.\n\n{}\n\nFor more information try --help.",
                              matches.usage()));
    }

    // ok to unwrap since clap checks it's there
    let s = matches.value_of("BOOK").unwrap();
    let verbosity = if matches.is_present("debug") {
        InfoLevel::Debug
    } else if matches.is_present("verbose") {
        InfoLevel::Warning
    } else if matches.is_present("quiet") {
        InfoLevel::Quiet
    } else {
        InfoLevel::Info
    };

    let mut book = Book::new();
    book.set_verbosity(verbosity)
        .set_options(&get_book_options(&matches));

    if matches.is_present("single") {
        book.load_markdown_file(s)?;
    } else {
        book.load_file(s)?;
    }

    set_book_options(&mut book, &matches);
    if let Some(format) = matches.value_of("to") {
        render_format(&mut book, &matches, format);
    } else {
        book.render_all();
    }

    Ok(())
}

pub fn real_main() {
    match try_main() {
        Err(err) => print_error(&format!("{}", err)),
        Ok(_) => (),
    }
}
<|endoftext|>


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

pub extern mod core_foundation;
pub extern mod io_surface;
pub extern mod opengles;

use platform::opengles::gl2;

use core::cast::transmute;
use core::ptr::{null, to_unsafe_ptr};
use geom::size::Size2D;
use platform::io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow};
use platform::io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth};
use platform::io_surface::IOSurfaceMethods;
use platform::opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext};
use platform::opengles::cgl::{CGLLockContext, CGLSetCurrentContext, CGLTexImageIOSurface2D};
use platform::opengles::cgl::{kCGLNoError, kCGLPFACompliant, kCGLPFADoubleBuffer};
use platform::opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER};
use platform::opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR};
use platform::opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use platform::opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use platform::opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV};

use base::ShareContext;

// FIXME: This is not good.
#[link_args="-framework IOSurface -framework CoreFoundation"]
#[nolink]
extern {}

pub type Context = MacContext;

pub struct MacContext {
    surface: IOSurface,
    framebuffer: GLuint,
    texture: GLuint

    // FIXME: Needs drop.
}

pub fn init_cgl() -> CGLContextObj {
    // FIXME: We should expose some higher-level, safe abstractions inside the CGL module.
    unsafe {
        // Choose a pixel format.
        let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ];
        let pixel_format = null();
        let pixel_format_count = 1;
        let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]),
                                            to_unsafe_ptr(&pixel_format),
                                            to_unsafe_ptr(&pixel_format_count));
        assert!(gl_error == kCGLNoError);

        // Create the context.
        let cgl_context = null();
        let gl_error = CGLCreateContext(pixel_format, null(), to_unsafe_ptr(&cgl_context));
        assert!(gl_error == kCGLNoError);

        // Set the context.
        let gl_error = CGLSetCurrentContext(cgl_context);
        assert!(gl_error == kCGLNoError);

        // Lock the context.
        let gl_error = CGLLockContext(cgl_context);
        assert!(gl_error == kCGLNoError);

        return cgl_context;
    }
}

pub fn init_surface(size: Size2D<int>) -> IOSurface {
    use platform::core_foundation::boolean::CFBoolean;
    use number = platform::core_foundation::number::CFNumber::new;
    use string = platform::core_foundation::string::CFString::wrap_shared;

    // TODO: dictionary constructor should be less ridiculous.
    // Or, we could add bindings for mutable dictionaries.
    let k_width = string(kIOSurfaceWidth);
    let v_width = number(size.width as i32);

    let k_height = string(kIOSurfaceHeight);
    let v_height = number(size.height as i32);

    let k_bytes_per_row = string(kIOSurfaceBytesPerRow);
    let v_bytes_per_row = number(size.width as i32 * 4);

    let k_bytes_per_elem = string(kIOSurfaceBytesPerElement);
    let v_bytes_per_elem = number(4i32);

    let k_is_global = string(kIOSurfaceIsGlobal);
    let v_is_global = CFBoolean::true_value();

    io_surface::new(&core_foundation::dictionary::CFDictionary::new([
        (*k_width.contents.borrow_ref(),          *v_width.contents.borrow_type_ref()),
        (*k_height.contents.borrow_ref(),         *v_height.contents.borrow_type_ref()),
        (*k_bytes_per_row.contents.borrow_ref(),  *v_bytes_per_row.contents.borrow_type_ref()),
        (*k_bytes_per_elem.contents.borrow_ref(), *v_bytes_per_elem.contents.borrow_type_ref()),
        (*k_is_global.contents.borrow_ref(),      *v_is_global.contents.borrow_type_ref()),
    ]))
}

pub fn init_texture() -> GLuint {
    gl2::enable(TEXTURE_RECTANGLE_ARB);

    let texture = gl2::gen_textures(1)[0];
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, texture);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MAG_FILTER, LINEAR as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MIN_FILTER, NEAREST as GLint);
    return texture;
}

// Assumes the texture is already bound via gl2::bind_texture().
pub fn bind_surface_to_texture(context: &CGLContextObj, surface: &IOSurface, size: Size2D<int>) {
    // FIXME: There should be safe wrappers for this.
    unsafe {
        let gl_error = CGLTexImageIOSurface2D(*context,
                                              TEXTURE_RECTANGLE_ARB,
                                              RGBA as GLenum,
                                              size.width as GLsizei,
                                              size.height as GLsizei,
                                              BGRA as GLenum,
                                              UNSIGNED_INT_8_8_8_8_REV,
                                              transmute(copy surface.obj),
                                              0);
        assert!(gl_error == kCGLNoError);
    }
}

pub fn bind_texture_to_framebuffer(texture: GLuint) {
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, 0);
    gl2::framebuffer_texture_2d(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_RECTANGLE_ARB, texture, 0);
    assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE);
}

impl ShareContext for MacContext {
    fn new(size: Size2D<int>) -> MacContext {
        // Initialize CGL.
        let context = init_cgl();

        // Create the surface.
        let surface = init_surface(copy size);

        // Create a framebuffer.
        let framebuffer = gl2::gen_framebuffers(1)[0];
        gl2::bind_framebuffer(FRAMEBUFFER, framebuffer);

        // Create and bind to the texture.
        let texture = init_texture();
        bind_surface_to_texture(&context, &surface, size);

        // Bind the texture to the framebuffer.
        bind_texture_to_framebuffer(texture);

        MacContext {
            surface: surface,
            framebuffer: framebuffer,
            texture: texture
        }
    }

    fn flush(&self) {
        gl2::finish();
    }

    fn id(&self) -> int {
        self.surface.get_id() as int
    }
}

<commit_msg>Update for language changes<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub extern mod core_foundation;
pub extern mod io_surface;
pub extern mod opengles;

use platform::opengles::gl2;

use std::cast::transmute;
use std::ptr::{null, to_unsafe_ptr};
use geom::size::Size2D;
use platform::io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow};
use platform::io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth};
use platform::io_surface::IOSurfaceMethods;
use platform::opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext};
use platform::opengles::cgl::{CGLLockContext, CGLSetCurrentContext, CGLTexImageIOSurface2D};
use platform::opengles::cgl::{kCGLNoError, kCGLPFACompliant, kCGLPFADoubleBuffer};
use platform::opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER};
use platform::opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR};
use platform::opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use platform::opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use platform::opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV};

use base::ShareContext;

// FIXME: This is not good.
#[link_args="-framework IOSurface -framework CoreFoundation"]
#[nolink]
extern {}

pub type Context = MacContext;

pub struct MacContext {
    surface: IOSurface,
    framebuffer: GLuint,
    texture: GLuint

    // FIXME: Needs drop.
}

pub fn init_cgl() -> CGLContextObj {
    // FIXME: We should expose some higher-level, safe abstractions inside the CGL module.
    unsafe {
        // Choose a pixel format.
        let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ];
        let pixel_format = null();
        let pixel_format_count = 1;
        let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]),
                                            to_unsafe_ptr(&pixel_format),
                                            to_unsafe_ptr(&pixel_format_count));
        assert!(gl_error == kCGLNoError);

        // Create the context.
        let cgl_context = null();
        let gl_error = CGLCreateContext(pixel_format, null(), to_unsafe_ptr(&cgl_context));
        assert!(gl_error == kCGLNoError);

        // Set the context.
        let gl_error = CGLSetCurrentContext(cgl_context);
        assert!(gl_error == kCGLNoError);

        // Lock the context.
        let gl_error = CGLLockContext(cgl_context);
        assert!(gl_error == kCGLNoError);

        return cgl_context;
    }
}

pub fn init_surface(size: Size2D<int>) -> IOSurface {
    use platform::core_foundation::boolean::CFBoolean;
    use platform::core_foundation::string::CFString;
    use platform::core_foundation::number::CFNumber;

    // TODO: dictionary constructor should be less ridiculous.
    // Or, we could add bindings for mutable dictionaries.
    let k_width = CFString::wrap_shared(kIOSurfaceWidth);
    let v_width = CFNumber::new(size.width as i32);

    let k_height = CFString::wrap_shared(kIOSurfaceHeight);
    let v_height = CFNumber::new(size.height as i32);

    let k_bytes_per_row = CFString::wrap_shared(kIOSurfaceBytesPerRow);
    let v_bytes_per_row = CFNumber::new(size.width as i32 * 4);

    let k_bytes_per_elem = CFString::wrap_shared(kIOSurfaceBytesPerElement);
    let v_bytes_per_elem = CFNumber::new(4i32);

    let k_is_global = CFString::wrap_shared(kIOSurfaceIsGlobal);
    let v_is_global = CFBoolean::true_value();

    io_surface::new(&core_foundation::dictionary::CFDictionary::new([
        (*k_width.contents.borrow_ref(),          *v_width.contents.borrow_type_ref()),
        (*k_height.contents.borrow_ref(),         *v_height.contents.borrow_type_ref()),
        (*k_bytes_per_row.contents.borrow_ref(),  *v_bytes_per_row.contents.borrow_type_ref()),
        (*k_bytes_per_elem.contents.borrow_ref(), *v_bytes_per_elem.contents.borrow_type_ref()),
        (*k_is_global.contents.borrow_ref(),      *v_is_global.contents.borrow_type_ref()),
    ]))
}

pub fn init_texture() -> GLuint {
    gl2::enable(TEXTURE_RECTANGLE_ARB);

    let texture = gl2::gen_textures(1)[0];
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, texture);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MAG_FILTER, LINEAR as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MIN_FILTER, NEAREST as GLint);
    return texture;
}

// Assumes the texture is already bound via gl2::bind_texture().
pub fn bind_surface_to_texture(context: &CGLContextObj, surface: &IOSurface, size: Size2D<int>) {
    // FIXME: There should be safe wrappers for this.
    unsafe {
        let gl_error = CGLTexImageIOSurface2D(*context,
                                              TEXTURE_RECTANGLE_ARB,
                                              RGBA as GLenum,
                                              size.width as GLsizei,
                                              size.height as GLsizei,
                                              BGRA as GLenum,
                                              UNSIGNED_INT_8_8_8_8_REV,
                                              transmute(copy surface.obj),
                                              0);
        assert!(gl_error == kCGLNoError);
    }
}

pub fn bind_texture_to_framebuffer(texture: GLuint) {
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, 0);
    gl2::framebuffer_texture_2d(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_RECTANGLE_ARB, texture, 0);
    assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE);
}

impl ShareContext for MacContext {
    fn new(size: Size2D<int>) -> MacContext {
        // Initialize CGL.
        let context = init_cgl();

        // Create the surface.
        let surface = init_surface(copy size);

        // Create a framebuffer.
        let framebuffer = gl2::gen_framebuffers(1)[0];
        gl2::bind_framebuffer(FRAMEBUFFER, framebuffer);

        // Create and bind to the texture.
        let texture = init_texture();
        bind_surface_to_texture(&context, &surface, size);

        // Bind the texture to the framebuffer.
        bind_texture_to_framebuffer(texture);

        MacContext {
            surface: surface,
            framebuffer: framebuffer,
            texture: texture
        }
    }

    fn flush(&self) {
        gl2::finish();
    }

    fn id(&self) -> int {
        self.surface.get_id() as int
    }
}

<|endoftext|>


<commit_before>// Copyright (c) The Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! aptos_channel provides an mpsc channel which has two ends `aptos_channel::Receiver`
//! and `aptos_channel::Sender` similar to existing mpsc data structures.
//! What makes it different from existing mpsc channels is that we have full control
//! over how the internal queueing in the channel happens and how we schedule messages
//! to be sent out from this channel.
//! Internally, it uses the `PerKeyQueue` to store messages
use crate::message_queues::{PerKeyQueue, QueueStyle};
use anyhow::{ensure, Result};
use aptos_infallible::{Mutex, NonZeroUsize};
use aptos_metrics::IntCounterVec;
use futures::{
    channel::oneshot,
    stream::{FusedStream, Stream},
};
use std::{
    fmt::{Debug, Formatter},
    hash::Hash,
    pin::Pin,
    sync::Arc,
    task::{Context, Poll, Waker},
};

/// SharedState is a data structure private to this module which is
/// shared by the `Receiver` and any `Sender`s.
#[derive(Debug)]
struct SharedState<K: Eq + Hash + Clone, M> {
    /// The internal queue of messages in this channel.
    internal_queue: PerKeyQueue<K, (M, Option<oneshot::Sender<ElementStatus<M>>>)>,
    /// The `Receiver` registers its `Waker` in this slot when the queue is empty.
    /// `Sender`s will try to wake the `Receiver` (if any) when they push a new
    /// item onto the queue. The last live `Sender` will also wake the `Receiver`
    /// as it's tearing down so the `Receiver` can gracefully drain and shutdown
    /// the channel.
    waker: Option<Waker>,
    /// The number of active senders. When this value reaches 0, all senders have
    /// been dropped.
    num_senders: usize,
    /// A boolean which tracks whether the receiver has dropped.
    receiver_dropped: bool,
    /// A boolean which tracks whether the stream has terminated. A stream is
    /// considered terminated when sender has dropped and we have drained everything
    /// inside our internal queue.
    stream_terminated: bool,
}

/// The sending end of the aptos_channel.
#[derive(Debug)]
pub struct Sender<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

/// The status of an element inserted into a aptos_channel. If the element is successfully
/// dequeued, ElementStatus::Dequeued is sent to the sender. If it is dropped
/// ElementStatus::Dropped is sent to the sender along with the dropped element.
pub enum ElementStatus<M> {
    Dequeued,
    Dropped(M),
}

impl<M: PartialEq> PartialEq for ElementStatus<M> {
    fn eq(&self, other: &ElementStatus<M>) -> bool {
        match (self, other) {
            (ElementStatus::Dequeued, ElementStatus::Dequeued) => true,
            (ElementStatus::Dropped(a), ElementStatus::Dropped(b)) => a.eq(b),
            _ => false,
        }
    }
}

impl<M: Debug> Debug for ElementStatus<M> {
    fn fmt(&self, f: &mut Formatter) -> std::result::Result<(), std::fmt::Error> {
        match self {
            ElementStatus::Dequeued => write!(f, "Dequeued"),
            ElementStatus::Dropped(v) => write!(f, "Dropped({:?})", v),
        }
    }
}

impl<K: Eq + Hash + Clone, M> Sender<K, M> {
    /// This adds the message into the internal queue data structure. This is a
    /// synchronous call.
    pub fn push(&self, key: K, message: M) -> Result<()> {
        self.push_with_feedback(key, message, None)
    }

    /// Same as `push`, but this function also accepts a oneshot::Sender over which the sender can
    /// be notified when the message eventually gets delivered or dropped.
    pub fn push_with_feedback(
        &self,
        key: K,
        message: M,
        status_ch: Option<oneshot::Sender<ElementStatus<M>>>,
    ) -> Result<()> {
        let mut shared_state = self.shared_state.lock();
        ensure!(!shared_state.receiver_dropped, "Channel is closed");
        debug_assert!(shared_state.num_senders > 0);

        let dropped = shared_state.internal_queue.push(key, (message, status_ch));
        // If this or an existing message had to be dropped because of the queue being full, we
        // notify the corresponding status channel if it was registered.
        if let Some((dropped_val, Some(dropped_status_ch))) = dropped {
            // Ignore errors.
            let _err = dropped_status_ch.send(ElementStatus::Dropped(dropped_val));
        }
        if let Some(w) = shared_state.waker.take() {
            w.wake();
        }
        Ok(())
    }
}

impl<K: Eq + Hash + Clone, M> Clone for Sender<K, M> {
    fn clone(&self) -> Self {
        let shared_state = self.shared_state.clone();
        {
            let mut shared_state_lock = shared_state.lock();
            debug_assert!(shared_state_lock.num_senders > 0);
            shared_state_lock.num_senders += 1;
        }
        Sender { shared_state }
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Sender<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock();

        debug_assert!(shared_state.num_senders > 0);
        shared_state.num_senders -= 1;

        if shared_state.num_senders == 0 {
            if let Some(waker) = shared_state.waker.take() {
                waker.wake();
            }
        }
    }
}

/// The receiving end of the aptos_channel.
#[derive(Debug)]
pub struct Receiver<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

impl<K: Eq + Hash + Clone, M> Receiver<K, M> {
    /// Removes all the previously sent transactions that have not been consumed yet and cleans up
    /// the internal queue structure (GC of the previous keys).
    pub fn clear(&self) {
        let mut shared_state = self.shared_state.lock();
        shared_state.internal_queue.clear();
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Receiver<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock();
        debug_assert!(!shared_state.receiver_dropped);
        shared_state.receiver_dropped = true;
    }
}

impl<K: Eq + Hash + Clone, M> Stream for Receiver<K, M> {
    type Item = M;
    /// poll_next checks whether there is something ready for consumption from the internal
    /// queue. If there is, then it returns immediately. If the internal_queue is empty,
    /// it sets the waker passed to it by the scheduler/executor and returns Pending
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut shared_state = self.shared_state.lock();
        if let Some((val, status_ch)) = shared_state.internal_queue.pop() {
            if let Some(status_ch) = status_ch {
                let _err = status_ch.send(ElementStatus::Dequeued);
            }
            Poll::Ready(Some(val))
        // all senders have been dropped (and so the stream is terminated)
        } else if shared_state.num_senders == 0 {
            shared_state.stream_terminated = true;
            Poll::Ready(None)
        } else {
            shared_state.waker = Some(cx.waker().clone());
            Poll::Pending
        }
    }
}

impl<K: Eq + Hash + Clone, M> FusedStream for Receiver<K, M> {
    fn is_terminated(&self) -> bool {
        self.shared_state.lock().stream_terminated
    }
}

/// Configuration for a new aptos_channel queue.
#[derive(Clone, Copy)]
pub struct Config {
    pub queue_style: QueueStyle,
    pub max_capacity: usize,
    pub counters: Option<&'static IntCounterVec>,
}

impl Config {
    /// The aptos_channel has a "sub-queue" per key. The `max_capacity` controls
    /// the capacity of each "sub-queue"; when the queues exceed the max
    /// capacity the messages will be dropped according to the queue style/eviction
    /// policy.
    pub fn new(max_capacity: usize) -> Self {
        Self {
            queue_style: QueueStyle::FIFO,
            max_capacity,
            counters: None,
        }
    }

    /// The queue's push/pop and eviction behavior. Defaults to FIFO.
    pub fn queue_style(mut self, queue_style: QueueStyle) -> Self {
        self.queue_style = queue_style;
        self
    }

    /// Optional prometheus counters for this queue, which keep track of items
    /// in the queue and # dropped items. Defaults to no counters.
    pub fn counters(mut self, counters: &'static IntCounterVec) -> Self {
        self.counters = Some(counters);
        self
    }

    pub fn build<K: Eq + Hash + Clone, M>(self) -> (Sender<K, M>, Receiver<K, M>) {
        new(self.queue_style, self.max_capacity, self.counters)
    }
}

/// Create a new Diem Channel and returns the two ends of the channel.
pub fn new<K: Eq + Hash + Clone, M>(
    queue_style: QueueStyle,
    max_queue_size_per_key: usize,
    counters: Option<&'static IntCounterVec>,
) -> (Sender<K, M>, Receiver<K, M>) {
    let max_queue_size_per_key =
        NonZeroUsize!(max_queue_size_per_key, "aptos_channel cannot be of size 0");
    let shared_state = Arc::new(Mutex::new(SharedState {
        internal_queue: PerKeyQueue::new(queue_style, max_queue_size_per_key, counters),
        waker: None,
        num_senders: 1,
        receiver_dropped: false,
        stream_terminated: false,
    }));
    let shared_state_clone = Arc::clone(&shared_state);
    (
        Sender { shared_state },
        Receiver {
            shared_state: shared_state_clone,
        },
    )
}
<commit_msg>[channel] Diem -> Aptos<commit_after>// Copyright (c) The Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! aptos_channel provides an mpsc channel which has two ends `aptos_channel::Receiver`
//! and `aptos_channel::Sender` similar to existing mpsc data structures.
//! What makes it different from existing mpsc channels is that we have full control
//! over how the internal queueing in the channel happens and how we schedule messages
//! to be sent out from this channel.
//! Internally, it uses the `PerKeyQueue` to store messages
use crate::message_queues::{PerKeyQueue, QueueStyle};
use anyhow::{ensure, Result};
use aptos_infallible::{Mutex, NonZeroUsize};
use aptos_metrics::IntCounterVec;
use futures::{
    channel::oneshot,
    stream::{FusedStream, Stream},
};
use std::{
    fmt::{Debug, Formatter},
    hash::Hash,
    pin::Pin,
    sync::Arc,
    task::{Context, Poll, Waker},
};

/// SharedState is a data structure private to this module which is
/// shared by the `Receiver` and any `Sender`s.
#[derive(Debug)]
struct SharedState<K: Eq + Hash + Clone, M> {
    /// The internal queue of messages in this channel.
    internal_queue: PerKeyQueue<K, (M, Option<oneshot::Sender<ElementStatus<M>>>)>,
    /// The `Receiver` registers its `Waker` in this slot when the queue is empty.
    /// `Sender`s will try to wake the `Receiver` (if any) when they push a new
    /// item onto the queue. The last live `Sender` will also wake the `Receiver`
    /// as it's tearing down so the `Receiver` can gracefully drain and shutdown
    /// the channel.
    waker: Option<Waker>,
    /// The number of active senders. When this value reaches 0, all senders have
    /// been dropped.
    num_senders: usize,
    /// A boolean which tracks whether the receiver has dropped.
    receiver_dropped: bool,
    /// A boolean which tracks whether the stream has terminated. A stream is
    /// considered terminated when sender has dropped and we have drained everything
    /// inside our internal queue.
    stream_terminated: bool,
}

/// The sending end of the aptos_channel.
#[derive(Debug)]
pub struct Sender<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

/// The status of an element inserted into a aptos_channel. If the element is successfully
/// dequeued, ElementStatus::Dequeued is sent to the sender. If it is dropped
/// ElementStatus::Dropped is sent to the sender along with the dropped element.
pub enum ElementStatus<M> {
    Dequeued,
    Dropped(M),
}

impl<M: PartialEq> PartialEq for ElementStatus<M> {
    fn eq(&self, other: &ElementStatus<M>) -> bool {
        match (self, other) {
            (ElementStatus::Dequeued, ElementStatus::Dequeued) => true,
            (ElementStatus::Dropped(a), ElementStatus::Dropped(b)) => a.eq(b),
            _ => false,
        }
    }
}

impl<M: Debug> Debug for ElementStatus<M> {
    fn fmt(&self, f: &mut Formatter) -> std::result::Result<(), std::fmt::Error> {
        match self {
            ElementStatus::Dequeued => write!(f, "Dequeued"),
            ElementStatus::Dropped(v) => write!(f, "Dropped({:?})", v),
        }
    }
}

impl<K: Eq + Hash + Clone, M> Sender<K, M> {
    /// This adds the message into the internal queue data structure. This is a
    /// synchronous call.
    pub fn push(&self, key: K, message: M) -> Result<()> {
        self.push_with_feedback(key, message, None)
    }

    /// Same as `push`, but this function also accepts a oneshot::Sender over which the sender can
    /// be notified when the message eventually gets delivered or dropped.
    pub fn push_with_feedback(
        &self,
        key: K,
        message: M,
        status_ch: Option<oneshot::Sender<ElementStatus<M>>>,
    ) -> Result<()> {
        let mut shared_state = self.shared_state.lock();
        ensure!(!shared_state.receiver_dropped, "Channel is closed");
        debug_assert!(shared_state.num_senders > 0);

        let dropped = shared_state.internal_queue.push(key, (message, status_ch));
        // If this or an existing message had to be dropped because of the queue being full, we
        // notify the corresponding status channel if it was registered.
        if let Some((dropped_val, Some(dropped_status_ch))) = dropped {
            // Ignore errors.
            let _err = dropped_status_ch.send(ElementStatus::Dropped(dropped_val));
        }
        if let Some(w) = shared_state.waker.take() {
            w.wake();
        }
        Ok(())
    }
}

impl<K: Eq + Hash + Clone, M> Clone for Sender<K, M> {
    fn clone(&self) -> Self {
        let shared_state = self.shared_state.clone();
        {
            let mut shared_state_lock = shared_state.lock();
            debug_assert!(shared_state_lock.num_senders > 0);
            shared_state_lock.num_senders += 1;
        }
        Sender { shared_state }
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Sender<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock();

        debug_assert!(shared_state.num_senders > 0);
        shared_state.num_senders -= 1;

        if shared_state.num_senders == 0 {
            if let Some(waker) = shared_state.waker.take() {
                waker.wake();
            }
        }
    }
}

/// The receiving end of the aptos_channel.
#[derive(Debug)]
pub struct Receiver<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

impl<K: Eq + Hash + Clone, M> Receiver<K, M> {
    /// Removes all the previously sent transactions that have not been consumed yet and cleans up
    /// the internal queue structure (GC of the previous keys).
    pub fn clear(&self) {
        let mut shared_state = self.shared_state.lock();
        shared_state.internal_queue.clear();
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Receiver<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock();
        debug_assert!(!shared_state.receiver_dropped);
        shared_state.receiver_dropped = true;
    }
}

impl<K: Eq + Hash + Clone, M> Stream for Receiver<K, M> {
    type Item = M;
    /// poll_next checks whether there is something ready for consumption from the internal
    /// queue. If there is, then it returns immediately. If the internal_queue is empty,
    /// it sets the waker passed to it by the scheduler/executor and returns Pending
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut shared_state = self.shared_state.lock();
        if let Some((val, status_ch)) = shared_state.internal_queue.pop() {
            if let Some(status_ch) = status_ch {
                let _err = status_ch.send(ElementStatus::Dequeued);
            }
            Poll::Ready(Some(val))
        // all senders have been dropped (and so the stream is terminated)
        } else if shared_state.num_senders == 0 {
            shared_state.stream_terminated = true;
            Poll::Ready(None)
        } else {
            shared_state.waker = Some(cx.waker().clone());
            Poll::Pending
        }
    }
}

impl<K: Eq + Hash + Clone, M> FusedStream for Receiver<K, M> {
    fn is_terminated(&self) -> bool {
        self.shared_state.lock().stream_terminated
    }
}

/// Configuration for a new aptos_channel queue.
#[derive(Clone, Copy)]
pub struct Config {
    pub queue_style: QueueStyle,
    pub max_capacity: usize,
    pub counters: Option<&'static IntCounterVec>,
}

impl Config {
    /// The aptos_channel has a "sub-queue" per key. The `max_capacity` controls
    /// the capacity of each "sub-queue"; when the queues exceed the max
    /// capacity the messages will be dropped according to the queue style/eviction
    /// policy.
    pub fn new(max_capacity: usize) -> Self {
        Self {
            queue_style: QueueStyle::FIFO,
            max_capacity,
            counters: None,
        }
    }

    /// The queue's push/pop and eviction behavior. Defaults to FIFO.
    pub fn queue_style(mut self, queue_style: QueueStyle) -> Self {
        self.queue_style = queue_style;
        self
    }

    /// Optional prometheus counters for this queue, which keep track of items
    /// in the queue and # dropped items. Defaults to no counters.
    pub fn counters(mut self, counters: &'static IntCounterVec) -> Self {
        self.counters = Some(counters);
        self
    }

    pub fn build<K: Eq + Hash + Clone, M>(self) -> (Sender<K, M>, Receiver<K, M>) {
        new(self.queue_style, self.max_capacity, self.counters)
    }
}

/// Create a new Channel and returns the two ends of the channel.
pub fn new<K: Eq + Hash + Clone, M>(
    queue_style: QueueStyle,
    max_queue_size_per_key: usize,
    counters: Option<&'static IntCounterVec>,
) -> (Sender<K, M>, Receiver<K, M>) {
    let max_queue_size_per_key =
        NonZeroUsize!(max_queue_size_per_key, "aptos_channel cannot be of size 0");
    let shared_state = Arc::new(Mutex::new(SharedState {
        internal_queue: PerKeyQueue::new(queue_style, max_queue_size_per_key, counters),
        waker: None,
        num_senders: 1,
        receiver_dropped: false,
        stream_terminated: false,
    }));
    let shared_state_clone = Arc::clone(&shared_state);
    (
        Sender { shared_state },
        Receiver {
            shared_state: shared_state_clone,
        },
    )
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2018 Fabian Schuiki

//! Utilities to implement diagnostics and error reporting facilities.

use source::Span;
use std::fmt;

/// A handler deals with errors.
#[derive(Debug)]
pub struct Handler {
}

pub static DUMMY_HANDLER: Handler = Handler{};

/// Used to emit structured error messages.
#[must_use]
#[derive(Clone, Debug)]
pub struct DiagnosticBuilder<'a> {
	pub handler: &'a Handler,
	pub message: String,
}

/// A diagnostic result type. Either carries the result `T` in the Ok variant,
/// or an assembled diagnostic in the Err variant.
pub type DiagResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;

/// Emits diagnostic messages.
pub trait DiagEmitter {
	/// Emit a diagnostic message.
	fn emit(&self, diag: DiagBuilder2);
}

#[must_use]
#[derive(Clone, Debug)]
pub struct DiagBuilder2 {
	pub severity: Severity,
	pub message: String,
	pub segments: Vec<DiagSegment>,
}

#[derive(Clone, Debug)]
pub enum DiagSegment {
	Span(Span),
	Note(String),
}

/// A diagnostic result type. Either carries the result `T` in the Ok variant,
/// or an assembled diagnostic in the Err variant.
pub type DiagResult2<T> = Result<T, DiagBuilder2>;

impl DiagBuilder2 {
	pub fn new<S: Into<String>>(severity: Severity, message: S) -> DiagBuilder2 {
		DiagBuilder2 {
			severity: severity,
			message: message.into(),
			segments: Vec::new(),
		}
	}

	pub fn bug<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Bug, message)
	}

	pub fn fatal<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Fatal, message)
	}

	pub fn error<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Error, message)
	}

	pub fn warning<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Warning, message)
	}

	pub fn note<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Note, message)
	}

	pub fn segment(self, segment: DiagSegment) -> DiagBuilder2 {
		let mut segments = self.segments;
		segments.push(segment);
		DiagBuilder2 {
			segments: segments,
			..self
		}
	}

	pub fn span<S: Into<Span>>(self, span: S) -> DiagBuilder2 {
		self.segment(DiagSegment::Span(span.into()))
	}

	pub fn add_note<S: Into<String>>(self, message: S) -> DiagBuilder2 {
		self.segment(DiagSegment::Note(message.into()))
	}

	pub fn get_severity(&self) -> Severity {
		self.severity
	}

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

	pub fn get_segments(&self) -> &[DiagSegment] {
		&self.segments
	}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Severity {
	Note,
	Warning,
	Error,
	Fatal,
	Bug,
}

impl Severity {
	pub fn to_str(self) -> &'static str {
		match self {
			Severity::Fatal => "fatal",
			Severity::Error => "error",
			Severity::Warning => "warning",
			Severity::Note => "note",
			Severity::Bug => "compiler bug",
		}
	}
}

impl fmt::Display for Severity {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", self.to_str())
	}
}

impl fmt::Display for DiagBuilder2 {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		let mut colorcode = match self.get_severity() {
			Severity::Bug | Severity::Fatal | Severity::Error => "\x1B[31;1m",
			Severity::Warning => "\x1B[33;1m",
			Severity::Note => "\x1B[36;1m",
		};
		write!(f, "{}{}:\x1B[m\x1B[1m {}\x1B[m\n", colorcode, self.get_severity(), self.get_message())?;

		for segment in &self.segments {
			match *segment {
				DiagSegment::Span(sp) => {
					let c = sp.source.get_content();
					let mut iter = c.extract_iter(0, sp.begin);

					// Look for the start of the line.
					let mut col = 1;
					let mut line = 1;
					let mut line_offset = sp.begin;
					while let Some(c) = iter.next_back() {
						match c.1 {
							'\n' => { line += 1; break; },
							'\r' => continue,
							_ => {
								col += 1;
								line_offset = c.0;
							}
						}
					}

					// Count the number of lines.
					while let Some(c) = iter.next_back() {
						if c.1 == '\n' {
							line += 1;
						}
					}

					// Print the line in question.
					let text: String = c.iter_from(line_offset).map(|x| x.1).take_while(|c| *c != '\n' && *c != '\r').collect();
					write!(f, "  --> {}:{}:{}-{}:\n", sp.source.get_path(), line, col, col + sp.extract().len())?;
					write!(f, "   | \n")?;
					write!(f, "   | ")?;
					for (mut i,c) in text.char_indices() {
						i += line_offset;
						if sp.begin != sp.end {
							if i == sp.begin { write!(f, "{}", colorcode)?; }
							if i == sp.end { write!(f, "\x1B[m")?; }
						}
						match c {
							'\t' => write!(f, "    ")?,
							c => write!(f, "{}", c)?,
						}
					}
					write!(f, "\x1B[m\n")?;
					write!(f, "   | ")?;

					// Print the caret markers for the line in question.
					let mut pd = ' ';
					for (mut i,c) in text.char_indices() {
						i += line_offset;
						let d = if (i >= sp.begin && i < sp.end) || (i == sp.begin && sp.begin == sp.end) {
							'^'
						} else {
							' '
						};
						if d != pd {
							write!(f, "{}", if d == ' ' {"\x1B[m"} else {colorcode})?;
						}
						pd = d;
						match c {
							'\t' => write!(f, "{}{}{}{}", d, d, d, d)?,
							_ => write!(f, "{}", d)?,
						}
					}
					write!(f, "\x1B[m\n")?;
					colorcode = "\x1B[1m";
				}
				DiagSegment::Note(ref message) => write!(f, "\x1B[1mnote:\x1B[m {}\n", message)?,
			}
		}

		if self.get_severity() == Severity::Bug {
			write!(f, "\nYou have encountered a compiler bug. Sorry about that! We would appreciate if you open an issue [1] and describe how you triggered the bug, together with a minimal snippet of code to reproduce it. Thanks!\n")?;
			write!(f, "[1]: https://github.com/fabianschuiki/moore\n")?;
		}

		Ok(())
	}
}
<commit_msg>common: add debugln macro<commit_after>// Copyright (c) 2016-2018 Fabian Schuiki

//! Utilities to implement diagnostics and error reporting facilities.

use source::Span;
use std::fmt;

/// Print debug information. Omitted in release builds.
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! debugln {
    ($($arg:tt)*) => { eprintln!("\x1B[34;1mdebug:\x1B[m {}", format!($($arg)*)); }
}

/// Print debug information. Omitted in release builds.
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! debugln {
    ($($arg:tt)*) => {}
}

/// A handler deals with errors.
#[derive(Debug)]
pub struct Handler {
}

pub static DUMMY_HANDLER: Handler = Handler{};

/// Used to emit structured error messages.
#[must_use]
#[derive(Clone, Debug)]
pub struct DiagnosticBuilder<'a> {
	pub handler: &'a Handler,
	pub message: String,
}

/// A diagnostic result type. Either carries the result `T` in the Ok variant,
/// or an assembled diagnostic in the Err variant.
pub type DiagResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;

/// Emits diagnostic messages.
pub trait DiagEmitter {
	/// Emit a diagnostic message.
	fn emit(&self, diag: DiagBuilder2);
}

#[must_use]
#[derive(Clone, Debug)]
pub struct DiagBuilder2 {
	pub severity: Severity,
	pub message: String,
	pub segments: Vec<DiagSegment>,
}

#[derive(Clone, Debug)]
pub enum DiagSegment {
	Span(Span),
	Note(String),
}

/// A diagnostic result type. Either carries the result `T` in the Ok variant,
/// or an assembled diagnostic in the Err variant.
pub type DiagResult2<T> = Result<T, DiagBuilder2>;

impl DiagBuilder2 {
	pub fn new<S: Into<String>>(severity: Severity, message: S) -> DiagBuilder2 {
		DiagBuilder2 {
			severity: severity,
			message: message.into(),
			segments: Vec::new(),
		}
	}

	pub fn bug<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Bug, message)
	}

	pub fn fatal<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Fatal, message)
	}

	pub fn error<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Error, message)
	}

	pub fn warning<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Warning, message)
	}

	pub fn note<S: Into<String>>(message: S) -> DiagBuilder2 {
		DiagBuilder2::new(Severity::Note, message)
	}

	pub fn segment(self, segment: DiagSegment) -> DiagBuilder2 {
		let mut segments = self.segments;
		segments.push(segment);
		DiagBuilder2 {
			segments: segments,
			..self
		}
	}

	pub fn span<S: Into<Span>>(self, span: S) -> DiagBuilder2 {
		self.segment(DiagSegment::Span(span.into()))
	}

	pub fn add_note<S: Into<String>>(self, message: S) -> DiagBuilder2 {
		self.segment(DiagSegment::Note(message.into()))
	}

	pub fn get_severity(&self) -> Severity {
		self.severity
	}

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

	pub fn get_segments(&self) -> &[DiagSegment] {
		&self.segments
	}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Severity {
	Note,
	Warning,
	Error,
	Fatal,
	Bug,
}

impl Severity {
	pub fn to_str(self) -> &'static str {
		match self {
			Severity::Fatal => "fatal",
			Severity::Error => "error",
			Severity::Warning => "warning",
			Severity::Note => "note",
			Severity::Bug => "compiler bug",
		}
	}
}

impl fmt::Display for Severity {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", self.to_str())
	}
}

impl fmt::Display for DiagBuilder2 {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		let mut colorcode = match self.get_severity() {
			Severity::Bug | Severity::Fatal | Severity::Error => "\x1B[31;1m",
			Severity::Warning => "\x1B[33;1m",
			Severity::Note => "\x1B[36;1m",
		};
		write!(f, "{}{}:\x1B[m\x1B[1m {}\x1B[m\n", colorcode, self.get_severity(), self.get_message())?;

		for segment in &self.segments {
			match *segment {
				DiagSegment::Span(sp) => {
					let c = sp.source.get_content();
					let mut iter = c.extract_iter(0, sp.begin);

					// Look for the start of the line.
					let mut col = 1;
					let mut line = 1;
					let mut line_offset = sp.begin;
					while let Some(c) = iter.next_back() {
						match c.1 {
							'\n' => { line += 1; break; },
							'\r' => continue,
							_ => {
								col += 1;
								line_offset = c.0;
							}
						}
					}

					// Count the number of lines.
					while let Some(c) = iter.next_back() {
						if c.1 == '\n' {
							line += 1;
						}
					}

					// Print the line in question.
					let text: String = c.iter_from(line_offset).map(|x| x.1).take_while(|c| *c != '\n' && *c != '\r').collect();
					write!(f, "  --> {}:{}:{}-{}:\n", sp.source.get_path(), line, col, col + sp.extract().len())?;
					write!(f, "   | \n")?;
					write!(f, "   | ")?;
					for (mut i,c) in text.char_indices() {
						i += line_offset;
						if sp.begin != sp.end {
							if i == sp.begin { write!(f, "{}", colorcode)?; }
							if i == sp.end { write!(f, "\x1B[m")?; }
						}
						match c {
							'\t' => write!(f, "    ")?,
							c => write!(f, "{}", c)?,
						}
					}
					write!(f, "\x1B[m\n")?;
					write!(f, "   | ")?;

					// Print the caret markers for the line in question.
					let mut pd = ' ';
					for (mut i,c) in text.char_indices() {
						i += line_offset;
						let d = if (i >= sp.begin && i < sp.end) || (i == sp.begin && sp.begin == sp.end) {
							'^'
						} else {
							' '
						};
						if d != pd {
							write!(f, "{}", if d == ' ' {"\x1B[m"} else {colorcode})?;
						}
						pd = d;
						match c {
							'\t' => write!(f, "{}{}{}{}", d, d, d, d)?,
							_ => write!(f, "{}", d)?,
						}
					}
					write!(f, "\x1B[m\n")?;
					colorcode = "\x1B[1m";
				}
				DiagSegment::Note(ref message) => write!(f, "\x1B[1mnote:\x1B[m {}\n", message)?,
			}
		}

		if self.get_severity() == Severity::Bug {
			write!(f, "\nYou have encountered a compiler bug. Sorry about that! We would appreciate if you open an issue [1] and describe how you triggered the bug, together with a minimal snippet of code to reproduce it. Thanks!\n")?;
			write!(f, "[1]: https://github.com/fabianschuiki/moore\n")?;
		}

		Ok(())
	}
}
<|endoftext|>


<commit_before>//! The module containing all outgoing packet implementations
//!
//! Enjoy

//pub mod handshake; No handshake packets (yet)!
pub mod status;

/*
use super::super::super::deque_buffer::DequeBuffer;
use super::super::super::game_connection::GameConnection; //It's super super super effective!
use super::super::{ PacketHeader, ID_STC_ };

/// A packet sent from the server to
pub struct Packet {

    // Your fields here

}

impl Packet {

    /// Gets this Packet's ID
    pub fn get_id(&self) -> u32 {

        ID_STC_

    }

    /// Creates a new Packet
    pub fn new() -> Packet {

        Packet { /*etc*/ }

    }

    /// Encodes and sends this Packet
    pub fn send(&self, connection: &mut GameConnection) {

        let mut data_buffer = DequeBuffer::new();

        data_buffer.write_unsigned_varint_32(self.get_id());

        //Write your data here

        let mut length_buffer = DequeBuffer::new();

        length_buffer.write_unsigned_varint_32(data_buffer.remaining() as u32); //TODO: Try to get this as write_unsigned_varint_32_front

        let buffer: Vec<u8> = length_buffer.data.into_iter().chain(data_buffer.data.into_iter()).collect();

        let _buffer: &[u8] = &buffer[..];

        if connection.stream.write(_buffer).is_ok() {
            let _ = connection.stream.flush();
        }

    }

}
*/
<commit_msg>Get the skeleton of the new OutgoingPacket trait up and running<commit_after>//! The module containing all outgoing packet implementations

use super::super::deque_buffer::DequeBuffer;
use super::super::game_connection::GameConnection; //It's super super super effective!

use std::io::Write;

//pub mod handshake; No handshake packets (yet)!
pub mod status;

/// A trait for outgoing packets
pub trait OutgoingPacket {

    /// Gets this Packet's ID
    ///
    /// One day, this will be replaced with an associated constant
    fn get_id(&self) -> u32;

    /// Encodes this Packet's data
    fn encode_data(&self, buffer: &mut DequeBuffer);

    /// Encodes this OutgoingPacket
    fn encode(&self, buffer: &mut DequeBuffer) {

        buffer.write_unsigned_varint_32(self.get_id());

        self.encode_data(buffer);

    }

    /// Sends this OutgoingPacket to the client
    fn send(&self, connection: &mut GameConnection) {

        let mut data_buffer = DequeBuffer::new();

        self.encode(&mut data_buffer);

        let mut length_buffer = DequeBuffer::new();

        length_buffer.write_unsigned_varint_32(data_buffer.remaining() as u32); //TODO: Try to get this as data_buffer.prepend_varint_32 - One day

        let buffer: Vec<u8> = length_buffer.data.into_iter().chain(data_buffer.data.into_iter()).collect();

        let _buffer: &[u8] = &buffer[..];

        if connection.stream.write(_buffer).is_ok() {
            let _ = connection.stream.flush();
        }

    }

}

/*
use super::super::super::deque_buffer::DequeBuffer;
use super::super::super::game_connection::GameConnection; //It's super super super effective!
use super::super::{ PacketHeader, ID_STC_ };

/// A packet sent from the server to
pub struct Packet {

    // Your fields here

}

impl OutgoingPacket for Packet {

    /// Gets this Packet's ID
    ///
    /// One day, this will be replaced with an associated constant
    fn get_id(&self) -> u32 {
        ID_STC_
    }

    /// Encodes this Packet's data
    fn encode_data(&self, buffer: &mut DequeBuffer) {

    }

    /// Creates a new Packet
    pub fn new() -> Packet {

        Packet { /*etc*/ }

    }

}
*/
<|endoftext|>