<commit_before>// Copyright (C) 2017 É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 error::Result;
use book::Book;

use crowbook_text_processing::escape;

#[cfg(feature="syntect")]
use syntect;

/// Wrapper around syntect, so it can be more easily optionally compiled.
#[cfg(feature="syntect")]
pub struct Syntax {
    syntax_set: syntect::parsing::SyntaxSet,
    theme: syntect::highlighting::Theme,
}

#[cfg(not(feature="syntect"))]
pub struct Syntax {}

#[cfg(feature="syntect")]
impl Syntax {
    /// Creates a new Syntax wrapper
    pub fn new(book: &Book, theme_name: &str) -> Syntax {
        let mut theme_set = syntect::highlighting::ThemeSet::load_defaults();
        let theme = match theme_set.themes.remove(theme_name) {
            Some(theme) => theme,
            None => {
                book.logger.error(lformat!("could not set syntect theme to {theme}, defaulting to \"InspiredGithub\"",
                                           theme = theme_name));
                book.logger.info(lformat!("valid theme names are: {themes}",
                                          themes = theme_set.themes
                                          .keys()
                                          .map(|s| s.to_owned())
                                          .collect::<Vec<_>>()
                                          .join(", ")));
                theme_set.themes.remove("InspiredGitHub").unwrap()
            }
        };
        Syntax {
            syntax_set: syntect::parsing::SyntaxSet::load_defaults_nonewlines(),
            theme: theme,
        }
    }

    /// Convert a string containing code to HTML
    pub fn to_html(&self, code: &str, language: &str) -> Result<String> {
        let syntax = self.syntax_set.find_syntax_by_token(language)
            .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
        let mut h = syntect::easy::HighlightLines::new(syntax, &self.theme);
        let regions = h.highlight(code);
        Ok(format!("
{}
",
                   syntect::html::styles_to_coloured_html(&regions[..],
                                                          syntect::html::IncludeBackground::No)))
    }

    pub fn to_tex(&self, code: &str, language: &str) -> Result<String> {
        use latex::insert_breaks;
        use syntect::highlighting::{BLACK, FONT_STYLE_BOLD, FONT_STYLE_ITALIC, FONT_STYLE_UNDERLINE};
        let syntax = self.syntax_set.find_syntax_by_token(language)
            .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
        let mut h = syntect::easy::HighlightLines::new(syntax, &self.theme);
        let regions = h.highlight(code);

        let mut result = String::with_capacity(code.len());
        for (style, text) in regions {
            let mut content = escape::tex(text).into_owned();
            content = insert_breaks(&content);
            content = content.replace('\n', "\\\\\n")
                .replace(' ', "\\hphantom{ }\\allowbreak{}");
            content = format!("\\texttt{{{}}}", content);
            if style.foreground != BLACK {
                let r = style.foreground.r as f32 / 255.0;
                let g = style.foreground.g as f32 / 255.0;
                let b = style.foreground.b as f32 / 255.0;
                content = format!("\\textcolor[rgb]{{{r}, {g}, {b}}}{{{text}}}",
                                  r = r,
                                  g = g,
                                  b = b,
                                  text = content);
            }
            if style.font_style.contains(FONT_STYLE_BOLD) {
                content = format!("\\textbf{{{}}}", content);
            }
            if style.font_style.contains(FONT_STYLE_ITALIC) {
                content = format!("\\emph{{{}}}", content);
            }
            if style.font_style.contains(FONT_STYLE_UNDERLINE) {
                content = format!("\\underline{{{}}}", content);
            }
            result.push_str(&content);
        }
        Ok(format!("{{\\sloppy {}}}", result))
    }
}

#[cfg(not(feature="syntect"))]
impl Syntax {
    pub fn new(book: &Book, _: &str) -> Syntax {
        book.logger.error(lformat!("crowbook was compiled without syntect support, syntax highlighting will be disabled"));
        Syntax {}
    }

    pub fn to_html(&self, code: &str, language: &str) -> Result<String> {
        Ok(format!("
{code}
",
                code = escape::html(code),
                lang = language))
    }

    pub fn to_tex(&self, code: &str, _: &str) -> Result<String> {
        Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n",
                code))
    }
}
<commit_msg>Fix syntax::to_tex<commit_after>// Copyright (C) 2017 É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 error::Result;
use book::Book;

use crowbook_text_processing::escape;

#[cfg(feature="syntect")]
use syntect;

/// Wrapper around syntect, so it can be more easily optionally compiled.
#[cfg(feature="syntect")]
pub struct Syntax {
    syntax_set: syntect::parsing::SyntaxSet,
    theme: syntect::highlighting::Theme,
}

#[cfg(not(feature="syntect"))]
pub struct Syntax {}

#[cfg(feature="syntect")]
impl Syntax {
    /// Creates a new Syntax wrapper
    pub fn new(book: &Book, theme_name: &str) -> Syntax {
        let mut theme_set = syntect::highlighting::ThemeSet::load_defaults();
        let theme = match theme_set.themes.remove(theme_name) {
            Some(theme) => theme,
            None => {
                book.logger.error(lformat!("could not set syntect theme to {theme}, defaulting to \"InspiredGithub\"",
                                           theme = theme_name));
                book.logger.info(lformat!("valid theme names are: {themes}",
                                          themes = theme_set.themes
                                          .keys()
                                          .map(|s| s.to_owned())
                                          .collect::<Vec<_>>()
                                          .join(", ")));
                theme_set.themes.remove("InspiredGitHub").unwrap()
            }
        };
        Syntax {
            syntax_set: syntect::parsing::SyntaxSet::load_defaults_nonewlines(),
            theme: theme,
        }
    }

    /// Convert a string containing code to HTML
    pub fn to_html(&self, code: &str, language: &str) -> Result<String> {
        let syntax = self.syntax_set.find_syntax_by_token(language)
            .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
        let mut h = syntect::easy::HighlightLines::new(syntax, &self.theme);
        let regions = h.highlight(code);
        Ok(format!("
{}
",
                   syntect::html::styles_to_coloured_html(&regions[..],
                                                          syntect::html::IncludeBackground::No)))
    }

    pub fn to_tex(&self, code: &str, language: &str) -> Result<String> {
        use latex::insert_breaks;
        use syntect::highlighting::{BLACK, FONT_STYLE_BOLD, FONT_STYLE_ITALIC, FONT_STYLE_UNDERLINE};
        let syntax = self.syntax_set.find_syntax_by_token(language)
            .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
        let mut h = syntect::easy::HighlightLines::new(syntax, &self.theme);
        let regions = h.highlight(code);

        let mut result = String::with_capacity(code.len());
        for (style, text) in regions {
            let mut content = escape::tex(text).into_owned();
            content = insert_breaks(&content);
            content = content.replace('\n', "\\\\{}\n")
                .replace(' ', "\\hphantom{ }\\allowbreak{}");
            content = format!("\\texttt{{{}}}", content);
            if style.foreground != BLACK {
                let r = style.foreground.r as f32 / 255.0;
                let g = style.foreground.g as f32 / 255.0;
                let b = style.foreground.b as f32 / 255.0;
                content = format!("\\textcolor[rgb]{{{r}, {g}, {b}}}{{{text}}}",
                                  r = r,
                                  g = g,
                                  b = b,
                                  text = content);
            }
            if style.font_style.contains(FONT_STYLE_BOLD) {
                content = format!("\\textbf{{{}}}", content);
            }
            if style.font_style.contains(FONT_STYLE_ITALIC) {
                content = format!("\\emph{{{}}}", content);
            }
            if style.font_style.contains(FONT_STYLE_UNDERLINE) {
                content = format!("\\underline{{{}}}", content);
            }
            result.push_str(&content);
        }
        Ok(format!("{{\\sloppy {}}}", result))
    }
}

#[cfg(not(feature="syntect"))]
impl Syntax {
    pub fn new(book: &Book, _: &str) -> Syntax {
        book.logger.error(lformat!("crowbook was compiled without syntect support, syntax highlighting will be disabled"));
        Syntax {}
    }

    pub fn to_html(&self, code: &str, language: &str) -> Result<String> {
        Ok(format!("
{code}
",
                code = escape::html(code),
                lang = language))
    }

    pub fn to_tex(&self, code: &str, _: &str) -> Result<String> {
        Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n",
                code))
    }
}
<|endoftext|>


<commit_before>// TODO support reading from standard input

extern crate extra;
extern crate termion;

use std::env::args;
use std::fs::File;
use std::io::{self, Write, Read};
use std::path::{Path, PathBuf};
use std::str::Chars;

use extra::option::OptionalExt;

use termion::{clear, color, cursor, style, terminal_size};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};

static MAN_PAGE: &str = /* @MANSTART{mdless} */ r#"
NAME
    mdless- view a markdown file.

SYNOPSIS
    mdless [-h | --help] [input]

DESCRIPTION
    This utility views md files. If no input file is specified as an argument, standard input is
    used.

OPTIONS
    --help, -h
        Print this manual page.

AUTHOR
    This program was written by MovingtoMars and Jeremy Soller for Redox OS. Bugs, issues, or feature requests should
    be reported in the Gitlab repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 MovingtoMars, Jeremy Soller

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

#[cfg(target_os = "redox")]
fn terminal_path() -> String {
    use std::env;
    env::var("TTY").unwrap()
}

#[cfg(not(target_os = "redox"))]
fn terminal_path() -> String {
    "/dev/tty".to_string()
}

