<commit_before>use std::ascii::AsciiExt;
use time::Timespec;

use conduit::{Request, Response};
use conduit_router::RequestParams;
use diesel::pg::PgConnection;
use diesel::prelude::*;
use diesel;
use pg::rows::Row;

use {Model, Crate};
use db::RequestTransaction;
use pagination::Paginate;
use schema::*;
use util::{RequestUtils, CargoResult};

#[derive(Clone, Identifiable, Queryable, Debug)]
pub struct Keyword {
    pub id: i32,
    pub keyword: String,
    pub crates_cnt: i32,
    pub created_at: Timespec,
}

#[derive(Associations, Insertable, Identifiable, Debug)]
#[belongs_to(Keyword)]
#[belongs_to(Crate)]
#[table_name = "crates_keywords"]
#[primary_key(crate_id, keyword_id)]
pub struct CrateKeyword {
    crate_id: i32,
    keyword_id: i32,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct EncodableKeyword {
    pub id: String,
    pub keyword: String,
    pub created_at: String,
    pub crates_cnt: i32,
}

impl Keyword {
    pub fn find_by_keyword(conn: &PgConnection, name: &str) -> QueryResult<Keyword> {
        keywords::table
            .filter(keywords::keyword.eq(::lower(name)))
            .first(&*conn)
    }

    pub fn find_or_create_all(conn: &PgConnection, names: &[&str]) -> QueryResult<Vec<Keyword>> {
        use diesel::pg::upsert::*;
        use diesel::expression::dsl::any;

        #[derive(Insertable)]
        #[table_name = "keywords"]
        struct NewKeyword<'a> {
            keyword: &'a str,
        }

        let lowercase_names: Vec<_> = names.iter().map(|s| s.to_lowercase()).collect();

        let new_keywords: Vec<_> = lowercase_names
            .iter()
            .map(|s| NewKeyword { keyword: s })
            .collect();

        // https://github.com/diesel-rs/diesel/issues/797
        if !new_keywords.is_empty() {
            diesel::insert(&new_keywords.on_conflict_do_nothing())
                .into(keywords::table)
                .execute(conn)?;
        }
        keywords::table
            .filter(::lower(keywords::keyword).eq(any(&lowercase_names)))
            .load(conn)
    }

    pub fn valid_name(name: &str) -> bool {
        if name.is_empty() {
            return false;
        }
        name.chars().next().unwrap().is_alphanumeric() &&
            name.chars().all(
                |c| c.is_alphanumeric() || c == '_' || c == '-',
            ) && name.chars().all(|c| c.is_ascii())
    }

    pub fn encodable(self) -> EncodableKeyword {
        let Keyword {
            crates_cnt,
            keyword,
            created_at,
            ..
        } = self;
        EncodableKeyword {
            id: keyword.clone(),
            created_at: ::encode_time(created_at),
            crates_cnt: crates_cnt,
            keyword: keyword,
        }
    }

    pub fn update_crate(conn: &PgConnection, krate: &Crate, keywords: &[&str]) -> QueryResult<()> {
        conn.transaction(|| {
            let keywords = Keyword::find_or_create_all(conn, keywords)?;
            diesel::delete(CrateKeyword::belonging_to(krate)).execute(
                conn,
            )?;
            let crate_keywords = keywords
                .into_iter()
                .map(|kw| {
                    CrateKeyword {
                        crate_id: krate.id,
                        keyword_id: kw.id,
                    }
                })
                .collect::<Vec<_>>();
            diesel::insert(&crate_keywords)
                .into(crates_keywords::table)
                .execute(conn)?;
            Ok(())
        })
    }
}

impl Model for Keyword {
    fn from_row(row: &Row) -> Keyword {
        Keyword {
            id: row.get("id"),
            created_at: row.get("created_at"),
            crates_cnt: row.get("crates_cnt"),
            keyword: row.get("keyword"),
        }
    }
    fn table_name(_: Option<Keyword>) -> &'static str {
        "keywords"
    }
}

/// Handles the `GET /keywords` route.
pub fn index(req: &mut Request) -> CargoResult<Response> {
    use schema::keywords;

    let conn = req.db_conn()?;
    let (offset, limit) = req.pagination(10, 100)?;
    let query = req.query();
    let sort = query.get("sort").map(|s| &s[..]).unwrap_or("alpha");

    let mut query = keywords::table.into_boxed();

    if sort == "crates" {
        query = query.order(keywords::crates_cnt.desc());
    } else {
        query = query.order(keywords::keyword.asc());
    }

    let data = query.paginate(limit, offset).load::<(Keyword, i64)>(&*conn)?;
    let total = data.get(0).map(|&(_, t)| t).unwrap_or(0);
    let kws = data.into_iter()
        .map(|(k, _)| k.encodable())
        .collect::<Vec<_>>();

    #[derive(Serialize)]
    struct R {
        keywords: Vec<EncodableKeyword>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        total: i64,
    }

    Ok(req.json(&R {
        keywords: kws,
        meta: Meta { total: total },
    }))
}

/// Handles the `GET /keywords/:keyword_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
    let name = &req.params()["keyword_id"];
    let conn = req.db_conn()?;

    let kw = Keyword::find_by_keyword(&conn, name)?;

    #[derive(Serialize)]
    struct R {
        keyword: EncodableKeyword,
    }
    Ok(req.json(&R { keyword: kw.encodable() }))
}
<commit_msg>Don't associate crates with non-lowercased keywords<commit_after>use std::ascii::AsciiExt;
use time::Timespec;

use conduit::{Request, Response};
use conduit_router::RequestParams;
use diesel::pg::PgConnection;
use diesel::prelude::*;
use diesel;
use pg::rows::Row;

use {Model, Crate};
use db::RequestTransaction;
use pagination::Paginate;
use schema::*;
use util::{RequestUtils, CargoResult};

#[derive(Clone, Identifiable, Queryable, Debug)]
pub struct Keyword {
    pub id: i32,
    pub keyword: String,
    pub crates_cnt: i32,
    pub created_at: Timespec,
}

#[derive(Associations, Insertable, Identifiable, Debug)]
#[belongs_to(Keyword)]
#[belongs_to(Crate)]
#[table_name = "crates_keywords"]
#[primary_key(crate_id, keyword_id)]
pub struct CrateKeyword {
    crate_id: i32,
    keyword_id: i32,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct EncodableKeyword {
    pub id: String,
    pub keyword: String,
    pub created_at: String,
    pub crates_cnt: i32,
}

impl Keyword {
    pub fn find_by_keyword(conn: &PgConnection, name: &str) -> QueryResult<Keyword> {
        keywords::table
            .filter(keywords::keyword.eq(::lower(name)))
            .first(&*conn)
    }

    pub fn find_or_create_all(conn: &PgConnection, names: &[&str]) -> QueryResult<Vec<Keyword>> {
        use diesel::pg::upsert::*;
        use diesel::expression::dsl::any;

        #[derive(Insertable)]
        #[table_name = "keywords"]
        struct NewKeyword<'a> {
            keyword: &'a str,
        }

        let lowercase_names: Vec<_> = names.iter().map(|s| s.to_lowercase()).collect();

        let new_keywords: Vec<_> = lowercase_names
            .iter()
            .map(|s| NewKeyword { keyword: s })
            .collect();

        // https://github.com/diesel-rs/diesel/issues/797
        if !new_keywords.is_empty() {
            diesel::insert(&new_keywords.on_conflict_do_nothing())
                .into(keywords::table)
                .execute(conn)?;
        }
        keywords::table
            .filter(keywords::keyword.eq(any(&lowercase_names)))
            .load(conn)
    }

    pub fn valid_name(name: &str) -> bool {
        if name.is_empty() {
            return false;
        }
        name.chars().next().unwrap().is_alphanumeric() &&
            name.chars().all(
                |c| c.is_alphanumeric() || c == '_' || c == '-',
            ) && name.chars().all(|c| c.is_ascii())
    }

    pub fn encodable(self) -> EncodableKeyword {
        let Keyword {
            crates_cnt,
            keyword,
            created_at,
            ..
        } = self;
        EncodableKeyword {
            id: keyword.clone(),
            created_at: ::encode_time(created_at),
            crates_cnt: crates_cnt,
            keyword: keyword,
        }
    }

    pub fn update_crate(conn: &PgConnection, krate: &Crate, keywords: &[&str]) -> QueryResult<()> {
        conn.transaction(|| {
            let keywords = Keyword::find_or_create_all(conn, keywords)?;
            diesel::delete(CrateKeyword::belonging_to(krate)).execute(
                conn,
            )?;
            let crate_keywords = keywords
                .into_iter()
                .map(|kw| {
                    CrateKeyword {
                        crate_id: krate.id,
                        keyword_id: kw.id,
                    }
                })
                .collect::<Vec<_>>();
            diesel::insert(&crate_keywords)
                .into(crates_keywords::table)
                .execute(conn)?;
            Ok(())
        })
    }
}

impl Model for Keyword {
    fn from_row(row: &Row) -> Keyword {
        Keyword {
            id: row.get("id"),
            created_at: row.get("created_at"),
            crates_cnt: row.get("crates_cnt"),
            keyword: row.get("keyword"),
        }
    }
    fn table_name(_: Option<Keyword>) -> &'static str {
        "keywords"
    }
}

/// Handles the `GET /keywords` route.
pub fn index(req: &mut Request) -> CargoResult<Response> {
    use schema::keywords;

    let conn = req.db_conn()?;
    let (offset, limit) = req.pagination(10, 100)?;
    let query = req.query();
    let sort = query.get("sort").map(|s| &s[..]).unwrap_or("alpha");

    let mut query = keywords::table.into_boxed();

    if sort == "crates" {
        query = query.order(keywords::crates_cnt.desc());
    } else {
        query = query.order(keywords::keyword.asc());
    }

    let data = query.paginate(limit, offset).load::<(Keyword, i64)>(&*conn)?;
    let total = data.get(0).map(|&(_, t)| t).unwrap_or(0);
    let kws = data.into_iter()
        .map(|(k, _)| k.encodable())
        .collect::<Vec<_>>();

    #[derive(Serialize)]
    struct R {
        keywords: Vec<EncodableKeyword>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        total: i64,
    }

    Ok(req.json(&R {
        keywords: kws,
        meta: Meta { total: total },
    }))
}

