<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.
//
// Caribon 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 ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::{Book, compile_str};
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use zipper::Zipper;
use escape::escape_tex;
use resource_handler::ResourceHandler;
use renderer::Renderer;
use parser::Parser;

use std::iter::Iterator;
use std::fs::File;
use std::io::Read;
use std::borrow::Cow;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
    escape: bool,
    first_letter: bool,
    first_paragraph: bool,
    is_short: bool,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
            escape: true,
            first_letter: false,
            first_paragraph: true,
            is_short: book.options.get_str("tex.class").unwrap() == "article",
        }
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        if let Ok(pdf_file) = self.book.options.get_path("output.pdf") {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("crowbook.temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::file_not_found(&self.source,
                                                                                "image",
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.source,
                                                                               format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::render(&self.source,
                                  "no output pdf file specified in book config"))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("rendering.num_depth").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("rendering.inline_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(self.book.filenames[i].as_ref());
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = try!(compile_str(try!(self.book.get_template("tex.template")).as_ref(),
                                        &self.book.source,
                                        "could not compile template 'tex.template'"));
        let mut data = try!(self.book.get_metadata(|s| {
            let tokens = try!(Parser::new().parse_inline(s));
            self.render_vec(&tokens)}))
            .insert_str("content", content)
            .insert_str("class", self.book.options.get_str("tex.class").unwrap())
            .insert_str("tex_lang", tex_lang);
        // If class isn't book, set open_any to true, so margins are symetric.
        if self.book.options.get_str("tex.class").unwrap() == "book" {
            data = data.insert_bool("book", true);
        }
        if self.book.options.get_bool("rendering.initials") == Ok(true) {
            data = data.insert_bool("initials", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }
}

impl<'a> Renderer for LatexRenderer<'a> {
    fn render_token(&mut self, token: &Token) -> Result<String> {
        match *token {
            Token::Str(ref text) => {
                let content = if self.escape {
                    self.book.clean(escape_tex(text.as_ref()), true)
                } else {
                    Cow::Borrowed(text.as_ref())
                };
                if self.first_letter {
                    self.first_letter = false;
                    if self.book.options.get_bool("rendering.initials").unwrap() {
                        let mut chars = content.chars().peekable();
                        let initial = try!(chars.next()
                                           .ok_or(Error::parser(&self.book.source,
                                                                    "empty str token, could not find initial")));
                        let mut first_word = String::new();
                        loop  {
                            let c = if let Some(next_char) = chars.peek() {
                                *next_char
                            } else {
                                break;
                            };
                            if !c.is_whitespace() {
                                first_word.push(c);
                                chars.next();
                            } else {
                                break;
                            }
                        }

                        let rest = chars.collect::<String>();

                        if initial.is_alphanumeric() {
                            Ok(format!("\\lettrine{{{}}}{{{}}}{}", initial, first_word, rest))
                        } else {
                            Ok(format!("{}{}{}", initial, first_word, rest))
                        }
                    } else {
                        Ok(content.into_owned())
                    }
                } else {
                    Ok(content.into_owned())
                }
            }
            Token::Paragraph(ref vec) => {
                if self.first_paragraph {
                    self.first_paragraph = false;
                    if !vec.is_empty() && vec[0].is_str() {
                        // Only use initials if first element is a Token::str
                        self.first_letter = true;
                    }
                }
                Ok(format!("{}\n\n",
                           try!(self.render_vec(vec))))
            },
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 {
                    self.first_paragraph = true;
                    if self.current_chapter == Number::Hidden {
                        if !self.is_short {
                            return Ok(r#"\chapter*{}"#.to_owned());
                        } else {
                            return Ok(r#"\section*{}"#.to_owned());
                        }
                    } else {
                        if let Number::Specified(n) = self.current_chapter {
                            content.push_str(r"\setcounter{chapter}{");
                            content.push_str(&format!("{}", n - 1));
                            content.push_str("}\n");
                        }
                    }
                }
                match n {
                    1 => {
                        if !self.is_short {
                            content.push_str(r"\chapter");
                        } else {
                            // Chapters aren't handlled for class article
                            content.push_str(r"\section");
                        }
                    },
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec)))),
            Token::CodeBlock(_, ref vec) => {
                self.escape = false;
                let res = try!(self.render_vec(vec));
                self.escape = true;
                Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", res))
            },
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url.as_ref());
                    if &content == &url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          url.as_ref()));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it.", url));
                    Ok(String::new())
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        url.as_ref()))))
                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it.", url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\footnote{{{}}}", try!(self.render_vec(vec)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.render_token(v)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec),
            Token::__NonExhaustive => unreachable!()
        }
    }
}
<commit_msg>Make HardBreak use \ in latex<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.
//
// Caribon 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 ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::{Book, compile_str};
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use zipper::Zipper;
use escape::escape_tex;
use resource_handler::ResourceHandler;
use renderer::Renderer;
use parser::Parser;