fn main() {
    let mut args = args().skip(1).peekable();
    let mut stdout = io::stdout();
    let stdin = io::stdin();
    let mut stdin = stdin.lock();
    let mut stderr = io::stderr();

    if let Some(x) = args.peek() {
        if x == "--help" || x == "-h" {
            // Print help.
            print!("{}", MAN_PAGE);
            return;
        }
    } else {
        let mut terminal = File::open(terminal_path()).try(&mut stderr);
        run(PathBuf::from("-"), &mut stdin, &mut terminal, &mut stdout).try(&mut stderr);
    };

    while let Some(filename) = args.next() {
        let filepath = PathBuf::from(filename.as_str());
        let file = File::open(&filepath);
        match file {
            Ok(mut open_file) => {
                if let Err(err) = run(filepath, &mut open_file, &mut stdin, &mut io::stdout()) {
                    eprintln!("{}: {}", &filename, err);
                }
            }
            Err(err) => {
                eprintln!("{}: {}", &filename, err);
            }
        }
    }
}

//TODO: String in Text
enum Block {
    Text(char),
    Bold(Vec<Block>),
    Italic(Vec<Block>),
    Code(Vec<Block>),
    Link(Vec<Block>, String),
}

impl Block {
    fn parse_bold(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '*' && s.as_str().chars().next() == Some('*') {
                let _ = s.next();
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Bold(blocks)
    }

    fn parse_italic(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '*' {
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Italic(blocks)
    }

    fn parse_code(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '`' {
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Code(blocks)
    }

    fn parse_link(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();
        let mut link = String::new();

        while let Some(c) = s.next() {
            match c {
                ']' => break,
                _ => blocks.push(Block::Text(c))
            }
        }

        if s.as_str().chars().next() == Some('(') {
            while let Some(c) = s.next() {
                match c {
                    '(' => (),
                    ')' => break,
                    _ => link.push(c)
                }
            }
        }

        Block::Link(blocks, link)
    }

    fn parse(s: &mut Chars) -> Vec<Block> {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            match c {
                '*' => if s.as_str().chars().next() == Some('*') {
                    let _ = s.next();
                    blocks.push(Block::parse_bold(s));
                } else {
                    blocks.push(Block::parse_italic(s));
                },
                '`' => blocks.push(Block::parse_code(s)),
                '[' => blocks.push(Block::parse_link(s)),
                _ => blocks.push(Block::Text(c))
            }
        }

        blocks
    }

    fn draw<W: IntoRawMode>(&self, to: &mut RawTerminal<W>, path: &PathBuf, next: &mut Vec<PathBuf>, next_i: usize) -> std::io::Result<usize> {
        let mut count = 0;

        match *self {
            Block::Text(c) => {
                count += to.write(&[c as u8])?;
            },
            Block::Bold(ref blocks) => {
                write!(to, "{}", style::Bold)?;
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", style::NoBold)?;
            },
            Block::Italic(ref blocks) => {
                write!(to, "{}", style::Italic)?;
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", style::NoItalic)?;
            },
            Block::Code(ref blocks) => {
                write!(to, "{}", color::Bg(color::AnsiValue::grayscale(6)))?;
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", color::Bg(color::Reset))?;
            },
            Block::Link(ref blocks, ref link) => {
                let highlight = next.len() == next_i;

                if link.starts_with('/') {
                    next.push(PathBuf::from(link));
                } else {
                    let mut next_path = path.clone();
                    next_path.pop();

                    for part in Path::new(&link).iter() {
                        match part.to_str().unwrap() {
                            "." => {},
                            ".." => {
                                while next_path.ends_with(".") && next_path.pop() {}
                                if next_path.ends_with("..") || ! next_path.pop() {
                                    next_path.push(part);
                                }
                            },
                            _ => next_path.push(part)
                        }
                    }

                    next.push(next_path);
                }

                write!(to, "{}", style::Underline)?;
                if highlight {
                    write!(to, "{}", style::Invert)?;
                }
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", style::NoUnderline)?;
                if highlight {
                    write!(to, "{}", style::NoInvert)?;
                }
            },
        }

        Ok(count)
    }
}

struct Buffer {
    lines: Vec<Vec<Block>>,
    y_off: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer{
            lines: Vec::new(),
            y_off: 0,
        }
    }

    fn read(&mut self, from: &mut dyn Read) -> std::io::Result<usize> {
        let mut tmp = String::new();
        let res = from.read_to_string(&mut tmp)?;

        self.lines.append(&mut tmp
            .as_str()
            .split('\n')
            .map(|x| { Block::parse(&mut x.chars()) })
            .collect());

        Ok(res)
    }

    fn draw<W: IntoRawMode>(&self, to: &mut RawTerminal<W>, path: &PathBuf, next: &mut Vec<PathBuf>, next_i: usize, w: u16, h: u16) -> std::io::Result<usize> {
        write!(to, "{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1))?;

        let mut y = 0;
        let mut count = 0;

        let lines = if self.lines.len() <= h as usize {
            &self.lines
        } else if self.y_off as usize >= self.lines.len() {
            &self.lines[0..0]
        } else {
            &self.lines[self.y_off as usize..]
        };

        for line in lines {
            for block in line.iter() {
                let x = block.draw(to, path, next, next_i)?;
                count += x;
                if x >= w as usize {
                    break;
                }
            }

            y += 1;
            write!(to, "{}", cursor::Goto(1, y + 1))?;
            if y >= h {
                break;
            }
        }

        Ok(count)
    }

    fn scroll_up(&mut self) {
        if self.y_off > 0 {
            self.y_off -= 1;
        }
    }

    fn scroll_down(&mut self, height: u16) {
        if ((self.y_off + height) as usize) <= self.lines.len() {
            self.y_off += 1;
        }
    }
}

fn run<W: IntoRawMode>(mut path: PathBuf, file: &mut dyn Read, controls: &mut dyn Read, stdout: &mut W) -> std::io::Result<()> {
    let mut stdout = stdout.into_raw_mode()?;

    let (w, h) = {
        let (w, h) = terminal_size()?;
        (w as u16, h as u16)
    };

    let mut next = Vec::new();
    let mut next_i = 0;

    let mut buffer = Buffer::new();
    buffer.read(file)?;

    buffer.draw(&mut stdout, &path, &mut next, next_i, w, h - 1)?;

    write!(stdout, "{}{}{} Press q to exit.{}", cursor::Goto(1, h), style::Invert, path.display(), style::NoInvert)?;

    stdout.flush()?;

    for c in controls.keys() {
        match c.unwrap() {
            Key::Char('q') => {
                write!(stdout, "{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1))?;
                break;
            },
            Key::Char('b') => for _i in 1..h {
                buffer.scroll_up()
            },
            Key::Char(' ') => for _i in 1..h {
                buffer.scroll_down(h)
            },
            Key::Up | Key::Char('k') => buffer.scroll_up(),
            Key::Down | Key::Char('j') => buffer.scroll_down(h),
            Key::Char('\t') => {
                next_i += 1;
                if next_i >= next.len() {
                    next_i = 0;
                }
            },
            Key::Char('\r') | Key::Char('\n') => {
                if let Some(next_path) = next.get(next_i) {
                    if let Ok(mut next_file) = File::open(&next_path) {
                        path = next_path.clone();
                        buffer = Buffer::new();
                        buffer.read(&mut next_file)?;
                        next_i = 0;
                    }
                }
            }
            _ => {},
        }

        next = Vec::new();

        buffer.draw(&mut stdout, &path, &mut next, next_i, w, h - 1)?;

        write!(stdout, "{}{}{} Press q to exit.{}", cursor::Goto(1, h), style::Invert, path.display(), style::NoInvert)?;

        stdout.flush()?;
    }

    Ok(())
}
<commit_msg>Modernize: use starts_with() and for loops<commit_after>// TODO support reading from standard input

extern crate extra;
extern crate termion;

use std::env::args;
use std::fs::File;
use std::io::{self, Write, Read};
use std::path::{Path, PathBuf};
use std::str::Chars;

use extra::option::OptionalExt;

use termion::{clear, color, cursor, style, terminal_size};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};

static MAN_PAGE: &str = /* @MANSTART{mdless} */ r#"
NAME
    mdless- view a markdown file.

SYNOPSIS
    mdless [-h | --help] [input]

DESCRIPTION
    This utility views md files. If no input file is specified as an argument, standard input is
    used.

OPTIONS
    --help, -h
        Print this manual page.

AUTHOR
    This program was written by MovingtoMars and Jeremy Soller for Redox OS. Bugs, issues, or feature requests should
    be reported in the Gitlab repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 MovingtoMars, Jeremy Soller

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

#[cfg(target_os = "redox")]
fn terminal_path() -> String {
    use std::env;
    env::var("TTY").unwrap()
}

#[cfg(not(target_os = "redox"))]
fn terminal_path() -> String {
    "/dev/tty".to_string()
}

fn main() {
    let mut args = args().skip(1).peekable();
    let mut stdout = io::stdout();
    let stdin = io::stdin();
    let mut stdin = stdin.lock();
    let mut stderr = io::stderr();

    if let Some(x) = args.peek() {
        if x == "--help" || x == "-h" {
            // Print help.
            print!("{}", MAN_PAGE);
            return;
        }
    } else {
        let mut terminal = File::open(terminal_path()).try(&mut stderr);
        run(PathBuf::from("-"), &mut stdin, &mut terminal, &mut stdout).try(&mut stderr);
    };

    for filename in args {
        let filepath = PathBuf::from(filename.as_str());
        let file = File::open(&filepath);
        match file {
            Ok(mut open_file) => {
                if let Err(err) = run(filepath, &mut open_file, &mut stdin, &mut io::stdout()) {
                    eprintln!("{}: {}", &filename, err);
                }
            }
            Err(err) => {
                eprintln!("{}: {}", &filename, err);
            }
        }
    }
}

//TODO: String in Text
enum Block {
    Text(char),
    Bold(Vec<Block>),
    Italic(Vec<Block>),
    Code(Vec<Block>),
    Link(Vec<Block>, String),
}

impl Block {
    fn parse_bold(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '*' && s.as_str().starts_with('*') {
                let _ = s.next();
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Bold(blocks)
    }

    fn parse_italic(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        for c in s {
            if c == '*' {
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Italic(blocks)
    }

    fn parse_code(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        for c in s {
            if c == '`' {
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Code(blocks)
    }

    #[allow(clippy::while_let_on_iterator)]
    fn parse_link(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();
        let mut link = String::new();

        while let Some(c) = s.next() {
            match c {
                ']' => break,
                _ => blocks.push(Block::Text(c))
            }
        }

        if s.as_str().starts_with('(') {
            while let Some(c) = s.next() {
                match c {
                    '(' => (),
                    ')' => break,
                    _ => link.push(c)
                }
            }
        }

        Block::Link(blocks, link)
    }

    fn parse(s: &mut Chars) -> Vec<Block> {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            match c {
                '*' => if s.as_str().starts_with('*') {
                    let _ = s.next();
                    blocks.push(Block::parse_bold(s));
                } else {
                    blocks.push(Block::parse_italic(s));
                },
                '`' => blocks.push(Block::parse_code(s)),
                '[' => blocks.push(Block::parse_link(s)),
                _ => blocks.push(Block::Text(c))
            }
        }

        blocks
    }

    fn draw<W: IntoRawMode>(&self, to: &mut RawTerminal<W>, path: &PathBuf, next: &mut Vec<PathBuf>, next_i: usize) -> std::io::Result<usize> {
        let mut count = 0;

        match *self {
            Block::Text(c) => {
                count += to.write(&[c as u8])?;
            },
            Block::Bold(ref blocks) => {
                write!(to, "{}", style::Bold)?;
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", style::NoBold)?;
            },
            Block::Italic(ref blocks) => {
                write!(to, "{}", style::Italic)?;
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", style::NoItalic)?;
            },
            Block::Code(ref blocks) => {
                write!(to, "{}", color::Bg(color::AnsiValue::grayscale(6)))?;
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", color::Bg(color::Reset))?;
            },
            Block::Link(ref blocks, ref link) => {
                let highlight = next.len() == next_i;

                if link.starts_with('/') {
                    next.push(PathBuf::from(link));
                } else {
                    let mut next_path = path.clone();
                    next_path.pop();

                    for part in Path::new(&link).iter() {
                        match part.to_str().unwrap() {
                            "." => {},
                            ".." => {
                                while next_path.ends_with(".") && next_path.pop() {}
                                if next_path.ends_with("..") || ! next_path.pop() {
                                    next_path.push(part);
                                }
                            },
                            _ => next_path.push(part)
                        }
                    }

                    next.push(next_path);
                }

                write!(to, "{}", style::Underline)?;
                if highlight {
                    write!(to, "{}", style::Invert)?;
                }
                for block in blocks.iter() {
                    count += block.draw(to, path, next, next_i)?;
                }
                write!(to, "{}", style::NoUnderline)?;
                if highlight {
                    write!(to, "{}", style::NoInvert)?;
                }
            },
        }

        Ok(count)
    }
}

struct Buffer {
    lines: Vec<Vec<Block>>,
    y_off: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer{
            lines: Vec::new(),
            y_off: 0,
        }
    }

    fn read(&mut self, from: &mut dyn Read) -> std::io::Result<usize> {
        let mut tmp = String::new();
        let res = from.read_to_string(&mut tmp)?;

        self.lines.append(&mut tmp
            .as_str()
            .split('\n')
            .map(|x| { Block::parse(&mut x.chars()) })
            .collect());

        Ok(res)
    }

    #[allow(clippy::explicit_counter_loop)]
    fn draw<W: IntoRawMode>(&self, to: &mut RawTerminal<W>, path: &PathBuf, next: &mut Vec<PathBuf>, next_i: usize, w: u16, h: u16) -> std::io::Result<usize> {
        write!(to, "{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1))?;

        let mut y = 0;
        let mut count = 0;

        let lines = if self.lines.len() <= h as usize {
            &self.lines
        } else if self.y_off as usize >= self.lines.len() {
            &self.lines[0..0]
        } else {
            &self.lines[self.y_off as usize..]
        };

        for line in lines {
            for block in line.iter() {
                let x = block.draw(to, path, next, next_i)?;
                count += x;
                if x >= w as usize {
                    break;
                }
            }

            y += 1;
            write!(to, "{}", cursor::Goto(1, y + 1))?;
            if y >= h {
                break;
            }
        }

        Ok(count)
    }

    fn scroll_up(&mut self) {
        if self.y_off > 0 {
            self.y_off -= 1;
        }
    }

    fn scroll_down(&mut self, height: u16) {
        if ((self.y_off + height) as usize) <= self.lines.len() {
            self.y_off += 1;
        }
    }
}

fn run<W: IntoRawMode>(mut path: PathBuf, file: &mut dyn Read, controls: &mut dyn Read, stdout: &mut W) -> std::io::Result<()> {
    let mut stdout = stdout.into_raw_mode()?;

    let (w, h) = {
        let (w, h) = terminal_size()?;
        (w as u16, h as u16)
    };

    let mut next = Vec::new();
    let mut next_i = 0;

    let mut buffer = Buffer::new();
    buffer.read(file)?;

    buffer.draw(&mut stdout, &path, &mut next, next_i, w, h - 1)?;

    write!(stdout, "{}{}{} Press q to exit.{}", cursor::Goto(1, h), style::Invert, path.display(), style::NoInvert)?;

    stdout.flush()?;

    for c in controls.keys() {
        match c.unwrap() {
            Key::Char('q') => {
                write!(stdout, "{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1))?;
                break;
            },
            Key::Char('b') => for _i in 1..h {
                buffer.scroll_up()
            },
            Key::Char(' ') => for _i in 1..h {
                buffer.scroll_down(h)
            },
            Key::Up | Key::Char('k') => buffer.scroll_up(),
            Key::Down | Key::Char('j') => buffer.scroll_down(h),
            Key::Char('\t') => {
                next_i += 1;
                if next_i >= next.len() {
                    next_i = 0;
                }
            },
            Key::Char('\r') | Key::Char('\n') => {
                if let Some(next_path) = next.get(next_i) {
                    if let Ok(mut next_file) = File::open(&next_path) {
                        path = next_path.clone();
                        buffer = Buffer::new();
                        buffer.read(&mut next_file)?;
                        next_i = 0;
                    }
                }
            }
            _ => {},
        }

        next = Vec::new();

        buffer.draw(&mut stdout, &path, &mut next, next_i, w, h - 1)?;

        write!(stdout, "{}{}{} Press q to exit.{}", cursor::Goto(1, h), style::Invert, path.display(), style::NoInvert)?;

        stdout.flush()?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>/// Represents an ASN.1 `BIT STRING`.
#[derive(Debug, PartialEq, Clone)]
pub struct BitString<'a> {
    data: &'a [u8],
    padding_bits: u8,
}

impl<'a> BitString<'a> {
    pub(crate) fn new(data: &'a [u8], padding_bits: u8) -> Option<BitString<'a>> {
        if padding_bits > 7 || (data.is_empty() && padding_bits != 0) {
            return None;
        }
        if padding_bits > 0 && data[data.len() - 1] & ((1 << padding_bits) - 1) != 0 {
            return None;
        }

        Some(BitString { data, padding_bits })
    }

    /// Returns a sequence of bytes representing the data in the `BIT STRING`. Padding bits will
    /// always be 0.
    pub fn as_bytes(&self) -> &'a [u8] {
        self.data
    }

    /// Returns the number of padding bits. Will always be in [0, 8).
    pub fn padding_bits(&self) -> u8 {
        self.padding_bits
    }

    /// Returns whether the requested bit is set. Padding bits will always return false and
    /// asking for bits that exceed the length of the bit string will also return false.
    pub fn has_bit_set(&self, n: usize) -> bool {
        let idx = n / 8;
        let v = 1 << (7 - (n & 0x07));
        if self.data.len() < (idx + 1) {
            false
        } else {
            self.data[idx] & v != 0
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::BitString;

    #[test]
    fn test_bitstring_new() {
        assert_eq!(BitString::new(b"abc", 8), None);
        assert_eq!(BitString::new(b"", 2), None);
        assert_eq!(BitString::new(b"\xff", 1), None);

        assert!(BitString::new(b"\xff", 0).is_some());
        assert!(BitString::new(b"\xfe", 1).is_some());
    }

    #[test]
    fn test_bitstring_as_bytes() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.as_bytes(), b"\xfe");
    }

    #[test]
    fn test_bitstring_padding_bits() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.padding_bits(), 1);
        let bs = BitString::new(b"\xe0", 5).unwrap();
        assert_eq!(bs.padding_bits(), 5);
    }

    #[test]
    fn test_bitstring_has_bit_set() {
        let bs = BitString::new(b"\x80", 0).unwrap();
        assert!(bs.has_bit_set(0));
        assert!(!bs.has_bit_set(1));
        assert!(!bs.has_bit_set(7));
        // An arbitrary bit much bigger than the underlying size of the bitfield
        assert!(!bs.has_bit_set(50));
        let bs = BitString::new(b"\xc0", 4).unwrap();
        // padding bits should always return false when asking if the bit is set
        assert!(bs.has_bit_set(0));
        assert!(bs.has_bit_set(1));
        assert!(!bs.has_bit_set(2));
        assert!(!bs.has_bit_set(3));
        assert!(!bs.has_bit_set(4));
        assert!(!bs.has_bit_set(5));
        assert!(!bs.has_bit_set(6));
        assert!(!bs.has_bit_set(7));
    }
}
<commit_msg>bit string is hashable (#177)<commit_after>/// Represents an ASN.1 `BIT STRING`.
#[derive(Debug, PartialEq, Clone, Hash)]
pub struct BitString<'a> {
    data: &'a [u8],
    padding_bits: u8,
}

impl<'a> BitString<'a> {
    pub(crate) fn new(data: &'a [u8], padding_bits: u8) -> Option<BitString<'a>> {
        if padding_bits > 7 || (data.is_empty() && padding_bits != 0) {
            return None;
        }
        if padding_bits > 0 && data[data.len() - 1] & ((1 << padding_bits) - 1) != 0 {
            return None;
        }

        Some(BitString { data, padding_bits })
    }

    /// Returns a sequence of bytes representing the data in the `BIT STRING`. Padding bits will
    /// always be 0.
    pub fn as_bytes(&self) -> &'a [u8] {
        self.data
    }

    /// Returns the number of padding bits. Will always be in [0, 8).
    pub fn padding_bits(&self) -> u8 {
        self.padding_bits
    }

    /// Returns whether the requested bit is set. Padding bits will always return false and
    /// asking for bits that exceed the length of the bit string will also return false.
    pub fn has_bit_set(&self, n: usize) -> bool {
        let idx = n / 8;
        let v = 1 << (7 - (n & 0x07));
        if self.data.len() < (idx + 1) {
            false
        } else {
            self.data[idx] & v != 0
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::BitString;

    #[test]
    fn test_bitstring_new() {
        assert_eq!(BitString::new(b"abc", 8), None);
        assert_eq!(BitString::new(b"", 2), None);
        assert_eq!(BitString::new(b"\xff", 1), None);

        assert!(BitString::new(b"\xff", 0).is_some());
        assert!(BitString::new(b"\xfe", 1).is_some());
    }

    #[test]
    fn test_bitstring_as_bytes() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.as_bytes(), b"\xfe");
    }

    #[test]
    fn test_bitstring_padding_bits() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.padding_bits(), 1);
        let bs = BitString::new(b"\xe0", 5).unwrap();
        assert_eq!(bs.padding_bits(), 5);
    }

    #[test]
    fn test_bitstring_has_bit_set() {
        let bs = BitString::new(b"\x80", 0).unwrap();
        assert!(bs.has_bit_set(0));
        assert!(!bs.has_bit_set(1));
        assert!(!bs.has_bit_set(7));
        // An arbitrary bit much bigger than the underlying size of the bitfield
        assert!(!bs.has_bit_set(50));
        let bs = BitString::new(b"\xc0", 4).unwrap();
        // padding bits should always return false when asking if the bit is set
        assert!(bs.has_bit_set(0));
        assert!(bs.has_bit_set(1));
        assert!(!bs.has_bit_set(2));
        assert!(!bs.has_bit_set(3));
        assert!(!bs.has_bit_set(4));
        assert!(!bs.has_bit_set(5));
        assert!(!bs.has_bit_set(6));
        assert!(!bs.has_bit_set(7));
    }
}
<|endoftext|>


<commit_before>
//! Various methods for computing with vectors.

use {Matrix2d, Ray, Vec2d, Rectangle, RoundRectangle};

/// Multiplies two matrices.
#[inline(always)]
pub fn multiply(m: &[f64, ..6], b: &[f64, ..6]) -> Matrix2d {
    [m[0]*b[0]+m[1]*b[3]+m[2]*0.0,  m[0]*b[1]+m[1]*b[4]+m[2]*0.0,  m[0]*b[2]+m[1]*b[5]+m[2]*1.0,
     m[3]*b[0]+m[4]*b[3]+m[5]*0.0,  m[3]*b[1]+m[4]*b[4]+m[5]*0.0,  m[3]*b[2]+m[4]*b[5]+m[5]*1.0]
}

/// Creates a translation matrix.
#[inline(always)]
pub fn translate(x: f64, y: f64) -> Matrix2d {
    [1.0, 0.0, x,
     0.0, 1.0, y]
}

/// Creates a rotation matrix.
#[inline(always)]
pub fn rotate_radians(angle: f64) -> Matrix2d {
    let c = angle.cos();
    let s = angle.sin();
    [c, s, 0.0,
    -s, c, 0.0]
}

/// Create a scale matrix.
#[inline(always)]
pub fn scale(sx: f64, sy: f64) -> Matrix2d {
    [sx, 0.0, 0.0,
     0.0, sy, 0.0]
}

/// Create a shear matrix.
#[inline(always)]
pub fn shear(sx: f64, sy: f64) -> Matrix2d {
    [1.0, sx, 0.0,
     sy, 1.0, 0.0]
}

/// Compute the shortest vector from point to ray.
/// A ray stores starting point and directional vector.
#[inline(always)]
pub fn separation(ray: Ray, x: f64, y: f64) -> Vec2d {
    // Get the directional vector.
    let (dir_x, dir_y) = (ray[2], ray[3]);
    // Get displacement vector from point.
    let (dx, dy) = (ray[0] - x, ray[1] - y);
    // Compute the component of position in ray direction.
    let dot = dir_x * x + dir_y * y;
    // The directional vector multiplied with the dot gives us a parallel vector.
    // When we subtract this from the displacement we get a vector normal to the ray.
    // This is the shortest vector from the point to the ray.
    [dx - dot * dir_x, dy - dot * dir_y]
}

/// Returns the least separation out of four.
/// Each seperation can be computed using `separation` function.
/// The separation returned can be used to solve collision of rectangles.
#[inline(always)]
pub fn least_separation_4(sep1: Vec2d, sep2: Vec2d, sep3: Vec2d, sep4: Vec2d) -> Vec2d {
    let dot1 = sep1[0] * sep1[0] + sep1[1] * sep1[1];
    let dot2 = sep2[0] * sep2[0] + sep2[1] * sep2[1];
    let dot3 = sep3[0] * sep3[0] + sep3[1] * sep3[1];
    let dot4 = sep4[0] * sep4[0] + sep4[1] * sep4[1];
    // Search for the smallest dot product.
    if dot1 < dot2 {
        if dot3 < dot4 {
            if dot1 < dot3 { sep1 } else { sep3 }
        } else {
            if dot1 < dot4 { sep1 } else { sep4 }
        }
    } else {
        if dot3 < dot4 {
            if dot2 < dot3 { sep2 } else { sep3 }
        } else {
            if dot2 < dot4 { sep2 } else { sep4 }
        }
    }
}

/// Shrinks a rectangle by a factor on all sides.
#[inline(always)]
pub fn margin_rectangle(rect: &Rectangle, m: f64) -> Rectangle {
    let w = rect[2] - 2.0 * m;
    let h = rect[3] - 2.0 * m;
    let (x, w) = if w < 0.0 { (rect[0] + 0.5 * rect[2], 0.0) } else { (rect[0] + m, w) };
    let (y, h) = if h < 0.0 { (rect[1] + 0.5 * rect[3], 0.0) } else { (rect[1] + m, h) };
    [x, y, w, h]
}

/// Shrinks a rectangle by a factor on all sides.
#[inline(always)]
pub fn margin_round_rectangle(rect: &RoundRectangle, m: f64) -> RoundRectangle {
    let w = rect[2] - 2.0 * m;
    let h = rect[3] - 2.0 * m;
    let (x, w) = if w < 0.0 { (rect[0] + 0.5 * rect[2], 0.0) } else { (rect[0] + m, w) };
    let (y, h) = if h < 0.0 { (rect[1] + 0.5 * rect[3], 0.0) } else { (rect[1] + m, h) };
    [x, y, w, h, rect[4]]
}

/// Computes a relative rectangle using the rectangle as a tile.
#[inline(always)]
pub fn relative_rectangle(rect: &Rectangle, x: f64, y: f64) -> Rectangle {
    [rect[0] + x * rect[2], rect[1] + y * rect[3], rect[2], rect[3]]
}

/// Computes a relative round rectangle using the round rectangle as a tile.
#[inline(always)]
pub fn relative_round_rectangle(rect: &RoundRectangle, x: f64, y: f64) -> RoundRectangle {
    [rect[0] + x * rect[2], rect[1] + y * rect[3], rect[2], rect[3], rect[4]]
}

<commit_msg>Added modular offset functions<commit_after>
//! Various methods for computing with vectors.

use {Matrix2d, Ray, Vec2d, Rectangle, RoundRectangle};

/// Multiplies two matrices.
#[inline(always)]
pub fn multiply(m: &[f64, ..6], b: &[f64, ..6]) -> Matrix2d {
    [m[0]*b[0]+m[1]*b[3]+m[2]*0.0,  m[0]*b[1]+m[1]*b[4]+m[2]*0.0,  m[0]*b[2]+m[1]*b[5]+m[2]*1.0,
     m[3]*b[0]+m[4]*b[3]+m[5]*0.0,  m[3]*b[1]+m[4]*b[4]+m[5]*0.0,  m[3]*b[2]+m[4]*b[5]+m[5]*1.0]
}

/// Creates a translation matrix.
#[inline(always)]
pub fn translate(x: f64, y: f64) -> Matrix2d {
    [1.0, 0.0, x,
     0.0, 1.0, y]
}

/// Creates a rotation matrix.
#[inline(always)]
pub fn rotate_radians(angle: f64) -> Matrix2d {
    let c = angle.cos();
    let s = angle.sin();
    [c, s, 0.0,
    -s, c, 0.0]
}

/// Create a scale matrix.
#[inline(always)]
pub fn scale(sx: f64, sy: f64) -> Matrix2d {
    [sx, 0.0, 0.0,
     0.0, sy, 0.0]
}

/// Create a shear matrix.
#[inline(always)]
pub fn shear(sx: f64, sy: f64) -> Matrix2d {
    [1.0, sx, 0.0,
     sy, 1.0, 0.0]
}

/// Compute the shortest vector from point to ray.
/// A ray stores starting point and directional vector.
#[inline(always)]
pub fn separation(ray: Ray, x: f64, y: f64) -> Vec2d {
    // Get the directional vector.
    let (dir_x, dir_y) = (ray[2], ray[3]);
    // Get displacement vector from point.
    let (dx, dy) = (ray[0] - x, ray[1] - y);
    // Compute the component of position in ray direction.
    let dot = dir_x * x + dir_y * y;
    // The directional vector multiplied with the dot gives us a parallel vector.
    // When we subtract this from the displacement we get a vector normal to the ray.
    // This is the shortest vector from the point to the ray.
    [dx - dot * dir_x, dy - dot * dir_y]
}

/// Returns the least separation out of four.
/// Each seperation can be computed using `separation` function.
/// The separation returned can be used to solve collision of rectangles.
#[inline(always)]
pub fn least_separation_4(sep1: Vec2d, sep2: Vec2d, sep3: Vec2d, sep4: Vec2d) -> Vec2d {
    let dot1 = sep1[0] * sep1[0] + sep1[1] * sep1[1];
    let dot2 = sep2[0] * sep2[0] + sep2[1] * sep2[1];
    let dot3 = sep3[0] * sep3[0] + sep3[1] * sep3[1];
    let dot4 = sep4[0] * sep4[0] + sep4[1] * sep4[1];
    // Search for the smallest dot product.
    if dot1 < dot2 {
        if dot3 < dot4 {
            if dot1 < dot3 { sep1 } else { sep3 }
        } else {
            if dot1 < dot4 { sep1 } else { sep4 }
        }
    } else {
        if dot3 < dot4 {
            if dot2 < dot3 { sep2 } else { sep3 }
        } else {
            if dot2 < dot4 { sep2 } else { sep4 }
        }
    }
}

/// Shrinks a rectangle by a factor on all sides.
#[inline(always)]
pub fn margin_rectangle(rect: &Rectangle, m: f64) -> Rectangle {
    let w = rect[2] - 2.0 * m;
    let h = rect[3] - 2.0 * m;
    let (x, w) = if w < 0.0 { (rect[0] + 0.5 * rect[2], 0.0) } else { (rect[0] + m, w) };
    let (y, h) = if h < 0.0 { (rect[1] + 0.5 * rect[3], 0.0) } else { (rect[1] + m, h) };
    [x, y, w, h]
}

/// Shrinks a rectangle by a factor on all sides.
#[inline(always)]
pub fn margin_round_rectangle(rect: &RoundRectangle, m: f64) -> RoundRectangle {
    let w = rect[2] - 2.0 * m;
    let h = rect[3] - 2.0 * m;
    let (x, w) = if w < 0.0 { (rect[0] + 0.5 * rect[2], 0.0) } else { (rect[0] + m, w) };
    let (y, h) = if h < 0.0 { (rect[1] + 0.5 * rect[3], 0.0) } else { (rect[1] + m, h) };
    [x, y, w, h, rect[4]]
}

/// Computes a relative rectangle using the rectangle as a tile.
#[inline(always)]
pub fn relative_rectangle(rect: &Rectangle, x: f64, y: f64) -> Rectangle {
    [rect[0] + x * rect[2], rect[1] + y * rect[3], rect[2], rect[3]]
}

/// Computes a relative round rectangle using the round rectangle as a tile.
#[inline(always)]
pub fn relative_round_rectangle(rect: &RoundRectangle, x: f64, y: f64) -> RoundRectangle {
    [rect[0] + x * rect[2], rect[1] + y * rect[3], rect[2], rect[3], rect[4]]
}

/// Computes modular offset safely for indices.
#[inline(always)]
pub fn modular_offset_index(n: uint, i: uint, off: int) -> uint {
    (i + (off % n as int + n as int) as uint) % n
}

#[test]
fn test_modular_offset_index() {
    assert_eq!(modular_offset_index(3, 0, -1), 2);
    assert_eq!(modular_offset_index(3, 1, -1), 0);
    assert_eq!(modular_offset_index(3, 2, -1), 1);
    assert_eq!(modular_offset_index(3, 3, -1), 2);

    assert_eq!(modular_offset_index(3, 0, 1), 1);
    assert_eq!(modular_offset_index(3, 1, 1), 2);
    assert_eq!(modular_offset_index(3, 2, 1), 0);
    assert_eq!(modular_offset_index(3, 3, 1), 1);
}

/// Computes modular offset safely for numbers.
#[inline(always)]
pub fn modular_offset<T: Add<T, T> + Rem<T, T>>(n: &T, i: &T, off: &T) -> T {
    (*i + (*off % *n + *n)) % *n
}

#[test]
fn test_modular_offset() {
    assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &-1.0_f64), 2.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &-1.0_f64), 0.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &-1.0_f64), 1.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &-1.0_f64), 2.0_f64);

    assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &1.0_f64), 1.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &1.0_f64), 2.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &1.0_f64), 0.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &1.0_f64), 1.0_f64);
}
<|endoftext|>