/// Handles the `GET /keywords/:keyword_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
    let name = &req.params()["keyword_id"];
    let conn = req.db_conn()?;

    let kw = Keyword::find_by_keyword(&conn, name)?;

    #[derive(Serialize)]
    struct R {
        keyword: EncodableKeyword,
    }
    Ok(req.json(&R { keyword: kw.encodable() }))
}

#[cfg(test)]
mod tests {
    use super::*;
    use dotenv::dotenv;
    use std::env;
    use diesel;
    use diesel::connection::SimpleConnection;

    #[derive(Insertable)]
    #[table_name = "keywords"]
    struct NewKeyword<'a> {
        keyword: &'a str,
    }

    fn pg_connection() -> PgConnection {
        let _ = dotenv();
        let database_url =
            env::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL must be set to run tests");
        let conn = PgConnection::establish(&database_url).unwrap();
        // These tests deadlock if run concurrently
        conn.batch_execute("BEGIN;").unwrap();
        conn
    }

    #[test]
    fn dont_associate_with_non_lowercased_keywords() {
        let conn = pg_connection();
        // The code should be preventing lowercased keywords from existing,
        // but if one happens to sneak in there, don't associate crates with it.
        let bad_keyword = NewKeyword { keyword: "NO" };

        diesel::insert(&bad_keyword)
            .into(keywords::table)
            .execute(&conn)
            .unwrap();

        let associated = Keyword::find_or_create_all(&conn, &["no"]).unwrap();
        assert_eq!(associated.len(), 1);
        assert_eq!(associated.first().unwrap().keyword, "no");
    }
}
<|endoftext|>


<commit_before>use bitflags::bitflags;
#[cfg(feature = "chrono")]
use chrono::{DateTime, Local, TimeZone};
use colored::{Color, Colorize};
use std::fmt::{self, Write};
use {At, History, Meta, Record};

/// Configurable display formatting of structures.
#[derive(Copy, Clone, Debug)]
pub struct Display<'a, T: 'a> {
    data: &'a T,
    view: View,
}

impl<'a, T: 'a> Display<'a, T> {
    /// Show colored output (off by default).
    #[inline]
    pub fn colored(&mut self, on: bool) -> &mut Self {
        self.view.set(View::COLORED, on);
        self
    }

    /// Show detailed output (on by default).
    #[inline]
    pub fn detailed(&mut self, on: bool) -> &mut Self {
        self.view.set(View::DETAILED, on);
        self
    }

    /// Show the position of the command (on by default).
    #[inline]
    pub fn position(&mut self, on: bool) -> &mut Self {
        self.view.set(View::POSITION, on);
        self
    }

    /// Show the saved command (on by default).
    #[inline]
    pub fn saved(&mut self, on: bool) -> &mut Self {
        self.view.set(View::SAVED, on);
        self
    }
}

impl<'a, R> Display<'a, History<R>> {
    /// Show the history as a graph (off by default).
    #[inline]
    pub fn graph(&mut self, on: bool) -> &mut Self {
        self.view.set(View::GRAPH, on);
        self
    }
}

impl<'a, R> Display<'a, Record<R>> {
    #[inline]
    fn fmt_list(&self, f: &mut fmt::Formatter, at: At, meta: &Meta<R>) -> fmt::Result {
        self.view.mark(f)?;
        self.view.position(f, at, false)?;
        if self.view.contains(View::DETAILED) {
            #[cfg(feature = "chrono")]
            self.view.timestamp(f, &meta.timestamp)?;
        }
        self.view.current(
            f,
            at,
            At {
                branch: 0,
                cursor: self.data.cursor(),
            },
        )?;
        self.view.saved(
            f,
            at,
            self.data.saved.map(|saved| At {
                branch: 0,
                cursor: saved,
            }),
        )?;
        if self.view.contains(View::DETAILED) {
            writeln!(f)?;
            self.view.message(f, meta, 0)
        } else {
            f.write_char(' ')?;
            self.view.message(f, meta, 0)?;
            writeln!(f)
        }
    }
}

impl<'a, R> Display<'a, History<R>> {
    #[inline]
    fn fmt_list(
        &self,
        f: &mut fmt::Formatter,
        at: At,
        meta: &Meta<R>,
        level: usize,
    ) -> fmt::Result {
        self.view.mark(f)?;
        self.view.position(f, at, true)?;
        if self.view.contains(View::DETAILED) {
            #[cfg(feature = "chrono")]
            self.view.timestamp(f, &meta.timestamp)?;
        }
        self.view.current(
            f,
            at,
            At {
                branch: self.data.root(),
                cursor: self.data.cursor(),
            },
        )?;
        self.view.saved(
            f,
            at,
            self.data
                .record
                .saved
                .map(|saved| At {
                    branch: self.data.root(),
                    cursor: saved,
                })
                .or(self.data.saved),
        )?;
        if self.view.contains(View::DETAILED) {
            writeln!(f)?;
            self.view.message(f, meta, level)
        } else {
            f.write_char(' ')?;
            self.view.message(f, meta, level)?;
            writeln!(f)
        }
    }

    #[inline]
    fn fmt_graph(
        &self,
        f: &mut fmt::Formatter,
        at: At,
        meta: &Meta<R>,
        level: usize,
    ) -> fmt::Result {
        for (&i, branch) in self
            .data
            .branches
            .iter()
            .filter(|(_, branch)| branch.parent == at)
        {
            for (j, cmd) in branch.commands.iter().enumerate().rev() {
                let at = At {
                    branch: i,
                    cursor: j + branch.parent.cursor + 1,
                };
                self.fmt_graph(f, at, cmd, level + 1)?;
            }
            for j in 0..level {
                self.view.edge(f, j)?;
                f.write_char(' ')?;
            }
            self.view.split(f, level)?;
            writeln!(f)?;
        }
        for i in 0..level {
            self.view.edge(f, i)?;
            f.write_char(' ')?;
        }
        self.fmt_list(f, at, meta, level)
    }
}

impl<'a, T: 'a> From<&'a T> for Display<'a, T> {
    #[inline]
    fn from(data: &'a T) -> Self {
        Display {
            data,
            view: View::default(),
        }
    }
}

impl<'a, R> fmt::Display for Display<'a, Record<R>> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (i, cmd) in self.data.commands.iter().enumerate().rev() {
            let at = At {
                branch: 0,
                cursor: i + 1,
            };
            self.fmt_list(f, at, cmd)?;
        }
        Ok(())
    }
}

impl<'a, R> fmt::Display for Display<'a, History<R>> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (i, cmd) in self.data.record.commands.iter().enumerate().rev() {
            let at = At {
                branch: self.data.root(),
                cursor: i + 1,
            };
            if self.view.contains(View::GRAPH) {
                self.fmt_graph(f, at, cmd, 0)?;
            } else {
                self.fmt_list(f, at, cmd, 0)?;
            }
        }
        Ok(())
    }
}

bitflags! {
    struct View: u8 {
        const COLORED   = 0b_0000_0001;
        const CURRENT   = 0b_0000_0010;
        const DETAILED  = 0b_0000_0100;
        const GRAPH     = 0b_0000_1000;
        const LIGATURES = 0b_0001_0000;
        const MARK      = 0b_0010_0000;
        const POSITION  = 0b_0100_0000;
        const SAVED     = 0b_1000_0000;
    }
}

impl Default for View {
    #[inline]
    fn default() -> Self {
        View::CURRENT | View::DETAILED | View::MARK | View::POSITION | View::SAVED
    }
}

impl View {
    #[inline]
    fn message(self, f: &mut fmt::Formatter, msg: &impl ToString, level: usize) -> fmt::Result {
        let msg = msg.to_string();
        let lines = msg.lines();
        if self.contains(View::DETAILED) {
            for line in lines {
                for i in 0..=level {
                    self.edge(f, i)?;
                    f.write_char(' ')?;
                }
                writeln!(f, "{}", line.trim())?;
            }
        } else if let Some(line) = lines.map(|s| s.trim()).find(|s| !s.is_empty()) {
            f.write_str(&line)?;
        }
        Ok(())
    }

    #[inline]
    fn mark(self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.contains(View::MARK) {
            f.write_str("*")
        } else {
            Ok(())
        }
    }

    #[inline]
    fn edge(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
        if self.contains(View::COLORED) {
            write!(f, "{}", "|".color(color(level)))
        } else {
            f.write_str("|")
        }
    }

    #[inline]
    fn split(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
        if self.contains(View::COLORED) {
            write!(
                f,
                "{}{}",
                "|".color(color(level)),
                "/".color(color(level + 1))
            )
        } else {
            f.write_str("|/")
        }
    }