use std::iter::Iterator;
use std::fs::File;
use std::io::Read;
use std::borrow::Cow;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
    escape: bool,
    first_letter: bool,
    first_paragraph: bool,
    is_short: bool,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
            escape: true,
            first_letter: false,
            first_paragraph: true,
            is_short: book.options.get_str("tex.class").unwrap() == "article",
        }
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        if let Ok(pdf_file) = self.book.options.get_path("output.pdf") {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("crowbook.temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::file_not_found(&self.source,
                                                                                "image",
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.source,
                                                                               format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::render(&self.source,
                                  "no output pdf file specified in book config"))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("rendering.num_depth").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("rendering.inline_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(self.book.filenames[i].as_ref());
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = try!(compile_str(try!(self.book.get_template("tex.template")).as_ref(),
                                        &self.book.source,
                                        "could not compile template 'tex.template'"));
        let mut data = try!(self.book.get_metadata(|s| {
            let tokens = try!(Parser::new().parse_inline(s));
            self.render_vec(&tokens)}))
            .insert_str("content", content)
            .insert_str("class", self.book.options.get_str("tex.class").unwrap())
            .insert_str("tex_lang", tex_lang);
        // If class isn't book, set open_any to true, so margins are symetric.
        if self.book.options.get_str("tex.class").unwrap() == "book" {
            data = data.insert_bool("book", true);
        }
        if self.book.options.get_bool("rendering.initials") == Ok(true) {
            data = data.insert_bool("initials", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }
}

impl<'a> Renderer for LatexRenderer<'a> {
    fn render_token(&mut self, token: &Token) -> Result<String> {
        match *token {
            Token::Str(ref text) => {
                let content = if self.escape {
                    self.book.clean(escape_tex(text.as_ref()), true)
                } else {
                    Cow::Borrowed(text.as_ref())
                };
                if self.first_letter {
                    self.first_letter = false;
                    if self.book.options.get_bool("rendering.initials").unwrap() {
                        let mut chars = content.chars().peekable();
                        let initial = try!(chars.next()
                                           .ok_or(Error::parser(&self.book.source,
                                                                    "empty str token, could not find initial")));
                        let mut first_word = String::new();
                        loop  {
                            let c = if let Some(next_char) = chars.peek() {
                                *next_char
                            } else {
                                break;
                            };
                            if !c.is_whitespace() {
                                first_word.push(c);
                                chars.next();
                            } else {
                                break;
                            }
                        }

                        let rest = chars.collect::<String>();

                        if initial.is_alphanumeric() {
                            Ok(format!("\\lettrine{{{}}}{{{}}}{}", initial, first_word, rest))
                        } else {
                            Ok(format!("{}{}{}", initial, first_word, rest))
                        }
                    } else {
                        Ok(content.into_owned())
                    }
                } else {
                    Ok(content.into_owned())
                }
            }
            Token::Paragraph(ref vec) => {
                if self.first_paragraph {
                    self.first_paragraph = false;
                    if !vec.is_empty() && vec[0].is_str() {
                        // Only use initials if first element is a Token::str
                        self.first_letter = true;
                    }
                }
                Ok(format!("{}\n\n",
                           try!(self.render_vec(vec))))
            },
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 {
                    self.first_paragraph = true;
                    if self.current_chapter == Number::Hidden {
                        if !self.is_short {
                            return Ok(r#"\chapter*{}"#.to_owned());
                        } else {
                            return Ok(r#"\section*{}"#.to_owned());
                        }
                    } else {
                        if let Number::Specified(n) = self.current_chapter {
                            content.push_str(r"\setcounter{chapter}{");
                            content.push_str(&format!("{}", n - 1));
                            content.push_str("}\n");
                        }
                    }
                }
                match n {
                    1 => {
                        if !self.is_short {
                            content.push_str(r"\chapter");
                        } else {
                            // Chapters aren't handlled for class article
                            content.push_str(r"\section");
                        }
                    },
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec)))),
            Token::CodeBlock(_, ref vec) => {
                self.escape = false;
                let res = try!(self.render_vec(vec));
                self.escape = true;
                Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", res))
            },
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\\\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url.as_ref());
                    if &content == &url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          url.as_ref()));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it.", url));
                    Ok(String::new())
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        url.as_ref()))))
                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it.", url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\footnote{{{}}}", try!(self.render_vec(vec)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.render_token(v)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec),
            Token::__NonExhaustive => unreachable!()
        }
    }
}
<|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.
//
// Caribon 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 ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::{Book, compile_str};
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use token::Data;
use zipper::Zipper;
use resource_handler::ResourceHandler;
use renderer::Renderer;
use parser::Parser;

use crowbook_text_processing::escape::escape_tex;

use std::iter::Iterator;
use std::fs::File;
use std::io::Read;
use std::borrow::Cow;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
    escape: bool,
    first_letter: bool,
    first_paragraph: bool,
    is_short: bool,
    proofread: bool,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
            escape: true,
            first_letter: false,
            first_paragraph: true,
            is_short: book.options.get_str("tex.class").unwrap() == "article",
            proofread: false,
        }
    }

    /// Set proofreading to true
    #[doc(hidden)]
    pub fn proofread(mut self) -> Self {
        self.proofread = true;
        self
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        let output = if self.proofread { "output.proofread.pdf" } else { "output.pdf" };
        if let Ok(pdf_file) = self.book.options.get_path(output) {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("crowbook.temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::file_not_found(&self.source,
                                                                                "image",
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.source,
                                                                               format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::render(&self.source,
                                  format!("no output pdf file specified '{}' in book config", output)))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("rendering.num_depth").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("rendering.inline_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(&self.book.filenames[i] as &str);
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = try!(compile_str(try!(self.book.get_template("tex.template")).as_ref(),
                                        &self.book.source,
                                        "could not compile template 'tex.template'"));
        let mut data = try!(self.book.get_metadata(|s| {
            let tokens = try!(Parser::new().parse_inline(s));
            self.render_vec(&tokens)}))
            .insert_str("content", content)
            .insert_str("class", self.book.options.get_str("tex.class").unwrap())
            .insert_str("tex_lang", tex_lang);
        // If class isn't book, set open_any to true, so margins are symetric.
        if self.book.options.get_str("tex.class").unwrap() == "book" {
            data = data.insert_bool("book", true);
        }
        if self.book.options.get_bool("rendering.initials") == Ok(true) {
            data = data.insert_bool("initials", true);
        }
        // Insert xelatex if tex.command is set to xelatex
        if self.book.options.get_str("tex.command") == Ok("xelatex") {
            data = data.insert_bool("xelatex", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }
}

impl<'a> Renderer for LatexRenderer<'a> {
    fn render_token(&mut self, token: &Token) -> Result<String> {
        match *token {
            Token::Str(ref text) => {
                let content = if self.escape {
                    self.book.clean(escape_tex(text.as_ref()), true)
                } else {
                    Cow::Borrowed(text.as_ref())
                };
                if self.first_letter {
                    self.first_letter = false;
                    if self.book.options.get_bool("rendering.initials").unwrap() {
                        let mut chars = content.chars().peekable();
                        let initial = try!(chars.next()
                                           .ok_or(Error::parser(&self.book.source,
                                                                    "empty str token, could not find initial")));
                        let mut first_word = String::new();
                        loop  {
                            let c = if let Some(next_char) = chars.peek() {
                                *next_char
                            } else {
                                break;
                            };
                            if !c.is_whitespace() {
                                first_word.push(c);
                                chars.next();
                            } else {
                                break;
                            }
                        }

                        let rest = chars.collect::<String>();

                        if initial.is_alphanumeric() {
                            Ok(format!("\\lettrine{{{}}}{{{}}}{}", initial, first_word, rest))
                        } else {
                            Ok(format!("{}{}{}", initial, first_word, rest))
                        }
                    } else {
                        Ok(content.into_owned())
                    }
                } else {
                    Ok(content.into_owned())
                }
            }
            Token::Paragraph(ref vec) => {
                if self.first_paragraph {
                    self.first_paragraph = false;
                    if !vec.is_empty() && vec[0].is_str() {
                        // Only use initials if first element is a Token::str
                        self.first_letter = true;
                    }
                }
                Ok(format!("{}\n\n",
                           try!(self.render_vec(vec))))
            },
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 {
                    self.first_paragraph = true;
                    if self.current_chapter == Number::Hidden {
                        if !self.is_short {
                            return Ok(r#"\chapter*{}"#.to_owned());
                        } else {
                            return Ok(r#"\section*{}"#.to_owned());
                        }
                    } else {
                        if let Number::Specified(n) = self.current_chapter {
                            content.push_str(r"\setcounter{chapter}{");
                            content.push_str(&format!("{}", n - 1));
                            content.push_str("}\n");
                        }
                    }
                }
                match n {
                    1 => {
                        if !self.is_short {
                            content.push_str(r"\chapter");
                        } else {
                            // Chapters aren't handlled for class article
                            content.push_str(r"\section");
                        }
                    },
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec)))),
            Token::CodeBlock(_, ref vec) => {
                self.escape = false;
                let res = try!(self.render_vec(vec));
                self.escape = true;
                Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", res))
            },
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\\\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url.as_ref());
                    if &content == &url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\protect\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          url.as_ref()));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(&format!("LaTeX ({}): image '{}' doesn't seem to be local; ignoring it.",
                                                    self.source,
                                                    url));
                    Ok(String::new())
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        url.as_ref()))))
                } else {
                    self.book.logger.error(&format!("LaTeX ({}): image '{}' doesn't seem to be local; ignoring it.",
                                                    self.source,
                                                    url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\protect\\footnote{{{}}}", try!(self.render_vec(vec)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.render_token(v)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec),
            Token::Annotation(ref annotation, ref vec) => {
                let content = try!(self.render_vec(vec));
                if self.proofread {
                    match annotation {
                        &Data::GrammarError(ref s) => Ok(format!("\\underline{{{}}}\\protect\\footnote{{{}}}",
                                                                 content,
                                                                 escape_tex(s.as_str()))),
                        _ => unreachable!(),
                    }
                } else {
                    Ok(content)
                }
            },

            Token::__NonExhaustive => unreachable!()
        }
    }
}
<commit_msg>Replace format by lformat in latex.rs<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.
//
// Caribon 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 ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::{Book, compile_str};
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use token::Data;
use zipper::Zipper;
use resource_handler::ResourceHandler;
use renderer::Renderer;
use parser::Parser;