<commit_before>//! Code for the internal Lua thread which handles all Lua requests.

use std::collections::btree_map::BTreeMap;
use std::thread;
use std::fs::{File};
use std::path::Path;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;
use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

use convert::json::lua_to_json;

use rustc_serialize::json::Json;
use uuid::Uuid;
use hlua::{self, Lua, LuaError};
use hlua::any::AnyLuaValue;

use super::types::*;
use super::rust_interop;
use super::init_path;
use super::super::keys;

use registry::{self};

use ::layout::{lock_tree, ContainerType};

lazy_static! {
    /// Sends requests to the Lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the Lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);

    /// Requests to update the registry state from Lua
    static ref REGISTRY_QUEUE: RwLock<Vec<String>> = RwLock::new(vec![]);
}

pub const ERR_LOCK_RUNNING: &'static str = "Lua thread: unable to lock RUNNING";
pub const ERR_LOCK_SENDER: &'static str = "Lua thread: unable to lock SENDER";
pub const ERR_LOCK_QUEUE: &'static str =
    "Lua thread: unable to lock REGISTRY_QUEUE";

const INIT_LUA_FUNC: &'static str = "way_cooler.on_init()";
const LUA_TERMINATE_CODE: &'static str = "way_cooler.on_terminate()";
const LUA_RESTART_CODE: &'static str = "way_cooler.on_restart()";