    #[inline]
    fn position(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result {
        if self.contains(View::POSITION) {
            if self.contains(View::COLORED) {
                let position = if use_branch {
                    format!("[{}:{}]", at.branch, at.cursor)
                } else {
                    format!("[{}]", at.cursor)
                };
                write!(f, " {}", position.yellow())
            } else if use_branch {
                write!(f, " [{}:{}]", at.branch, at.cursor)
            } else {
                write!(f, " [{}]", at.cursor)
            }
        } else {
            Ok(())
        }
    }

    #[inline]
    fn current(self, f: &mut fmt::Formatter, at: At, current: At) -> fmt::Result {
        if self.contains(View::CURRENT) && at == current {
            if self.contains(View::COLORED) {
                write!(f, " {}{}{}", "(".yellow(), "current".cyan(), ")".yellow())
            } else {
                f.write_str(" (current)")
            }
        } else {
            Ok(())
        }
    }

    #[inline]
    fn saved(self, f: &mut fmt::Formatter, at: At, saved: Option<At>) -> fmt::Result {
        if self.contains(View::SAVED) && saved.map_or(false, |saved| saved == at) {
            if self.contains(View::COLORED) {
                write!(
                    f,
                    " {}{}{}",
                    "(".yellow(),
                    "saved".bright_green(),
                    ")".yellow()
                )
            } else {
                f.write_str(" (saved)")
            }
        } else {
            Ok(())
        }
    }

    #[inline]
    #[cfg(feature = "chrono")]
    fn timestamp(self, f: &mut fmt::Formatter, timestamp: &DateTime<impl TimeZone>) -> fmt::Result {
        let local = Local.from_utc_datetime(&timestamp.naive_utc());
        if self.contains(View::COLORED) {
            let ts = format!("[{}]", local.format("%T %F"));
            write!(f, " {}", ts.yellow())
        } else {
            write!(f, " [{}]", local.format("%T %F"))
        }
    }
}

#[inline]
fn color(i: usize) -> Color {
    match i % 6 {
        0 => Color::Red,
        1 => Color::Blue,
        2 => Color::Magenta,
        3 => Color::Yellow,
        4 => Color::Green,
        5 => Color::Cyan,
        _ => unreachable!(),
    }
}
<commit_msg>Use str::trim in map<commit_after>use bitflags::bitflags;
#[cfg(feature = "chrono")]
use chrono::{DateTime, Local, TimeZone};
use colored::{Color, Colorize};
use std::fmt::{self, Write};
use {At, History, Meta, Record};

/// Configurable display formatting of structures.
#[derive(Copy, Clone, Debug)]
pub struct Display<'a, T: 'a> {
    data: &'a T,
    view: View,
}

impl<'a, T: 'a> Display<'a, T> {
    /// Show colored output (off by default).
    #[inline]
    pub fn colored(&mut self, on: bool) -> &mut Self {
        self.view.set(View::COLORED, on);
        self
    }

    /// Show detailed output (on by default).
    #[inline]
    pub fn detailed(&mut self, on: bool) -> &mut Self {
        self.view.set(View::DETAILED, on);
        self
    }

    /// Show the position of the command (on by default).
    #[inline]
    pub fn position(&mut self, on: bool) -> &mut Self {
        self.view.set(View::POSITION, on);
        self
    }

    /// Show the saved command (on by default).
    #[inline]
    pub fn saved(&mut self, on: bool) -> &mut Self {
        self.view.set(View::SAVED, on);
        self
    }
}

impl<'a, R> Display<'a, History<R>> {
    /// Show the history as a graph (off by default).
    #[inline]
    pub fn graph(&mut self, on: bool) -> &mut Self {
        self.view.set(View::GRAPH, on);
        self
    }
}

impl<'a, R> Display<'a, Record<R>> {
    #[inline]
    fn fmt_list(&self, f: &mut fmt::Formatter, at: At, meta: &Meta<R>) -> fmt::Result {
        self.view.mark(f)?;
        self.view.position(f, at, false)?;
        if self.view.contains(View::DETAILED) {
            #[cfg(feature = "chrono")]
            self.view.timestamp(f, &meta.timestamp)?;
        }
        self.view.current(
            f,
            at,
            At {
                branch: 0,
                cursor: self.data.cursor(),
            },
        )?;
        self.view.saved(
            f,
            at,
            self.data.saved.map(|saved| At {
                branch: 0,
                cursor: saved,
            }),
        )?;
        if self.view.contains(View::DETAILED) {
            writeln!(f)?;
            self.view.message(f, meta, 0)
        } else {
            f.write_char(' ')?;
            self.view.message(f, meta, 0)?;
            writeln!(f)
        }
    }
}

impl<'a, R> Display<'a, History<R>> {
    #[inline]
    fn fmt_list(
        &self,
        f: &mut fmt::Formatter,
        at: At,
        meta: &Meta<R>,
        level: usize,
    ) -> fmt::Result {
        self.view.mark(f)?;
        self.view.position(f, at, true)?;
        if self.view.contains(View::DETAILED) {
            #[cfg(feature = "chrono")]
            self.view.timestamp(f, &meta.timestamp)?;
        }
        self.view.current(
            f,
            at,
            At {
                branch: self.data.root(),
                cursor: self.data.cursor(),
            },
        )?;
        self.view.saved(
            f,
            at,
            self.data
                .record
                .saved
                .map(|saved| At {
                    branch: self.data.root(),
                    cursor: saved,
                })
                .or(self.data.saved),
        )?;
        if self.view.contains(View::DETAILED) {
            writeln!(f)?;
            self.view.message(f, meta, level)
        } else {
            f.write_char(' ')?;
            self.view.message(f, meta, level)?;
            writeln!(f)
        }
    }

    #[inline]
    fn fmt_graph(
        &self,
        f: &mut fmt::Formatter,
        at: At,
        meta: &Meta<R>,
        level: usize,
    ) -> fmt::Result {
        for (&i, branch) in self
            .data
            .branches
            .iter()
            .filter(|(_, branch)| branch.parent == at)
        {
            for (j, cmd) in branch.commands.iter().enumerate().rev() {
                let at = At {
                    branch: i,
                    cursor: j + branch.parent.cursor + 1,
                };
                self.fmt_graph(f, at, cmd, level + 1)?;
            }
            for j in 0..level {
                self.view.edge(f, j)?;
                f.write_char(' ')?;
            }
            self.view.split(f, level)?;
            writeln!(f)?;
        }
        for i in 0..level {
            self.view.edge(f, i)?;
            f.write_char(' ')?;
        }
        self.fmt_list(f, at, meta, level)
    }
}

impl<'a, T: 'a> From<&'a T> for Display<'a, T> {
    #[inline]
    fn from(data: &'a T) -> Self {
        Display {
            data,
            view: View::default(),
        }
    }
}

impl<'a, R> fmt::Display for Display<'a, Record<R>> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (i, cmd) in self.data.commands.iter().enumerate().rev() {
            let at = At {
                branch: 0,
                cursor: i + 1,
            };
            self.fmt_list(f, at, cmd)?;
        }
        Ok(())
    }
}

impl<'a, R> fmt::Display for Display<'a, History<R>> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (i, cmd) in self.data.record.commands.iter().enumerate().rev() {
            let at = At {
                branch: self.data.root(),
                cursor: i + 1,
            };
            if self.view.contains(View::GRAPH) {
                self.fmt_graph(f, at, cmd, 0)?;
            } else {
                self.fmt_list(f, at, cmd, 0)?;
            }
        }
        Ok(())
    }
}

bitflags! {
    struct View: u8 {
        const COLORED   = 0b_0000_0001;
        const CURRENT   = 0b_0000_0010;
        const DETAILED  = 0b_0000_0100;
        const GRAPH     = 0b_0000_1000;
        const LIGATURES = 0b_0001_0000;
        const MARK      = 0b_0010_0000;
        const POSITION  = 0b_0100_0000;
        const SAVED     = 0b_1000_0000;
    }
}

impl Default for View {
    #[inline]
    fn default() -> Self {
        View::CURRENT | View::DETAILED | View::MARK | View::POSITION | View::SAVED
    }
}

impl View {
    #[inline]
    fn message(self, f: &mut fmt::Formatter, msg: &impl ToString, level: usize) -> fmt::Result {
        let msg = msg.to_string();
        let lines = msg.lines();
        if self.contains(View::DETAILED) {
            for line in lines {
                for i in 0..=level {
                    self.edge(f, i)?;
                    f.write_char(' ')?;
                }
                writeln!(f, "{}", line.trim())?;
            }
        } else if let Some(line) = lines.map(str::trim).find(|s| !s.is_empty()) {
            f.write_str(&line)?;
        }
        Ok(())
    }

    #[inline]
    fn mark(self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.contains(View::MARK) {
            f.write_str("*")
        } else {
            Ok(())
        }
    }

    #[inline]
    fn edge(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
        if self.contains(View::COLORED) {
            write!(f, "{}", "|".color(color(level)))
        } else {
            f.write_str("|")
        }
    }

    #[inline]
    fn split(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
        if self.contains(View::COLORED) {
            write!(
                f,
                "{}{}",
                "|".color(color(level)),
                "/".color(color(level + 1))
            )
        } else {
            f.write_str("|/")
        }
    }

    #[inline]
    fn position(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result {
        if self.contains(View::POSITION) {
            if self.contains(View::COLORED) {
                let position = if use_branch {
                    format!("[{}:{}]", at.branch, at.cursor)
                } else {
                    format!("[{}]", at.cursor)
                };
                write!(f, " {}", position.yellow())
            } else if use_branch {
                write!(f, " [{}:{}]", at.branch, at.cursor)
            } else {
                write!(f, " [{}]", at.cursor)
            }
        } else {
            Ok(())
        }
    }

    #[inline]
    fn current(self, f: &mut fmt::Formatter, at: At, current: At) -> fmt::Result {
        if self.contains(View::CURRENT) && at == current {
            if self.contains(View::COLORED) {
                write!(f, " {}{}{}", "(".yellow(), "current".cyan(), ")".yellow())
            } else {
                f.write_str(" (current)")
            }
        } else {
            Ok(())
        }
    }

    #[inline]
    fn saved(self, f: &mut fmt::Formatter, at: At, saved: Option<At>) -> fmt::Result {
        if self.contains(View::SAVED) && saved.map_or(false, |saved| saved == at) {
            if self.contains(View::COLORED) {
                write!(
                    f,
                    " {}{}{}",
                    "(".yellow(),
                    "saved".bright_green(),
                    ")".yellow()
                )
            } else {
                f.write_str(" (saved)")
            }
        } else {
            Ok(())
        }
    }

    #[inline]
    #[cfg(feature = "chrono")]
    fn timestamp(self, f: &mut fmt::Formatter, timestamp: &DateTime<impl TimeZone>) -> fmt::Result {
        let local = Local.from_utc_datetime(&timestamp.naive_utc());
        if self.contains(View::COLORED) {
            let ts = format!("[{}]", local.format("%T %F"));
            write!(f, " {}", ts.yellow())
        } else {
            write!(f, " [{}]", local.format("%T %F"))
        }
    }
}

#[inline]
fn color(i: usize) -> Color {
    match i % 6 {
        0 => Color::Red,
        1 => Color::Blue,
        2 => Color::Magenta,
        3 => Color::Yellow,
        4 => Color::Green,
        5 => Color::Cyan,
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>use crate::decor::{Formatted, Repr};
use crate::document::Document;
use crate::table::{Item, Table};
use crate::value::{Array, DateTime, InlineTable, Value};
use std::cell::{Cell, RefCell};
use std::fmt::{Display, Formatter, Result};

impl Display for Repr {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(
            f,
            "{}{}{}",
            self.decor.prefix, self.raw_value, self.decor.suffix
        )
    }
}

impl<T> Display for Formatted<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}", self.repr)
    }
}