use crowbook_text_processing::escape::escape_tex;

use std::iter::Iterator;
use std::fs::File;
use std::io::Read;
use std::borrow::Cow;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
    escape: bool,
    first_letter: bool,
    first_paragraph: bool,
    is_short: bool,
    proofread: bool,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
            escape: true,
            first_letter: false,
            first_paragraph: true,
            is_short: book.options.get_str("tex.class").unwrap() == "article",
            proofread: false,
        }
    }

    /// Set proofreading to true
    #[doc(hidden)]
    pub fn proofread(mut self) -> Self {
        self.proofread = true;
        self
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        let output = if self.proofread { "output.proofread.pdf" } else { "output.pdf" };
        if let Ok(pdf_file) = self.book.options.get_path(output) {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("crowbook.temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::file_not_found(&self.source,
                                                                                "image",
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.source,
                                                                               lformat!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::render(&self.source,
                                  lformat!("no output pdf file specified '{}' in book config", output)))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("rendering.num_depth").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("rendering.inline_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(&self.book.filenames[i] as &str);
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(lformat!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = try!(compile_str(try!(self.book.get_template("tex.template")).as_ref(),
                                        &self.book.source,
                                        "could not compile template 'tex.template'"));
        let mut data = try!(self.book.get_metadata(|s| {
            let tokens = try!(Parser::new().parse_inline(s));
            self.render_vec(&tokens)}))
            .insert_str("content", content)
            .insert_str("class", self.book.options.get_str("tex.class").unwrap())
            .insert_str("tex_lang", tex_lang);
        // If class isn't book, set open_any to true, so margins are symetric.
        if self.book.options.get_str("tex.class").unwrap() == "book" {
            data = data.insert_bool("book", true);
        }
        if self.book.options.get_bool("rendering.initials") == Ok(true) {
            data = data.insert_bool("initials", true);
        }
        // Insert xelatex if tex.command is set to xelatex
        if self.book.options.get_str("tex.command") == Ok("xelatex") {
            data = data.insert_bool("xelatex", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }
}

impl<'a> Renderer for LatexRenderer<'a> {
    fn render_token(&mut self, token: &Token) -> Result<String> {
        match *token {
            Token::Str(ref text) => {
                let content = if self.escape {
                    self.book.clean(escape_tex(text.as_ref()), true)
                } else {
                    Cow::Borrowed(text.as_ref())
                };
                if self.first_letter {
                    self.first_letter = false;
                    if self.book.options.get_bool("rendering.initials").unwrap() {
                        let mut chars = content.chars().peekable();
                        let initial = try!(chars.next()
                                           .ok_or(Error::parser(&self.book.source,
                                                                    "empty str token, could not find initial")));
                        let mut first_word = String::new();
                        loop  {
                            let c = if let Some(next_char) = chars.peek() {
                                *next_char
                            } else {
                                break;
                            };
                            if !c.is_whitespace() {
                                first_word.push(c);
                                chars.next();
                            } else {
                                break;
                            }
                        }

                        let rest = chars.collect::<String>();

                        if initial.is_alphanumeric() {
                            Ok(format!("\\lettrine{{{}}}{{{}}}{}", initial, first_word, rest))
                        } else {
                            Ok(format!("{}{}{}", initial, first_word, rest))
                        }
                    } else {
                        Ok(content.into_owned())
                    }
                } else {
                    Ok(content.into_owned())
                }
            }
            Token::Paragraph(ref vec) => {
                if self.first_paragraph {
                    self.first_paragraph = false;
                    if !vec.is_empty() && vec[0].is_str() {
                        // Only use initials if first element is a Token::str
                        self.first_letter = true;
                    }
                }
                Ok(format!("{}\n\n",
                           try!(self.render_vec(vec))))
            },
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 {
                    self.first_paragraph = true;
                    if self.current_chapter == Number::Hidden {
                        if !self.is_short {
                            return Ok(r#"\chapter*{}"#.to_owned());
                        } else {
                            return Ok(r#"\section*{}"#.to_owned());
                        }
                    } else {
                        if let Number::Specified(n) = self.current_chapter {
                            content.push_str(r"\setcounter{chapter}{");
                            content.push_str(&format!("{}", n - 1));
                            content.push_str("}\n");
                        }
                    }
                }
                match n {
                    1 => {
                        if !self.is_short {
                            content.push_str(r"\chapter");
                        } else {
                            // Chapters aren't handlled for class article
                            content.push_str(r"\section");
                        }
                    },
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec)))),
            Token::CodeBlock(_, ref vec) => {
                self.escape = false;
                let res = try!(self.render_vec(vec));
                self.escape = true;
                Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", res))
            },
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\\\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url.as_ref());
                    if &content == &url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\protect\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          url.as_ref()));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(lformat!("LaTeX ({}): image '{}' doesn't seem to be local; ignoring it.",
                                                    self.source,
                                                    url));
                    Ok(String::new())
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        url.as_ref()))))
                } else {
                    self.book.logger.error(lformat!("LaTeX ({}): image '{}' doesn't seem to be local; ignoring it.",
                                                    self.source,
                                                    url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\protect\\footnote{{{}}}", try!(self.render_vec(vec)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.render_token(v)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec),
            Token::Annotation(ref annotation, ref vec) => {
                let content = try!(self.render_vec(vec));
                if self.proofread {
                    match annotation {
                        &Data::GrammarError(ref s) => Ok(format!("\\underline{{{}}}\\protect\\footnote{{{}}}",
                                                                 content,
                                                                 escape_tex(s.as_str()))),
                        _ => unreachable!(),
                    }
                } else {
                    Ok(content)
                }
            },

            Token::__NonExhaustive => unreachable!()
        }
    }
}
<|endoftext|>