/// Struct sent to the Lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

// Reexported in lua/mod.rs:11
/// Whether the Lua thread is currently available.
pub fn running() -> bool {
    *RUNNING.read().expect(ERR_LOCK_RUNNING)
}

// Reexported in lua/mod.rs:11
/// Errors which may arise from attempting
/// to sending a message to the Lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Appends this combination of category and key to the registry queue.
pub fn update_registry_value(category: String) {
    let mut queue = REGISTRY_QUEUE.write().expect(ERR_LOCK_QUEUE);
    queue.push(category);
}

// Reexported in lua/mod.rs:11
/// Attemps to send a LuaQuery to the Lua thread.
pub fn send(query: LuaQuery) -> Result<Receiver<LuaResponse>, LuaSendError> {
    if !running() {
        return Err(LuaSendError::ThreadClosed);
    }
    let thread_sender: Sender<LuaMessage>;
    {
        let maybe_sender = SENDER.lock().expect(ERR_LOCK_SENDER);
        match *maybe_sender {
            Some(ref real_sender) => {
                // Senders are designed to be cloneable
                thread_sender = real_sender.clone();
            },
            // If the sender doesn't exist yet, the thread doesn't either
            None => {
                return Err(LuaSendError::ThreadUninitialized);
            }
        }
    }
    // Create a response channel
    let (response_tx, response_rx) = channel();
    let message = LuaMessage { reply: response_tx, query: query };
    match thread_sender.send(message) {
        Ok(_) => Ok(response_rx),
        Err(e) => Err(LuaSendError::Sender(e.0.query))
    }
}