impl Display for DateTime {
    fn fmt(&self, f: &mut Formatter) -> Result {
        match *self {
            DateTime::OffsetDateTime(d) => write!(f, "{}", d),
            DateTime::LocalDateTime(d) => write!(f, "{}", d),
            DateTime::LocalDate(d) => write!(f, "{}", d),
            DateTime::LocalTime(d) => write!(f, "{}", d),
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut Formatter) -> Result {
        match *self {
            Value::Integer(ref repr) => write!(f, "{}", repr),
            Value::String(ref repr) => write!(f, "{}", repr),
            Value::Float(ref repr) => write!(f, "{}", repr),
            Value::Boolean(ref repr) => write!(f, "{}", repr),
            Value::DateTime(ref repr) => write!(f, "{}", repr),
            Value::Array(ref array) => write!(f, "{}", array),
            Value::InlineTable(ref table) => write!(f, "{}", table),
        }
    }
}

impl Display for Array {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}[", self.decor.prefix)?;
        join(f, self.iter(), ",")?;
        if self.trailing_comma {
            write!(f, ",")?;
        }
        write!(f, "{}", self.trailing)?;
        write!(f, "]{}", self.decor.suffix)
    }
}

impl Display for InlineTable {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}{{", self.decor.prefix)?;
        write!(f, "{}", self.preamble)?;
        for (i, (key, value)) in self
            .items
            .iter()
            .filter(|&(_, kv)| kv.value.is_value())
            .map(|(_, kv)| (&kv.key, kv.value.as_value().unwrap()))
            .enumerate()
        {
            if i > 0 {
                write!(f, ",")?;
            }
            write!(f, "{}={}", key, value)?;
        }
        write!(f, "}}{}", self.decor.suffix)
    }
}

struct TableFormatState<'t> {
    path: RefCell<Vec<&'t str>>,
    table: Cell<&'t Table>,
}

impl<'a> Display for TableFormatState<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        let table = self.table.get();

        for kv in table.items.values() {
            if let Item::Value(ref value) = kv.value {
                writeln!(f, "{}={}", kv.key, value)?;
            }
        }

        for kv in table.items.values() {
            match kv.value {
                Item::Table(ref t) => {
                    self.path.borrow_mut().push(&kv.key.raw_value);
                    self.table.set(t);
                    if !(t.implicit && t.values_len() == 0) {
                        write!(f, "{}[", t.decor.prefix)?;
                        write!(f, "{}", self.path.borrow().join("."))?;
                        writeln!(f, "]{}", t.decor.suffix)?;
                    }
                    write!(f, "{}", self)?;
                    self.table.set(table);
                    self.path.borrow_mut().pop();
                }
                Item::ArrayOfTables(ref a) => {
                    self.path.borrow_mut().push(&kv.key.raw_value);
                    for t in a.iter() {
                        self.table.set(t);
                        write!(f, "{}[[", t.decor.prefix)?;
                        write!(f, "{}", self.path.borrow().join("."))?;
                        writeln!(f, "]]{}", t.decor.suffix)?;
                        write!(f, "{}", self)?;
                    }
                    self.table.set(table);
                    self.path.borrow_mut().pop();
                }
                _ => {}
            }
        }
        Ok(())
    }
}

impl Display for Table {
    fn fmt(&self, f: &mut Formatter) -> Result {
        let state = TableFormatState {
            path: RefCell::new(Vec::new()),
            table: Cell::new(self),
        };
        write!(f, "{}", state)
    }
}

impl Display for Document {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}", self.as_table())?;
        write!(f, "{}", self.trailing)
    }
}

fn join<D, I>(f: &mut Formatter, iter: I, sep: &str) -> Result
where
    D: Display,
    I: Iterator<Item = D>,
{
    for (i, v) in iter.enumerate() {
        if i > 0 {
            write!(f, "{}", sep)?;
        }
        write!(f, "{}", v)?;
    }
    Ok(())
}
<commit_msg>refactor impl Display for Table<commit_after>use crate::decor::{Formatted, Repr};
use crate::document::Document;
use crate::table::{Item, Table};
use crate::value::{Array, DateTime, InlineTable, Value};
use std::cell::{Cell, RefCell};
use std::fmt::{Display, Formatter, Result};

impl Display for Repr {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(
            f,
            "{}{}{}",
            self.decor.prefix, self.raw_value, self.decor.suffix
        )
    }
}

impl<T> Display for Formatted<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}", self.repr)
    }
}

impl Display for DateTime {
    fn fmt(&self, f: &mut Formatter) -> Result {
        match *self {
            DateTime::OffsetDateTime(d) => write!(f, "{}", d),
            DateTime::LocalDateTime(d) => write!(f, "{}", d),
            DateTime::LocalDate(d) => write!(f, "{}", d),
            DateTime::LocalTime(d) => write!(f, "{}", d),
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut Formatter) -> Result {
        match *self {
            Value::Integer(ref repr) => write!(f, "{}", repr),
            Value::String(ref repr) => write!(f, "{}", repr),
            Value::Float(ref repr) => write!(f, "{}", repr),
            Value::Boolean(ref repr) => write!(f, "{}", repr),
            Value::DateTime(ref repr) => write!(f, "{}", repr),
            Value::Array(ref array) => write!(f, "{}", array),
            Value::InlineTable(ref table) => write!(f, "{}", table),
        }
    }
}

impl Display for Array {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}[", self.decor.prefix)?;
        join(f, self.iter(), ",")?;
        if self.trailing_comma {
            write!(f, ",")?;
        }
        write!(f, "{}", self.trailing)?;
        write!(f, "]{}", self.decor.suffix)
    }
}

impl Display for InlineTable {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}{{", self.decor.prefix)?;
        write!(f, "{}", self.preamble)?;
        for (i, (key, value)) in self
            .items
            .iter()
            .filter(|&(_, kv)| kv.value.is_value())
            .map(|(_, kv)| (&kv.key, kv.value.as_value().unwrap()))
            .enumerate()
        {
            if i > 0 {
                write!(f, ",")?;
            }
            write!(f, "{}={}", key, value)?;
        }
        write!(f, "}}{}", self.decor.suffix)
    }
}

fn visit_nested_tables<'t, F>(
    table: &'t Table,
    path: &mut Vec<&'t str>,
    is_array_of_tables: bool,
    callback: &mut F,
) -> Result
where
    F: FnMut(&Table, &Vec<&'t str>, bool) -> Result,
{
    callback(table, path, is_array_of_tables)?;

    for kv in table.items.values() {
        match kv.value {
            Item::Table(ref t) => {
                path.push(&kv.key.raw_value);
                visit_nested_tables(t, path, false, callback)?;
                path.pop();
            }
            Item::ArrayOfTables(ref a) => {
                for t in a.iter() {
                    path.push(&kv.key.raw_value);
                    visit_nested_tables(t, path, true, callback)?;
                    path.pop();
                }
            }
            _ => {}
        }
    }
    Ok(())
}

impl Display for Table {
    fn fmt(&self, f: &mut Formatter) -> Result {
        let mut path = Vec::new();

        visit_nested_tables(
            self,
            &mut path,
            false,
            &mut |t: &Table, path, is_array_of_tables: bool| {
                if path.len() == 0 {
                    // don't print header for the root node
                } else if is_array_of_tables {
                    write!(f, "{}[[", t.decor.prefix)?;
                    write!(f, "{}", path.join("."))?;
                    writeln!(f, "]]{}", t.decor.suffix)?;
                } else if !(t.implicit && t.values_len() == 0) {
                    write!(f, "{}[", t.decor.prefix)?;
                    write!(f, "{}", path.join("."))?;
                    writeln!(f, "]{}", t.decor.suffix)?;
                }
                // print table body
                for kv in t.items.values() {
                    if let Item::Value(ref value) = kv.value {
                        writeln!(f, "{}={}", kv.key, value)?;
                    }
                }
                Ok(())
            },
        )?;
        Ok(())
    }
}

impl Display for Document {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}", self.as_table())?;
        write!(f, "{}", self.trailing)
    }
}

fn join<D, I>(f: &mut Formatter, iter: I, sep: &str) -> Result
where
    D: Display,
    I: Iterator<Item = D>,
{
    for (i, v) in iter.enumerate() {
        if i > 0 {
            write!(f, "{}", sep)?;
        }
        write!(f, "{}", v)?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::hash::Hash;
use std::slice;

pub struct OrderedSet<T> {
    set: HashSet<T>,
    vec: Vec<T>,
}

impl<'a, T> OrderedSet<&'a T>
where
    T: Hash + Eq,
{
    pub fn new() -> Self {
        OrderedSet {
            set: HashSet::new(),
            vec: Vec::new(),
        }
    }

    pub fn insert(&mut self, value: &'a T) -> bool {
        let new = self.set.insert(value);
        if new {
            self.vec.push(value);
        }
        new
    }

    pub fn contains(&self, value: &T) -> bool {
        self.set.contains(value)
    }
}

impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
    type Item = &'a T;
    type IntoIter = Iter<'s, 'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        Iter(self.vec.iter())
    }
}

pub struct Iter<'s, 'a, T>(slice::Iter<'s, &'a T>);

impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().copied()
    }
}
<commit_msg>Debug impl for OrderedSet<commit_after>use std::collections::HashSet;
use std::fmt::{self, Debug};
use std::hash::Hash;
use std::slice;