<commit_before>extern mod euler;

use euler::extnum::{ one, from_uint };
use euler::bigint::{ BigInt };

fn main() {
    let mut i = one::<BigInt>();
    for uint::range(0, 1000) |_n| {
        i = i.mul(from_uint(2));
    }
    let sum = do str::byte_slice(i.to_str()) |buf| {
        buf.map(|c| *c - ('0' as u8)).foldl(0, |s, e| s + (e as uint))
    };
    io::println(fmt!("%u", sum));
}

<commit_msg>use * operator instead of mul method<commit_after>extern mod euler;

use euler::extnum::{ one, from_uint };
use euler::bigint::{ BigInt };

fn main() {
    let mut i = one::<BigInt>();
    for uint::range(0, 1000) |_n| {
        i = i * from_uint(2);
    }
    let sum = do str::byte_slice(i.to_str()) |buf| {
        buf.map(|c| *c - ('0' as u8)).foldl(0, |s, e| s + (e as uint))
    };
    io::println(fmt!("%u", sum));
}

<|endoftext|>


<commit_before>use syntax::ast::{Item, ItemKind, Ty, TyKind};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use utils::{in_macro, span_lint_and_then};

/// **What it does:** Checks for constants with an explicit `'static` lifetime.
///
/// **Why is this bad?** Adding `'static` to every reference can create very
/// complicated types.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
/// &[...]
/// ```
/// This code can be rewritten as
/// ```rust
///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
/// ```

declare_lint! {
    pub CONST_STATIC_LIFETIME,
    Warn,
    "Using explicit `'static` lifetime for constants when elision rules would allow omitting them."
}

pub struct StaticConst;

impl LintPass for StaticConst {
    fn get_lints(&self) -> LintArray {
        lint_array!(CONST_STATIC_LIFETIME)
    }
}

impl StaticConst {
    // Recursively visit types
    fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext) {
        match ty.node {
            // Be careful of nested structures (arrays and tuples)
            TyKind::Array(ref ty, _) => {
                self.visit_type(&*ty, cx);
            },
            TyKind::Tup(ref tup) => for tup_ty in tup {
                self.visit_type(&*tup_ty, cx);
            },
            // This is what we are looking for !
            TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
                // Match the 'static lifetime
                if let Some(lifetime) = *optional_lifetime {
                    match borrow_type.ty.node {
                        TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) |
                        TyKind::Tup(..) => {
                            if lifetime.ident.name == "'static" {
                                let mut sug: String = String::new();
                                span_lint_and_then(
                                    cx,
                                    CONST_STATIC_LIFETIME,
                                    lifetime.span,
                                    "Constants have by default a `'static` lifetime",
                                    |db| {
                                        db.span_suggestion(lifetime.span, "consider removing `'static`", sug);
                                    },
                                );
                            }
                        }
                        _ => {}
                    }
                }
                self.visit_type(&*borrow_type.ty, cx);
            },
            TyKind::Slice(ref ty) => {
                self.visit_type(ty, cx);
            },
            _ => {},
        }
    }
}

impl EarlyLintPass for StaticConst {
    fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
        if !in_macro(item.span) {
            // Match only constants...
            if let ItemKind::Const(ref var_type, _) = item.node {
                self.visit_type(var_type, cx);
            }
        }
    }
}
<commit_msg>Try fixing const_with_static_lifetime docs<commit_after>use syntax::ast::{Item, ItemKind, Ty, TyKind};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use utils::{in_macro, span_lint_and_then};

/// **What it does:** Checks for constants with an explicit `'static` lifetime.
///
/// **Why is this bad?** Adding `'static` to every reference can create very
/// complicated types.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
/// &[...]
/// ```
/// This code can be rewritten as
/// ```rust
///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
/// ```
declare_lint! {
    pub CONST_STATIC_LIFETIME,
    Warn,
    "Using explicit `'static` lifetime for constants when elision rules would allow omitting them."
}

pub struct StaticConst;

impl LintPass for StaticConst {
    fn get_lints(&self) -> LintArray {
        lint_array!(CONST_STATIC_LIFETIME)
    }
}

impl StaticConst {
    // Recursively visit types
    fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext) {
        match ty.node {
            // Be careful of nested structures (arrays and tuples)
            TyKind::Array(ref ty, _) => {
                self.visit_type(&*ty, cx);
            },
            TyKind::Tup(ref tup) => for tup_ty in tup {
                self.visit_type(&*tup_ty, cx);
            },
            // This is what we are looking for !
            TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
                // Match the 'static lifetime
                if let Some(lifetime) = *optional_lifetime {
                    match borrow_type.ty.node {
                        TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) |
                        TyKind::Tup(..) => {
                            if lifetime.ident.name == "'static" {
                                let mut sug: String = String::new();
                                span_lint_and_then(
                                    cx,
                                    CONST_STATIC_LIFETIME,
                                    lifetime.span,
                                    "Constants have by default a `'static` lifetime",
                                    |db| {
                                        db.span_suggestion(lifetime.span, "consider removing `'static`", sug);
                                    },
                                );
                            }
                        }
                        _ => {}
                    }
                }
                self.visit_type(&*borrow_type.ty, cx);
            },
            TyKind::Slice(ref ty) => {
                self.visit_type(ty, cx);
            },
            _ => {},
        }
    }
}