/// Initialize the Lua thread.
pub fn init() {
    debug!("Initializing...");
    let (tx, receiver) = channel();
    *SENDER.lock().expect(ERR_LOCK_SENDER) = Some(tx);
    let mut lua = Lua::new();
    debug!("Loading Lua libraries...");
    lua.openlibs();
    debug!("Loading way-cooler libraries...");
    rust_interop::register_libraries(&mut lua);

    let (use_config, maybe_init_file) = init_path::get_config();
    if use_config {
        match maybe_init_file {
            Ok(init_file) => {
                let _: () = lua.execute_from_reader(init_file)
                    .map(|r| { debug!("Read init.lua successfully"); r })
                    .or_else(|err| {
                        error!("Lua error: {:?}", err);
                        warn!("Defaulting to pre-compiled init.lua");
                        lua.execute(init_path::DEFAULT_CONFIG)
                        })
                    .expect("Unable to load pre-compiled init file");
                debug!("Read init.lua successfully");
            }
            Err(_) => {
                warn!("Defaulting to pre-compiled init.lua");
                let _: () = lua.execute(init_path::DEFAULT_CONFIG)
                    .expect("Unable to load pre-compiled init file");
            }
        }
    }
    else {
        info!("Skipping config search");
    }

    // Only ready after loading libs
    *RUNNING.write().expect(ERR_LOCK_RUNNING) = true;
    debug!("Entering main loop...");
    let _lua_handle = thread::Builder::new()
        .name("Lua thread".to_string())
        .spawn(move || { main_loop(receiver, &mut lua) });
    // Immediately update all the values that the init file set
    send(LuaQuery::UpdateRegistryFromCache)
        .expect("Could not update registry from cache");
}

