<commit_before>use color::PuyoColor;
use small_int_set::SmallIntSet;

const MAX_SIZE: usize = 8;

pub struct ColumnPuyoList {
    size: [usize; 6],
    puyo: [[PuyoColor; 6]; MAX_SIZE],
    place_holders: [SmallIntSet; 6],
}

impl ColumnPuyoList {
    pub fn new() -> ColumnPuyoList {
        ColumnPuyoList {
            size: [0; 6],
            puyo: [[PuyoColor::EMPTY; 6]; MAX_SIZE],
            place_holders: [SmallIntSet::new(); 6],
        }
    }

    fn is_place_holder(c: PuyoColor) -> bool {
        c == PuyoColor::IRON
    }

    pub fn size_on(&self, x: usize) -> usize {
        self.size[x - 1]
    }

    pub fn is_empty(&self) -> bool {
        self.size() == 0
    }

    pub fn size(&self) -> usize {
        self.size[0] + self.size[1] + self.size[2] +
        self.size[3] + self.size[4] + self.size[5]
    }

    pub fn top(&self, x: usize) -> Option<PuyoColor> {
        if self.size_on(x) == 0 {
            None
        } else {
            Some(self.puyo[x - 1][self.size_on(x) - 1])
        }
    }

    pub fn has_place_holder(&self) -> bool {
        for i in 0..6 {
            if !self.place_holders[i].is_empty() {
                return true;
            }
        }

        false
    }

    /// Adds PuyoColor `c` on column `x`.
    /// Returns true if succeeded. Returns false otherwise.
    /// When failed, the `self` won't be changed.
    pub fn add(&mut self, x: usize, c: PuyoColor) -> bool {
        if MAX_SIZE <= self.size[x - 1] {
            return false;
        }
        if ColumnPuyoList::is_place_holder(c) {
            self.place_holders[x - 1].set(self.size[x - 1]);
        }
        self.puyo[x - 1][self.size[x - 1]] = c;
        self.size[x - 1] += 1;
        true
    }

    /// Adds `n` PuyoColor `c` on column `x`.
    /// Returns true if succeeded. Returns false otherwise.
    /// When failed, the `self` won't be changed.
    pub fn add_multi(&mut self, x: usize, c: PuyoColor, n: usize) -> bool {
        if MAX_SIZE < self.size_on(x) + n {
            return false;
        }
        for i in 0..n {
            if ColumnPuyoList::is_place_holder(c) {
                self.place_holders[x - 1].set(self.size[x - 1] + i);
            }
            self.puyo[x - 1][self.size[x - 1] + i] = c;
        }
        self.size[x - 1] += n;
        true
    }

    /// Removes top puyo from column x.
    pub fn remove_top(&mut self, x: usize) {
        if self.size_on(x) == 0 {
            return;
        }
        let c = self.puyo[x - 1][self.size[x - 1] - 1];
        if ColumnPuyoList::is_place_holder(c) {
            self.place_holders[x - 1].unset(self.size[x - 1] - 1);
        }
        self.size[x - 1] -= 1;
    }
}

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

    #[test]
    fn test_constructor() {
        let cpl = ColumnPuyoList::new();
        assert_eq!(cpl.size(), 0);
        assert!(cpl.is_empty());
    }

    #[test]
    fn test_add() {
        let mut cpl = ColumnPuyoList::new();
        cpl.add(1, PuyoColor::RED);
        cpl.add(1, PuyoColor::BLUE);
        cpl.add(2, PuyoColor::YELLOW);

        assert_eq!(cpl.size_on(1), 2);
        assert_eq!(cpl.size_on(2), 1);
        assert_eq!(cpl.size_on(3), 0);
        assert_eq!(cpl.size_on(4), 0);
        assert_eq!(cpl.size_on(5), 0);
        assert_eq!(cpl.size_on(6), 0);

        assert_eq!(cpl.top(1), Some(PuyoColor::BLUE));
        assert_eq!(cpl.top(2), Some(PuyoColor::YELLOW));
        assert_eq!(cpl.top(3), None);
    }

    #[test]
    fn test_place_holder() {
        let mut cpl = ColumnPuyoList::new();
        assert!(!cpl.has_place_holder());

        cpl.add(1, PuyoColor::RED);
        assert!(!cpl.has_place_holder());

        cpl.add(1, PuyoColor::IRON);
        assert!(cpl.has_place_holder());

        cpl.remove_top(1);
        assert!(!cpl.has_place_holder());
    }
}
<commit_msg>Add ColumnPuyoList::merge<commit_after>use color::PuyoColor;
use small_int_set::SmallIntSet;
use std;

const MAX_SIZE: usize = 8;

pub struct ColumnPuyoList {
    size: [usize; 6],
    puyo: [[PuyoColor; MAX_SIZE]; 6],
    place_holders: [SmallIntSet; 6],
}

impl ColumnPuyoList {
    pub fn new() -> ColumnPuyoList {
        ColumnPuyoList {
            size: [0; 6],
            puyo: [[PuyoColor::EMPTY; MAX_SIZE]; 6],
            place_holders: [SmallIntSet::new(); 6],
        }
    }

    fn is_place_holder(c: PuyoColor) -> bool {
        c == PuyoColor::IRON
    }

    pub fn size_on(&self, x: usize) -> usize {
        self.size[x - 1]
    }

    pub fn is_empty(&self) -> bool {
        self.size() == 0
    }

    pub fn size(&self) -> usize {
        self.size[0] + self.size[1] + self.size[2] +
        self.size[3] + self.size[4] + self.size[5]
    }

    pub fn top(&self, x: usize) -> Option<PuyoColor> {
        if self.size_on(x) == 0 {
            None
        } else {
            Some(self.puyo[x - 1][self.size_on(x) - 1])
        }
    }

    pub fn get(&self, x: usize, i: usize) -> PuyoColor {
        debug_assert!(i < self.size_on(x));
        self.puyo[x - 1][i]
    }

    pub fn has_place_holder(&self) -> bool {
        for i in 0..6 {
            if !self.place_holders[i].is_empty() {
                return true;
            }
        }

        false
    }

    /// Adds PuyoColor `c` on column `x`.
    /// Returns true if succeeded. Returns false otherwise.
    /// When failed, the `self` won't be changed.
    pub fn add(&mut self, x: usize, c: PuyoColor) -> bool {
        if MAX_SIZE <= self.size[x - 1] {
            return false;
        }
        if ColumnPuyoList::is_place_holder(c) {
            self.place_holders[x - 1].set(self.size[x - 1]);
        }
        self.puyo[x - 1][self.size[x - 1]] = c;
        self.size[x - 1] += 1;
        true
    }

    /// Adds `n` PuyoColor `c` on column `x`.
    /// Returns true if succeeded. Returns false otherwise.
    /// When failed, the `self` won't be changed.
    pub fn add_multi(&mut self, x: usize, c: PuyoColor, n: usize) -> bool {
        if MAX_SIZE < self.size_on(x) + n {
            return false;
        }
        for i in 0..n {
            if ColumnPuyoList::is_place_holder(c) {
                self.place_holders[x - 1].set(self.size[x - 1] + i);
            }
            self.puyo[x - 1][self.size[x - 1] + i] = c;
        }
        self.size[x - 1] += n;
        true
    }

    /// Removes top puyo from column x.
    pub fn remove_top(&mut self, x: usize) {
        if self.size_on(x) == 0 {
            return;
        }
        let c = self.puyo[x - 1][self.size[x - 1] - 1];
        if ColumnPuyoList::is_place_holder(c) {
            self.place_holders[x - 1].unset(self.size[x - 1] - 1);
        }
        self.size[x - 1] -= 1;
    }

    pub fn merge(&mut self, cpl: &ColumnPuyoList) -> bool {
        for i in 0..6 {
            if cpl.size[i] >= self.place_holders[i].size() && MAX_SIZE < self.size[i] + (cpl.size[i] - self.place_holders[i].size()) {
                return false;
            }
        }

        for i in 0..6 {
            if cpl.size[i] < self.place_holders[i].size() {
                let discard = self.place_holders[i].size() - cpl.size[i];
                for _ in 0..discard {
                    self.place_holders[i].remove_smallest();
                }
                for j in 0..cpl.size[i] {
                    let k = self.place_holders[i].smallest();
                    self.place_holders[i].remove_smallest();
                    self.puyo[i][k] = cpl.puyo[i][j];
                }
            } else {
                let mut j = 0;
                while !self.place_holders[i].is_empty() {
                    let k = self.place_holders[i].smallest();
                    self.place_holders[i].remove_smallest();
                    self.puyo[i][k] = cpl.puyo[i][j];
                    j += 1;
                }

                while j < cpl.size[i] {
                    self.puyo[i][self.size[i]] = cpl.puyo[i][j];
                    self.size[i] += 1;
                    j += 1;
                }
            }

            let mut new_place_holder = SmallIntSet::new();
            for j in 0..self.size[i] {
                if ColumnPuyoList::is_place_holder(cpl.puyo[i][j]) {
                    new_place_holder.set(j);
                }
            }
            self.place_holders[i] = new_place_holder;
        }

        true
    }
}

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

    #[test]
    fn test_constructor() {
        let cpl = ColumnPuyoList::new();
        assert_eq!(cpl.size(), 0);
        assert!(cpl.is_empty());
    }

    #[test]
    fn test_add() {
        let mut cpl = ColumnPuyoList::new();
        cpl.add(1, PuyoColor::RED);
        cpl.add(1, PuyoColor::BLUE);
        cpl.add(2, PuyoColor::YELLOW);

        assert_eq!(cpl.size_on(1), 2);
        assert_eq!(cpl.size_on(2), 1);
        assert_eq!(cpl.size_on(3), 0);
        assert_eq!(cpl.size_on(4), 0);
        assert_eq!(cpl.size_on(5), 0);
        assert_eq!(cpl.size_on(6), 0);

        assert_eq!(cpl.top(1), Some(PuyoColor::BLUE));
        assert_eq!(cpl.top(2), Some(PuyoColor::YELLOW));
        assert_eq!(cpl.top(3), None);
    }

    #[test]
    fn test_place_holder() {
        let mut cpl = ColumnPuyoList::new();
        assert!(!cpl.has_place_holder());

        cpl.add(1, PuyoColor::RED);
        assert!(!cpl.has_place_holder());

        cpl.add(1, PuyoColor::IRON);
        assert!(cpl.has_place_holder());

        cpl.remove_top(1);
        assert!(!cpl.has_place_holder());
    }

    #[test]
    fn test_merge() {
        let mut cpl = ColumnPuyoList::new();
        let mut cpl1 = ColumnPuyoList::new();
        assert!(cpl1.add_multi(3, PuyoColor::RED, 2));
        let mut cpl2 = ColumnPuyoList::new();
        assert!(cpl2.add_multi(3, PuyoColor::BLUE, 2));
        let mut cpl3 = ColumnPuyoList::new();
        assert!(cpl3.add_multi(3, PuyoColor::BLUE, 8));

        assert!(cpl.merge(&cpl1));
        assert!(cpl.merge(&cpl2));
        assert_eq!(4, cpl.size());

        assert!(!cpl.merge(&cpl3));
        assert_eq!(4, cpl.size());
    }

    #[test]
    fn test_merge_with_place_holders_1() {
        let mut cpl1 = ColumnPuyoList::new();
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));

        let mut cpl2 = ColumnPuyoList::new();
        assert!(cpl2.add(3, PuyoColor::IRON));
        assert!(cpl2.add(3, PuyoColor::IRON));
        assert!(cpl2.add(3, PuyoColor::YELLOW));
        assert!(cpl2.add(3, PuyoColor::YELLOW));

        assert!(cpl1.merge(&cpl2));

        assert_eq!(7, cpl1.size());
        assert_eq!(7, cpl1.size_on(3));
        assert_eq!(PuyoColor::IRON, cpl1.get(3, 0));
        assert_eq!(PuyoColor::IRON, cpl1.get(3, 1));
        assert_eq!(PuyoColor::YELLOW, cpl1.get(3, 2));
        assert_eq!(PuyoColor::RED, cpl1.get(3, 3));
        assert_eq!(PuyoColor::RED, cpl1.get(3, 4));
        assert_eq!(PuyoColor::RED, cpl1.get(3, 5));
        assert_eq!(PuyoColor::YELLOW, cpl1.get(3, 6));
    }

    #[test]
    fn test_merge_with_place_holders_2() {
        let mut cpl1 = ColumnPuyoList::new();
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));

        let mut cpl2 = ColumnPuyoList::new();
        assert!(cpl2.add(3, PuyoColor::YELLOW));
        assert!(cpl2.add(3, PuyoColor::YELLOW));

        assert!(cpl1.merge(&cpl2));

        assert_eq!(6, cpl1.size());
        assert_eq!(6, cpl1.size_on(3));
        assert_eq!(PuyoColor::IRON, cpl1.get(3, 0));
        assert_eq!(PuyoColor::YELLOW, cpl1.get(3, 1));
        assert_eq!(PuyoColor::YELLOW, cpl1.get(3, 2));
        assert_eq!(PuyoColor::RED, cpl1.get(3, 3));
        assert_eq!(PuyoColor::RED, cpl1.get(3, 4));
        assert_eq!(PuyoColor::RED, cpl1.get(3, 5));
    }

    #[test]
    fn test_merge_with_place_holders_3() {
        let mut cpl1 = ColumnPuyoList::new();
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::IRON));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));
        assert!(cpl1.add(3, PuyoColor::RED));

        let mut cpl2 = ColumnPuyoList::new();
        assert!(cpl2.add(3, PuyoColor::YELLOW));
        assert!(cpl2.add(3, PuyoColor::YELLOW));
        assert!(cpl2.add(3, PuyoColor::YELLOW));
        assert!(cpl2.add(3, PuyoColor::YELLOW));

        assert!(!cpl1.merge(&cpl2));
    }
}
<|endoftext|>