impl EarlyLintPass for StaticConst {
    fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
        if !in_macro(item.span) {
            // Match only constants...
            if let ItemKind::Const(ref var_type, _) = item.node {
                self.visit_type(var_type, cx);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Tools for the user interface.

use error::UIErrorKind;

use error::Result;
use std::io;
use std::io::Write;
use std::result;

/// Define commands with a selection string, enum and information string.
type CommandArg<'a, T> = (&'a str, T, &'a str);

/// These are put into a list. Unfortunately it is difficult to use
/// a const array for this since we need to send a Sized type to functions.
///
/// # Examples:
/// ```
/// # use define::uitools::CommandList;
/// // enum Command { First, Second }
/// let commands: CommandList<'static, Command> = vec![
///     ("a", Command::First, "Select option `First` by inputting "a")
///     ("bad", Command::Second, "and `Second` by inputting "bad")
/// ];
///
/// print_menu(&commands);
/// if let Some(cmd) = get_selection("bad", &commands) {
///     // Do something
/// }
/// ```
pub type CommandList<'a, T> = Vec<CommandArg<'a, T>>;

/// Read and trim a string from stdin.
pub fn get_input(query: &'static str) -> Result<String> {
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let mut selection = String::new();

    print!("{}: ", query);
    stdout.flush()?;
    stdin.read_line(&mut selection)?;

    Ok(selection.trim().to_string())
}

/// Parse a string for a command from the input list.
pub fn get_selection<'a, 'b, T: Copy>(input: &'a str, commands: &CommandList<'b, T>) -> Option<T> {
    if let Some(needle) = input.trim().to_lowercase().split_whitespace().next() {
        for &(ident, cmd, _) in commands.iter() {
            if needle == ident {
                return Some(cmd);
            }
        }
    }

    None
}

/// Parse a string for a command from the input list and return along with the remaining string.
pub fn get_selection_and_tail<'a, 'b, T: Copy>(input: &'a str, commands: &CommandList<'b, T>)
        -> Option<(T, String)> {
    let mut iter = input.split_whitespace();

    iter.next()
        .and_then(|needle| get_selection(needle, &commands))
        .and_then(|cmd| {
            let tail = iter.collect::<Vec<&str>>().join(" ");
            Some((cmd, tail))
        })
}

/// Parse an input tail for values. The input tail is a string with white space
/// separated values. Return an Error if any value of the string could not be parsed.
pub fn parse_tail<'a>(tail: &'a str) -> result::Result<Vec<usize>, UIErrorKind> {
    // Note to self: This uses `FromIterator` to turn a Vec<Result> into Result<Vec>. Neat!
    tail.split_whitespace()
        .map(|s| s.parse::<usize>().map_err(|_| {
            UIErrorKind::BadNumber(format!("'{}' is not a valid index", s))
        }))
        .collect()
}

/// Print a menu for the input commands.
pub fn print_menu<'a, T>(commands: &CommandList<'a, T>) {
    println!("Commands:");

    for &(ref c, _, ref info) in commands.iter() {
        println!("{:>4}{:4}{}", c, ' ', info);
    }

    println!("");
}

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

    #[derive(Clone, Copy, Debug, PartialEq)]
    enum TestCommands {
        One,
        Two,
    }

    #[test]
    fn select_a_command() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        assert_eq!(Some(TestCommands::One), get_selection("a", &command_list));
        assert_eq!(Some(TestCommands::Two), get_selection("b", &command_list));
        assert_eq!(None, get_selection("nope", &command_list));
    }

    #[test]
    fn select_a_command_trims_and_lowercases() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        assert_eq!(Some(TestCommands::One), get_selection("\n\ta\n", &command_list));
        assert_eq!(Some(TestCommands::Two), get_selection("  B  ", &command_list));
    }

    #[test]
    fn select_a_command_checks_first_word() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        assert_eq!(Some(TestCommands::One), get_selection("a ignore", &command_list));
        assert_eq!(None, get_selection("aignore", &command_list));
    }

    #[test]
    fn select_a_command_differentiates_between_similar() {
        let command_list = vec![
            ("at", TestCommands::One, "one"),
            ("ar", TestCommands::Two, "two")
        ];

        assert_eq!(None, get_selection("a", &command_list));
        assert_eq!(Some(TestCommands::One), get_selection("at", &command_list));
        assert_eq!(Some(TestCommands::Two), get_selection("ar", &command_list));
    }

    #[test]
    fn select_a_command_and_tail() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        let (cmd, tail) = get_selection_and_tail("a and a tail", &command_list).unwrap();
        assert_eq!(TestCommands::One, cmd);
        assert_eq!("and a tail", &tail);

        let (cmd, tail) = get_selection_and_tail("b\twith trimming\n", &command_list).unwrap();
        assert_eq!(TestCommands::Two, cmd);
        assert_eq!("with trimming", &tail);

        assert_eq!(None, get_selection_and_tail("", &command_list));
        assert_eq!(None, get_selection_and_tail("tail", &command_list));
    }

    #[test]
    fn parse_tail_for_usize() {
        assert_eq!(vec![3, 2, 1, 100], parse_tail("3 2 1 100").unwrap());
        assert_eq!(vec![3, 2, 1, 0], parse_tail("3\t2\t1 0").unwrap());
        assert!(parse_tail("").unwrap().is_empty());
    }

    #[test]
    fn parse_tail_for_usize_bad_numbers() {
        assert!(parse_tail("3a").is_err());
        assert!(parse_tail("3 1 2 a 3").is_err());
    }
}
<commit_msg>Make `parse_tail` generic for `FromStr` objects<commit_after>//! Tools for the user interface.

use error::UIErrorKind;

use error::Result;
use std::io;
use std::io::Write;
use std::result;
use std::str::FromStr;

/// Define commands with a selection string, enum and information string.
type CommandArg<'a, T> = (&'a str, T, &'a str);