pub fn on_compositor_ready() {
    // Call the special init hook function that we read from the init file
    send(LuaQuery::Execute(INIT_LUA_FUNC.to_owned()))
        .err()
        .map(|error| { error!("Lua init callback returned an error: {:?}", error); error });

    // Re-tile the layout tree, to make any changes appear immediantly.
    if let Ok(mut tree) = lock_tree() {
        tree.layout_active_of(ContainerType::Root)
            .unwrap_or_else(|_| {
                warn!("Lua thread could not re-tile the layout tree");
            });
        // Yeah this is silly, it's so the active border can be updated properly.
        if let Some(active_id) = tree.active_id() {
            tree.focus(active_id)
                .expect("Could not focus on the focused id");
        }
    }
}

/// Main loop of the Lua thread:
///
/// ## Loop
/// * Wait for a message from the receiver
/// * Handle message
/// * Send response
fn main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        trace!("Lua: awaiting request");
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().expect(ERR_LOCK_RUNNING) = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                if !handle_message(message, lua) {
                    return
                }
            }
        }
    }
}

/// Handle each LuaQuery option sent to the thread
fn handle_message(request: LuaMessage, lua: &mut Lua) -> bool {
    match request.query {
        LuaQuery::Terminate => {
            trace!("Received terminate signal");
            if let Err(error) = lua.execute::<()>(LUA_TERMINATE_CODE) {
                error!("Lua termination callback returned an error: {:?}", error);
            }
            *RUNNING.write().expect(ERR_LOCK_RUNNING) = false;
            thread_send(request.reply, LuaResponse::Pong);

            info!("Lua thread terminating!");
            return false
        },
        LuaQuery::Restart => {
            trace!("Received restart signal!");
            if let Err(error) = lua.execute::<()>(LUA_RESTART_CODE) {
                error!("Lua restart callback returned an error: {:?}", error);
            }
            *RUNNING.write().expect(ERR_LOCK_RUNNING) = false;
            thread_send(request.reply, LuaResponse::Pong);

            // The only real way to restart
            let _new_handle = thread::Builder::new()
                .name("Lua re-init".to_string())
                .spawn(move || {
                    init();
                    keys::init();
                });

            info!("Lua thread restarting");
            return false
        },
        LuaQuery::Execute(code) => {
            trace!("Received request to execute {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },
        LuaQuery::ExecFile(name) => {
            info!("Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("Error executing {}!", name);
                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let Ok
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());
                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },
        LuaQuery::ExecRust(func) => {
            let result = func(lua);
            thread_send(request.reply, LuaResponse::Variable(Some(result)));
        },
        LuaQuery::HandleKey(press) => {
            trace!("Lua: handling keypress {}", &press);
            let press_ix = press.get_lua_index_string();
            // Access the index
            let code = format!("__key_map['{}']()", press_ix);
            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("Error handling {}: {:?}", &press, error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("Handled keypress okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        }
        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
        LuaQuery::UpdateRegistryFromCache => {
            let lock = registry::clients_read();
            // Lua has access to everything
            let client = lock.client(Uuid::nil()).unwrap();
            let mut handle = registry::WriteHandle::new(&client);

            let mut queue = REGISTRY_QUEUE.write().expect(ERR_LOCK_QUEUE);
            for category in queue.drain(0..) {
                let mut registry_cache = lua.get::<hlua::LuaTable<_>, _>("__registry_cache")
                    .expect("__registry_cache wasn't defined");
                if let Some(mut category_table) = registry_cache.get::<hlua::LuaTable<_>, _>(category.clone()) {
                    let cat_table = match handle.write(category.clone()) {
                        Ok(cat) => cat,
                        Err(err) => {
                            warn!("Could not lock {}: {:?}", category, err);
                            break;
                        }
                    };
                    update_values(&mut category_table, cat_table);
                }
                drop(registry_cache)
            }
            lua.execute::<()>("__registry_cache = {}")
                .expect("Could not clear __registry_cache");
        },
    }
    return true
}

fn update_values<L>(mut table: &mut hlua::LuaTable<L>,
                    category: &mut registry::Category)
    where L: hlua::AsMutLua {
    let mut keys = Vec::new();
    for entry in table.iter::<String, AnyLuaValue>() {
        if let Some((key, value)) = entry {
            match value {
                AnyLuaValue::LuaOther => {
                    keys.push(key.clone());
                    category.insert(key, Json::Object(BTreeMap::new()));
                },
                value => {
                    match lua_to_json(value) {
                        Ok(val) => {
                            trace!("Updating {}:{} = {:#?}", category.name(), key, &val);
                            category.insert(key, val);
                        },
                        Err(value) => {
                            warn!("Could not translate {:?} to JSON", value);
                        }
                    }
                }
            }
        }
    }
    for key in keys {
        let inner_mapping = category.get_mut(&key)
            .expect("Could not get the value we just made")
            .as_object_mut()
            .expect("The inner value was not an object!");
        if let Some(mut inner_table) = table.get::<hlua::LuaTable<_>, _>(key) {
            for entry in inner_table.iter::<String, AnyLuaValue>() {
                if let Some((key, value)) = entry {
                    match value {
                        AnyLuaValue::LuaOther => {
                            warn!("Dropping inner table {:?}", key);
                        },
                        value => {
                            if let Ok(val) = lua_to_json(value) {
                                inner_mapping.insert(key, val);
                            }
                        }
                    }
                }
            }
        }
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}
<commit_msg>add some logging for lua init<commit_after>//! Code for the internal Lua thread which handles all Lua requests.

use std::collections::btree_map::BTreeMap;
use std::thread;
use std::fs::{File};
use std::path::Path;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;
use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

use convert::json::lua_to_json;

use rustc_serialize::json::Json;
use uuid::Uuid;
use hlua::{self, Lua, LuaError};
use hlua::any::AnyLuaValue;

use super::types::*;
use super::rust_interop;
use super::init_path;
use super::super::keys;

use registry::{self};

use ::layout::{lock_tree, ContainerType};

lazy_static! {
    /// Sends requests to the Lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the Lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);

    /// Requests to update the registry state from Lua
    static ref REGISTRY_QUEUE: RwLock<Vec<String>> = RwLock::new(vec![]);
}

pub const ERR_LOCK_RUNNING: &'static str = "Lua thread: unable to lock RUNNING";
pub const ERR_LOCK_SENDER: &'static str = "Lua thread: unable to lock SENDER";
pub const ERR_LOCK_QUEUE: &'static str =
    "Lua thread: unable to lock REGISTRY_QUEUE";

const INIT_LUA_FUNC: &'static str = "way_cooler.on_init()";
const LUA_TERMINATE_CODE: &'static str = "way_cooler.on_terminate()";
const LUA_RESTART_CODE: &'static str = "way_cooler.on_restart()";

/// Struct sent to the Lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

// Reexported in lua/mod.rs:11
/// Whether the Lua thread is currently available.
pub fn running() -> bool {
    *RUNNING.read().expect(ERR_LOCK_RUNNING)
}

// Reexported in lua/mod.rs:11
/// Errors which may arise from attempting
/// to sending a message to the Lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Appends this combination of category and key to the registry queue.
pub fn update_registry_value(category: String) {
    let mut queue = REGISTRY_QUEUE.write().expect(ERR_LOCK_QUEUE);
    queue.push(category);
}

// Reexported in lua/mod.rs:11
/// Attemps to send a LuaQuery to the Lua thread.
pub fn send(query: LuaQuery) -> Result<Receiver<LuaResponse>, LuaSendError> {
    if !running() {
        return Err(LuaSendError::ThreadClosed);
    }
    let thread_sender: Sender<LuaMessage>;
    {
        let maybe_sender = SENDER.lock().expect(ERR_LOCK_SENDER);
        match *maybe_sender {
            Some(ref real_sender) => {
                // Senders are designed to be cloneable
                thread_sender = real_sender.clone();
            },
            // If the sender doesn't exist yet, the thread doesn't either
            None => {
                return Err(LuaSendError::ThreadUninitialized);
            }
        }
    }
    // Create a response channel
    let (response_tx, response_rx) = channel();
    let message = LuaMessage { reply: response_tx, query: query };
    match thread_sender.send(message) {
        Ok(_) => Ok(response_rx),
        Err(e) => Err(LuaSendError::Sender(e.0.query))
    }
}

/// Initialize the Lua thread.
pub fn init() {
    info!("Initializing lua...");
    let (tx, receiver) = channel();
    *SENDER.lock().expect(ERR_LOCK_SENDER) = Some(tx);
    let mut lua = Lua::new();
    debug!("Loading Lua libraries...");
    lua.openlibs();
    debug!("Loading way-cooler libraries...");
    rust_interop::register_libraries(&mut lua);

    let (use_config, maybe_init_file) = init_path::get_config();
    if use_config {
        match maybe_init_file {
            Ok(init_file) => {
                let _: () = lua.execute_from_reader(init_file)
                    .map(|r| { debug!("Read init.lua successfully"); r })
                    .or_else(|err| {
                        error!("Lua error: {:?}", err);
                        warn!("Defaulting to pre-compiled init.lua");
                        lua.execute(init_path::DEFAULT_CONFIG)
                        })
                    .expect("Unable to load pre-compiled init file");
                debug!("Read init.lua successfully");
            }
            Err(_) => {
                warn!("Defaulting to pre-compiled init.lua");
                let _: () = lua.execute(init_path::DEFAULT_CONFIG)
                    .expect("Unable to load pre-compiled init file");
            }
        }
    }
    else {
        info!("Skipping config search");
    }

    // Only ready after loading libs
    *RUNNING.write().expect(ERR_LOCK_RUNNING) = true;
    debug!("Entering main loop...");
    let _lua_handle = thread::Builder::new()
        .name("Lua thread".to_string())
        .spawn(move || { main_loop(receiver, &mut lua) });
    // Immediately update all the values that the init file set
    send(LuaQuery::UpdateRegistryFromCache)
        .expect("Could not update registry from cache");
}

pub fn on_compositor_ready() {
    info!("Running lua on_init()");
    // Call the special init hook function that we read from the init file
    send(LuaQuery::Execute(INIT_LUA_FUNC.to_owned()))
        .err()
        .map(|error| { error!("Lua init callback returned an error: {:?}", error); error });

    // Re-tile the layout tree, to make any changes appear immediantly.
    if let Ok(mut tree) = lock_tree() {
        tree.layout_active_of(ContainerType::Root)
            .unwrap_or_else(|_| {
                warn!("Lua thread could not re-tile the layout tree");
            });
        // Yeah this is silly, it's so the active border can be updated properly.
        if let Some(active_id) = tree.active_id() {
            tree.focus(active_id)
                .expect("Could not focus on the focused id");
        }
    }
}

/// Main loop of the Lua thread:
///
/// ## Loop
/// * Wait for a message from the receiver
/// * Handle message
/// * Send response
fn main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        trace!("Lua: awaiting request");
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().expect(ERR_LOCK_RUNNING) = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                if !handle_message(message, lua) {
                    return
                }
            }
        }
    }
}

/// Handle each LuaQuery option sent to the thread
fn handle_message(request: LuaMessage, lua: &mut Lua) -> bool {
    match request.query {
        LuaQuery::Terminate => {
            trace!("Received terminate signal");
            if let Err(error) = lua.execute::<()>(LUA_TERMINATE_CODE) {
                error!("Lua termination callback returned an error: {:?}", error);
            }
            *RUNNING.write().expect(ERR_LOCK_RUNNING) = false;
            thread_send(request.reply, LuaResponse::Pong);

            info!("Lua thread terminating!");
            return false
        },
        LuaQuery::Restart => {
            trace!("Received restart signal!");
            if let Err(error) = lua.execute::<()>(LUA_RESTART_CODE) {
                error!("Lua restart callback returned an error: {:?}", error);
            }
            *RUNNING.write().expect(ERR_LOCK_RUNNING) = false;
            thread_send(request.reply, LuaResponse::Pong);

            // The only real way to restart
            let _new_handle = thread::Builder::new()
                .name("Lua re-init".to_string())
                .spawn(move || {
                    init();
                    keys::init();
                });

            info!("Lua thread restarting");
            return false
        },
        LuaQuery::Execute(code) => {
            trace!("Received request to execute {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },
        LuaQuery::ExecFile(name) => {
            info!("Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("Error executing {}!", name);
                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let Ok
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());
                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },
        LuaQuery::ExecRust(func) => {
            let result = func(lua);
            thread_send(request.reply, LuaResponse::Variable(Some(result)));
        },
        LuaQuery::HandleKey(press) => {
            trace!("Lua: handling keypress {}", &press);
            let press_ix = press.get_lua_index_string();
            // Access the index
            let code = format!("__key_map['{}']()", press_ix);
            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("Error handling {}: {:?}", &press, error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("Handled keypress okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        }
        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
        LuaQuery::UpdateRegistryFromCache => {
            let lock = registry::clients_read();
            // Lua has access to everything
            let client = lock.client(Uuid::nil()).unwrap();
            let mut handle = registry::WriteHandle::new(&client);

            let mut queue = REGISTRY_QUEUE.write().expect(ERR_LOCK_QUEUE);
            for category in queue.drain(0..) {
                let mut registry_cache = lua.get::<hlua::LuaTable<_>, _>("__registry_cache")
                    .expect("__registry_cache wasn't defined");
                if let Some(mut category_table) = registry_cache.get::<hlua::LuaTable<_>, _>(category.clone()) {
                    let cat_table = match handle.write(category.clone()) {
                        Ok(cat) => cat,
                        Err(err) => {
                            warn!("Could not lock {}: {:?}", category, err);
                            break;
                        }
                    };
                    update_values(&mut category_table, cat_table);
                }
                drop(registry_cache)
            }
            lua.execute::<()>("__registry_cache = {}")
                .expect("Could not clear __registry_cache");
        },
    }
    return true
}

fn update_values<L>(mut table: &mut hlua::LuaTable<L>,
                    category: &mut registry::Category)
    where L: hlua::AsMutLua {
    let mut keys = Vec::new();
    for entry in table.iter::<String, AnyLuaValue>() {
        if let Some((key, value)) = entry {
            match value {
                AnyLuaValue::LuaOther => {
                    keys.push(key.clone());
                    category.insert(key, Json::Object(BTreeMap::new()));
                },
                value => {
                    match lua_to_json(value) {
                        Ok(val) => {
                            trace!("Updating {}:{} = {:#?}", category.name(), key, &val);
                            category.insert(key, val);
                        },
                        Err(value) => {
                            warn!("Could not translate {:?} to JSON", value);
                        }
                    }
                }
            }
        }
    }
    for key in keys {
        let inner_mapping = category.get_mut(&key)
            .expect("Could not get the value we just made")
            .as_object_mut()
            .expect("The inner value was not an object!");
        if let Some(mut inner_table) = table.get::<hlua::LuaTable<_>, _>(key) {
            for entry in inner_table.iter::<String, AnyLuaValue>() {
                if let Some((key, value)) = entry {
                    match value {
                        AnyLuaValue::LuaOther => {
                            warn!("Dropping inner table {:?}", key);
                        },
                        value => {
                            if let Ok(val) = lua_to_json(value) {
                                inner_mapping.insert(key, val);
                            }
                        }
                    }
                }
            }
        }
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Hooks for for the RPC system.
//!
//! Roughly corresponds to capability.h in the C++ implementation.

use any_pointer;
use private::capability::{ClientHook, ParamsHook, RequestHook, ResponseHook, ResultsHook};

#[cfg(feature = "rpc")]
use futures::Future;

pub struct Promise<T, E> {
    #[allow(dead_code)]
    inner: PromiseInner<T, E>,
}

enum PromiseInner<T, E> {
    Immediate(Result<T,E>),

    #[cfg(feature = "rpc")]
    Deferred(Box<Future<Item=T,Error=E> + 'static>),

    #[cfg(feature = "rpc")]
    Empty,
}

impl <T, E> Promise<T, E> {
    pub fn ok(value: T) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Ok(value)) }
    }

    pub fn err(error: E) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Err(error)) }
    }

    #[cfg(feature = "rpc")]
    pub fn from_future<F>(f: F) -> Promise<T, E>
        where F: Future<Item=T,Error=E> + 'static
    {
        Promise { inner: PromiseInner::Deferred(Box::new(f)) }
    }

    #[cfg(feature = "rpc")]
    pub fn deferred(f: Box<Future<Item=T,Error=E> + 'static>) -> Promise<T, E> {
        Promise { inner: PromiseInner::Deferred(f) }
    }
}

#[cfg(feature = "rpc")]
impl <T, E> Future for Promise<T, E>
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> ::futures::Poll<Self::Item, Self::Error> {
        match self.inner {
            PromiseInner::Empty => panic!("Promise polled after done."),
            ref mut imm @ PromiseInner::Immediate(_) => {
                match ::std::mem::replace(imm, PromiseInner::Empty) {
                    PromiseInner::Immediate(Ok(v)) => Ok(::futures::Async::Ready(v)),
                    PromiseInner::Immediate(Err(e)) => Err(e),
                    _ => unreachable!(),
                }
            }
            PromiseInner::Deferred(ref mut f) => f.poll(),
        }
    }
}

#[must_use]
pub struct RemotePromise<Results> where Results: ::traits::Pipelined + for<'a> ::traits::Owned<'a> + 'static {
    pub promise: Promise<Response<Results>, ::Error>,
    pub pipeline: Results::Pipeline,
}

pub struct Response<Results> {
    pub marker: ::std::marker::PhantomData<Results>,
    pub hook: Box<ResponseHook>,
}

impl <Results> Response<Results>
    where Results: ::traits::Pipelined + for<'a> ::traits::Owned<'a>
{
    pub fn new(hook: Box<ResponseHook>) -> Response<Results> {
        Response { marker: ::std::marker::PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<Results as ::traits::Owned<'a>>::Reader> {
        try!(self.hook.get()).get_as()
    }
}

pub struct Request<Params, Results> {
    pub marker: ::std::marker::PhantomData<(Params, Results)>,
    pub hook: Box<RequestHook>
}

impl <Params, Results> Request <Params, Results>
    where Params: for<'a> ::traits::Owned<'a>
{
    pub fn new(hook: Box<RequestHook>) -> Request <Params, Results> {
        Request { hook: hook, marker: ::std::marker::PhantomData }
    }

    pub fn get<'a>(&'a mut self) -> <Params as ::traits::Owned<'a>>::Builder {
        self.hook.get().get_as().unwrap()
    }

    pub fn set(&mut self, from: <Params as ::traits::Owned>::Reader) -> ::Result<()> {
        self.hook.get().set_as(from)
    }
}

#[cfg(feature = "rpc")]
impl <Params, Results> Request <Params, Results>
where Results: ::traits::Pipelined + for<'a> ::traits::Owned<'a> + 'static,
      <Results as ::traits::Pipelined>::Pipeline: FromTypelessPipeline
{
    pub fn send(self) -> RemotePromise<Results> {
        let RemotePromise {promise, pipeline, ..} = self.hook.send();
        let typed_promise = Promise::from_future(promise.map(|response| {
            Response {hook: response.hook,
                      marker: ::std::marker::PhantomData}
        }));
        RemotePromise { promise: typed_promise,
                        pipeline: FromTypelessPipeline::new(pipeline)
                      }
    }
}

pub struct Params<T> {
    pub marker: ::std::marker::PhantomData<T>,
    pub hook: Box<ParamsHook>,
}

impl <T> Params <T> {
    pub fn new(hook: Box<ParamsHook>) -> Params<T> {
        Params { marker: ::std::marker::PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<T as ::traits::Owned<'a>>::Reader>
        where T: ::traits::Owned<'a>
    {
        Ok(try!(try!(self.hook.get()).get_as()))
    }
}

pub struct Results<T> {
    pub marker: ::std::marker::PhantomData<T>,
    pub hook: Box<ResultsHook>,
}

impl <T> Results<T>
    where T: for<'a> ::traits::Owned<'a>
{
    pub fn new(hook: Box<ResultsHook>) -> Results<T> {
        Results { marker: ::std::marker::PhantomData, hook: hook }
    }

    pub fn get<'a>(&'a mut self) -> <T as ::traits::Owned<'a>>::Builder {
        self.hook.get().unwrap().get_as().unwrap()
    }

    pub fn set(&mut self, other: <T as ::traits::Owned>::Reader) -> ::Result<()>
    {
        self.hook.get().unwrap().set_as(other)
    }

}

pub trait FromTypelessPipeline {
    fn new (typeless: any_pointer::Pipeline) -> Self;
}

pub trait FromClientHook {
    fn new(Box<ClientHook>) -> Self;
}

pub struct Client {
    pub hook: Box<ClientHook>
}

impl Client {
    pub fn new(hook: Box<ClientHook>) -> Client {
        Client { hook : hook }
    }

    pub fn new_call<Params, Results>(&self,
                                     interface_id : u64,
                                     method_id : u16,
                                     size_hint : Option<::MessageSize>)
                                     -> Request<Params, Results> {
        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
        Request { hook: typeless.hook, marker: ::std::marker::PhantomData }
    }

    /// If the capability is actually only a promise, the returned promise resolves once the
    /// capability itself has resolved to its final destination (or propagates the exception if
    /// the capability promise is rejected).  This is mainly useful for error-checking in the case
    /// where no calls are being made.  There is no reason to wait for this before making calls; if
    /// the capability does not resolve, the call results will propagate the error.
    #[cfg(feature = "rpc")]
    pub fn when_resolved(&self) -> Promise<(), ::Error> {
        self.hook.when_resolved()
    }
}

pub trait Server {
    fn dispatch_call(&mut self, interface_id: u64, method_id: u16,
                     params: Params<any_pointer::Owned>,
                     results: Results<any_pointer::Owned>)
                     -> Promise<(), ::Error>;
}

<commit_msg>remove Promise::deferred()<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Hooks for for the RPC system.
//!
//! Roughly corresponds to capability.h in the C++ implementation.

use any_pointer;
use private::capability::{ClientHook, ParamsHook, RequestHook, ResponseHook, ResultsHook};

#[cfg(feature = "rpc")]
use futures::Future;

pub struct Promise<T, E> {
    #[allow(dead_code)]
    inner: PromiseInner<T, E>,
}

enum PromiseInner<T, E> {
    Immediate(Result<T,E>),

    #[cfg(feature = "rpc")]
    Deferred(Box<Future<Item=T,Error=E> + 'static>),

    #[cfg(feature = "rpc")]
    Empty,
}

impl <T, E> Promise<T, E> {
    pub fn ok(value: T) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Ok(value)) }
    }

    pub fn err(error: E) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Err(error)) }
    }

    #[cfg(feature = "rpc")]
    pub fn from_future<F>(f: F) -> Promise<T, E>
        where F: Future<Item=T,Error=E> + 'static
    {
        Promise { inner: PromiseInner::Deferred(Box::new(f)) }
    }
}