<commit_before>use std::error;
use std::fmt;

/// Errors that can occur during code generation.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
    /// Tried to generate an opaque blob for a type that did not have a layout.
    NoLayoutForOpaqueBlob,

    /// Tried to instantiate an opaque template definition, or a template
    /// definition that is too difficult for us to understand (like a partial
    /// template specialization).
    InstantiationOfOpaqueType,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", error::Error::description(self))
    }
}

impl error::Error for Error {
    fn cause(&self) -> Option<&dyn error::Error> {
        None
    }

    fn description(&self) -> &'static str {
        match *self {
            Error::NoLayoutForOpaqueBlob => {
                "Tried to generate an opaque blob, but had no layout"
            }
            Error::InstantiationOfOpaqueType => {
                "Instantiation of opaque template type or partial template \
                 specialization"
            }
        }
    }
}

/// A `Result` of `T` or an error of `bindgen::codegen::error::Error`.
pub type Result<T> = ::std::result::Result<T, Error>;
<commit_msg>Remove deprecated Error::description<commit_after>use std::error;
use std::fmt;

/// Errors that can occur during code generation.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
    /// Tried to generate an opaque blob for a type that did not have a layout.
    NoLayoutForOpaqueBlob,

    /// Tried to instantiate an opaque template definition, or a template
    /// definition that is too difficult for us to understand (like a partial
    /// template specialization).
    InstantiationOfOpaqueType,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match *self {
            Error::NoLayoutForOpaqueBlob => {
                "Tried to generate an opaque blob, but had no layout"
            }
            Error::InstantiationOfOpaqueType => {
                "Instantiation of opaque template type or partial template \
                 specialization"
            }
        })
    }
}

impl error::Error for Error {}

/// A `Result` of `T` or an error of `bindgen::codegen::error::Error`.
pub type Result<T> = ::std::result::Result<T, Error>;
<|endoftext|>


<commit_before>use {
    futures::prelude::*,
    jsonrpc_core::IoHandler,
    lsp_types::{Hover, HoverContents, HoverParams, MarkedString},
};

use crate::{completion, rpc::LanguageServerCommand, BoxFuture};

use super::*;

struct HoverCommand(RootedThread);
impl LanguageServerCommand<HoverParams> for HoverCommand {
    type Future = BoxFuture<Self::Output, ServerError<()>>;
    type Output = Option<Hover>;
    type Error = ();
    fn execute(&self, change: HoverParams) -> BoxFuture<Option<Hover>, ServerError<()>> {
        let thread = self.0.clone();
        async move {
            retrieve_expr(
                &thread,
                &change.text_document_position_params.text_document.uri,
                |module| {
                    let expr = module.expr.expr();

                    let source = &module.source;
                    let byte_index = position_to_byte_index(
                        &source,
                        &change.text_document_position_params.position,
                    )?;

                    let env = thread.get_env();
                    let (_, metadata_map) = gluon::check::metadata::metadata(&env, &expr);
                    let opt_metadata =
                        completion::get_metadata(&metadata_map, source.span(), expr, byte_index);
                    let extract = (completion::TypeAt { env: &env }, completion::SpanAt);
                    Ok(
                        completion::completion(extract, source.span(), expr, byte_index)
                            .map(|(typ, span)| {
                                let contents = match opt_metadata.and_then(|m| m.comment.as_ref()) {
                                    Some(comment) => format!("{}\n\n{}", typ, comment.content),
                                    None => format!("{}", typ),
                                };
                                Some(Hover {
                                    contents: HoverContents::Scalar(MarkedString::String(contents)),
                                    range: byte_span_to_range(&source, span).ok(),
                                })
                            })
                            .unwrap_or_else(|()| None),
                    )
                },
            )
            .await
        }
        .boxed()
    }

    fn invalid_params(&self) -> Option<Self::Error> {
        None
    }
}

pub fn register(io: &mut IoHandler, thread: &RootedThread) {
    io.add_async_method(request!("textDocument/hover"), HoverCommand(thread.clone()));
}
<commit_msg>Render hover's with color<commit_after>use {
    futures::prelude::*,
    jsonrpc_core::IoHandler,
    lsp_types::{Hover, HoverContents, HoverParams, MarkedString},
};

use crate::{completion, rpc::LanguageServerCommand, BoxFuture};

use super::*;

struct HoverCommand(RootedThread);
impl LanguageServerCommand<HoverParams> for HoverCommand {
    type Future = BoxFuture<Self::Output, ServerError<()>>;
    type Output = Option<Hover>;
    type Error = ();
    fn execute(&self, change: HoverParams) -> BoxFuture<Option<Hover>, ServerError<()>> {
        let thread = self.0.clone();
        async move {
            retrieve_expr(
                &thread,
                &change.text_document_position_params.text_document.uri,
                |module| {
                    let expr = module.expr.expr();

                    let source = &module.source;
                    let byte_index = position_to_byte_index(
                        &source,
                        &change.text_document_position_params.position,
                    )?;

                    let env = thread.get_env();
                    let (_, metadata_map) = gluon::check::metadata::metadata(&env, &expr);
                    let opt_metadata =
                        completion::get_metadata(&metadata_map, source.span(), expr, byte_index);
                    let extract = (completion::TypeAt { env: &env }, completion::SpanAt);
                    Ok(
                        completion::completion(extract, source.span(), expr, byte_index)
                            .map(|(typ, span)| {
                                let contents = match opt_metadata.and_then(|m| m.comment.as_ref()) {
                                    Some(comment) => MarkedString::String(format!(
                                        "{}\n\n{}",
                                        typ, comment.content
                                    )),
                                    None => MarkedString::from_language_code(
                                        "gluon".into(),
                                        format!("{}", typ),
                                    ),
                                };
                                Some(Hover {
                                    contents: HoverContents::Scalar(contents),
                                    range: byte_span_to_range(&source, span).ok(),
                                })
                            })
                            .unwrap_or_else(|()| None),
                    )
                },
            )
            .await
        }
        .boxed()
    }

    fn invalid_params(&self) -> Option<Self::Error> {
        None
    }
}

pub fn register(io: &mut IoHandler, thread: &RootedThread) {
    io.add_async_method(request!("textDocument/hover"), HoverCommand(thread.clone()));
}
<|endoftext|>