/// These are put into a list. Unfortunately it is difficult to use
/// a const array for this since we need to send a Sized type to functions.
///
/// # Examples:
/// ```
/// # use define::uitools::CommandList;
/// // enum Command { First, Second }
/// let commands: CommandList<'static, Command> = vec![
///     ("a", Command::First, "Select option `First` by inputting "a")
///     ("bad", Command::Second, "and `Second` by inputting "bad")
/// ];
///
/// print_menu(&commands);
/// if let Some(cmd) = get_selection("bad", &commands) {
///     // Do something
/// }
/// ```
pub type CommandList<'a, T> = Vec<CommandArg<'a, T>>;

/// Read and trim a string from stdin.
pub fn get_input(query: &'static str) -> Result<String> {
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let mut selection = String::new();

    print!("{}: ", query);
    stdout.flush()?;
    stdin.read_line(&mut selection)?;

    Ok(selection.trim().to_string())
}

/// Parse a string for a command from the input list.
pub fn get_selection<'a, 'b, T: Copy>(input: &'a str, commands: &CommandList<'b, T>) -> Option<T> {
    if let Some(needle) = input.trim().to_lowercase().split_whitespace().next() {
        for &(ident, cmd, _) in commands.iter() {
            if needle == ident {
                return Some(cmd);
            }
        }
    }

    None
}

/// Parse a string for a command from the input list and return along with the remaining string.
pub fn get_selection_and_tail<'a, 'b, T: Copy>(input: &'a str, commands: &CommandList<'b, T>)
        -> Option<(T, String)> {
    let mut iter = input.split_whitespace();

    iter.next()
        .and_then(|needle| get_selection(needle, &commands))
        .and_then(|cmd| {
            let tail = iter.collect::<Vec<&str>>().join(" ");
            Some((cmd, tail))
        })
}

/// Parse an input tail for values. The input tail is a string with white space
/// separated values. Return an Error if any value of the string could not be parsed.
pub fn parse_tail<'a, T: FromStr>(tail: &'a str) -> result::Result<Vec<T>, UIErrorKind> {
    // Note to self: This uses `FromIterator` to turn a Vec<Result> into Result<Vec>. Neat!
    tail.split_whitespace()
        .map(|s| s.parse::<T>().map_err(|_| {
            UIErrorKind::BadNumber(format!("'{}' is not a valid index", s))
        }))
        .collect()
}

/// Print a menu for the input commands.
pub fn print_menu<'a, T>(commands: &CommandList<'a, T>) {
    println!("Commands:");

    for &(ref c, _, ref info) in commands.iter() {
        println!("{:>4}{:4}{}", c, ' ', info);
    }

    println!("");
}

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

    #[derive(Clone, Copy, Debug, PartialEq)]
    enum TestCommands {
        One,
        Two,
    }

    #[test]
    fn select_a_command() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        assert_eq!(Some(TestCommands::One), get_selection("a", &command_list));
        assert_eq!(Some(TestCommands::Two), get_selection("b", &command_list));
        assert_eq!(None, get_selection("nope", &command_list));
    }

    #[test]
    fn select_a_command_trims_and_lowercases() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        assert_eq!(Some(TestCommands::One), get_selection("\n\ta\n", &command_list));
        assert_eq!(Some(TestCommands::Two), get_selection("  B  ", &command_list));
    }

    #[test]
    fn select_a_command_checks_first_word() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        assert_eq!(Some(TestCommands::One), get_selection("a ignore", &command_list));
        assert_eq!(None, get_selection("aignore", &command_list));
    }

    #[test]
    fn select_a_command_differentiates_between_similar() {
        let command_list = vec![
            ("at", TestCommands::One, "one"),
            ("ar", TestCommands::Two, "two")
        ];

        assert_eq!(None, get_selection("a", &command_list));
        assert_eq!(Some(TestCommands::One), get_selection("at", &command_list));
        assert_eq!(Some(TestCommands::Two), get_selection("ar", &command_list));
    }

    #[test]
    fn select_a_command_and_tail() {
        let command_list = vec![
            ("a", TestCommands::One, "one"),
            ("b", TestCommands::Two, "two")
        ];

        let (cmd, tail) = get_selection_and_tail("a and a tail", &command_list).unwrap();
        assert_eq!(TestCommands::One, cmd);
        assert_eq!("and a tail", &tail);

        let (cmd, tail) = get_selection_and_tail("b\twith trimming\n", &command_list).unwrap();
        assert_eq!(TestCommands::Two, cmd);
        assert_eq!("with trimming", &tail);

        assert_eq!(None, get_selection_and_tail("", &command_list));
        assert_eq!(None, get_selection_and_tail("tail", &command_list));
    }

    #[test]
    fn parse_tail_for_usize() {
        assert_eq!(vec![3, 2, 1, 100], parse_tail::<usize>("3 2 1 100").unwrap());
        assert_eq!(vec![3, 2, 1, 0], parse_tail::<usize>("3\t2\t1 0").unwrap());
        assert!(parse_tail::<usize>("").unwrap().is_empty());
    }

    #[test]
    fn parse_tail_for_usize_bad_numbers() {
        assert!(parse_tail::<usize>("3a").is_err());
        assert!(parse_tail::<usize>("3 1 2 a 3").is_err());
    }
}
<|endoftext|>


<commit_before>extern crate compiletest_rs as compiletest;

use std::path::Path;

#[allow(dead_code)]
fn run_mode(directory: &'static str, mode: &'static str) {
    let mut config = compiletest::default_config();
    let cfg_mode = mode.parse().ok().expect("Invalid mode");

    config.mode = cfg_mode;
    let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let flags = format!("-L {}", dir.join("../target/debug/deps").display());
    config.target_rustcflags = Some(flags);
    config.src_base = dir.join("tests").join(directory);

    compiletest::run_tests(&config);
}

#[cfg(feature = "unicase_support")]
#[test]
fn compile_test_unicase() {
    run_mode("compile-fail-unicase", "compile-fail");
}
<commit_msg>Cleanup<commit_after>extern crate compiletest_rs as compiletest;

use std::path::Path;

#[allow(dead_code)]
fn run_mode(directory: &'static str, mode: &'static str) {
    let mut config = compiletest::default_config();
    let cfg_mode = mode.parse().ok().expect("Invalid mode");

    config.mode = cfg_mode;
    let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    config.target_rustcflags = Some(format!("-L {}", dir.join("../target/debug/deps").display()));
    config.src_base = dir.join("tests").join(directory);

    compiletest::run_tests(&config);
}

#[cfg(feature = "unicase_support")]
#[test]
fn compile_test_unicase() {
    run_mode("compile-fail-unicase", "compile-fail");
}
<|endoftext|>