#[cfg(feature = "rpc")]
impl <T, E> Future for Promise<T, E>
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> ::futures::Poll<Self::Item, Self::Error> {
        match self.inner {
            PromiseInner::Empty => panic!("Promise polled after done."),
            ref mut imm @ PromiseInner::Immediate(_) => {
                match ::std::mem::replace(imm, PromiseInner::Empty) {
                    PromiseInner::Immediate(Ok(v)) => Ok(::futures::Async::Ready(v)),
                    PromiseInner::Immediate(Err(e)) => Err(e),
                    _ => unreachable!(),
                }
            }
            PromiseInner::Deferred(ref mut f) => f.poll(),
        }
    }
}

#[must_use]
pub struct RemotePromise<Results> where Results: ::traits::Pipelined + for<'a> ::traits::Owned<'a> + 'static {
    pub promise: Promise<Response<Results>, ::Error>,
    pub pipeline: Results::Pipeline,
}

pub struct Response<Results> {
    pub marker: ::std::marker::PhantomData<Results>,
    pub hook: Box<ResponseHook>,
}

impl <Results> Response<Results>
    where Results: ::traits::Pipelined + for<'a> ::traits::Owned<'a>
{
    pub fn new(hook: Box<ResponseHook>) -> Response<Results> {
        Response { marker: ::std::marker::PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<Results as ::traits::Owned<'a>>::Reader> {
        try!(self.hook.get()).get_as()
    }
}

pub struct Request<Params, Results> {
    pub marker: ::std::marker::PhantomData<(Params, Results)>,
    pub hook: Box<RequestHook>
}

impl <Params, Results> Request <Params, Results>
    where Params: for<'a> ::traits::Owned<'a>
{
    pub fn new(hook: Box<RequestHook>) -> Request <Params, Results> {
        Request { hook: hook, marker: ::std::marker::PhantomData }
    }

    pub fn get<'a>(&'a mut self) -> <Params as ::traits::Owned<'a>>::Builder {
        self.hook.get().get_as().unwrap()
    }

    pub fn set(&mut self, from: <Params as ::traits::Owned>::Reader) -> ::Result<()> {
        self.hook.get().set_as(from)
    }
}