<commit_before>/*
 This Source Code Form is subject to the terms of the Mozilla Public
 License, v. 2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use rusqlite::{params, Connection};

use crate::collections::Collection;
use crate::folders::{Folder, Folders, RootFolder};
use crate::fromdb::FromDb;
use crate::images::Image;
use crate::keywords::Keyword;
use crate::keywordtree::KeywordTree;
use crate::libraryfiles::LibraryFile;
use crate::lrobject::{LrId, LrObject};

const LR2_VERSION: i32 = 2;
const LR3_VERSION: i32 = 3;
const LR4_VERSION: i32 = 4;
const LR6_VERSION: i32 = 6;

/// Catalog version.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CatalogVersion {
    /// Unknown version
    Unknown,
    /// Lightroom 2.x catalog.
    Lr2,
    /// Lightroom 3.x catalog. Unhandled.
    Lr3,
    /// Lightroom 4.x catalog.
    Lr4,
    /// Lightroom 6.x catalog.
    Lr6,
}

impl CatalogVersion {
    /// Return if we support this catalog version
    pub fn is_supported(&self) -> bool {
        (*self == Self::Lr2) || (*self == Self::Lr4) || (*self == Self::Lr6)
    }
}

/// Catalog is the main container for Lightroom. It represents
/// the .lrcat database.
pub struct Catalog {
    /// Catalog path
    path: PathBuf,
    /// The version string
    pub version: String,
    /// The catalog version
    pub catalog_version: CatalogVersion,
    /// Id for the root (top level) keyword
    pub root_keyword_id: LrId,

    /// The keywords, mapped in the local `LrId`
    keywords: BTreeMap<LrId, Keyword>,
    /// The `Folders` container.
    folders: Folders,
    /// The `Image` container
    images: Vec<Image>,
    /// The `LibraryFile` container
    libfiles: Vec<LibraryFile>,
    /// The `Collection` container
    collections: Vec<Collection>,

    /// The sqlite connectio to the catalog
    dbconn: Option<Connection>,
}

impl Catalog {
    /// Create a new catalog.
    pub fn new<P>(path: P) -> Catalog
    where
        P: AsRef<Path>,
    {
        Catalog {
            path: path.as_ref().to_path_buf(),
            version: "".to_string(),
            catalog_version: CatalogVersion::Unknown,
            root_keyword_id: 0,
            keywords: BTreeMap::new(),
            folders: Folders::new(),
            images: vec![],
            libfiles: vec![],
            collections: vec![],
            dbconn: None,
        }
    }

    /// Open catalog. Return false in failure.
    /// This doesn't check if the content is valid beyond the backing sqlite3.
    pub fn open(&mut self) -> bool {
        let conn_attempt = Connection::open(&self.path);
        if let Ok(conn) = conn_attempt {
            self.dbconn = Some(conn);

            return true;
        }

        false
    }

    /// Get a variable from the table.
    fn get_variable<T>(&self, name: &str) -> Option<T>
    where
        T: rusqlite::types::FromSql,
    {
        let conn = self.dbconn.as_ref()?;
        if let Ok(mut stmt) = conn.prepare("SELECT value FROM Adobe_variablesTable WHERE name=?1") {
            let mut rows = stmt.query(&[&name]).unwrap();
            if let Ok(Some(row)) = rows.next() {
                return row.get(0).ok();
            }
        }
        None
    }

    /// Pare the version string from the database.
    fn parse_version(mut v: String) -> i32 {
        v.truncate(2);
        if let Ok(version) = v.parse::<i32>() {
            version
        } else {
            0
        }
    }

    /// Load version info for the catalog.
    pub fn load_version(&mut self) {
        if let Some(version) = self.get_variable::<String>("Adobe_DBVersion") {
            self.version = version;
            let v = Catalog::parse_version(self.version.clone());
            self.catalog_version = match v {
                LR6_VERSION => CatalogVersion::Lr6,
                LR4_VERSION => CatalogVersion::Lr4,
                LR3_VERSION => CatalogVersion::Lr3,
                LR2_VERSION => CatalogVersion::Lr2,
                _ => CatalogVersion::Unknown,
            };
        }

        if let Some(root_keyword_id) = self.get_variable::<f64>("AgLibraryKeyword_rootTagID") {
            self.root_keyword_id = root_keyword_id.round() as LrId;
        }
    }

    /// Generic object loader leveraging the FromDb protocol
    fn load_objects<T: FromDb>(conn: &Connection, catalog_version: CatalogVersion) -> Vec<T> {
        let mut query = format!(
            "SELECT {} FROM {}",
            T::read_db_columns(catalog_version),
            T::read_db_tables(catalog_version)
        );
        let where_join = T::read_join_where(catalog_version);
        if !where_join.is_empty() {
            query += &format!(" WHERE {}", where_join);
        }
        if let Ok(mut stmt) = conn.prepare(&query) {
            if let Ok(rows) =
                stmt.query_and_then(params![], |row| T::read_from(catalog_version, row))
            {
                return rows
                    .into_iter()
                    .filter(|ref obj| obj.is_ok())
                    .map(|obj| obj.unwrap())
                    .collect();
            }
        }
        vec![]
    }

    /// Load a keyword tree
    pub fn load_keywords_tree(&mut self) -> KeywordTree {
        let keywords = self.load_keywords();

        let mut tree = KeywordTree::new();
        tree.add_children(keywords);

        tree
    }

    /// Load keywords.
    pub fn load_keywords(&mut self) -> &BTreeMap<LrId, Keyword> {
        if self.keywords.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let result = Catalog::load_objects::<Keyword>(&conn, self.catalog_version);
                for keyword in result {
                    self.keywords.insert(keyword.id(), keyword);
                }
            }
        }
        &self.keywords
    }

    /// Load folders.
    pub fn load_folders(&mut self) -> &Folders {
        if self.folders.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let folders = Catalog::load_objects::<RootFolder>(&conn, self.catalog_version);
                self.folders.append_root_folders(folders);
                let mut folders = Catalog::load_objects::<Folder>(&conn, self.catalog_version);
                for folder in &mut folders {
                    folder.content = Some(folder.read_content(conn));
                }
                self.folders.append_folders(folders);
            }
        }
        &self.folders
    }

    /// Load library files (that back images)
    pub fn load_library_files(&mut self) -> &Vec<LibraryFile> {
        if self.libfiles.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<LibraryFile>(&conn, self.catalog_version);
                self.libfiles.append(&mut result);
            }
        }
        &self.libfiles
    }

    /// Load images.
    pub fn load_images(&mut self) -> &Vec<Image> {
        if self.images.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<Image>(&conn, self.catalog_version);
                self.images.append(&mut result);
            }
        }
        &self.images
    }

    /// Load collectons.
    pub fn load_collections(&mut self) -> &Vec<Collection> {
        if self.collections.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut collections =
                    Catalog::load_objects::<Collection>(&conn, self.catalog_version);
                for collection in &mut collections {
                    collection.content = Some(collection.read_content(conn));
                }
                self.collections.append(&mut collections);
            }
        }
        &self.collections
    }
}
<commit_msg>catalog: implement images_for_collection() for Lr2 and Lr4+<commit_after>/*
 This Source Code Form is subject to the terms of the Mozilla Public
 License, v. 2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use rusqlite::{params, Connection};

use crate::collections::Collection;
use crate::folders::{Folder, Folders, RootFolder};
use crate::fromdb::FromDb;
use crate::images::Image;
use crate::keywords::Keyword;
use crate::keywordtree::KeywordTree;
use crate::libraryfiles::LibraryFile;
use crate::lrobject::{LrId, LrObject};

const LR2_VERSION: i32 = 2;
const LR3_VERSION: i32 = 3;
const LR4_VERSION: i32 = 4;
const LR6_VERSION: i32 = 6;

/// Catalog version.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CatalogVersion {
    /// Unknown version
    Unknown,
    /// Lightroom 2.x catalog.
    Lr2,
    /// Lightroom 3.x catalog. Unhandled.
    Lr3,
    /// Lightroom 4.x catalog.
    Lr4,
    /// Lightroom 6.x catalog.
    Lr6,
}

impl CatalogVersion {
    /// Return if we support this catalog version
    pub fn is_supported(&self) -> bool {
        (*self == Self::Lr2) || (*self == Self::Lr4) || (*self == Self::Lr6)
    }
}

/// Catalog is the main container for Lightroom. It represents
/// the .lrcat database.
pub struct Catalog {
    /// Catalog path
    path: PathBuf,
    /// The version string
    pub version: String,
    /// The catalog version
    pub catalog_version: CatalogVersion,
    /// Id for the root (top level) keyword
    pub root_keyword_id: LrId,

    /// The keywords, mapped in the local `LrId`
    keywords: BTreeMap<LrId, Keyword>,
    /// The `Folders` container.
    folders: Folders,
    /// The `Image` container
    images: Vec<Image>,
    /// The `LibraryFile` container
    libfiles: Vec<LibraryFile>,
    /// The `Collection` container
    collections: Vec<Collection>,

    /// The sqlite connectio to the catalog
    dbconn: Option<Connection>,
}

impl Catalog {
    /// Create a new catalog.
    pub fn new<P>(path: P) -> Catalog
    where
        P: AsRef<Path>,
    {
        Catalog {
            path: path.as_ref().to_path_buf(),
            version: "".to_string(),
            catalog_version: CatalogVersion::Unknown,
            root_keyword_id: 0,
            keywords: BTreeMap::new(),
            folders: Folders::new(),
            images: vec![],
            libfiles: vec![],
            collections: vec![],
            dbconn: None,
        }
    }

    /// Open catalog. Return false in failure.
    /// This doesn't check if the content is valid beyond the backing sqlite3.
    pub fn open(&mut self) -> bool {
        let conn_attempt = Connection::open(&self.path);
        if let Ok(conn) = conn_attempt {
            self.dbconn = Some(conn);

            return true;
        }

        false
    }

    /// Get a variable from the table.
    fn get_variable<T>(&self, name: &str) -> Option<T>
    where
        T: rusqlite::types::FromSql,
    {
        let conn = self.dbconn.as_ref()?;
        if let Ok(mut stmt) = conn.prepare("SELECT value FROM Adobe_variablesTable WHERE name=?1") {
            let mut rows = stmt.query(&[&name]).unwrap();
            if let Ok(Some(row)) = rows.next() {
                return row.get(0).ok();
            }
        }
        None
    }

    /// Pare the version string from the database.
    fn parse_version(mut v: String) -> i32 {
        v.truncate(2);
        if let Ok(version) = v.parse::<i32>() {
            version
        } else {
            0
        }
    }

    /// Load version info for the catalog.
    pub fn load_version(&mut self) {
        if let Some(version) = self.get_variable::<String>("Adobe_DBVersion") {
            self.version = version;
            let v = Catalog::parse_version(self.version.clone());
            self.catalog_version = match v {
                LR6_VERSION => CatalogVersion::Lr6,
                LR4_VERSION => CatalogVersion::Lr4,
                LR3_VERSION => CatalogVersion::Lr3,
                LR2_VERSION => CatalogVersion::Lr2,
                _ => CatalogVersion::Unknown,
            };
        }

        if let Some(root_keyword_id) = self.get_variable::<f64>("AgLibraryKeyword_rootTagID") {
            self.root_keyword_id = root_keyword_id.round() as LrId;
        }
    }

    /// Generic object loader leveraging the FromDb protocol
    fn load_objects<T: FromDb>(conn: &Connection, catalog_version: CatalogVersion) -> Vec<T> {
        let mut query = format!(
            "SELECT {} FROM {}",
            T::read_db_columns(catalog_version),
            T::read_db_tables(catalog_version)
        );
        let where_join = T::read_join_where(catalog_version);
        if !where_join.is_empty() {
            query += &format!(" WHERE {}", where_join);
        }
        if let Ok(mut stmt) = conn.prepare(&query) {
            if let Ok(rows) =
                stmt.query_and_then(params![], |row| T::read_from(catalog_version, row))
            {
                return rows
                    .into_iter()
                    .filter(|ref obj| obj.is_ok())
                    .map(|obj| obj.unwrap())
                    .collect();
            }
        }
        vec![]
    }

    /// Load a keyword tree
    pub fn load_keywords_tree(&mut self) -> KeywordTree {
        let keywords = self.load_keywords();

        let mut tree = KeywordTree::new();
        tree.add_children(keywords);

        tree
    }

    /// Load keywords.
    pub fn load_keywords(&mut self) -> &BTreeMap<LrId, Keyword> {
        if self.keywords.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let result = Catalog::load_objects::<Keyword>(&conn, self.catalog_version);
                for keyword in result {
                    self.keywords.insert(keyword.id(), keyword);
                }
            }
        }
        &self.keywords
    }

    /// Load folders.
    pub fn load_folders(&mut self) -> &Folders {
        if self.folders.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let folders = Catalog::load_objects::<RootFolder>(&conn, self.catalog_version);
                self.folders.append_root_folders(folders);
                let mut folders = Catalog::load_objects::<Folder>(&conn, self.catalog_version);
                for folder in &mut folders {
                    folder.content = Some(folder.read_content(conn));
                }
                self.folders.append_folders(folders);
            }
        }
        &self.folders
    }

    /// Load library files (that back images)
    pub fn load_library_files(&mut self) -> &Vec<LibraryFile> {
        if self.libfiles.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<LibraryFile>(&conn, self.catalog_version);
                self.libfiles.append(&mut result);
            }
        }
        &self.libfiles
    }

    /// Load images.
    pub fn load_images(&mut self) -> &Vec<Image> {
        if self.images.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<Image>(&conn, self.catalog_version);
                self.images.append(&mut result);
            }
        }
        &self.images
    }

    /// Load collectons.
    pub fn load_collections(&mut self) -> &Vec<Collection> {
        if self.collections.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut collections =
                    Catalog::load_objects::<Collection>(&conn, self.catalog_version);
                for collection in &mut collections {
                    collection.content = Some(collection.read_content(conn));
                }
                self.collections.append(&mut collections);
            }
        }
        &self.collections
    }

    /// Get the collections. This assume the collection has been loaded first.
    /// This allow non-mutable borrowing that would be caused by `load_collections()`.
    pub fn collections(&self) ->  &Vec<Collection> {
        &self.collections
    }

    /// Lr2 use "Tags".
    const LR2_QUERY: &'static str = "SELECT image FROM AgLibraryTagImage WHERE tag = ?1 AND tagKind = \"AgCollectionTagKind\"";
    /// Lr4 and Lr6 store the relation in `AgLibraryCollectionImage`
    const LR4_QUERY: &'static str = "SELECT image FROM AgLibraryCollectionImage WHERE collection = ?1";

    /// Collect images from collections using a specific query.
    fn images_for_collection_with_query(&self, query: &str, collection_id: LrId) -> super::Result<Vec<LrId>> {
        let conn = self.dbconn.as_ref().unwrap();
        let mut stmt = conn.prepare(query)?;
        let rows = stmt.query_map(&[&collection_id], |row| row.get::<usize, i64>(0))?;
        let mut ids = Vec::new();
        for id in rows {
            ids.push(id?);
        }
        Ok(ids)
    }

    /// Return the of images in the given collection.
    /// Not to be confused with Content.
    pub fn images_for_collection(&self, collection_id: LrId) -> super::Result<Vec<LrId>> {
        match self.catalog_version {
            CatalogVersion::Lr2 => {
                self.images_for_collection_with_query(Self::LR2_QUERY, collection_id)
            }
            CatalogVersion::Lr4 |
            CatalogVersion::Lr6 => {
                self.images_for_collection_with_query(Self::LR4_QUERY, collection_id)
            }
            _ => Err(super::Error::UnsupportedVersion)
        }
    }
}
<|endoftext|>


<commit_before>/// An action request message.
pub struct Request {
    /// A unique id of the request.
    id: RequestId,
    /// A name of the action to execute.
    action: String,
    /// Serialized Protocol Buffers message with request arguments.
    serialized_args: Option<Vec<u8>>,
}

/// A unique identifier of a request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RequestId {
    /// A server-issued session identifier (usually corresponds to a flow).
    pub(super) session_id: String,
    /// A server-issued request identifier.
    pub(super) request_id: u64,
}

impl Request {

    /// Parses the action arguments stored in this request.
    ///
    /// At the moment the request is received we don't know yet what is the type
    /// of the arguments it contains and so we cannot interpret it. Once the
    /// request is dispatched to an appropriate action handler, we can parse the
    /// arguments to a concrete type.
    pub fn parse_args<A>(&self) -> Result<A, crate::action::ParseArgsError>
    where
        A: crate::action::Args,
    {
        let proto_args = match &self.serialized_args {
            Some(ref bytes) => protobuf::Message::parse_from_bytes(bytes)?,
            None => Default::default(),
        };

        A::from_proto(proto_args)
    }

    /// Awaits for a new request message from Fleetspeak.
    ///
    /// This will suspend execution until the request is actually available.
    /// However, the process will keep heartbeating at the specified rate.
    pub fn receive(heartbeat_rate: std::time::Duration) -> Result<Request, ReceiveRequestError> {
        let proto = crate::message::receive_raw(heartbeat_rate)?;

        use std::convert::TryFrom as _;
        Ok(Request::try_from(proto)?)
    }
}

impl std::convert::TryFrom<rrg_proto::jobs::GrrMessage> for Request {

    type Error = ParseRequestError;

    fn try_from(mut proto: rrg_proto::jobs::GrrMessage) -> Result<Request, ParseRequestError> {
        use ParseRequestErrorKind::*;

        if !proto.has_session_id() {
            return Err(NoSessionId.into());
        }
        if !proto.has_request_id() {
            return Err(NoRequestId.into());
        }
        let request_id = RequestId {
            session_id: proto.take_session_id(),
            request_id: proto.get_request_id(),
        };

        if !proto.has_name() {
            return Err(NoActionName.into());
        }
        let action = proto.take_name();

        let serialized_args = if proto.has_args() {
            Some(proto.take_args())
        } else {
            None
        };

        Ok(Request {
            id: request_id,
            action: action,
            serialized_args,
        })
    }
}

/// The error type for cases when action request parsing fails.
#[derive(Debug)]
pub struct ParseRequestError {
    /// A corresponding [`ParseRequestErrorKind`] of this error.
    kind: ParseRequestErrorKind,
}

/// Kinds of errors that can happen when parsing an action request.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParseRequestErrorKind {
    /// No session identifier in the action request protobuf message.
    NoSessionId,
    /// No request identifier in the action request protobuf message.
    NoRequestId,
    /// No action name in the action request protobuf message.
    NoActionName,
}

impl ParseRequestErrorKind {

    fn as_str(&self) -> &'static str {
        use ParseRequestErrorKind::*;

        match *self {
            NoSessionId => "no session id in the action request",
            NoRequestId => "no request id in the action request",
            NoActionName => "no action name in the action request",
        }
    }
}

impl From<ParseRequestErrorKind> for ParseRequestError {

    fn from(kind: ParseRequestErrorKind) -> Self {
        ParseRequestError {
            kind,
        }
    }
}

impl std::fmt::Display for ParseRequestError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(fmt, "{}", self.kind.as_str())
    }
}

impl std::error::Error for ParseRequestError {
}

/// The error type for cases when action request cannot be obtained.
#[derive(Debug)]
pub struct ReceiveRequestError {
    /// A corresponding [`ReceiveRequestErrorKind`] of this error.
    kind: ReceiveRequestErrorKind,
    /// A nested error that caused this error.
    error: Box<dyn std::error::Error + Send + Sync>,
}

/// Kinds of errors that can happen when trying to obtain an action request.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum ReceiveRequestErrorKind {
    /// Fleetspeak connector was unable to receive the message.
    FleetspeakIssue,
    /// The Protocol Buffers message was ill-formed.
    MalformedProtobuf,
}

impl ReceiveRequestErrorKind {

    fn as_str(&self) -> &'static str {
        use ReceiveRequestErrorKind::*;

        match *self {
            FleetspeakIssue => "unable to retrieve a Fleetspeak message",
            MalformedProtobuf => "request protobuf is ill-formed",
        }
    }
}

impl From<ParseRequestError> for ReceiveRequestError {

    fn from(error: ParseRequestError) -> ReceiveRequestError {
        ReceiveRequestError {
            kind: ReceiveRequestErrorKind::MalformedProtobuf,
            error: Box::new(error),
        }
    }
}

impl From<fleetspeak::ReadError> for ReceiveRequestError {

    fn from(error: fleetspeak::ReadError) -> ReceiveRequestError {
        ReceiveRequestError {
            kind: ReceiveRequestErrorKind::FleetspeakIssue,
            error: Box::new(error),
        }
    }
}

impl From<protobuf::ProtobufError> for ReceiveRequestError {

    fn from(error: protobuf::ProtobufError) -> ReceiveRequestError {
        ReceiveRequestError {
            kind: ReceiveRequestErrorKind::MalformedProtobuf,
            error: Box::new(error),
        }
    }
}

impl std::fmt::Display for ReceiveRequestError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(fmt, "{}: {}", self.kind.as_str(), self.error)
    }
}

impl std::error::Error for ReceiveRequestError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&*self.error)
    }
}
<commit_msg>Add accessors for request message wrapper fields.<commit_after>/// An action request message.
pub struct Request {
    /// A unique id of the request.
    id: RequestId,
    /// A name of the action to execute.
    action_name: String,
    /// Serialized Protocol Buffers message with request arguments.
    serialized_args: Option<Vec<u8>>,
}

/// A unique identifier of a request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RequestId {
    /// A server-issued session identifier (usually corresponds to a flow).
    pub(super) session_id: String,
    /// A server-issued request identifier.
    pub(super) request_id: u64,
}

impl Request {
    /// Gets the unique identifier of this request.
    pub fn id(&self) -> RequestId {
        self.id.clone()
    }

    /// Gets the name of the action this request wants ought to execute.
    pub fn action_name(&self) -> &str {
        &self.action_name
    }

    /// Parses the action arguments stored in this request.
    ///
    /// At the moment the request is received we don't know yet what is the type
    /// of the arguments it contains and so we cannot interpret it. Once the
    /// request is dispatched to an appropriate action handler, we can parse the
    /// arguments to a concrete type.
    pub fn parse_args<A>(&self) -> Result<A, crate::action::ParseArgsError>
    where
        A: crate::action::Args,
    {
        let proto_args = match &self.serialized_args {
            Some(ref bytes) => protobuf::Message::parse_from_bytes(bytes)?,
            None => Default::default(),
        };

        A::from_proto(proto_args)
    }

    /// Awaits for a new request message from Fleetspeak.
    ///
    /// This will suspend execution until the request is actually available.
    /// However, the process will keep heartbeating at the specified rate.
    pub fn receive(heartbeat_rate: std::time::Duration) -> Result<Request, ReceiveRequestError> {
        let proto = crate::message::receive_raw(heartbeat_rate)?;

        use std::convert::TryFrom as _;
        Ok(Request::try_from(proto)?)
    }
}

impl std::convert::TryFrom<rrg_proto::jobs::GrrMessage> for Request {

    type Error = ParseRequestError;

    fn try_from(mut proto: rrg_proto::jobs::GrrMessage) -> Result<Request, ParseRequestError> {
        use ParseRequestErrorKind::*;

        if !proto.has_session_id() {
            return Err(NoSessionId.into());
        }
        if !proto.has_request_id() {
            return Err(NoRequestId.into());
        }
        let request_id = RequestId {
            session_id: proto.take_session_id(),
            request_id: proto.get_request_id(),
        };

        if !proto.has_name() {
            return Err(NoActionName.into());
        }
        let action_name = proto.take_name();

        let serialized_args = if proto.has_args() {
            Some(proto.take_args())
        } else {
            None
        };

        Ok(Request {
            id: request_id,
            action_name: action_name,
            serialized_args,
        })
    }
}

/// The error type for cases when action request parsing fails.
#[derive(Debug)]
pub struct ParseRequestError {
    /// A corresponding [`ParseRequestErrorKind`] of this error.
    kind: ParseRequestErrorKind,
}

/// Kinds of errors that can happen when parsing an action request.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParseRequestErrorKind {
    /// No session identifier in the action request protobuf message.
    NoSessionId,
    /// No request identifier in the action request protobuf message.
    NoRequestId,
    /// No action name in the action request protobuf message.
    NoActionName,
}

impl ParseRequestErrorKind {

    fn as_str(&self) -> &'static str {
        use ParseRequestErrorKind::*;

        match *self {
            NoSessionId => "no session id in the action request",
            NoRequestId => "no request id in the action request",
            NoActionName => "no action name in the action request",
        }
    }
}

impl From<ParseRequestErrorKind> for ParseRequestError {

    fn from(kind: ParseRequestErrorKind) -> Self {
        ParseRequestError {
            kind,
        }
    }
}

impl std::fmt::Display for ParseRequestError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(fmt, "{}", self.kind.as_str())
    }
}

impl std::error::Error for ParseRequestError {
}

/// The error type for cases when action request cannot be obtained.
#[derive(Debug)]
pub struct ReceiveRequestError {
    /// A corresponding [`ReceiveRequestErrorKind`] of this error.
    kind: ReceiveRequestErrorKind,
    /// A nested error that caused this error.
    error: Box<dyn std::error::Error + Send + Sync>,
}

/// Kinds of errors that can happen when trying to obtain an action request.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum ReceiveRequestErrorKind {
    /// Fleetspeak connector was unable to receive the message.
    FleetspeakIssue,
    /// The Protocol Buffers message was ill-formed.
    MalformedProtobuf,
}

impl ReceiveRequestErrorKind {

    fn as_str(&self) -> &'static str {
        use ReceiveRequestErrorKind::*;

        match *self {
            FleetspeakIssue => "unable to retrieve a Fleetspeak message",
            MalformedProtobuf => "request protobuf is ill-formed",
        }
    }
}

impl From<ParseRequestError> for ReceiveRequestError {

    fn from(error: ParseRequestError) -> ReceiveRequestError {
        ReceiveRequestError {
            kind: ReceiveRequestErrorKind::MalformedProtobuf,
            error: Box::new(error),
        }
    }
}

impl From<fleetspeak::ReadError> for ReceiveRequestError {

    fn from(error: fleetspeak::ReadError) -> ReceiveRequestError {
        ReceiveRequestError {
            kind: ReceiveRequestErrorKind::FleetspeakIssue,
            error: Box::new(error),
        }
    }
}

impl From<protobuf::ProtobufError> for ReceiveRequestError {

    fn from(error: protobuf::ProtobufError) -> ReceiveRequestError {
        ReceiveRequestError {
            kind: ReceiveRequestErrorKind::MalformedProtobuf,
            error: Box::new(error),
        }
    }
}

impl std::fmt::Display for ReceiveRequestError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(fmt, "{}: {}", self.kind.as_str(), self.error)
    }
}

impl std::error::Error for ReceiveRequestError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&*self.error)
    }
}
<|endoftext|>


<commit_before>extern crate cgmath;
extern crate png;

use std::default::Default;
use std::f64;
use time;

use cgmath::{Matrix4, Point, Point3, Vector3};
use noise;
use noise::{Brownian3, Seed};

#[cfg(target_os = "android")]
use egl_context::EglContext;
use gl;
use gl::Texture;
use mesh;
use program::Program;
use vertices::Vertices;
use world::{Block, World};
#[cfg(target_os = "linux")]
use x11::{PollEventsIterator, XWindow};

#[cfg(target_os = "android")]
pub struct EngineImpl {
  pub egl_context: Option<Box<EglContext>>,
  pub program: Option<Program>,
}

#[cfg(target_os = "android")]
impl Default for EngineImpl {
  fn default() -> EngineImpl {
    EngineImpl {
      egl_context: None,
      program: None,
    }
  }
}

#[cfg(target_os = "linux")]
pub struct EngineImpl {
  pub window: XWindow,
  pub program: Program,
}

pub struct Engine {
  engine_impl: EngineImpl,
  animating: bool,
  angle: f32,  // in degrees.
  /// GL projection matrix
  projection_matrix: Matrix4<f32>,
  /// Texture atlas.
  texture: Texture,
  world: World,
  // Important: need to save position and texture coordinates since OpenGL tries to reload them
  // from memory!
  vertices: Vertices,
}

impl Engine {
  #[cfg(target_os = "android")]
  pub fn new() -> Engine {
    Engine {
      engine_impl: Default::default(),
      animating: false,
      angle: 0.0,
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: generate_chunk_of_perlin(),
      vertices: Vertices::new(0),
    }
  }

  #[cfg(target_os = "linux")]
  pub fn new(window: XWindow, program: Program) -> Engine {
    Engine {
      engine_impl: EngineImpl {
        window: window,
        program: program,
      },
      animating: false,
      angle: 0.0,
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: generate_chunk_of_perlin(),
      vertices: Vertices::new(0),
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "android")]
  pub fn init(&mut self, egl_context: Box<EglContext>, texture_atlas_bytes: &[u8]) {
    self.engine_impl.egl_context = Some(egl_context);

    self.engine_impl.program = match Program::new() {
      Ok(p) => Some(p),
      Err(e) => panic!("Program failed: {:?}", e),
    };

    self.common_init(texture_atlas_bytes);

    if let Some(ref p) = self.engine_impl.program {
      p.set_texture_unit(0);
    }

    // Silly gymnastics to satisfy the borrow checker.
    let wh = if let Some(ref ec) = self.engine_impl.egl_context {
      Some((ec.width, ec.height))
    } else {
      None
    };
    if let Some((w, h)) = wh {
      self.set_viewport(w, h);
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "linux")]
  pub fn init(&mut self, texture_atlas_bytes: &[u8]) {
    self.common_init(texture_atlas_bytes);
    self.engine_impl.program.set_texture_unit(0);
  }

  fn common_init(&mut self, texture_atlas_bytes: &[u8]) {
    // Set the background clear color to sky blue.
    gl::clear_color(0.5, 0.69, 1.0, 1.0);

    // Enable reverse face culling.
    gl::enable(gl::CULL_FACE);
    // Enable depth test.
    gl::enable(gl::DEPTH_TEST);
    gl::depth_func(gl::LEQUAL);

    // Set up textures.
    self.texture = Engine::load_texture_atlas(texture_atlas_bytes);
    gl::active_texture(gl::TEXTURE0);
    gl::bind_texture_2d(self.texture);

    self.load_mesh();
  }

  #[cfg(target_os = "android")]
  fn load_mesh(&mut self) {
    let vertices = create_mesh_vertices(&self.world);
    if let Some(ref mut p) = self.engine_impl.program {
      p.set_vertices(&vertices);
    }
    self.vertices = vertices;
  }

  #[cfg(target_os = "linux")]
  fn load_mesh(&mut self) {
    let vertices = create_mesh_vertices(&self.world);
    self.engine_impl.program.set_vertices(&vertices);
    self.vertices = vertices;
  }

  pub fn set_viewport(&mut self, w: i32, h: i32) {
    gl::viewport(0, 0, w, h);
    self.projection_matrix = projection_matrix(w, h);
  }

  /// Load texture atlas from assets folder.
  fn load_texture_atlas(texture_atlas_bytes: &[u8]) -> Texture {
    let image = png::load_png_from_memory(texture_atlas_bytes)
      .unwrap_or_else(|s| panic!("load_png_from_memory() failed: {}", s));

    let pixels = match image.pixels {
      png::PixelsByColorType::RGBA8(v) => v,
      _ => {
        let color_type = match image.pixels {
            png::PixelsByColorType::K8(_) => "K8",
            png::PixelsByColorType::KA8(_) => "KA8",
            png::PixelsByColorType::RGB8(_) => "RGB8",
            png::PixelsByColorType::RGBA8(_) => panic!("Should not happen"),
        };
        panic!("Only RGBA8 image format supported, was: {}", color_type);
      }
    };

    let texture = gl::gen_texture();
    gl::bind_texture_2d(texture);
    gl::texture_2d_param(gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR);
    gl::texture_2d_param(gl::TEXTURE_MAG_FILTER, gl::LINEAR);
    gl::texture_2d_param(gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE);
    gl::texture_2d_param(gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE);
    gl::texture_2d_image_rgba(image.width as i32, image.height as i32, &pixels);
    gl::generate_mipmap_2d();
    gl::bind_texture_2d(0);

    texture
  }

  /// Draw a frame.
  #[cfg(target_os = "android")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    match self.engine_impl.egl_context {
      None => return,  // No display.
      Some(ref egl_context) => {
        gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

        match self.engine_impl.program {
          Some(ref p) => {
            // Compute the composite mvp_matrix and send it to program.  Model matrix
            // is always identity so instead of MVP = P * V * M just do MVP = P * V.
            let mvp_matrix = self.projection_matrix * view_matrix(self.angle);
            p.set_mvp_matrix(mvp_matrix);
            // Finally, draw the cube mesh.
            let position_indices = self.vertices.position_indices();
            if position_indices.len() > 0 {
              // TODO: Switch to VBOs once it works for better performance!!!
              gl::draw_elements_triangles_u16(position_indices.len() as i32, position_indices);
            }
          },
          None => panic!("Missing program, should never happen"),
        }

        egl_context.swap_buffers();
      }
    }
  }

  /// Draw a frame.
  #[cfg(target_os = "linux")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

    let p = &self.engine_impl.program;

    // Compute the composite mvp_matrix and send it to program.  Model matrix
    // is always identity so instead of MVP = P * V * M just do MVP = P * V.
    let mvp_matrix = self.projection_matrix * view_matrix(self.angle);
    p.set_mvp_matrix(mvp_matrix);

    // Finally, draw the cube mesh.
    let position_indices = self.vertices.position_indices();
    if position_indices.len() > 0 {
      // TODO: Switch to VBOs once it works for better performance!!!
      gl::draw_elements_triangles_u16(position_indices.len() as i32, position_indices);
    }

    self.engine_impl.window.swap_buffers();
    self.engine_impl.window.flush();
  }

  /// Update for time passed and draw a frame.
  pub fn update_draw(&mut self) {
    if self.animating {
      // Done processing events; draw next animation frame.
      // Do a complete rotation every 10 seconds, assuming 60 FPS.
      self.angle += 360.0 / 600.0;
      if self.angle > 360.0 {
        self.angle = 0.0;
      }

      // Drawing is throttled to the screen update rate, so there is no need to do timing here.
      self.draw();
    }
  }

  /// Terminate the engine.
  #[cfg(target_os = "android")]
  pub fn term(&mut self) {
    self.animating = false;
    // Drop the program and the EGL context.
    self.engine_impl = Default::default();
    log!("*** Renderer terminated");
  }

  /// Called when window gains input focus.
  pub fn gained_focus(&mut self) {
    self.animating = true;
  }

  /// Called when window loses input focus.
  pub fn lost_focus(&mut self) {
    self.animating = false;
  }

  #[cfg(target_os = "linux")]
  pub fn is_closed(&self) -> bool {
    self.engine_impl.window.is_closed()
  }

  #[cfg(target_os = "linux")]
  pub fn poll_events(&self) -> PollEventsIterator {
    self.engine_impl.window.poll_events()
  }
}

fn generate_chunk_of_perlin() -> World {
  let start_s = time::precise_time_s();

  let seed = Seed::new(1);
  let noise = Brownian3::new(noise::perlin3, 4).wavelength(16.0);

  let y_min = -8;
  let y_max = 7;
  let y_range = y_max - y_min;

  let mut min = f64::MAX;
  let mut max = f64::MIN;
  let mut world: World = Default::default();

  for y in y_min..(y_max + 1) {
    // Normalize into [0, 1].
    let normalized_y = (y as f64 - y_min as f64) / y_range as f64;
    for x in -8..8 {
      for z in -8..8 {
        let p = [x as f64, y as f64, z as f64];
        let val = noise.apply(&seed, &p);

        if val < min {
          min = val;
        }
        if val > max {
          max = val;
        }

        // Probablility to have a block added linearly increases from 0.0 at y_max to 1.0 at y_min.
        if 0.5 * (val + 1.0) >= normalized_y {
          world.add(Block::new(x, y, z));
        }
      }
    }
  }

  let spent_ms = (time::precise_time_s() - start_s) * 1000.0;
  log!("*** Generating a chunk of perlin: {:.3}ms, {} blocks", spent_ms, world.len());
  log!("***   min = {}, max = {}", min, max);

  world
}

fn create_mesh_vertices(world: &World) -> Vertices {
  let mut vertices = Vertices::new(world.len());
  for block in world.iter() {
    // Eliminate definitely invisible faces, i.e. those between two neighboring cubes.
    for face in mesh::CUBE_FACES.iter() {
      if !world.contains(&block.add_v(&face.direction)) {
        vertices.add(&translate(&face.position_coords, block), &face.position_indices, &face.texture_coords);
      }
    }
  }
  vertices
}

/// Accepts vertex coordinates as a flat list: x, y, z, x, y, z, ...
/// Translates them along the vector corresponding to the block.
fn translate(coords: &[f32; 12], block: &Block) -> [f32; 12] {
  let x = block.x as f32;
  let y = block.y as f32;
  let z = block.z as f32;

  [
    coords[0] + x, coords[1] + y, coords[2] + z,
    coords[3] + x, coords[4] + y, coords[5] + z,
    coords[6] + x, coords[7] + y, coords[8] + z,
    coords[9] + x, coords[10] + y, coords[11] + z,
  ]
}

/// A view matrix, eye is on a 10.0 radius circle starting at (0.0, 2.1, 10.0),
/// rotating around (0, 1, 0) counter-clockwise and looking at (0, 2.1, 0).
fn view_matrix(angle: f32) -> Matrix4<f32> {
  let r = 13.0;
  let y = 2.1;  // 0.5 for half block under feet + 1.6 up to eye height.
  let (s, c) = angle.to_radians().sin_cos();
  let eye = Point3::new(r * s, y, r * c);
  let center = Point3::new(0.0, y, 0.0);
  let up = Vector3::new(0.0, 1.0, 0.0);
  Matrix4::look_at(&eye, &center, &up)
}

/// Perspective projection matrix as frustum matrix.
fn projection_matrix(width: i32, height: i32) -> Matrix4<f32> {
  let ratio = width as f32 / height as f32;
  let left = -ratio;
  let right = ratio;
  let bottom = -1.0;
  let top = 1.0;
  let near = 1.0;
  let far = 50.0;
  cgmath::frustum(left, right, bottom, top, near, far)
}
<commit_msg>Put vertices in a box so that the coordinate arrays never move.<commit_after>extern crate cgmath;
extern crate png;

use std::default::Default;
use std::f64;
use time;

use cgmath::{Matrix4, Point, Point3, Vector3};
use noise;
use noise::{Brownian3, Seed};

#[cfg(target_os = "android")]
use egl_context::EglContext;
use gl;
use gl::Texture;
use mesh;
use program::Program;
use vertices::Vertices;
use world::{Block, World};
#[cfg(target_os = "linux")]
use x11::{PollEventsIterator, XWindow};

#[cfg(target_os = "android")]
pub struct EngineImpl {
  pub egl_context: Option<Box<EglContext>>,
  pub program: Option<Program>,
}

#[cfg(target_os = "android")]
impl Default for EngineImpl {
  fn default() -> EngineImpl {
    EngineImpl {
      egl_context: None,
      program: None,
    }
  }
}

#[cfg(target_os = "linux")]
pub struct EngineImpl {
  pub window: XWindow,
  pub program: Program,
}

pub struct Engine {
  engine_impl: EngineImpl,
  animating: bool,
  angle: f32,  // in degrees.
  /// GL projection matrix
  projection_matrix: Matrix4<f32>,
  /// Texture atlas.
  texture: Texture,
  world: World,
  // Important: need to save position and texture coordinates since OpenGL tries to reload them
  // from memory!  Put them in a box so that the contents of arrays never ever moves.
  vertices: Box<Vertices>,
}

impl Engine {
  #[cfg(target_os = "android")]
  pub fn new() -> Engine {
    Engine {
      engine_impl: Default::default(),
      animating: false,
      angle: 0.0,
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: generate_chunk_of_perlin(),
      vertices: Box::new(Vertices::new(0)),
    }
  }

  #[cfg(target_os = "linux")]
  pub fn new(window: XWindow, program: Program) -> Engine {
    Engine {
      engine_impl: EngineImpl {
        window: window,
        program: program,
      },
      animating: false,
      angle: 0.0,
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: generate_chunk_of_perlin(),
      vertices: Box::new(Vertices::new(0)),
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "android")]
  pub fn init(&mut self, egl_context: Box<EglContext>, texture_atlas_bytes: &[u8]) {
    self.engine_impl.egl_context = Some(egl_context);

    self.engine_impl.program = match Program::new() {
      Ok(p) => Some(p),
      Err(e) => panic!("Program failed: {:?}", e),
    };

    self.common_init(texture_atlas_bytes);

    if let Some(ref p) = self.engine_impl.program {
      p.set_texture_unit(0);
    }

    // Silly gymnastics to satisfy the borrow checker.
    let wh = if let Some(ref ec) = self.engine_impl.egl_context {
      Some((ec.width, ec.height))
    } else {
      None
    };
    if let Some((w, h)) = wh {
      self.set_viewport(w, h);
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "linux")]
  pub fn init(&mut self, texture_atlas_bytes: &[u8]) {
    self.common_init(texture_atlas_bytes);
    self.engine_impl.program.set_texture_unit(0);
  }

  fn common_init(&mut self, texture_atlas_bytes: &[u8]) {
    // Set the background clear color to sky blue.
    gl::clear_color(0.5, 0.69, 1.0, 1.0);

    // Enable reverse face culling.
    gl::enable(gl::CULL_FACE);
    // Enable depth test.
    gl::enable(gl::DEPTH_TEST);
    gl::depth_func(gl::LEQUAL);

    // Set up textures.
    self.texture = Engine::load_texture_atlas(texture_atlas_bytes);
    gl::active_texture(gl::TEXTURE0);
    gl::bind_texture_2d(self.texture);

    self.load_mesh();
  }

  #[cfg(target_os = "android")]
  fn load_mesh(&mut self) {
    self.vertices = Box::new(create_mesh_vertices(&self.world));
    if let Some(ref mut p) = self.engine_impl.program {
      p.set_vertices(&self.vertices);
    }
  }

  #[cfg(target_os = "linux")]
  fn load_mesh(&mut self) {
    self.vertices = Box::new(create_mesh_vertices(&self.world));
    self.engine_impl.program.set_vertices(&self.vertices);
  }

  pub fn set_viewport(&mut self, w: i32, h: i32) {
    gl::viewport(0, 0, w, h);
    self.projection_matrix = projection_matrix(w, h);
  }

  /// Load texture atlas from assets folder.
  fn load_texture_atlas(texture_atlas_bytes: &[u8]) -> Texture {
    let image = png::load_png_from_memory(texture_atlas_bytes)
      .unwrap_or_else(|s| panic!("load_png_from_memory() failed: {}", s));

    let pixels = match image.pixels {
      png::PixelsByColorType::RGBA8(v) => v,
      _ => {
        let color_type = match image.pixels {
            png::PixelsByColorType::K8(_) => "K8",
            png::PixelsByColorType::KA8(_) => "KA8",
            png::PixelsByColorType::RGB8(_) => "RGB8",
            png::PixelsByColorType::RGBA8(_) => panic!("Should not happen"),
        };
        panic!("Only RGBA8 image format supported, was: {}", color_type);
      }
    };

    let texture = gl::gen_texture();
    gl::bind_texture_2d(texture);
    gl::texture_2d_param(gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR);
    gl::texture_2d_param(gl::TEXTURE_MAG_FILTER, gl::LINEAR);
    gl::texture_2d_param(gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE);
    gl::texture_2d_param(gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE);
    gl::texture_2d_image_rgba(image.width as i32, image.height as i32, &pixels);
    gl::generate_mipmap_2d();
    gl::bind_texture_2d(0);

    texture
  }

  /// Draw a frame.
  #[cfg(target_os = "android")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    match self.engine_impl.egl_context {
      None => return,  // No display.
      Some(ref egl_context) => {
        gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

        match self.engine_impl.program {
          Some(ref p) => {
            // Compute the composite mvp_matrix and send it to program.  Model matrix
            // is always identity so instead of MVP = P * V * M just do MVP = P * V.
            let mvp_matrix = self.projection_matrix * view_matrix(self.angle);
            p.set_mvp_matrix(mvp_matrix);
            // Finally, draw the cube mesh.
            let position_indices = self.vertices.position_indices();
            if position_indices.len() > 0 {
              // TODO: Switch to VBOs once it works for better performance!!!
              gl::draw_elements_triangles_u16(position_indices.len() as i32, position_indices);
            }
          },
          None => panic!("Missing program, should never happen"),
        }

        egl_context.swap_buffers();
      }
    }
  }

  /// Draw a frame.
  #[cfg(target_os = "linux")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

    let p = &self.engine_impl.program;

    // Compute the composite mvp_matrix and send it to program.  Model matrix
    // is always identity so instead of MVP = P * V * M just do MVP = P * V.
    let mvp_matrix = self.projection_matrix * view_matrix(self.angle);
    p.set_mvp_matrix(mvp_matrix);

    // Finally, draw the cube mesh.
    let position_indices = self.vertices.position_indices();
    if position_indices.len() > 0 {
      // TODO: Switch to VBOs once it works for better performance!!!
      gl::draw_elements_triangles_u16(position_indices.len() as i32, position_indices);
    }

    self.engine_impl.window.swap_buffers();
    self.engine_impl.window.flush();
  }

  /// Update for time passed and draw a frame.
  pub fn update_draw(&mut self) {
    if self.animating {
      // Done processing events; draw next animation frame.
      // Do a complete rotation every 10 seconds, assuming 60 FPS.
      self.angle += 360.0 / 600.0;
      if self.angle > 360.0 {
        self.angle = 0.0;
      }

      // Drawing is throttled to the screen update rate, so there is no need to do timing here.
      self.draw();
    }
  }

  /// Terminate the engine.
  #[cfg(target_os = "android")]
  pub fn term(&mut self) {
    self.animating = false;
    // Drop the program and the EGL context.
    self.engine_impl = Default::default();
    log!("*** Renderer terminated");
  }

  /// Called when window gains input focus.
  pub fn gained_focus(&mut self) {
    self.animating = true;
  }

  /// Called when window loses input focus.
  pub fn lost_focus(&mut self) {
    self.animating = false;
  }

  #[cfg(target_os = "linux")]
  pub fn is_closed(&self) -> bool {
    self.engine_impl.window.is_closed()
  }

  #[cfg(target_os = "linux")]
  pub fn poll_events(&self) -> PollEventsIterator {
    self.engine_impl.window.poll_events()
  }
}

fn generate_chunk_of_perlin() -> World {
  let start_s = time::precise_time_s();

  let seed = Seed::new(1);
  let noise = Brownian3::new(noise::perlin3, 4).wavelength(16.0);

  let y_min = -8;
  let y_max = 7;
  let y_range = y_max - y_min;

  let mut min = f64::MAX;
  let mut max = f64::MIN;
  let mut world: World = Default::default();

  for y in y_min..(y_max + 1) {
    // Normalize into [0, 1].
    let normalized_y = (y as f64 - y_min as f64) / y_range as f64;
    for x in -8..8 {
      for z in -8..8 {
        let p = [x as f64, y as f64, z as f64];
        let val = noise.apply(&seed, &p);

        if val < min {
          min = val;
        }
        if val > max {
          max = val;
        }

        // Probablility to have a block added linearly increases from 0.0 at y_max to 1.0 at y_min.
        if 0.5 * (val + 1.0) >= normalized_y {
          world.add(Block::new(x, y, z));
        }
      }
    }
  }

  let spent_ms = (time::precise_time_s() - start_s) * 1000.0;
  log!("*** Generating a chunk of perlin: {:.3}ms, {} blocks", spent_ms, world.len());
  log!("***   min = {}, max = {}", min, max);

  world
}

fn create_mesh_vertices(world: &World) -> Vertices {
  let mut vertices = Vertices::new(world.len());
  for block in world.iter() {
    // Eliminate definitely invisible faces, i.e. those between two neighboring cubes.
    for face in mesh::CUBE_FACES.iter() {
      if !world.contains(&block.add_v(&face.direction)) {
        vertices.add(&translate(&face.position_coords, block), &face.position_indices, &face.texture_coords);
      }
    }
  }
  vertices
}

/// Accepts vertex coordinates as a flat list: x, y, z, x, y, z, ...
/// Translates them along the vector corresponding to the block.
fn translate(coords: &[f32; 12], block: &Block) -> [f32; 12] {
  let x = block.x as f32;
  let y = block.y as f32;
  let z = block.z as f32;

  [
    coords[0] + x, coords[1] + y, coords[2] + z,
    coords[3] + x, coords[4] + y, coords[5] + z,
    coords[6] + x, coords[7] + y, coords[8] + z,
    coords[9] + x, coords[10] + y, coords[11] + z,
  ]
}

/// A view matrix, eye is on a 10.0 radius circle starting at (0.0, 2.1, 10.0),
/// rotating around (0, 1, 0) counter-clockwise and looking at (0, 2.1, 0).
fn view_matrix(angle: f32) -> Matrix4<f32> {
  let r = 13.0;
  let y = 2.1;  // 0.5 for half block under feet + 1.6 up to eye height.
  let (s, c) = angle.to_radians().sin_cos();
  let eye = Point3::new(r * s, y, r * c);
  let center = Point3::new(0.0, y, 0.0);
  let up = Vector3::new(0.0, 1.0, 0.0);
  Matrix4::look_at(&eye, &center, &up)
}

/// Perspective projection matrix as frustum matrix.
fn projection_matrix(width: i32, height: i32) -> Matrix4<f32> {
  let ratio = width as f32 / height as f32;
  let left = -ratio;
  let right = ratio;
  let bottom = -1.0;
  let top = 1.0;
  let near = 1.0;
  let far = 50.0;
  cgmath::frustum(left, right, bottom, top, near, far)
}
<|endoftext|>


<commit_before>#![crate_name = "document"]

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

#[deriving(Show)]
pub struct Document {
    /// Storage for each maintained type
    elements: Vec<Element>,
    attributes: Vec<Attribute>,

    // Primary associations between nodes
    children: HashMap<Node, Vec<Node>>,
    parents: HashMap<Node, Node>,

    // Associated attributes
    assigned_attributes: HashMap<Node, HashSet<Node>>,
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Node {
    ElementNode(uint),
    AttributeNode(uint),
}

impl Document {
    pub fn new() -> Document {
        Document {
            elements: Vec::new(),
            attributes: Vec::new(),
            children: HashMap::new(),
            parents: HashMap::new(),
            assigned_attributes: HashMap::new(),
        }
    }

    fn next_element_ref(& self) -> Node {
        ElementNode(self.elements.len())
    }

    fn next_attribute_ref(& self) -> Node {
        AttributeNode(self.attributes.len())
    }

    pub fn new_element(&mut self, name: &str) -> Node {
        let eref = self.next_element_ref();
        self.elements.push(Element {
            name: name.to_string(),
        });
        eref
    }

    fn new_attribute(&mut self, name: &str, value: &str) -> Node {
        let aref = self.next_attribute_ref();
        self.attributes.push(Attribute {
            name: name.to_string(),
            value: value.to_string(),
        });
        aref
    }

    pub fn element<'a>(&'a self, element: Node) -> &'a Element {
        match element {
            ElementNode(i) => &self.elements[i],
            _ => fail!("Not an element"),
        }
    }

    pub fn mut_element<'a>(&'a mut self, element: Node) -> &'a mut Element {
        match element {
            ElementNode(i) => self.elements.get_mut(i),
            _ => fail!("Not an element"),
        }
    }

    fn attribute<'a>(&'a self, attribute: Node) -> &'a Attribute {
        match attribute {
            AttributeNode(i) => &self.attributes[i],
            _ => fail!("Not an attribute"),
        }
    }

    fn mut_attribute<'a>(&'a mut self, attribute: Node) -> &'a mut Attribute {
        match attribute {
            AttributeNode(i) => self.attributes.get_mut(i),
            _ => fail!("Not an attribute"),
        }
    }

    fn attribute_for(&self, element: Node, name: &str) -> Option<Node> {
        match self.assigned_attributes.find(&element) {
            Some(ref attributes) => {
                let mut node_attrs = attributes.iter().map(|node| (node, self.attribute(*node)));
                match node_attrs.find(|&(_, attr)| attr.name.as_slice() == name) {
                    Some((node, _)) => Some(*node),
                    None => None,
                }
            },
            None => None,
        }
    }

    pub fn get_attribute(&self, element: Node, name: &str) -> Option<&str> {
        match self.attribute_for(element, name) {
            Some(aref) => Some(self.attribute(aref).value.as_slice()),
            None => None,
        }
    }

    pub fn set_attribute(&mut self, element: Node, name: &str, value: &str) -> Node {
        match self.attribute_for(element, name) {
            Some(aref) => {
                self.mut_attribute(aref).value = value.to_string();
                aref
            },
            None => {
                let aref = self.new_attribute(name, value);
                let assigned_attributes = self.assigned_attributes.find_or_insert(element, HashSet::new());
                assigned_attributes.insert(aref);
                aref
            },
        }
    }

    fn remove_parentage(&mut self, child: Node) {
        match self.parent(child) {
            Some(ref parent) => {
                match self.children.find_mut(parent) {
                    Some(children) => {
                        match children.as_slice().position_elem(&child) {
                            Some(idx) => { children.remove(idx); },
                            None => {},
                        }
                    },
                    None => {},
                }
            },
            None => {}
        }
    }

    pub fn append_child(&mut self, parent: Node, child: Node) {
        {
            let kids = self.children.find_or_insert(parent, Vec::new());
            kids.push(child);
        }

        self.remove_parentage(child);
        self.parents.insert(child, parent);
    }

    pub fn children(&self, parent: Node) -> Vec<Node> {
        match self.children.find(&parent) {
            Some(v) => v.iter().map(|r| r.clone()).collect(),
            None => vec![],
        }
    }

    pub fn parent(&self, child: Node) -> Option<Node> {
        self.parents.find_copy(&child)
    }
}

#[deriving(Show)]
pub struct Element {
    name: String,
}

impl Element {
    pub fn name(&self) -> &str {
        self.name.as_slice()
    }

    pub fn set_name(&mut self, name: &str) {
        self.name = name.to_string();
    }
}

#[deriving(Show)]
pub struct Attribute {
    name: String,
    value: String,
}

pub struct Text;
pub struct Comment;
pub struct Root;

#[test]
fn can_add_an_element_as_a_child() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let children = d.children(alpha);
    assert_eq!(1, children.len());

    let result = d.element(children[0]);
    assert_eq!(result.name(), "beta");
}

#[test]
fn children_are_ordered() {
    let mut d = Document::new();
    let greek = d.new_element("greek");
    let alpha = d.new_element("alpha");
    let omega = d.new_element("omega");

    d.append_child(greek, alpha);
    d.append_child(greek, omega);
    let children = d.children(greek);

    assert_eq!(2, children.len());
    assert_eq!(d.element(children[0]).name(), "alpha");
    assert_eq!(d.element(children[1]).name(), "omega");
}

#[test]
fn children_know_their_parent() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let child = d.children(alpha)[0];
    let parent = d.parent(child).unwrap();

    let result = d.element(parent);
    assert_eq!(result.name(), "alpha");
}

#[test]
fn replacing_parent_updates_original_parent() {
    let mut d = Document::new();
    let parent1 = d.new_element("parent1");
    let parent2 = d.new_element("parent2");
    let child = d.new_element("child");

    d.append_child(parent1, child);
    d.append_child(parent2, child);

    assert!(d.children(parent1).is_empty());
    assert_eq!(1, d.children(parent2).len());
}

#[test]
fn can_rename_an_element() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    {
        let element = d.mut_element(alpha);
        element.set_name("beta");
    }

    let beta = d.element(alpha);
    assert_eq!(beta.name(), "beta");
}

#[test]
fn elements_have_attributes() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "world");
}

#[test]
fn attributes_can_be_reset() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    d.set_attribute(alpha, "hello", "universe");

    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "universe");
}
<commit_msg>Work the type system for all its worth<commit_after>#![crate_name = "document"]

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

#[deriving(Show)]
pub struct Document {
    /// Storage for each maintained type
    elements: Vec<Element>,
    attributes: Vec<Attribute>,

    // Primary associations between nodes
    children: HashMap<Parent, Vec<Child>>,
    parents: HashMap<Child, Parent>,

    // Associated attributes
    assigned_attributes: HashMap<ElementNode, HashSet<AttributeNode>>,
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct ElementNode   { i: uint }
#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct AttributeNode { i: uint }

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Parent {
    ElementParent(ElementNode),
}

impl Parent {
    pub fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementParent(e) => Some(e),
        }
    }
}

pub trait ToParent {
    fn to_parent(&self) -> Parent;
}

impl ToParent for ElementNode {
    fn to_parent(&self) -> Parent { ElementParent(*self) }
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Child {
    ElementChild(ElementNode),
}

impl Child {
    pub fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementChild(e) => Some(e),
        }
    }
}

pub trait ToChild {
    fn to_child(&self) -> Child;
}

impl ToChild for ElementNode {
    fn to_child(&self) -> Child { ElementChild(*self) }
}

impl Document {
    pub fn new() -> Document {
        Document {
            elements: Vec::new(),
            attributes: Vec::new(),
            children: HashMap::new(),
            parents: HashMap::new(),
            assigned_attributes: HashMap::new(),
        }
    }

    fn next_element_ref(& self) -> ElementNode {
        ElementNode{i: self.elements.len()}
    }

    fn next_attribute_ref(& self) -> AttributeNode {
        AttributeNode{i: self.attributes.len()}
    }

    pub fn new_element(&mut self, name: &str) -> ElementNode {
        let eref = self.next_element_ref();
        self.elements.push(Element {
            name: name.to_string(),
        });
        eref
    }

    fn new_attribute(&mut self, name: &str, value: &str) -> AttributeNode {
        let aref = self.next_attribute_ref();
        self.attributes.push(Attribute {
            name: name.to_string(),
            value: value.to_string(),
        });
        aref
    }

    pub fn element<'a>(&'a self, element: ElementNode) -> &'a Element {
        &self.elements[element.i]
    }

    pub fn mut_element<'a>(&'a mut self, element: ElementNode) -> &'a mut Element {
        self.elements.get_mut(element.i)
    }

    fn attribute<'a>(&'a self, attribute: AttributeNode) -> &'a Attribute {
        &self.attributes[attribute.i]
    }

    fn mut_attribute<'a>(&'a mut self, attribute: AttributeNode) -> &'a mut Attribute {
        self.attributes.get_mut(attribute.i)
    }

    fn attribute_for(&self, element: ElementNode, name: &str) -> Option<AttributeNode> {
        match self.assigned_attributes.find(&element) {
            Some(ref attributes) => {
                let mut node_attrs = attributes.iter().map(|node| (node, self.attribute(*node)));
                match node_attrs.find(|&(_, attr)| attr.name.as_slice() == name) {
                    Some((node, _)) => Some(*node),
                    None => None,
                }
            },
            None => None,
        }
    }

    pub fn get_attribute(&self, element: ElementNode, name: &str) -> Option<&str> {
        match self.attribute_for(element, name) {
            Some(aref) => Some(self.attribute(aref).value.as_slice()),
            None => None,
        }
    }

    pub fn set_attribute(&mut self, element: ElementNode, name: &str, value: &str) -> AttributeNode {
        match self.attribute_for(element, name) {
            Some(aref) => {
                self.mut_attribute(aref).value = value.to_string();
                aref
            },
            None => {
                let aref = self.new_attribute(name, value);
                let assigned_attributes = self.assigned_attributes.find_or_insert(element, HashSet::new());
                assigned_attributes.insert(aref);
                aref
            },
        }
    }

    fn remove_parentage(&mut self, child: Child) {
        match self.parent(child) {
            Some(ref parent) => {
                match self.children.find_mut(parent) {
                    Some(children) => {
                        match children.as_slice().position_elem(&child) {
                            Some(idx) => { children.remove(idx); },
                            None => {},
                        }
                    },
                    None => {},
                }
            },
            None => {}
        }
    }

    pub fn append_child<P: ToParent, C: ToChild>(&mut self, parent: P, child: C) {
        let parent = parent.to_parent();
        let child = child.to_child();

        {
            let kids = self.children.find_or_insert(parent, Vec::new());
            kids.push(child);
        }

        self.remove_parentage(child);
        self.parents.insert(child, parent);
    }

    pub fn children<P: ToParent>(&self, parent: P) -> Vec<Child> {
        let parent = parent.to_parent();

        match self.children.find(&parent) {
            Some(v) => v.iter().map(|r| r.clone()).collect(),
            None => vec![],
        }
    }

    pub fn parent(&self, child: Child) -> Option<Parent> {
        self.parents.find_copy(&child)
    }
}

#[deriving(Show)]
pub struct Element {
    name: String,
}

impl Element {
    pub fn name(&self) -> &str {
        self.name.as_slice()
    }

    pub fn set_name(&mut self, name: &str) {
        self.name = name.to_string();
    }
}

#[deriving(Show)]
pub struct Attribute {
    name: String,
    value: String,
}

pub struct Text;
pub struct Comment;
pub struct Root;

#[test]
fn can_add_an_element_as_a_child() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let children = d.children(alpha);
    assert_eq!(1, children.len());

    let child_elem = children[0].element().unwrap();
    let result = d.element(child_elem);
    assert_eq!(result.name(), "beta");
}

#[test]
fn children_are_ordered() {
    let mut d = Document::new();
    let greek = d.new_element("greek");
    let alpha = d.new_element("alpha");
    let omega = d.new_element("omega");

    d.append_child(greek, alpha);
    d.append_child(greek, omega);
    let children = d.children(greek);

    assert_eq!(2, children.len());
    let child_elem1 = children[0].element().unwrap();
    let child_elem2 = children[1].element().unwrap();
    assert_eq!(d.element(child_elem1).name(), "alpha");
    assert_eq!(d.element(child_elem2).name(), "omega");
}

#[test]
fn children_know_their_parent() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let child = d.children(alpha)[0];
    let parent = d.parent(child).unwrap();

    let parent_elem = parent.element().unwrap();
    let result = d.element(parent_elem);
    assert_eq!(result.name(), "alpha");
}

#[test]
fn replacing_parent_updates_original_parent() {
    let mut d = Document::new();
    let parent1 = d.new_element("parent1");
    let parent2 = d.new_element("parent2");
    let child = d.new_element("child");

    d.append_child(parent1, child);
    d.append_child(parent2, child);

    assert!(d.children(parent1).is_empty());
    assert_eq!(1, d.children(parent2).len());
}

#[test]
fn can_rename_an_element() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    {
        let element = d.mut_element(alpha);
        element.set_name("beta");
    }

    let beta = d.element(alpha);
    assert_eq!(beta.name(), "beta");
}

#[test]
fn elements_have_attributes() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "world");
}

#[test]
fn attributes_can_be_reset() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    d.set_attribute(alpha, "hello", "universe");

    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "universe");
}
<|endoftext|>


<commit_before>//! Representations of various client errors

use hyper::{self, StatusCode};
use serde_json::Error as SerdeError;
use std::{error::Error as StdError, fmt, io::Error as IoError, string::FromUtf8Error};

#[derive(Debug)]
pub enum Error {
    SerdeJsonError(SerdeError),
    Hyper(hyper::Error),
    Http(hyper::http::Error),
    IO(IoError),
    Encoding(FromUtf8Error),
    InvalidResponse(String),
    Fault { code: StatusCode, message: String },
    ConnectionNotUpgraded,
}

impl From<SerdeError> for Error {
    fn from(error: SerdeError) -> Error {
        Error::SerdeJsonError(error)
    }
}

impl From<hyper::Error> for Error {
    fn from(error: hyper::Error) -> Error {
        Error::Hyper(error)
    }
}

impl From<hyper::http::Error> for Error {
    fn from(error: hyper::http::Error) -> Error {
        Error::Http(error)
    }
}

impl From<IoError> for Error {
    fn from(error: IoError) -> Error {
        Error::IO(error)
    }
}

impl fmt::Display for Error {
    fn fmt(
        &self,
        f: &mut fmt::Formatter,
    ) -> fmt::Result {
        write!(f, "Docker Error: ")?;
        match self {
            Error::SerdeJsonError(ref err) => err.fmt(f),
            Error::Http(ref err) => err.fmt(f),
            Error::Hyper(ref err) => err.fmt(f),
            Error::IO(ref err) => err.fmt(f),
            Error::Encoding(ref err) => err.fmt(f),
            Error::InvalidResponse(ref cause) => {
                write!(f, "Response doesn't have the expected format: {}", cause)
            }
            Error::Fault { code, .. } => write!(f, "{}", code),
            Error::ConnectionNotUpgraded => write!(
                f,
                "expected the docker host to upgrade the HTTP connection but it did not"
            ),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match self {
            Error::SerdeJsonError(e) => e.description(),
            Error::Hyper(e) => e.description(),
            Error::Http(e) => e.description(),
            Error::IO(e) => e.description(),
            Error::Encoding(e) => e.description(),
            Error::InvalidResponse(msg) => msg.as_str(),
            Error::Fault { message, .. } => message.as_str(),
            Error::ConnectionNotUpgraded => "connection not upgraded",
        }
    }

    fn cause(&self) -> Option<&dyn StdError> {
        match self {
            Error::SerdeJsonError(ref err) => Some(err),
            Error::Http(ref err) => Some(err),
            Error::IO(ref err) => Some(err),
            Error::Encoding(e) => Some(e),
            _ => None,
        }
    }
}
<commit_msg>Update errors to match current Error trait (#223)<commit_after>//! Representations of various client errors

use hyper::{self, StatusCode};
use serde_json::Error as SerdeError;
use std::{error::Error as StdError, fmt, io::Error as IoError, string::FromUtf8Error};

#[derive(Debug)]
pub enum Error {
    SerdeJsonError(SerdeError),
    Hyper(hyper::Error),
    Http(hyper::http::Error),
    IO(IoError),
    Encoding(FromUtf8Error),
    InvalidResponse(String),
    Fault { code: StatusCode, message: String },
    ConnectionNotUpgraded,
}

impl From<SerdeError> for Error {
    fn from(error: SerdeError) -> Error {
        Error::SerdeJsonError(error)
    }
}

impl From<hyper::Error> for Error {
    fn from(error: hyper::Error) -> Error {
        Error::Hyper(error)
    }
}

impl From<hyper::http::Error> for Error {
    fn from(error: hyper::http::Error) -> Error {
        Error::Http(error)
    }
}

impl From<IoError> for Error {
    fn from(error: IoError) -> Error {
        Error::IO(error)
    }
}

impl fmt::Display for Error {
    fn fmt(
        &self,
        f: &mut fmt::Formatter,
    ) -> fmt::Result {
        write!(f, "Docker Error: ")?;
        match self {
            Error::SerdeJsonError(ref err) => err.fmt(f),
            Error::Http(ref err) => err.fmt(f),
            Error::Hyper(ref err) => err.fmt(f),
            Error::IO(ref err) => err.fmt(f),
            Error::Encoding(ref err) => err.fmt(f),
            Error::InvalidResponse(ref cause) => {
                write!(f, "Response doesn't have the expected format: {}", cause)
            }
            Error::Fault { code, .. } => write!(f, "{}", code),
            Error::ConnectionNotUpgraded => write!(
                f,
                "expected the docker host to upgrade the HTTP connection but it did not"
            ),
        }
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Error::SerdeJsonError(ref err) => Some(err),
            Error::Http(ref err) => Some(err),
            Error::IO(ref err) => Some(err),
            Error::Encoding(e) => Some(e),
            _ => None,
        }
    }
}
<|endoftext|>