<commit_before>use std::io;
use std::collections::HashMap;

fn read_input() -> String {
    println!("Enter the number");

    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .ok()
        .expect("Failed to read the line from the console");

    input
}

fn main() {
    let mut encodings = HashMap::new();
    encodings.insert('0', [" _ ",
                           "| |",
                           "|_|"]);

    encodings.insert('1', [" ",
                           "|",
                           "|"]);

    encodings.insert('2', [" _ ",
                           " _|",
                           "|_ "]);

    encodings.insert('3', ["_ ",
                           "_|",
                           "_|"]);

    encodings.insert('4', ["   ",
                           "|_|",
                           "  |"]);

    encodings.insert('5', [" _ ",
                           "|_ ",
                           " _|"]);

    encodings.insert('6', [" _ ",
                           "|_ ",
                           "|_|"]);

    encodings.insert('7', ["_ ",
                           " |",
                           " |"]);

    encodings.insert('8', [" _ ",
                           "|_|",
                           "|_|"]);

    encodings.insert('9', [" _ ",
                           "|_|",
                           " _|"]);

    let input = read_input();
    print!("You've entered: {}", input);

    let to_print: Vec<_> = input
        .trim()
        .chars()
        .map(|c| match encodings.get(&c){
            Some(encoded) => encoded,
            None => panic!("Character {} is not known", &c)
        })
        .collect();

    for i in 0..3 {
        for s in &to_print {
            print!("{} ", s[i]);
        }
        println!("");
    }
}
<commit_msg>Initialize alphabet in a separate function<commit_after>use std::io;
use std::collections::HashMap;

fn read_input() -> String {
    println!("Enter the number");

    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .ok()
        .expect("Failed to read the line from the console");

    input
}

fn build_alphabet() -> HashMap<char, [&'static str; 3]>{
    let mut alphabet = HashMap::with_capacity(10);
    alphabet.insert('0', [" _ ",
                           "| |",
                           "|_|"]);

    alphabet.insert('1', [" ",
                           "|",
                           "|"]);

    alphabet.insert('2', [" _ ",
                           " _|",
                           "|_ "]);

    alphabet.insert('3', ["_ ",
                           "_|",
                           "_|"]);

    alphabet.insert('4', ["   ",
                           "|_|",
                           "  |"]);

    alphabet.insert('5', [" _ ",
                           "|_ ",
                           " _|"]);

    alphabet.insert('6', [" _ ",
                           "|_ ",
                           "|_|"]);

    alphabet.insert('7', ["_ ",
                           " |",
                           " |"]);

    alphabet.insert('8', [" _ ",
                           "|_|",
                           "|_|"]);

    alphabet.insert('9', [" _ ",
                           "|_|",
                           " _|"]);

    alphabet
}

fn main() {
    let alphabet = build_alphabet();
    let input = read_input();
    print!("You've entered: {}", input);

    let to_print: Vec<_> = input
        .trim()
        .chars()
        .map(|c| match alphabet.get(&c){
            Some(encoded) => encoded,
            None => panic!("Character {} is not known", &c)
        })
        .collect();

    for i in 0..3 {
        for s in &to_print {
            print!("{} ", s[i]);
        }
        println!("");
    }
}
<|endoftext|>


<commit_before>mod audio;
mod ioregs;
mod performance;

use egui::{Color32, Context, Visuals};
use gba::{Command, Gba};
use parking_lot::Mutex;
use pyrite::{CallbackId, GbaHandle, GbaThreadState};
use std::{sync::Arc, time::Duration};

#[derive(Default)]
pub struct Debugger {
    current_pane: Pane,
    performance_pane: performance::PerformancePane,
    audio_pane: audio::AudioPane,
    ioregs_pane: ioregs::IoRegistersPane,

    has_initialized: bool,

    gba_data: GbaData,
    gba_data_buffer: Arc<Mutex<GbaData>>,
    frame_callback: Option<CallbackId>,
}

impl Debugger {
    pub fn render(&mut self, ctx: &Context, gba: &GbaHandle) {
        if !self.has_initialized {
            self.init(ctx, gba);
            self.has_initialized = true;
        } else {
            self.fetch_updated_data();

            if self.gba_data.updated {
                ctx.request_repaint();
            }
        }

        egui::CentralPanel::default().show(ctx, |ui| {
            ui.horizontal(|ui| {
                ui.selectable_value(&mut self.current_pane, Pane::Performance, "Performance");
                ui.selectable_value(&mut self.current_pane, Pane::IoRegisters, "IO Registers");
                ui.selectable_value(&mut self.current_pane, Pane::Audio, "Audio");
            });

            match self.current_pane {
                Pane::Performance => self.performance_pane.render(ui, &mut self.gba_data),
                Pane::Audio => self.audio_pane.render(ui, &mut self.gba_data),
                Pane::IoRegisters => self.ioregs_pane.render(ui, &mut self.gba_data),
            }
        });
    }

    fn fetch_updated_data(&mut self) {
        let mut locked = self.gba_data_buffer.lock();
        self.gba_data.copy_data(&mut *locked);
    }

    #[cold]
    pub fn init(&mut self, ctx: &Context, gba: &GbaHandle) {
        let mut visuals = Visuals::dark();
        visuals.override_text_color = Some(Color32::from_rgb(0xe9, 0xec, 0xef));
        ctx.set_visuals(visuals);

        let gba_data_buffer = Arc::clone(&self.gba_data_buffer);
        let callback_id = gba.on_frame(move |gba, state| {
            let mut locked = gba_data_buffer.lock();
            pull_data_from_gba(&mut *locked, gba, state);
        });
        self.frame_callback = Some(callback_id);

        self.ioregs_pane.init();
    }

    pub fn destroy(&mut self, gba: &GbaHandle) {
        if let Some(cb) = self.frame_callback.take() {
            gba.remove_on_frame(cb);
        }
    }
}

#[derive(PartialEq, Clone, Copy)]
enum Pane {
    Performance,
    Audio,
    IoRegisters,
}

impl Default for Pane {
    fn default() -> Self {
        Self::Performance
    }
}

#[derive(Default)]
struct GbaData {
    frame_duration: Option<Duration>,
    frame_processing_duration: Option<Duration>,

    audio_commands: Vec<Command>,
    has_audio_commands: bool,

    ioreg: Option<u32>,

    updated: bool,
    requests: GbaDataRequests,
}