#[cfg(feature = "rpc")]
impl <Params, Results> Request <Params, Results>
where Results: ::traits::Pipelined + for<'a> ::traits::Owned<'a> + 'static,
      <Results as ::traits::Pipelined>::Pipeline: FromTypelessPipeline
{
    pub fn send(self) -> RemotePromise<Results> {
        let RemotePromise {promise, pipeline, ..} = self.hook.send();
        let typed_promise = Promise::from_future(promise.map(|response| {
            Response {hook: response.hook,
                      marker: ::std::marker::PhantomData}
        }));
        RemotePromise { promise: typed_promise,
                        pipeline: FromTypelessPipeline::new(pipeline)
                      }
    }
}

pub struct Params<T> {
    pub marker: ::std::marker::PhantomData<T>,
    pub hook: Box<ParamsHook>,
}

impl <T> Params <T> {
    pub fn new(hook: Box<ParamsHook>) -> Params<T> {
        Params { marker: ::std::marker::PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<T as ::traits::Owned<'a>>::Reader>
        where T: ::traits::Owned<'a>
    {
        Ok(try!(try!(self.hook.get()).get_as()))
    }
}

pub struct Results<T> {
    pub marker: ::std::marker::PhantomData<T>,
    pub hook: Box<ResultsHook>,
}

impl <T> Results<T>
    where T: for<'a> ::traits::Owned<'a>
{
    pub fn new(hook: Box<ResultsHook>) -> Results<T> {
        Results { marker: ::std::marker::PhantomData, hook: hook }
    }

    pub fn get<'a>(&'a mut self) -> <T as ::traits::Owned<'a>>::Builder {
        self.hook.get().unwrap().get_as().unwrap()
    }

    pub fn set(&mut self, other: <T as ::traits::Owned>::Reader) -> ::Result<()>
    {
        self.hook.get().unwrap().set_as(other)
    }

}

pub trait FromTypelessPipeline {
    fn new (typeless: any_pointer::Pipeline) -> Self;
}

pub trait FromClientHook {
    fn new(Box<ClientHook>) -> Self;
}

pub struct Client {
    pub hook: Box<ClientHook>
}

impl Client {
    pub fn new(hook: Box<ClientHook>) -> Client {
        Client { hook : hook }
    }

    pub fn new_call<Params, Results>(&self,
                                     interface_id : u64,
                                     method_id : u16,
                                     size_hint : Option<::MessageSize>)
                                     -> Request<Params, Results> {
        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
        Request { hook: typeless.hook, marker: ::std::marker::PhantomData }
    }

    /// If the capability is actually only a promise, the returned promise resolves once the
    /// capability itself has resolved to its final destination (or propagates the exception if
    /// the capability promise is rejected).  This is mainly useful for error-checking in the case
    /// where no calls are being made.  There is no reason to wait for this before making calls; if
    /// the capability does not resolve, the call results will propagate the error.
    #[cfg(feature = "rpc")]
    pub fn when_resolved(&self) -> Promise<(), ::Error> {
        self.hook.when_resolved()
    }
}

pub trait Server {
    fn dispatch_call(&mut self, interface_id: u64, method_id: u16,
                     params: Params<any_pointer::Owned>,
                     results: Results<any_pointer::Owned>)
                     -> Promise<(), ::Error>;
}

<|endoftext|>