pub struct OrderedSet<T> {
    set: HashSet<T>,
    vec: Vec<T>,
}

impl<'a, T> OrderedSet<&'a T>
where
    T: Hash + Eq,
{
    pub fn new() -> Self {
        OrderedSet {
            set: HashSet::new(),
            vec: Vec::new(),
        }
    }

    pub fn insert(&mut self, value: &'a T) -> bool {
        let new = self.set.insert(value);
        if new {
            self.vec.push(value);
        }
        new
    }

    pub fn contains(&self, value: &T) -> bool {
        self.set.contains(value)
    }
}

impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
    type Item = &'a T;
    type IntoIter = Iter<'s, 'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        Iter(self.vec.iter())
    }
}

pub struct Iter<'s, 'a, T>(slice::Iter<'s, &'a T>);

impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().copied()
    }
}

impl<'a, T> Debug for OrderedSet<&'a T>
where
    T: Debug,
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.debug_set().entries(self).finish()
    }
}
<|endoftext|>


<commit_before>// hex encoder and decoder used by rust-protobuf unittests

use std::str;

fn decode_hex_digit(digit: char) -> u8 {
    if digit >= '0' && digit <= '9' {
        digit as u8 - '0' as u8
    } else if digit >= 'a' && digit <= 'f' {
        digit as u8 - 'a' as u8 + 10
    } else if digit >= 'A' && digit <= 'F' {
        digit as u8 - 'A' as u8 + 10
    } else {
        fail!();
    }
}

pub fn decode_hex(hex: &str) -> ~[u8] {
    let mut r: ~[u8] = ~[];
    let mut pos = 0;
    loop {
        while pos < hex.char_len() && hex[pos] == ' ' as u8 {
            pos += 1;
        }
        if hex.char_len() - pos >= 2 {
            r.push((decode_hex_digit(hex.char_at(pos)) << 4) | decode_hex_digit(hex.char_at(pos + 1)));
            pos += 2;
            loop;
        }
        if pos == hex.char_len() {
            break;
        }
        fail!("pos = %ud", pos);
    }
    r
}

fn encode_hex_digit(digit: u8) -> char {
    if digit < 10 {
        ('0' as u8 + digit) as char
    } else if digit < 16 {
        ('a' as u8 + digit - 10) as char
    } else {
        fail!();
    }
}

fn encode_hex_byte(byte: u8) -> [char, ..2] {
    [encode_hex_digit(byte >> 4), encode_hex_digit(byte & 0x0Fu8)]
}

pub fn encode_hex(bytes: &[u8]) -> ~str {
    (do bytes.map |byte| {
        str::from_chars(encode_hex_byte(*byte))
    }).connect(" ")
}

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

    #[test]
    fn test_decode_hex() {
        assert_eq!(~[], decode_hex(""));
        assert_eq!(~[0x00], decode_hex("00"));
        assert_eq!(~[0xff], decode_hex("ff"));
        assert_eq!(~[0xab], decode_hex("AB"));
        assert_eq!(~[0xfa, 0x19], decode_hex("fa 19"));
    }

    #[test]
    fn test_encode_hex() {
        assert_eq!(~"", encode_hex([]));
        assert_eq!(~"00", encode_hex([0x00]));
        assert_eq!(~"ab", encode_hex([0xab]));
        assert_eq!(~"01 a2 1a fe", encode_hex([0x01, 0xa2, 0x1a, 0xfe]));
    }
}
<commit_msg>Char is no longer treated as an integer type<commit_after>// hex encoder and decoder used by rust-protobuf unittests

use std::str;
use std::char;

fn decode_hex_digit(digit: char) -> u8 {
    match char::to_digit(digit, 16) {
        Some(d) => d as u8,
        _ => fail!()
    }
}

pub fn decode_hex(hex: &str) -> ~[u8] {
    let mut r: ~[u8] = ~[];
    let mut pos = 0;
    loop {
        while pos < hex.char_len() && hex[pos] == ' ' as u8 {
            pos += 1;
        }
        if hex.char_len() - pos >= 2 {
            r.push((decode_hex_digit(hex.char_at(pos)) << 4) | decode_hex_digit(hex.char_at(pos + 1)));
            pos += 2;
            loop;
        }
        if pos == hex.char_len() {
            break;
        }
        fail!("pos = %ud", pos);
    }
    r
}

fn encode_hex_digit(digit: u8) -> char {
    match char::from_digit(digit as uint, 16) {
        Some(c) => c,
        _ => fail!()
    }
}

fn encode_hex_byte(byte: u8) -> [char, ..2] {
    [encode_hex_digit(byte >> 4), encode_hex_digit(byte & 0x0Fu8)]
}

pub fn encode_hex(bytes: &[u8]) -> ~str {
    (do bytes.map |byte| {
        str::from_chars(encode_hex_byte(*byte))
    }).connect(" ")
}

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

    #[test]
    fn test_decode_hex() {
        assert_eq!(~[], decode_hex(""));
        assert_eq!(~[0x00], decode_hex("00"));
        assert_eq!(~[0xff], decode_hex("ff"));
        assert_eq!(~[0xab], decode_hex("AB"));
        assert_eq!(~[0xfa, 0x19], decode_hex("fa 19"));
    }

    #[test]
    fn test_encode_hex() {
        assert_eq!(~"", encode_hex([]));
        assert_eq!(~"00", encode_hex([0x00]));
        assert_eq!(~"ab", encode_hex([0xab]));
        assert_eq!(~"01 a2 1a fe", encode_hex([0x01, 0xa2, 0x1a, 0xfe]));
    }
}
<|endoftext|>


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

//!  Note: this documentation is relative to `crowbook` *as a library*.
//!  For documentation regarding the *program* `crowbook`, see
//!  [the Github page](https://github.com/lise-henry/crowbook).
//!
//! # Book
//!
//! The central structure of Crowbook is `Book`, who coordinates everything.
//!
//! Its roles are:
//!
//! * reading a book configuration file and setting the book options accordingly
//! * reading the chapters (written in Markdown) listed in this configuration file
//!   to `Parser`, get back an AST and store it in memory
//! * call `HtmlRenderer`, `EpubRenderer`, `LatexRenderer` and/or `OdtRenderer`
//!   according to the book's parameters and generate the appopriate files.
//!
//! ## Example
//!
//! ```ignore
//! use crowbook::{Book, InfoLevel};
//! // Reads configuration file "foo.book" (and set verbosity do default level)
//! let mut book = Book::new_from_file("foo.book", InfoLevel::Info, &[]).unwrap();
//! // Render all formats according to this configuration file
//! book.render_all().unwrap();
//! ```
//!
//! This is basically the code for the `crowbook` binary (though it contains a
//! bit more error handling, checking parameters from command line and so on).
//! This is, however, not very interesting for a library usage.
//!
//! The `Book` structure, however, exposes its `chapter` fields, which contains
//! a vector with an element by chapter. With it, you can access the Markdown
//! for all chapters represented as an Abstact Syntax Tree (i.e., a vector of `Token`s).
//! It is thus possible to create a new renderer (or manipulate this AST in other ways).
//!
//! # Parser
//!
//! It is also possible to directly use `Parser` to transform some markdown string or file
//! to this AST:
//!
//! ```
//! use crowbook::{Parser,Token};
//! let mut parser = Parser::new();
//! let result = parser.parse("Some *valid* Markdown").unwrap();
//! assert_eq!(format!("{:?}", result),
//! r#"[Paragraph([Str("Some "), Emphasis([Str("valid")]), Str(" Markdown")])]"#);
//! ```
//!
//! Of course, you probably want to do something else with this AST than display it.
//! Let's assume you want to count the number of links in a document.
//!
//! ```
//! use crowbook::{Parser,Token};
//! fn count(ast: &[Token]) -> u32 {
//!    let mut n = 0;
//!    for token in ast {
//!        match *token {
//!            // It's a link, increase counter
//!            Token::Link(_,_,_) => n += 1,
//!            // It's not a link, let's count the number of links
//!            // inside of the inner element (if there is one)
//!           _ => {
//!                if let Some(sub_ast) = token.inner() {
//!                    n += count(sub_ast);
//!                }
//!            }
//!       }
//!    }
//!    n
//! }
//!
//! let md = "# Here's a [link](http://foo.bar) #\n And *another [one](http://foo.bar)* !";
//!
//! let mut parser = Parser::new();
//! let ast = parser.parse(md).unwrap();
//! assert_eq!(count(&ast), 2);
//! ```

extern crate pulldown_cmark as cmark;
extern crate mustache;
extern crate chrono;
extern crate uuid;
extern crate yaml_rust;
extern crate mime_guess;
extern crate walkdir;
extern crate rustc_serialize;
extern crate crossbeam;

pub mod cleaner;
pub mod escape;

pub use parser::Parser;
pub use book::Book;
pub use bookoptions::BookOptions;
pub use html::HtmlRenderer;
pub use epub::EpubRenderer;
pub use latex::LatexRenderer;
pub use odt::OdtRenderer;
pub use error::{Result, Error, Source};
pub use token::Token;
pub use number::Number;
pub use resource_handler::ResourceHandler;
pub use logger::{Logger, InfoLevel};
pub use html_dir::HtmlDirRenderer;
pub use html_single::HtmlSingleRenderer;
pub use renderer::Renderer;

#[macro_use]
mod html;
mod html_dir;
mod error;
mod book;
mod epub;
mod latex;
mod odt;
mod parser;
mod token;

mod number;
mod resource_handler;
mod logger;
mod bookoptions;
mod lang;
mod renderer;
mod html_single;

mod toc;
mod zipper;
mod templates;
mod bookoption;

#[cfg(test)]
mod tests;

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