impl GbaData {
    fn copy_data(&mut self, source: &mut GbaData) {
        if !source.updated {
            self.updated = false;
            return;
        }
        self.updated = true;
        source.updated = false;

        self.frame_duration = source.frame_duration.take();
        self.frame_processing_duration = source.frame_processing_duration.take();

        if self.requests.audio_data {
            self.audio_commands.clear();
            self.audio_commands
                .extend(source.audio_commands.iter().rev().copied());
            self.has_audio_commands = source.has_audio_commands;
        } else {
            self.has_audio_commands = false;
        }

        self.ioreg = source.ioreg.take();

        source.requests = std::mem::take(&mut self.requests);
    }
}

#[derive(Default)]
struct GbaDataRequests {
    frame_duration: bool,
    audio_data: bool,
    ioreg: Option<u32>,
}

fn pull_data_from_gba(data: &mut GbaData, gba: &mut Gba, state: &mut GbaThreadState) {
    if data.requests.frame_duration {
        data.frame_duration = Some(state.frame_duration());
        data.frame_processing_duration = Some(state.frame_processing_duration());
    }

    if data.requests.audio_data {
        data.audio_commands.clear();
        data.audio_commands.extend(gba.audio().commands());
        data.has_audio_commands = true;
    } else {
        data.has_audio_commands = false;
    }

    if let Some(address) = data.requests.ioreg {
        data.ioreg = Some(gba.memory_mut().view32(address));
    }

    data.updated = true;
}

const fn rgb(col: u32) -> Color32 {
    Color32::from_rgb((col >> 16) as u8, (col >> 8) as u8, col as u8)
}
<commit_msg>Missing change in previous frame_count commit.<commit_after>mod audio;
mod ioregs;
mod performance;

use egui::{Color32, Context, Visuals};
use gba::{Command, Gba};
use parking_lot::Mutex;
use pyrite::{CallbackId, GbaHandle, GbaThreadState};
use std::{sync::Arc, time::Duration};

#[derive(Default)]
pub struct Debugger {
    current_pane: Pane,
    performance_pane: performance::PerformancePane,
    audio_pane: audio::AudioPane,
    ioregs_pane: ioregs::IoRegistersPane,

    has_initialized: bool,

    gba_data: GbaData,
    gba_data_buffer: Arc<Mutex<GbaData>>,
    frame_callback: Option<CallbackId>,
}

impl Debugger {
    pub fn render(&mut self, ctx: &Context, gba: &GbaHandle) {
        if !self.has_initialized {
            self.init(ctx, gba);
            self.has_initialized = true;
        } else {
            self.fetch_updated_data();

            if self.gba_data.updated {
                ctx.request_repaint();
            }
        }

        egui::CentralPanel::default().show(ctx, |ui| {
            ui.horizontal(|ui| {
                ui.selectable_value(&mut self.current_pane, Pane::Performance, "Performance");
                ui.selectable_value(&mut self.current_pane, Pane::IoRegisters, "IO Registers");
                ui.selectable_value(&mut self.current_pane, Pane::Audio, "Audio");
            });

            match self.current_pane {
                Pane::Performance => self.performance_pane.render(ui, &mut self.gba_data),
                Pane::Audio => self.audio_pane.render(ui, &mut self.gba_data),
                Pane::IoRegisters => self.ioregs_pane.render(ui, &mut self.gba_data),
            }
        });
    }

    fn fetch_updated_data(&mut self) {
        let mut locked = self.gba_data_buffer.lock();
        self.gba_data.copy_data(&mut *locked);
    }

    #[cold]
    pub fn init(&mut self, ctx: &Context, gba: &GbaHandle) {
        let mut visuals = Visuals::dark();
        visuals.override_text_color = Some(Color32::from_rgb(0xe9, 0xec, 0xef));
        ctx.set_visuals(visuals);

        let gba_data_buffer = Arc::clone(&self.gba_data_buffer);
        let callback_id = gba.on_frame(move |gba, state| {
            let mut locked = gba_data_buffer.lock();
            pull_data_from_gba(&mut *locked, gba, state);
        });
        self.frame_callback = Some(callback_id);

        self.ioregs_pane.init();
    }

    pub fn destroy(&mut self, gba: &GbaHandle) {
        if let Some(cb) = self.frame_callback.take() {
            gba.remove_on_frame(cb);
        }
    }
}

#[derive(PartialEq, Clone, Copy)]
enum Pane {
    Performance,
    Audio,
    IoRegisters,
}

impl Default for Pane {
    fn default() -> Self {
        Self::Performance
    }
}

#[derive(Default)]
struct GbaData {
    frame_duration: Option<Duration>,
    frame_processing_duration: Option<Duration>,
    frame_count: Option<u64>,

    audio_commands: Vec<Command>,
    has_audio_commands: bool,

    ioreg: Option<u32>,

    updated: bool,
    requests: GbaDataRequests,
}

impl GbaData {
    fn copy_data(&mut self, source: &mut GbaData) {
        if !source.updated {
            self.updated = false;
            return;
        }
        self.updated = true;
        source.updated = false;

        self.frame_duration = source.frame_duration.take();
        self.frame_processing_duration = source.frame_processing_duration.take();
        self.frame_count = source.frame_count.take();

        if self.requests.audio_data {
            self.audio_commands.clear();
            self.audio_commands
                .extend(source.audio_commands.iter().rev().copied());
            self.has_audio_commands = source.has_audio_commands;
        } else {
            self.has_audio_commands = false;
        }

        self.ioreg = source.ioreg.take();

        source.requests = std::mem::take(&mut self.requests);
    }
}

#[derive(Default)]
struct GbaDataRequests {
    performance: bool,
    audio_data: bool,
    ioreg: Option<u32>,
}

fn pull_data_from_gba(data: &mut GbaData, gba: &mut Gba, state: &mut GbaThreadState) {
    if data.requests.performance {
        data.frame_duration = Some(state.frame_duration());
        data.frame_processing_duration = Some(state.frame_processing_duration());
        data.frame_count = Some(state.frame_count());
    }

    if data.requests.audio_data {
        data.audio_commands.clear();
        data.audio_commands.extend(gba.audio().commands());
        data.has_audio_commands = true;
    } else {
        data.has_audio_commands = false;
    }

    if let Some(address) = data.requests.ioreg {
        data.ioreg = Some(gba.memory_mut().view32(address));
    }

    data.updated = true;
}

const fn rgb(col: u32) -> Color32 {
    Color32::from_rgb((col >> 16) as u8, (col >> 8) as u8, col as u8)
}
<|endoftext|>