//!  Note: this documentation is relative to `crowbook` *as a library*.
//!  For documentation regarding the *program* `crowbook`, see
//!  [the Github page](https://github.com/lise-henry/crowbook).
//!
//! # Book
//!
//! The central structure of Crowbook is `Book`, who coordinates everything.
//!
//! Its roles are:
//!
//! * reading a book configuration file and setting the book options accordingly
//! * reading the chapters (written in Markdown) listed in this configuration file
//!   to `Parser`, get back an AST and store it in memory
//! * call `HtmlRenderer`, `EpubRenderer`, `LatexRenderer` and/or `OdtRenderer`
//!   according to the book's parameters and generate the appopriate files.
//!
//! ## Example
//!
//! ```ignore
//! use crowbook::{Book, InfoLevel};
//! // Reads configuration file "foo.book" (and set verbosity do default level)
//! let mut book = Book::new_from_file("foo.book", InfoLevel::Info, &[]).unwrap();
//! // Render all formats according to this configuration file
//! book.render_all().unwrap();
//! ```
//!
//! This is basically the code for the `crowbook` binary (though it contains a
//! bit more error handling, checking parameters from command line and so on).
//! This is, however, not very interesting for a library usage.
//!
//! The `Book` structure, however, exposes its `chapter` fields, which contains
//! a vector with an element by chapter. With it, you can access the Markdown
//! for all chapters represented as an Abstact Syntax Tree (i.e., a vector of `Token`s).
//! It is thus possible to create a new renderer (or manipulate this AST in other ways).
//!
//! # Parser
//!
//! It is also possible to directly use `Parser` to transform some markdown string or file
//! to this AST:
//!
//! ```
//! use crowbook::{Parser,Token};
//! let mut parser = Parser::new();
//! let result = parser.parse("Some *valid* Markdown").unwrap();
//! assert_eq!(format!("{:?}", result),
//! r#"[Paragraph([Str("Some "), Emphasis([Str("valid")]), Str(" Markdown")])]"#);
//! ```
//!
//! Of course, you probably want to do something else with this AST than display it.
//! Let's assume you want to count the number of links in a document.
//!
//! ```
//! use crowbook::{Parser,Token};
//! fn count(ast: &[Token]) -> u32 {
//!    let mut n = 0;
//!    for token in ast {
//!        match *token {
//!            // It's a link, increase counter
//!            Token::Link(_,_,_) => n += 1,
//!            // It's not a link, let's count the number of links
//!            // inside of the inner element (if there is one)
//!           _ => {
//!                if let Some(sub_ast) = token.inner() {
//!                    n += count(sub_ast);
//!                }
//!            }
//!       }
//!    }
//!    n
//! }
//!
//! let md = "# Here's a [link](http://foo.bar) #\n And *another [one](http://foo.bar)* !";
//!
//! let mut parser = Parser::new();
//! let ast = parser.parse(md).unwrap();
//! assert_eq!(count(&ast), 2);
//! ```

extern crate pulldown_cmark as cmark;
extern crate mustache;
extern crate chrono;
extern crate uuid;
extern crate yaml_rust;
extern crate mime_guess;
extern crate walkdir;
extern crate rustc_serialize;
extern crate crossbeam;

pub mod cleaner;
pub mod escape;

pub use parser::Parser;
pub use book::Book;
pub use bookoption::BookOption;
pub use bookoptions::BookOptions;
pub use html::HtmlRenderer;
pub use epub::EpubRenderer;
pub use latex::LatexRenderer;
pub use odt::OdtRenderer;
pub use error::{Result, Error, Source};
pub use token::Token;
pub use number::Number;
pub use resource_handler::ResourceHandler;
pub use logger::{Logger, InfoLevel};
pub use html_dir::HtmlDirRenderer;
pub use html_single::HtmlSingleRenderer;
pub use renderer::Renderer;

#[macro_use]
mod html;
mod html_dir;
mod error;
mod book;
mod epub;
mod latex;
mod odt;
mod parser;
mod token;

mod number;
mod resource_handler;
mod logger;
mod bookoptions;
mod lang;
mod renderer;
mod html_single;

mod toc;
mod zipper;
mod templates;
mod bookoption;

#[cfg(test)]
mod tests;

<|endoftext|>


<commit_before>use std::mem;

macro_rules! boxed {
    [$($x:expr),*] => (Box::new([$($x),*]));
}

macro_rules! collect {
    ($x:expr, $y:ty) => ($x.collect::<Vec<$y>>().into_boxed_slice());
}

type Element = Box<[usize]>;

#[derive(Debug)]
pub struct Polytope<V> {
    dimensions: usize,
    vertices: Box<[V]>,
    elements: Box<[Box<[Element]>]>,
}

impl<V> Polytope<V> {
    pub fn new(dimensionless: V) -> Self {
        Polytope::<V> {
            dimensions: 0,
            vertices: boxed![dimensionless],
            elements: boxed![],
        }
    }
}

impl<V> Polytope<V> {
    pub fn extrude<F1, F2>(&self, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Vertices are a special case: Create a promoted pair of each and linked the pairs.
        let mut new_vertices = Vec::<V>::new();
        let mut new_edges = Vec::<Element>::new();
        for v in self.vertices.iter() {
            // Pulled in and pushed out copies.
            let i = new_vertices.len();
            new_vertices.push(pull_in(v));
            let j = new_vertices.len();
            new_vertices.push(push_out(v));

            // Linkage element.
            new_edges.push(boxed![i, j]);
        }

        // For the rest of the dimensions, create a pair of each element and linked them via a
        // higher dimensional element.
        let mut linkage_offset = 0;
        let mut new_elements = Vec::<Vec<Element>>::new();
        let mut new_elems_this = new_edges;
        let mut new_elems_next = Vec::<Element>::new();
        for elems_this in self.elements.iter() {
            let next_linkage_offset = new_elems_this.len();
            for e in elems_this.iter() {
                // Pulled in and pushed out copies.
                let i = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| linkage_offset + b * 2 + 0), usize));
                let j = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| linkage_offset + b * 2 + 1), usize));

                // Linkage element.
                new_elems_next.push(collect!(e.iter().chain([i, j].iter()).cloned(), usize));
            }
            linkage_offset = next_linkage_offset;
            // Reference-safe way of doing
            //   new_elements.push(new_elems_this);
            //   new_elems_this = new_elems_next;
            //   new_elems_next = vec![];
            new_elements.push(
                mem::replace(&mut new_elems_this,
                             mem::replace(&mut new_elems_next, vec![])));
        }
        // Don't forget the final dimension.
        new_elements.push(new_elems_this);

        let new_vertices = new_vertices.into_boxed_slice();
        let new_elements = collect!(new_elements.drain(..).map(
            |mut elements_of_this_dimension|
            collect!(elements_of_this_dimension.drain(..), Element)
        ), Box<[Element]>);
        Polytope::<V> {
            dimensions: self.dimensions + 1,
            vertices: new_vertices,
            elements: new_elements,
        }
    }

    pub fn cone<F1, F2>(&self, tip: V, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Create a promoted pair of each vertex and linked all of them to the tip.
        let mut new_vertices = Vec::<V>::new();
        let mut new_edges = Vec::<Element>::new();
        let tip_index = self.vertices.len() * 2;
        for v in self.vertices.iter() {
            // Pulled in and pushed out copies.
            let i = new_vertices.len();
            new_vertices.push(pull_in(v));
            let j = new_vertices.len();
            new_vertices.push(push_out(v));

            // Linkage elements.
            new_edges.push(boxed![i, tip_index]);
            new_edges.push(boxed![j, tip_index]);
        }
        new_vertices.push(tip);

        // For the rest of the dimensions, create a pair of each element and linked them to the tip.
        let mut linkage_offset = 0;
        let mut new_elements = Vec::<Vec<Element>>::new();
        let mut new_elems_this = new_edges;
        let mut new_elems_next = Vec::<Element>::new();
        for elems_this in self.elements.iter() {
            let next_linkage_offset = new_elems_this.len();
            for e in elems_this.iter() {
                // Pulled in and pushed out copies.
                let i = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| linkage_offset + b * 2 + 0), usize));
                let j = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| linkage_offset + b * 2 + 1), usize));

                // Linkage elements.
                new_elems_next.push(collect!(
                    e.iter().map(|b| b * 2 + 0).chain([i].iter().cloned()), usize));
                new_elems_next.push(collect!(
                    e.iter().map(|b| b * 2 + 1).chain([j].iter().cloned()), usize));
            }
            linkage_offset = next_linkage_offset;
            // Reference-safe way of doing
            //   new_elements.push(new_elems_this);
            //   new_elems_this = new_elems_next;
            //   new_elems_next = vec![];
            new_elements.push(
                mem::replace(&mut new_elems_this,
                             mem::replace(&mut new_elems_next, vec![])));
        }
        // Don't forget the final dimension.
        new_elements.push(new_elems_this);

        let new_vertices = new_vertices.into_boxed_slice();
        let new_elements = collect!(new_elements.drain(..).map(
            |mut elements_of_this_dimension|
            collect!(elements_of_this_dimension.drain(..), Element)
        ), Box<[Element]>);
        Polytope::<V> {
            dimensions: self.dimensions + 1,
            vertices: new_vertices,
            elements: new_elements,
        }
    }
}

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

    #[derive(Debug)]
    struct MyVertex {
        coords: Box<[f64]>,
    }

    impl Default for MyVertex {
        fn default() -> Self {
            MyVertex {
                coords: Box::new([]),
            }
        }
    }

    impl MyVertex {
        fn promote(&self, h: f64) -> Self {
            MyVertex {
                coords: collect!(self.coords.iter().chain([h].iter()).map(|&x| x), f64),
            }
        }
    }

    #[test]
    fn polytope_new() {
        let p = Polytope::<MyVertex>::new(Default::default());
        assert_eq!(p.dimensions, 0);
        assert_eq!(p.vertices.len(), 1);
        assert_eq!(p.elements.len(), 0);
    }

    #[test]
    fn extrude_point() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let q = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        assert_eq!(q.dimensions, 1);
        assert_eq!(q.vertices.len(), 2);
        assert!(q.vertices[0].coords == Box::new([-1.0]));
        assert!(q.vertices[1].coords == Box::new([ 1.0]));
        assert_eq!(q.elements.len(), 1);
        assert!(q.elements[0] == Box::new([Box::new([0, 1])]));
    }

    #[test]
    fn extrude_line() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.extrude(|v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.dimensions, 2);
        assert_eq!(q.vertices.len(), 4);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 1]),
            Box::new([2, 3]),
            Box::new([0, 2]),
            Box::new([1, 3]),
        ]));
        assert!(q.elements[1] == Box::new([Box::new([0, 1, 2, 3])]));
    }

    #[test]
    fn cone_line() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.cone(MyVertex { coords: boxed![0.0, 0.0] },
                       |v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.dimensions, 2);
        assert_eq!(q.vertices.len(), 5);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert!(q.vertices[4].coords == Box::new([ 0.0,  0.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 4]),
            Box::new([1, 4]),
            Box::new([2, 4]),
            Box::new([3, 4]),
            Box::new([0, 2]),
            Box::new([1, 3]),
        ]));
        assert!(q.elements[1] == Box::new([
            Box::new([0, 2, 4]),
            Box::new([1, 3, 5]),
        ]));
    }
}
<commit_msg>Extract common parts of extrude and cone.<commit_after>macro_rules! boxed {
    [$($x:expr),*] => (Box::new([$($x),*]));
}

macro_rules! collect {
    ($x:expr, $y:ty) => ($x.collect::<Vec<$y>>().into_boxed_slice());
}

type Element = Box<[usize]>;

#[derive(Debug)]
pub struct Polytope<V> {
    vertices: Box<[V]>,
    elements: Box<[Box<[Element]>]>,
}

impl<V> Polytope<V> {
    pub fn new(dimensionless: V) -> Self {
        Polytope::<V> {
            vertices: boxed![dimensionless],
            elements: boxed![],
        }
    }

    pub fn dimensions(&self) -> usize {
        self.elements.len()
    }

    fn from_vecs(vertices: Vec<V>, elements: Vec<Vec<Element>>) -> Self {
        let mut elements = elements;

        let vertices = vertices.into_boxed_slice();
        let elements = collect!(elements.drain(..)
            .map(|mut elems| collect!(elems.drain(..), Element)), Box<[Element]>);
        Polytope::<V> {
            vertices: vertices,
            elements: elements,
        }
    }

    fn replicate_vertices<F1, F2>(&self, map_1: F1, map_2: F2, out: &mut Vec<V>)
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        for v in self.vertices.iter() {
            out.push(map_1(v));
            out.push(map_2(v));
        }
    }

    fn replicate_elements(&self, out: &mut Vec<Vec<Element>>) {
        for elems in self.elements.iter() {
            let mut new_elems = Vec::<Element>::new();
            for e in elems.iter() {
                new_elems.push(collect!(e.iter().map(|b| b * 2 + 0), usize));
                new_elems.push(collect!(e.iter().map(|b| b * 2 + 1), usize));
            }
            out.push(new_elems);
        }
    }

    pub fn extrude<F1, F2>(&self, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Replicate vertices by pulling them in and pushing them out.
        let mut new_vertices = Vec::<V>::new();
        self.replicate_vertices(pull_in, push_out, &mut new_vertices);

        // Replicate the rest of the elements;
        let mut new_elements = Vec::<Vec<Element>>::new();
        self.replicate_elements(&mut new_elements);
        new_elements.push(vec![]);  // make room for the new dimension

        // Link replicated vertices to each other using edges
        let mut new_edges = Vec::<Element>::new();
        for i in 0..self.vertices.len() {
            new_edges.push(boxed![i * 2 + 0, i * 2 + 1]);
        }
        let mut offset = new_elements[0].len();
        new_elements[0].extend(new_edges);

        // Link the rest of the elements using higher-dimensional elements.
        for (d, elems_this) in self.elements.iter().enumerate() {
            let mut new_elems_next = Vec::<Element>::new();
            for (i, e) in elems_this.iter().enumerate() {
                new_elems_next.push(collect!(e.iter()
                    .map(|b| offset + b)
                    .chain([i * 2 + 0, i * 2 + 1].iter().cloned()), usize));
            }
            offset = new_elements[d + 1].len();
            new_elements[d + 1].extend(new_elems_next);
        }

        Self::from_vecs(new_vertices, new_elements)
    }

    pub fn cone<F1, F2>(&self, tip: V, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Replicate vertices by pulling them in and pushing them out.
        let mut new_vertices = Vec::<V>::new();
        self.replicate_vertices(pull_in, push_out, &mut new_vertices);

        // Replicate the rest of the elements;
        let mut new_elements = Vec::<Vec<Element>>::new();
        self.replicate_elements(&mut new_elements);
        new_elements.push(vec![]);  // make room for the new dimension

        // Add the tip vertex.
        let tip_index = new_vertices.len();
        new_vertices.push(tip);

        // Link replicated vertices to the tip vertex using edges.
        let mut new_edges = Vec::<Element>::new();
        for i in 0..self.vertices.len() {
            new_edges.push(boxed![i * 2 + 0, tip_index]);
            new_edges.push(boxed![i * 2 + 1, tip_index]);
        }
        let mut offset = new_elements[0].len();
        new_elements[0].extend(new_edges);

        // Link the rest of the elements using higher-dimensional elements.
        for (d, elems_this) in self.elements.iter().enumerate() {
            let mut new_elems_next = Vec::<Element>::new();
            for (i, e) in elems_this.iter().enumerate() {
                new_elems_next.push(collect!(e.iter()
                    .map(|b| offset + b * 2 + 0)
                    .chain([i * 2 + 0].iter().cloned()), usize));
                new_elems_next.push(collect!(e.iter()
                    .map(|b| offset + b * 2 + 1)
                    .chain([i * 2 + 1].iter().cloned()), usize));
            }
            offset = new_elements[d + 1].len();
            new_elements[d + 1].extend(new_elems_next);
        }

        Self::from_vecs(new_vertices, new_elements)
    }
}

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

    #[derive(Debug)]
    struct MyVertex {
        coords: Box<[f64]>,
    }

    impl Default for MyVertex {
        fn default() -> Self {
            MyVertex {
                coords: Box::new([]),
            }
        }
    }

    impl MyVertex {
        fn promote(&self, h: f64) -> Self {
            MyVertex {
                coords: collect!(self.coords.iter().chain([h].iter()).map(|&x| x), f64),
            }
        }
    }

    #[test]
    fn polytope_new() {
        let p = Polytope::<MyVertex>::new(Default::default());
        assert_eq!(p.vertices.len(), 1);
        assert_eq!(p.elements.len(), 0);
    }

    #[test]
    fn extrude_point() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let q = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        assert_eq!(q.vertices.len(), 2);
        assert!(q.vertices[0].coords == Box::new([-1.0]));
        assert!(q.vertices[1].coords == Box::new([ 1.0]));
        assert_eq!(q.elements.len(), 1);
        assert!(q.elements[0] == Box::new([Box::new([0, 1])]));
    }

    #[test]
    fn extrude_line() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.extrude(|v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.vertices.len(), 4);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 2]),
            Box::new([1, 3]),
            Box::new([0, 1]),
            Box::new([2, 3]),
        ]));
        assert!(q.elements[1] == Box::new([Box::new([2, 3, 0, 1])]));
    }

    #[test]
    fn cone_line() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.cone(MyVertex { coords: boxed![0.0, 0.0] },
                       |v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.vertices.len(), 5);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert!(q.vertices[4].coords == Box::new([ 0.0,  0.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 2]),
            Box::new([1, 3]),
            Box::new([0, 4]),
            Box::new([1, 4]),
            Box::new([2, 4]),
            Box::new([3, 4]),
        ]));
        assert!(q.elements[1] == Box::new([
            Box::new([2, 4, 0]),
            Box::new([3, 5, 1]),
        ]));
    }

    #[test]
    fn extrude_cone_line() {
        let p = Polytope::<MyVertex>::new(Default::default());
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let p = p.cone(MyVertex { coords: boxed![0.0, 0.0] },
                       |v| v.promote(-2.0), |v| v.promote(2.0));
        let q = p.extrude(|v| v.promote(-3.0), |v| v.promote(3.0));
        assert_eq!(q.vertices.len(), 10);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0, -3.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0, -2.0,  3.0]));
        assert!(q.vertices[2].coords == Box::new([-1.0,  2.0, -3.0]));
        assert!(q.vertices[3].coords == Box::new([-1.0,  2.0,  3.0]));
        assert!(q.vertices[4].coords == Box::new([ 1.0, -2.0, -3.0]));
        assert!(q.vertices[5].coords == Box::new([ 1.0, -2.0,  3.0]));
        assert!(q.vertices[6].coords == Box::new([ 1.0,  2.0, -3.0]));
        assert!(q.vertices[7].coords == Box::new([ 1.0,  2.0,  3.0]));
        assert!(q.vertices[8].coords == Box::new([ 0.0,  0.0, -3.0]));
        assert!(q.vertices[9].coords == Box::new([ 0.0,  0.0,  3.0]));
        assert_eq!(q.elements.len(), 3);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 4]),
            Box::new([1, 5]),
            Box::new([2, 6]),
            Box::new([3, 7]),
            Box::new([0, 8]),
            Box::new([1, 9]),
            Box::new([2, 8]),
            Box::new([3, 9]),
            Box::new([4, 8]),
            Box::new([5, 9]),
            Box::new([6, 8]),
            Box::new([7, 9]),
            Box::new([0, 1]),
            Box::new([2, 3]),
            Box::new([4, 5]),
            Box::new([6, 7]),
            Box::new([8, 9]),
        ]));
        assert!(q.elements[1] == Box::new([
            Box::new([4, 8, 0]),
            Box::new([5, 9, 1]),
            Box::new([6, 10, 2]),
            Box::new([7, 11, 3]),
            Box::new([12, 14, 0, 1]),
            Box::new([13, 15, 2, 3]),
            Box::new([12, 16, 4, 5]),
            Box::new([13, 16, 6, 7]),
            Box::new([14, 16, 8, 9]),
            Box::new([15, 16, 10, 11]),
        ]));
        assert!(q.elements[2] == Box::new([
            Box::new([6, 8, 4, 0, 1]),
            Box::new([7, 9, 5, 2, 3]),
        ]));
    }
}
<|endoftext|>


<commit_before>use rustbox::{RustBox, Key};
use time::Tm;

use std::collections::HashMap;
use std::collections::HashSet;

use rand;
use rand::Rng;

use tui::msg_area::MsgArea;
use tui::style;
use tui::text_field::TextField;
use tui::widget::{WidgetRet};

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub struct MessagingUI {
    /// Channel topic, user info etc.
    topic      : Option<String>,

    /// Incoming and sent messages appear
    msg_area   : MsgArea,

    /// User input field
    text_field : TextField,

    width      : i32,
    height     : i32,

    // NOTE: Color is encoded in Termbox's 216 colors. (in 256-color mode)
    nick_colors      : HashMap<String, u8>,
    available_colors : HashSet<u8>,
}

impl MessagingUI {
    pub fn new(width : i32, height : i32) -> MessagingUI {
        MessagingUI {
            topic: None,
            msg_area: MsgArea::new(width, height - 1),
            text_field: TextField::new(width),
            width: width,
            height: height,
            nick_colors: HashMap::new(),
            available_colors: HashSet::new(),
        }
    }

    pub fn set_topic(&mut self, topic : String) {
        self.topic = Some(topic);
        // FIXME: Disabling this - need to decide when/how to draw channel topics
        // self.msg_area.resize(self.width, self.height - 2);
    }

    pub fn draw(&self, rustbox : &RustBox, pos_x : i32, pos_y : i32) {
        // TODO: Most channels have long topics that don't fit into single line.
        // if let Some(ref topic) = self.topic {
        //     // rustbox.print(pos_x as usize, pos_y as usize,
        //     //               style::TOPIC.style,
        //     //               style::TOPIC.fg,
        //     //               style::TOPIC.bg,
        //     //               topic);
        //     self.msg_area.draw(rustbox, pos_x, pos_y + 1);
        // } else {
        //     self.msg_area.draw(rustbox, pos_x, pos_y);
        // }

        self.msg_area.draw(rustbox, pos_x, pos_y);
        self.text_field.draw(rustbox, pos_x, pos_y + self.height - 1);
    }

    pub fn keypressed(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Ctrl('p') => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            },

            Key::Ctrl('n') => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            },

            Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            },

            Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            },

            key => {
                // TODO: Handle ret
                match self.text_field.keypressed(key) {
                    WidgetRet::KeyIgnored => {
                        // self.show_server_msg("KEY IGNORED", format!("{:?}", key).as_ref());
                        WidgetRet::KeyIgnored
                    },
                    ret => ret,
                }
            },
        }
    }

    pub fn resize(&mut self, width : i32, height : i32) {
        self.width = width;
        self.height = height;
        self.msg_area.resize(width, height - 1);
        self.text_field.resize(width, 1);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Methods delegeted to the msg_area

impl MessagingUI {
    #[inline]
    pub fn add_client_err_msg(&mut self, msg : &str) {
        self.msg_area.add_text(msg, &style::ERR_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_client_msg(&mut self, msg : &str) {
        self.msg_area.add_text(msg, &style::USER_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_privmsg(&mut self, sender : &str, msg : &str, tm : &Tm) {
        let fg = self.get_nick_color(sender);
        self.msg_area.add_text(&format!("[{}] <", tm.strftime("%H:%M").unwrap()),
                               &style::USER_MSG_SS);

        let mut sender_style = String::with_capacity(3);
        sender_style.push(style::TERMBOX_COLOR_PREFIX);
        sender_style.push(fg as char);
        sender_style.push('\x00');
        self.msg_area.add_text(sender, &style::StyleStr(&sender_style));

        // Need to write this to clear fg/bg. Otherwise we end up ORing old
        // fg/bg with new ones.
        self.msg_area.add_char(style::RESET_PREFIX);

        self.msg_area.add_text(&format!("> {}", msg), &style::USER_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_msg(&mut self, msg : &str, tm : &Tm) {
        self.msg_area.add_text(
            &format!("[{}] {}", tm.strftime("%H:%M").unwrap(), msg),
            &style::USER_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_err_msg(&mut self, msg : &str, tm : &Tm) {
        self.msg_area.add_text(
            &format!("[{}] ", tm.strftime("%H:%M").unwrap()),
            &style::USER_MSG_SS);
        self.msg_area.add_text(msg, &style::ERR_MSG_SS);
        self.msg_area.flush_line();
    }

    fn get_nick_color(&mut self, sender : &str) -> u8 {
        match self.nick_colors.get(sender) {
            Some(color) => {
                return *color;
            }
            None => {},
        }

        let mut rng = rand::thread_rng();
        let ret = {
            if self.available_colors.is_empty() {
                let available_colors = self.available_colors.iter().cloned().collect::<Vec<u8>>();
                *rng.choose(&available_colors).unwrap()
            } else {
                rng.gen_range(16, 232)
            }
        };

        self.nick_colors.insert(sender.to_owned(), ret);
        ret
    }
}
<commit_msg>fix a bug introduced with last commit<commit_after>use rustbox::{RustBox, Key};
use time::Tm;

use std::collections::HashMap;
use std::collections::HashSet;

use rand;
use rand::Rng;

use tui::msg_area::MsgArea;
use tui::style;
use tui::text_field::TextField;
use tui::widget::{WidgetRet};

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub struct MessagingUI {
    /// Channel topic, user info etc.
    topic      : Option<String>,

    /// Incoming and sent messages appear
    msg_area   : MsgArea,

    /// User input field
    text_field : TextField,

    width      : i32,
    height     : i32,

    // NOTE: Color is encoded in Termbox's 216 colors. (in 256-color mode)
    nick_colors      : HashMap<String, u8>,
    available_colors : HashSet<u8>,
}

impl MessagingUI {
    pub fn new(width : i32, height : i32) -> MessagingUI {
        MessagingUI {
            topic: None,
            msg_area: MsgArea::new(width, height - 1),
            text_field: TextField::new(width),
            width: width,
            height: height,
            nick_colors: HashMap::new(),
            available_colors: HashSet::new(),
        }
    }

    pub fn set_topic(&mut self, topic : String) {
        self.topic = Some(topic);
        // FIXME: Disabling this - need to decide when/how to draw channel topics
        // self.msg_area.resize(self.width, self.height - 2);
    }

    pub fn draw(&self, rustbox : &RustBox, pos_x : i32, pos_y : i32) {
        // TODO: Most channels have long topics that don't fit into single line.
        // if let Some(ref topic) = self.topic {
        //     // rustbox.print(pos_x as usize, pos_y as usize,
        //     //               style::TOPIC.style,
        //     //               style::TOPIC.fg,
        //     //               style::TOPIC.bg,
        //     //               topic);
        //     self.msg_area.draw(rustbox, pos_x, pos_y + 1);
        // } else {
        //     self.msg_area.draw(rustbox, pos_x, pos_y);
        // }

        self.msg_area.draw(rustbox, pos_x, pos_y);
        self.text_field.draw(rustbox, pos_x, pos_y + self.height - 1);
    }

    pub fn keypressed(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Ctrl('p') => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            },

            Key::Ctrl('n') => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            },

            Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            },

            Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            },

            key => {
                // TODO: Handle ret
                match self.text_field.keypressed(key) {
                    WidgetRet::KeyIgnored => {
                        // self.show_server_msg("KEY IGNORED", format!("{:?}", key).as_ref());
                        WidgetRet::KeyIgnored
                    },
                    ret => ret,
                }
            },
        }
    }

    pub fn resize(&mut self, width : i32, height : i32) {
        self.width = width;
        self.height = height;
        self.msg_area.resize(width, height - 1);
        self.text_field.resize(width, 1);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Methods delegeted to the msg_area

impl MessagingUI {
    #[inline]
    pub fn add_client_err_msg(&mut self, msg : &str) {
        self.msg_area.add_text(msg, &style::ERR_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_client_msg(&mut self, msg : &str) {
        self.msg_area.add_text(msg, &style::USER_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_privmsg(&mut self, sender : &str, msg : &str, tm : &Tm) {
        let fg = self.get_nick_color(sender);
        self.msg_area.add_text(&format!("[{}] <", tm.strftime("%H:%M").unwrap()),
                               &style::USER_MSG_SS);

        let mut sender_style = String::with_capacity(3);
        sender_style.push(style::TERMBOX_COLOR_PREFIX);
        sender_style.push(fg as char);
        sender_style.push('\x00');
        self.msg_area.add_text(sender, &style::StyleStr(&sender_style));

        // Need to write this to clear fg/bg. Otherwise we end up ORing old
        // fg/bg with new ones.
        self.msg_area.add_char(style::RESET_PREFIX);

        self.msg_area.add_text(&format!("> {}", msg), &style::USER_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_msg(&mut self, msg : &str, tm : &Tm) {
        self.msg_area.add_text(
            &format!("[{}] {}", tm.strftime("%H:%M").unwrap(), msg),
            &style::USER_MSG_SS);
        self.msg_area.flush_line();
    }

    #[inline]
    pub fn add_err_msg(&mut self, msg : &str, tm : &Tm) {
        self.msg_area.add_text(
            &format!("[{}] ", tm.strftime("%H:%M").unwrap()),
            &style::USER_MSG_SS);
        self.msg_area.add_text(msg, &style::ERR_MSG_SS);
        self.msg_area.flush_line();
    }

    fn get_nick_color(&mut self, sender : &str) -> u8 {
        match self.nick_colors.get(sender) {
            Some(color) => {
                return *color;
            }
            None => {},
        }

        let mut rng = rand::thread_rng();
        let ret = {
            if !self.available_colors.is_empty() {
                let available_colors = self.available_colors.iter().cloned().collect::<Vec<u8>>();
                *rng.choose(&available_colors).unwrap()
            } else {
                rng.gen_range(16, 232)
            }
        };

        self.nick_colors.insert(sender.to_owned(), ret);
        ret
    }
}
<|endoftext|>