<commit_before>//! Simple module to store files in database.
//!
//! cratesfyi is generating more than 5 million files, they are small and mostly html files.
//! They are using so many inodes and it is better to store them in database instead of
//! filesystem. This module is adding files into database and retrieving them.

use std::path::{PathBuf, Path};
use postgres::Connection;
use rustc_serialize::json::{Json, ToJson};
use std::fs;
use std::io::Read;
use error::Result;
use failure::err_msg;
use rusoto_s3::{S3, PutObjectRequest, GetObjectRequest, S3Client};
use rusoto_core::region::Region;
use rusoto_credential::EnvironmentProvider;

fn get_file_list_from_dir<P: AsRef<Path>>(path: P,
                                          files: &mut Vec<PathBuf>)
                                          -> Result<()> {
    let path = path.as_ref();

    for file in try!(path.read_dir()) {
        let file = try!(file);

        if try!(file.file_type()).is_file() {
            files.push(file.path());
        } else if try!(file.file_type()).is_dir() {
            try!(get_file_list_from_dir(file.path(), files));
        }
    }

    Ok(())
}

pub fn get_file_list<P: AsRef<Path>>(path: P) -> Result<Vec<PathBuf>> {
    let path = path.as_ref();
    let mut files = Vec::new();

    if !path.exists() {
        return Err(err_msg("File not found"));
    } else if path.is_file() {
        files.push(PathBuf::from(path.file_name().unwrap()));
    } else if path.is_dir() {
        try!(get_file_list_from_dir(path, &mut files));
        for file_path in &mut files {
            // We want the paths in this list to not be {path}/bar.txt but just bar.txt
            *file_path = PathBuf::from(file_path.strip_prefix(path).unwrap());
        }
    }

    Ok(files)
}

pub struct Blob {
    pub path: String,
    pub mime: String,
    pub date_updated: time::Timespec,
    pub content: Vec<u8>,
}

pub fn get_path(conn: &Connection, path: &str) -> Option<Blob> {
    let rows = conn.query("SELECT path, mime, date_updated, content
                           FROM files
                           WHERE path = $1", &[&path]).unwrap();

    if rows.len() == 0 {
        None
    } else {
        let client = s3_client();
        let row = rows.get(0);
        let content = client.get_object(GetObjectRequest {
            bucket: "rust-docs-rs".into(),
            key: path.into(),
            ..Default::default()
        }).sync().ok().and_then(|r| r.body).map(|b| {
            let mut b = b.into_blocking_read();
            let mut content = Vec::new();
            b.read_to_end(&mut content).unwrap();
            content
        }).unwrap_or(row.get(3));

        Some(Blob {
            path: row.get(0),
            mime: row.get(1),
            date_updated: row.get(2),
            content,
        })
    }
}

fn s3_client() -> S3Client {
    let client = S3Client::new_with(
        rusoto_core::request::HttpClient::new().unwrap(),
        EnvironmentProvider::default(),
        std::env::var("S3_ENDPOINT").ok().map(|e| Region::Custom {
            name: "us-west-1".to_owned(),
            endpoint: e,
        }).unwrap_or(Region::UsWest1),
    );
    client
}

/// Adds files into database and returns list of files with their mime type in Json
pub fn add_path_into_database<P: AsRef<Path>>(conn: &Connection,
                                              prefix: &str,
                                              path: P)
                                              -> Result<Json> {
    use magic::{Cookie, flags};
    let cookie = try!(Cookie::open(flags::MIME_TYPE));
    try!(cookie.load::<&str>(&[]));

    let trans = try!(conn.transaction());
    let client = s3_client();
    let mut file_list_with_mimes: Vec<(String, PathBuf)> = Vec::new();

    for file_path in try!(get_file_list(&path)) {
        let (path, mime) = {
            let path = Path::new(path.as_ref()).join(&file_path);
            // Some files have insufficient permissions (like .lock file created by cargo in
            // documentation directory). We are skipping this files.
            let mut file = match fs::File::open(path) {
                Ok(f) => f,
                Err(_) => continue,
            };
            let mut content: Vec<u8> = Vec::new();
            try!(file.read_to_end(&mut content));
            let bucket_path = Path::new(prefix).join(&file_path)
                .into_os_string().into_string().unwrap();

            let mime = {
                let mime = try!(cookie.buffer(&content));
                // css's are causing some problem in browsers
                // magic will return text/plain for css file types
                // convert them to text/css
                // do the same for javascript files
                if mime == "text/plain" {
                    let e = file_path.extension().unwrap_or_default();
                    if e == "css" {
                        "text/css".to_owned()
                    } else if e == "js" {
                        "application/javascript".to_owned()
                    } else {
                        mime.to_owned()
                    }
                } else {
                    mime.to_owned()
                }
            };

            client.put_object(PutObjectRequest {
                acl: Some("public-read".into()),
                bucket: "rust-docs-rs".into(),
                key: bucket_path.clone(),
                body: Some(content.clone().into()),
                content_type: Some(mime.clone()),
                ..Default::default()
            }).sync().unwrap();

            file_list_with_mimes.push((mime.clone(), file_path.clone()));

            (
                bucket_path,
                mime,
            )
        };

        // check if file already exists in database
        let rows = try!(conn.query("SELECT COUNT(*) FROM files WHERE path = $1", &[&path]));

        if rows.get(0).get::<usize, i64>(0) == 0 {
            try!(trans.query("INSERT INTO files (path, mime, content) VALUES ($1, $2, $3)",
                             &[&path, &mime, &"in-s3".to_owned()]));
        } else {
            try!(trans.query("UPDATE files SET mime = $2, date_updated = NOW() \
                              WHERE path = $1",
                             &[&path, &mime]));
        }
    }

    try!(trans.commit());

    file_list_to_json(file_list_with_mimes)
}

fn file_list_to_json(file_list: Vec<(String, PathBuf)>) -> Result<Json> {

    let mut file_list_json: Vec<Json> = Vec::new();

    for file in file_list {
        let mut v: Vec<String> = Vec::new();
        v.push(file.0.clone());
        v.push(file.1.into_os_string().into_string().unwrap());
        file_list_json.push(v.to_json());
    }

    Ok(file_list_json.to_json())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use std::env;
    use super::get_file_list;

    #[test]
    fn test_get_file_list() {
        let _ = env_logger::try_init();

        let files = get_file_list(env::current_dir().unwrap());
        assert!(files.is_ok());
        assert!(files.unwrap().len() > 0);

        let files = get_file_list(env::current_dir().unwrap().join("Cargo.toml")).unwrap();
        assert_eq!(files[0], std::path::Path::new("Cargo.toml"));
    }
}
<commit_msg>Fall back to reading/writing to the database<commit_after>//! Simple module to store files in database.
//!
//! cratesfyi is generating more than 5 million files, they are small and mostly html files.
//! They are using so many inodes and it is better to store them in database instead of
//! filesystem. This module is adding files into database and retrieving them.

use std::path::{PathBuf, Path};
use postgres::Connection;
use rustc_serialize::json::{Json, ToJson};
use std::fs;
use std::io::Read;
use error::Result;
use failure::err_msg;
use rusoto_s3::{S3, PutObjectRequest, GetObjectRequest, S3Client};
use rusoto_core::region::Region;
use rusoto_credential::EnvironmentProvider;

fn get_file_list_from_dir<P: AsRef<Path>>(path: P,
                                          files: &mut Vec<PathBuf>)
                                          -> Result<()> {
    let path = path.as_ref();

    for file in try!(path.read_dir()) {
        let file = try!(file);

        if try!(file.file_type()).is_file() {
            files.push(file.path());
        } else if try!(file.file_type()).is_dir() {
            try!(get_file_list_from_dir(file.path(), files));
        }
    }

    Ok(())
}

pub fn get_file_list<P: AsRef<Path>>(path: P) -> Result<Vec<PathBuf>> {
    let path = path.as_ref();
    let mut files = Vec::new();

    if !path.exists() {
        return Err(err_msg("File not found"));
    } else if path.is_file() {
        files.push(PathBuf::from(path.file_name().unwrap()));
    } else if path.is_dir() {
        try!(get_file_list_from_dir(path, &mut files));
        for file_path in &mut files {
            // We want the paths in this list to not be {path}/bar.txt but just bar.txt
            *file_path = PathBuf::from(file_path.strip_prefix(path).unwrap());
        }
    }

    Ok(files)
}

pub struct Blob {
    pub path: String,
    pub mime: String,
    pub date_updated: time::Timespec,
    pub content: Vec<u8>,
}

pub fn get_path(conn: &Connection, path: &str) -> Option<Blob> {
    let rows = conn.query("SELECT path, mime, date_updated, content
                           FROM files
                           WHERE path = $1", &[&path]).unwrap();

    if rows.len() == 0 {
        None
    } else {
        let client = s3_client();
        let row = rows.get(0);
        let content = client.and_then(|c| c.get_object(GetObjectRequest {
            bucket: "rust-docs-rs".into(),
            key: path.into(),
            ..Default::default()
        }).sync().ok()).and_then(|r| r.body).map(|b| {
            let mut b = b.into_blocking_read();
            let mut content = Vec::new();
            b.read_to_end(&mut content).unwrap();
            content
        }).unwrap_or(row.get(3));

        Some(Blob {
            path: row.get(0),
            mime: row.get(1),
            date_updated: row.get(2),
            content,
        })
    }
}

fn s3_client() -> Option<S3Client> {
    // If AWS keys aren't configured, then presume we should use the DB exclusively
    // for file storage.
    if std::env::var_os("AWS_ACCESS_KEY_ID").is_none() {
        return None;
    }
    Some(S3Client::new_with(
        rusoto_core::request::HttpClient::new().unwrap(),
        EnvironmentProvider::default(),
        std::env::var("S3_ENDPOINT").ok().map(|e| Region::Custom {
            name: "us-west-1".to_owned(),
            endpoint: e,
        }).unwrap_or(Region::UsWest1),
    ))
}

/// Adds files into database and returns list of files with their mime type in Json
pub fn add_path_into_database<P: AsRef<Path>>(conn: &Connection,
                                              prefix: &str,
                                              path: P)
                                              -> Result<Json> {
    use magic::{Cookie, flags};
    let cookie = try!(Cookie::open(flags::MIME_TYPE));
    try!(cookie.load::<&str>(&[]));

    let trans = try!(conn.transaction());
    let client = s3_client();
    let mut file_list_with_mimes: Vec<(String, PathBuf)> = Vec::new();

    for file_path in try!(get_file_list(&path)) {
        let (path, content, mime) = {
            let path = Path::new(path.as_ref()).join(&file_path);
            // Some files have insufficient permissions (like .lock file created by cargo in
            // documentation directory). We are skipping this files.
            let mut file = match fs::File::open(path) {
                Ok(f) => f,
                Err(_) => continue,
            };
            let mut content: Vec<u8> = Vec::new();
            try!(file.read_to_end(&mut content));
            let bucket_path = Path::new(prefix).join(&file_path)
                .into_os_string().into_string().unwrap();

            let mime = {
                let mime = try!(cookie.buffer(&content));
                // css's are causing some problem in browsers
                // magic will return text/plain for css file types
                // convert them to text/css
                // do the same for javascript files
                if mime == "text/plain" {
                    let e = file_path.extension().unwrap_or_default();
                    if e == "css" {
                        "text/css".to_owned()
                    } else if e == "js" {
                        "application/javascript".to_owned()
                    } else {
                        mime.to_owned()
                    }
                } else {
                    mime.to_owned()
                }
            };

            let content: Option<Vec<u8>> = if let Some(client) = &client {
                let s3_res = client.put_object(PutObjectRequest {
                    acl: Some("public-read".into()),
                    bucket: "rust-docs-rs".into(),
                    key: bucket_path.clone(),
                    body: Some(content.clone().into()),
                    content_type: Some(mime.clone()),
                    ..Default::default()
                }).sync();
                match s3_res {
                    // we've successfully uploaded the content, so steal it;
                    // we don't want to put it in the DB
                    Ok(_) => None,
                    // Since s3 was configured, we want to panic on failure to upload.
                    Err(e) => {
                        panic!("failed to upload to {}: {:?}", bucket_path, e)
                    },
                }
            } else {
                Some(content.clone().into())
            };

            file_list_with_mimes.push((mime.clone(), file_path.clone()));

            (
                bucket_path,
                content,
                mime,
            )
        };

        // check if file already exists in database
        let rows = try!(conn.query("SELECT COUNT(*) FROM files WHERE path = $1", &[&path]));

        let content = content.unwrap_or_else(|| "in-s3".to_owned().into());

        if rows.get(0).get::<usize, i64>(0) == 0 {
            try!(trans.query("INSERT INTO files (path, mime, content) VALUES ($1, $2, $3)",
                             &[&path, &mime, &content]));
        } else {
            try!(trans.query("UPDATE files SET mime = $2, content = $3, date_updated = NOW() \
                              WHERE path = $1",
                             &[&path, &mime, &content]));
        }
    }

    try!(trans.commit());

    file_list_to_json(file_list_with_mimes)
}

fn file_list_to_json(file_list: Vec<(String, PathBuf)>) -> Result<Json> {

    let mut file_list_json: Vec<Json> = Vec::new();

    for file in file_list {
        let mut v: Vec<String> = Vec::new();
        v.push(file.0.clone());
        v.push(file.1.into_os_string().into_string().unwrap());
        file_list_json.push(v.to_json());
    }

    Ok(file_list_json.to_json())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use std::env;
    use super::get_file_list;

    #[test]
    fn test_get_file_list() {
        let _ = env_logger::try_init();

        let files = get_file_list(env::current_dir().unwrap());
        assert!(files.is_ok());
        assert!(files.unwrap().len() > 0);

        let files = get_file_list(env::current_dir().unwrap().join("Cargo.toml")).unwrap();
        assert_eq!(files[0], std::path::Path::new("Cargo.toml"));
    }
}
<|endoftext|>


<commit_before>use kernel::stdio;
use kernel::keyboard;
use kernel::keyboard::*;
use platform::cpu::{Registers};
use platform::vga::{Black, White,Yellow, LightRed};

static mut shift: bool = false;

#[no_split_stack]
#[no_mangle]
pub fn handle_interrupt(regs: Registers, interrupt_number: u32, error_code: u32) -> Registers
{
	match interrupt_number
	{
		1 => keyboard_irq(),
		_ => unknown_irq(interrupt_number, error_code),
	};
	regs
}

#[no_split_stack]
fn keyboard_irq()
{
	stdio::set_fg(Yellow);
	stdio::set_bg(LightRed);
	match keyboard::get_key()
	{
		KeyUp(Escape) => { ::platform::cpu::request_irq3(); },
		KeyUp(Shift) => unsafe { shift = false },
		KeyDown(key) => match key
		{
			Printable(c, d) => { stdio::print_char(if unsafe {!shift} {c} else {d}); },
			Space => { stdio::print_char(' '); },
			Backspace => { stdio::backspace(); },
			Return => { stdio::crlf(); },
			Shift => unsafe { shift = true; },
			_ => {},
		},
		_ => {},
	};
}

fn unknown_irq(interrupt_number: u32, error_code: u32)
{
	stdio::set_fg(White); stdio::set_bg(Black);
	stdio::write_screen(10, 5, "Interrupt received");
	stdio::set_fg(Black); stdio::set_bg(White);
	stdio::write_hex(10, 6, interrupt_number as u32);
	stdio::write_bin(10, 7, error_code as u32);
}
<commit_msg>Handle multiple shift keyup/downs<commit_after>use kernel::stdio;
use kernel::keyboard;
use kernel::keyboard::*;
use platform::cpu::{Registers};
use platform::vga::{Black, White,Yellow, LightRed};

static mut shift: uint = 0;

#[no_split_stack]
#[no_mangle]
pub fn handle_interrupt(regs: Registers, interrupt_number: u32, error_code: u32) -> Registers
{
	match interrupt_number
	{
		1 => keyboard_irq(),
		_ => unknown_irq(interrupt_number, error_code),
	};
	regs
}

#[no_split_stack]
fn keyboard_irq()
{
	stdio::set_fg(Yellow);
	stdio::set_bg(LightRed);
	match keyboard::get_key()
	{
		KeyUp(Escape) => { ::platform::cpu::request_irq3(); },
		KeyUp(Shift) => unsafe { shift -= 1 },
		KeyDown(key) => match key
		{
			Printable(c, d) => { stdio::print_char(if unsafe {shift == 0} {c} else {d}); },
			Space => { stdio::print_char(' '); },
			Backspace => { stdio::backspace(); },
			Return => { stdio::crlf(); },
			Shift => unsafe { shift += 1; },
			_ => {},
		},
		_ => {},
	};
}

fn unknown_irq(interrupt_number: u32, error_code: u32)
{
	stdio::set_fg(White); stdio::set_bg(Black);
	stdio::write_screen(10, 5, "Interrupt received");
	stdio::set_fg(Black); stdio::set_bg(White);
	stdio::write_hex(10, 6, interrupt_number as u32);
	stdio::write_bin(10, 7, error_code as u32);
}
<|endoftext|>


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


<commit_before>extern crate sdl2;
use std::ops::DerefMut;
use displayer::Displayer;
use font;
use sdl2::pixels::Color;
use sdl2::surface::Surface;
use sdl2::rect::Rect;
use sdl2::render::{TextureQuery, BlendMode};
use std::vec::Vec;

pub trait Display {
    fn draw(self,&mut Displayer) ;
}

#[derive(Debug)]
pub struct TextElement<'a> {
    pub text:&'a str,
    pub color:Color,
    pub outline:Option<Color>,
    pub shadow:Option<Color>
}

#[derive(Copy,Debug,Clone)]
pub enum PosX {
    Centered,
    FromLeft(u32),
    FromRight(u32),
    FromLeftPercent(f32),
    FromRightPercent(f32),
}

#[derive(Copy,Debug,Clone)]
pub enum PosY {
    Centered,
    FromTop(u32),
    FromBottom(u32),
    FromTopPercent(f32),
    FromBottomPercent(f32),
}

#[derive(Copy,Debug,Clone)]
pub enum Size{
    //Percentage(f64),
    Raw(u32),
    FitWidth{width:u32,max_font_size:Option<u32>},
}

#[derive(Debug)]
pub struct Text2D<'a> {
    pub text:Vec<TextElement<'a>>,
    pub size:Size,
    pub pos:(PosX,PosY),
    pub anchor:(f32,f32)
}

impl<'a> Text2D<'a> {
    pub fn to_string(&self) -> String {
        self.text.iter().fold(String::default(),|accu_text,text_element|{
            accu_text + text_element.text
        })
    }
}

impl<'a> Display for Text2D<'a> {
    fn draw(self,displayer:&mut Displayer) {
        let (window_width,window_height) = displayer.sdl_renderer().window().unwrap().size() ;
        let mut target_texture = {
            let is_outline_enabled = self.text.iter().any(|text_element|{
                text_element.outline.is_some()
            });
            let fit_width = window_width * 9 / 10 ;
            let all_text = self.to_string();
            let font_set = displayer.fonts().get_fittest_font_set(all_text.as_str(), fit_width as u16,is_outline_enabled).unwrap();
            let (target_surface_width,target_surface_height) =
                font_set.get_outline_font().size_of(all_text.as_str()).expect("Unable to get outline pixel size of str");
            // ARGB8888 because it's the only one supported on my computer; i hope it's the same everywhere else ?
            let mut target_surface = Surface::new(target_surface_width,target_surface_height,sdl2::pixels::PixelFormatEnum::ARGB8888)
                .expect("Failed to create Surface with ARGB8888 Format");
            let mut width_offset : i32 = if is_outline_enabled {0} else {font::OUTLINE_WIDTH as i32} ;
            for text_element in self.text.iter() {
                let (temp_target_surface_width,temp_target_surface_height) =
                    font_set.get_outline_font().size_of(text_element.text).unwrap();
                let mut temp_target_surface = Surface::new(temp_target_surface_width,temp_target_surface_height,sdl2::pixels::PixelFormatEnum::ARGB8888)
                    .expect("Failed to create Surface with ARGB8888 Format");
                if text_element.outline.is_some() {
                    let mut outline_surface = font_set.get_outline_font()
                                                      .render(text_element.text)
                                                      .blended(text_element.outline.unwrap())
                                                      .unwrap();
                    let (surface_width,surface_height) = outline_surface.size();
                    outline_surface.blit(None,
                                         temp_target_surface.deref_mut(),
                                         Some(Rect::new(0,
                                                        0,
                                                        surface_width,
                                                        surface_height)));
                }

                {
                    let mut regular_surface = font_set.get_regular_font()
                                      .render(text_element.text)
                                      .blended(text_element.color)
                                      .unwrap();
                    let (surface_width,surface_height) = regular_surface.size();
                    regular_surface.blit(None,
                                         temp_target_surface.deref_mut(),
                                         Some(Rect::new(font::OUTLINE_WIDTH as i32,
                                                        font::OUTLINE_WIDTH as i32,
                                                        surface_width,
                                                        surface_height)));
                }

                temp_target_surface.set_alpha_mod(match text_element.color{
                    Color::RGB(_,_,_) => 255,
                    Color::RGBA(_,_,_,a) => a
                });
                temp_target_surface.blit(None,
                                         target_surface.deref_mut(),
                                         Some(Rect::new(width_offset,
                                                        0,
                                                        temp_target_surface_width,
                                                        temp_target_surface_height)));
                width_offset = width_offset + temp_target_surface_width as i32 - (font::OUTLINE_WIDTH as i32* 2);
            }
            let target_texture = displayer
                .sdl_renderer()
                .create_texture_from_surface(target_surface)
                .expect("Unable to create empty texture with pixel format ARGB8888");
            target_texture
        };
        let TextureQuery { width:texture_width, height:texture_height, .. } = target_texture.query();
        // POSITION
        let (pos_x,pos_y) = self.pos;
        let mut target_rect : Rect = Rect::new(0, 0,texture_width,texture_height);
        let delta_anchor_x = (self.anchor.0 * texture_width as f32) as i32 ;
        let delta_anchor_y = (self.anchor.1 * texture_height as f32) as i32 ;
        match pos_x {
            PosX::Centered => {
                target_rect.set_x((window_width/2) as i32 - delta_anchor_x )
            },
            PosX::FromLeft(value) => {
                target_rect.set_x(value as i32 - delta_anchor_x)
            },
            PosX::FromLeftPercent(percent) => {
                target_rect.set_x((percent * (window_width as f32)) as i32 - delta_anchor_x)
            },
            PosX::FromRight(value) => {
                target_rect.set_x(window_width as i32 - value as i32 - delta_anchor_x)
            },
            PosX::FromRightPercent(percent) => {
                target_rect.set_x(window_width as i32 - (percent * (window_width as f32)) as i32 - delta_anchor_x)
            }
        };
        match pos_y {
            PosY::Centered => {
                target_rect.set_y((window_height/2) as i32 - delta_anchor_y )
            },
            PosY::FromTop(value) => {
                target_rect.set_y(value as i32 - delta_anchor_y)
            },
            PosY::FromTopPercent(percent) => {
                target_rect.set_y((percent * (window_height as f32)) as i32 - delta_anchor_y)
            },
            PosY::FromBottom(value) => {
                target_rect.set_y(window_height as i32 - value as i32 - delta_anchor_y)
            },
            PosY::FromBottomPercent(percent) => {
                target_rect.set_y(window_height as i32 - (percent * (window_height as f32)) as i32 - delta_anchor_y)
            }
        };
        displayer.sdl_renderer_mut().copy(&target_texture,None,Some(target_rect));
    }
}
<commit_msg>refactor: moved blit into TextElement.as_surface()<commit_after>extern crate sdl2;
use std::ops::DerefMut;
use displayer::Displayer;
use font;
use sdl2::pixels::Color;
use sdl2::surface::Surface;
use sdl2::rect::Rect;
use sdl2::render::{TextureQuery, BlendMode};
use std::vec::Vec;

pub trait Display {
    fn draw(self,&mut Displayer) ;
}

#[derive(Debug)]
pub struct TextElement<'a> {
    pub text:&'a str,
    pub color:Color,
    pub outline:Option<Color>,
    pub shadow:Option<Color>
}

impl<'a> TextElement<'a> {
    fn as_surface(&self,font_set:&font::FontSet) -> Surface {
        let (surface_width,surface_height) = font_set.get_outline_font().size_of(self.text).unwrap();
        let mut target_surface : Surface = Surface::new(surface_width,surface_height,sdl2::pixels::PixelFormatEnum::ARGB8888)
            .expect("Failed to create Surface with ARGB8888 Format");
        match self.outline {
            Some(outline_color) => {
                // blit the surface containing the border
                let mut outline_surface = font_set.get_outline_font()
                                                  .render(self.text)
                                                  .blended(outline_color)
                                                  .unwrap();
                let (outline_surface_width,outline_surface_height) = outline_surface.size();
                outline_surface.blit(None,
                                     target_surface.deref_mut(),
                                     Some(Rect::new(0,
                                                    0,
                                                    outline_surface_width,
                                                    outline_surface_height)));
            },
            None => {} // do nothing about the outline
        }
        {
            // blit the surface containing the center font
            let mut surface = font_set.get_regular_font()
                                      .render(self.text)
                                      .blended(self.color)
                                      .unwrap();
            let (surface_width,surface_height) = surface.size();
            surface.blit(None,
                         target_surface.deref_mut(),
                         Some(Rect::new(font::OUTLINE_WIDTH as i32, // start from OUTLINE
                                        font::OUTLINE_WIDTH as i32, // to center the text
                                        surface_width,
                                        surface_height)));
        }

        target_surface.set_alpha_mod(match self.color {
            Color::RGB(_,_,_) => 255,
            Color::RGBA(_,_,_,a) => a
        });
        target_surface
    }
}

#[derive(Copy,Debug,Clone)]
pub enum PosX {
    Centered,
    FromLeft(u32),
    FromRight(u32),
    FromLeftPercent(f32),
    FromRightPercent(f32),
}

#[derive(Copy,Debug,Clone)]
pub enum PosY {
    Centered,
    FromTop(u32),
    FromBottom(u32),
    FromTopPercent(f32),
    FromBottomPercent(f32),
}

#[derive(Copy,Debug,Clone)]
pub enum Size{
    //Percentage(f64),
    Raw(u32),
    FitWidth{width:u32,max_font_size:Option<u32>},
}

#[derive(Debug)]
pub struct Text2D<'a> {
    pub text:Vec<TextElement<'a>>,
    pub size:Size,
    pub pos:(PosX,PosY),
    pub anchor:(f32,f32)
}

impl<'a> Text2D<'a> {
    pub fn to_string(&self) -> String {
        self.text.iter().fold(String::default(),|accu_text,text_element|{
            accu_text + text_element.text
        })
    }
}

impl<'a> Display for Text2D<'a> {
    fn draw(self,displayer:&mut Displayer) {
        let (window_width,window_height) = displayer.sdl_renderer().window().unwrap().size() ;
        let mut target_texture = {
            let is_outline_enabled = self.text.iter().any(|text_element|{
                text_element.outline.is_some()
            });
            let fit_width = window_width * 9 / 10 ;
            let all_text = self.to_string();
            let font_set = displayer.fonts().get_fittest_font_set(all_text.as_str(), fit_width as u16,is_outline_enabled).unwrap();
            let (target_surface_width,target_surface_height) =
                font_set.get_outline_font().size_of(all_text.as_str()).expect("Unable to get outline pixel size of str");
            // ARGB8888 because it's the only one supported on my computer; i hope it's the same everywhere else ?
            let mut target_surface = Surface::new(target_surface_width,target_surface_height,sdl2::pixels::PixelFormatEnum::ARGB8888)
                .expect("Failed to create Surface with ARGB8888 Format");
            let mut width_offset : i32 = if is_outline_enabled {0} else {font::OUTLINE_WIDTH as i32} ;
            for text_element in self.text.iter() {
                let text_surface = text_element.as_surface(font_set);
                let (text_surface_w,text_surface_h) =
                    text_surface.size();
                text_surface.blit(None,
                                  target_surface.deref_mut(),
                                  Some(Rect::new(width_offset,
                                                 0,
                                                 text_surface_w,
                                                 text_surface_h)));
                width_offset = width_offset + text_surface_w as i32 - (font::OUTLINE_WIDTH as i32* 2);
            }
            let target_texture = displayer
                .sdl_renderer()
                .create_texture_from_surface(target_surface)
                .expect("Unable to create empty texture with pixel format ARGB8888");
            target_texture
        };
        let TextureQuery { width:texture_width, height:texture_height, .. } = target_texture.query();
        // POSITION
        let (pos_x,pos_y) = self.pos;
        let mut target_rect : Rect = Rect::new(0, 0,texture_width,texture_height);
        let delta_anchor_x = (self.anchor.0 * texture_width as f32) as i32 ;
        let delta_anchor_y = (self.anchor.1 * texture_height as f32) as i32 ;
        match pos_x {
            PosX::Centered => {
                target_rect.set_x((window_width/2) as i32 - delta_anchor_x )
            },
            PosX::FromLeft(value) => {
                target_rect.set_x(value as i32 - delta_anchor_x)
            },
            PosX::FromLeftPercent(percent) => {
                target_rect.set_x((percent * (window_width as f32)) as i32 - delta_anchor_x)
            },
            PosX::FromRight(value) => {
                target_rect.set_x(window_width as i32 - value as i32 - delta_anchor_x)
            },
            PosX::FromRightPercent(percent) => {
                target_rect.set_x(window_width as i32 - (percent * (window_width as f32)) as i32 - delta_anchor_x)
            }
        };
        match pos_y {
            PosY::Centered => {
                target_rect.set_y((window_height/2) as i32 - delta_anchor_y )
            },
            PosY::FromTop(value) => {
                target_rect.set_y(value as i32 - delta_anchor_y)
            },
            PosY::FromTopPercent(percent) => {
                target_rect.set_y((percent * (window_height as f32)) as i32 - delta_anchor_y)
            },
            PosY::FromBottom(value) => {
                target_rect.set_y(window_height as i32 - value as i32 - delta_anchor_y)
            },
            PosY::FromBottomPercent(percent) => {
                target_rect.set_y(window_height as i32 - (percent * (window_height as f32)) as i32 - delta_anchor_y)
            }
        };
        displayer.sdl_renderer_mut().copy(&target_texture,None,Some(target_rect));
    }
}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use xdg::BaseDirectories;

pub struct ConfigDir {
    xdg: BaseDirectories
}

impl ConfigDir {
    pub fn new(xdg: BaseDirectories) -> ConfigDir {
        // TODO: Check that the home dir exists and is a directory.
        // TODO: Use a command line flag to turn every function into a no-op.
        ConfigDir { xdg }
    }

    pub fn home(&self) -> PathBuf {
        self.xdg.get_config_home()
    }

    /// Gets a filepath within the config directory, creating leading directories. Logs and returns None if an error occurs.
    pub fn place<P>(&self, path: P) -> Option<PathBuf>
        where P: AsRef<Path>
    {
        match self.xdg.place_config_file(&path) {
            Err(e) => { error!("Error attempting to place file {:?}, err = {}", &path.as_ref(), e); None },
            Ok(p) => Some(p)
        }
    }

    /// Get the path of an existing file, or return None if the file does not exist or the path refers to a directory.
    pub fn find<P>(&self, path: P) -> Option<PathBuf>
        where P: AsRef<Path>
    {
        self.xdg.find_config_file(path)
            .and_then(|p| if p.is_dir() {
                error!("The path {:?} is a directory, expected a file. Returning None.", p);
                None
            }
            else {
                Some(p)
            })
    }

    /// Opens a file in read-only mode, or returns None if the file cannot be opened.
    pub fn open<P>(&self, path: P) -> Option<File>
        where P: AsRef<Path>
    {
        match self.find(path) {
            None => None,
            Some(p) => {
                match File::open(&p) {
                    Err(e) => { error!("Error attempting to open file {:?}, err = {}", p, e); None },
                    Ok(f) => Some(f)
                }
            }
        }
    }

    // pub fn open_options<P>(&self, path: P, options: OpenOptions) -> Option<File>
    //     where P: AsRef<Path>
    // {
    //     let p = self.find(path).unwrap();
    //     options.open(p).ok()
    // }

    //pub fn create - opens in write-only mode
    //Move these methods to a trait and use them to implement it on both classes.
    //Allow "no-op" operations under the control of a flag?
}

pub struct DataDir {

}

/*
XDG supports these operations
  find_{config,data,cache,runtime}_file -> Option<PathBuf>       - gets an existing file, or returns None
  place_{config,data,cache,runtime}_file -> ioResult<PathBuf>    -

  create_{config,data,cache,runtime}_directory  - creates dirs under the XDG dir structure
  list_{config,data,cache,runtime}_files_[once] - lists files under the XDG dir structure
  get_{config,data,cache}_home                  - gets the root dir under the XDG dir structure

>> To load a file, use find(). We should wrap find() and check exists and is a file. Or we can write a series of open() methods.
>> To save a file, use place().

*/<commit_msg>ConfigDir: rename functions to be more explanatory.<commit_after>use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use xdg::BaseDirectories;

pub struct ConfigDir {
    xdg: BaseDirectories
}

impl ConfigDir {
    pub fn new(xdg: BaseDirectories) -> ConfigDir {
        // TODO: Check that the home dir exists and is a directory.
        // TODO: Use a command line flag to turn every function into a no-op.
        ConfigDir { xdg }
    }

    pub fn home(&self) -> PathBuf {
        self.xdg.get_config_home()
    }

    /// Gets a filepath within the config directory, creating leading directories. Logs and returns None if an error occurs.
    pub fn get_proposed_path<P>(&self, path: P) -> Option<PathBuf>
        where P: AsRef<Path>
    {
        match self.xdg.place_config_file(&path) {
            Err(e) => { error!("Error attempting to place file {:?}, err = {}. Returning None.", &path.as_ref(), e); None },
            Ok(p) => Some(p)
        }
    }

    /// Get the path of an existing file, or return None if the file does not exist or the path refers to a directory.
    pub fn get_existing_path<P>(&self, path: P) -> Option<PathBuf>
        where P: AsRef<Path>
    {
        self.xdg.find_config_file(path)
            .and_then(|p| if p.is_dir() {
                error!("The path {:?} is a directory, expected a file. Returning None.", p);
                None
            }
            else {
                Some(p)
            })
    }

    /// Opens a file in read-only mode, or returns None if the file cannot be opened.
    pub fn open<P>(&self, path: P) -> Option<File>
        where P: AsRef<Path>
    {
        match self.get_existing_path(path) {
            None => None,
            Some(p) => {
                match File::open(&p) {
                    Err(e) => { error!("Error attempting to open file {:?}, err = {}", p, e); None },
                    Ok(f) => Some(f)
                }
            }
        }
    }

    // pub fn open_options<P>(&self, path: P, options: OpenOptions) -> Option<File>
    //     where P: AsRef<Path>
    // {
    //     let p = self.find(path).unwrap();
    //     options.open(p).ok()
    // }

    //pub fn create - opens in write-only mode
    //Move these methods to a trait and use them to implement it on both classes.
    //Allow "no-op" operations under the control of a flag?
}

pub struct DataDir {

}

/*
XDG supports these operations
  find_{config,data,cache,runtime}_file -> Option<PathBuf>       - gets an existing file, or returns None
  place_{config,data,cache,runtime}_file -> ioResult<PathBuf>    -

  create_{config,data,cache,runtime}_directory  - creates dirs under the XDG dir structure
  list_{config,data,cache,runtime}_files_[once] - lists files under the XDG dir structure
  get_{config,data,cache}_home                  - gets the root dir under the XDG dir structure

>> To load a file, use find(). We should wrap find() and check exists and is a file. Or we can write a series of open() methods.
>> To save a file, use place().

*/<|endoftext|>


<commit_before>//! Implements `StdQsearch` and `StdQsearchResult`.

use std::cell::UnsafeCell;
use std::marker::PhantomData;
use uci::{SetOption, OptionDescription};
use chesstypes::*;
use search::*;
use search::quiescence::*;
use utils::MoveStack;

/// Implements the `QsearchResult` trait.
pub struct StdQsearchResult {
    value: Value,
    searched_nodes: u64,
}

impl QsearchResult for StdQsearchResult {
    #[inline(always)]
    fn new(value: Value, searched_nodes: u64) -> Self {
        debug_assert!(VALUE_EVAL_MIN <= value && value <= VALUE_EVAL_MAX);
        StdQsearchResult {
            value: value,
            searched_nodes: searched_nodes,
        }
    }

    #[inline(always)]
    fn value(&self) -> Value {
        self.value
    }

    #[inline(always)]
    fn searched_nodes(&self) -> u64 {
        self.searched_nodes
    }
}

/// Implements the `Qsearch` trait.
///
/// Performs classical quiescence search with stand pat, delta
/// pruning, static exchange evaluation, check evasions, limited
/// checks and recaptures.
pub struct StdQsearch<T: MoveGenerator> {
    phantom: PhantomData<T>,
}

impl<T: MoveGenerator> Qsearch for StdQsearch<T> {
    type MoveGenerator = T;

    type QsearchResult = StdQsearchResult;

    fn qsearch(params: QsearchParams<Self::MoveGenerator>) -> Self::QsearchResult {
        thread_local!(
            static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
        );
        let mut searched_nodes = 0;
        let value = MOVE_STACK.with(|s| unsafe {
            qsearch(params.position,
                    params.lower_bound,
                    params.upper_bound,
                    params.static_evaluation,
                    0,
                    -params.depth,
                    &mut *s.get(),
                    &mut searched_nodes)
        });
        StdQsearchResult::new(value, searched_nodes)
    }
}

impl<T: MoveGenerator> SetOption for StdQsearch<T> {
    fn options() -> Vec<(String, OptionDescription)> {
        T::options()
    }

    fn set_option(name: &str, value: &str) {
        T::set_option(name, value)
    }
}

/// A classical recursive quiescence search implementation.
fn qsearch<T: MoveGenerator>(position: &mut T,
                             mut lower_bound: Value, // alpha
                             upper_bound: Value, // beta
                             mut stand_pat: Value, // position's static evaluation
                             mut recapture_squares: Bitboard,
                             ply: i8, // the reached `qsearch` depth
                             move_stack: &mut MoveStack,
                             searched_nodes: &mut u64)
                             -> Value {
    debug_assert!(lower_bound < upper_bound);
    debug_assert!(stand_pat == VALUE_UNKNOWN ||
                  stand_pat == position.evaluator().evaluate(position.board()));
    const PIECE_VALUES: [Value; 7] = [10000, 975, 500, 325, 325, 100, 0];

    let in_check = position.checkers() != 0;

    // At the beginning of quiescence, position's static evaluation
    // (`stand_pat`) is used to establish a lower bound on the
    // result. We assume that even if none of the forcing moves can
    // improve on the stand pat, there will be at least one "quiet"
    // move that will at least preserve the stand pat value. (Note
    // that this assumption is not true if the the side to move is in
    // check, because in this case all possible check evasions will be
    // tried.)
    if in_check {
        // Position's static evaluation is useless when in check.
        stand_pat = lower_bound;
    } else if stand_pat == VALUE_UNKNOWN {
        stand_pat = position.evaluator().evaluate(position.board());
    }
    if stand_pat >= upper_bound {
        return stand_pat;
    }
    if stand_pat > lower_bound {
        lower_bound = stand_pat;
    }
    let obligatory_material_gain = (lower_bound as isize) - (stand_pat as isize) -
                                   (PIECE_VALUES[KNIGHT] - 4 * PIECE_VALUES[PAWN] / 3) as isize;

    // Generate all forcing moves. (Include checks only during the
    // first ply.)
    move_stack.save();
    position.generate_forcing(ply <= 0, move_stack);

    // Consider the generated moves one by one. See if any of them
    // can raise the lower bound.
    'trymoves: while let Some(m) = move_stack.remove_best() {
        let move_type = m.move_type();
        let dest_square_bb = 1 << m.dest_square();
        let captured_piece = m.captured_piece();

        // Decide whether to try the move. Check evasions,
        // en-passant captures (for them SEE is often wrong), and
        // mandatory recaptures are always tried. (In order to
        // correct SEE errors due to pinned and overloaded pieces,
        // at least one mandatory recapture is always tried at the
        // destination squares of previous moves.) For all other
        // moves, a static exchange evaluation is performed to
        // decide if the move should be tried.
        if !in_check && move_type != MOVE_ENPASSANT && recapture_squares & dest_square_bb == 0 {
            match position.evaluate_move(m) {
                // A losing move -- do not try it.
                x if x < 0 => continue 'trymoves,

                // An even exchange -- try it only during the first few plys.
                0 if ply >= 2 && captured_piece < NO_PIECE => continue 'trymoves,

                // A safe or winning move -- try it always.
                _ => (),
            }
        }

        // Try the move.
        if position.do_move(m).is_some() {
            // If the move does not give check, ensure that
            // the immediate material gain from the move is
            // big enough.
            if position.checkers() == 0 {
                let material_gain = if move_type == MOVE_PROMOTION {
                    PIECE_VALUES[captured_piece] +
                    PIECE_VALUES[Move::piece_from_aux_data(m.aux_data())] -
                    PIECE_VALUES[PAWN]
                } else {
                    PIECE_VALUES[captured_piece]
                };
                if (material_gain as isize) < obligatory_material_gain {
                    position.undo_move(m);
                    continue 'trymoves;
                }
            }

            // Recursively call `qsearch`.
            *searched_nodes += 1;
            let value = -qsearch(position,
                                 -upper_bound,
                                 -lower_bound,
                                 VALUE_UNKNOWN,
                                 recapture_squares ^ dest_square_bb,
                                 ply + 1,
                                 move_stack,
                                 searched_nodes);
            position.undo_move(m);

            // Update the lower bound.
            if value >= upper_bound {
                lower_bound = value;
                break 'trymoves;
            }
            if value > lower_bound {
                lower_bound = value;
            }

            // Mark that a recapture at this square has been tried.
            recapture_squares &= !dest_square_bb;
        }
    }
    move_stack.restore();

    // Return the determined lower bound. (We should make sure
    // that the returned value is between `VALUE_EVAL_MIN` and
    // `VALUE_EVAL_MAX`, regardless of the initial bounds passed
    // to `qsearch`. If we do not take this precautions, the
    // search algorithm will abstain from checkmating the
    // opponent, seeking the huge material gain that `qsearch`
    // promised.)
    match lower_bound {
        x if x < VALUE_EVAL_MIN => VALUE_EVAL_MIN,
        x if x > VALUE_EVAL_MAX => VALUE_EVAL_MAX,
        x => x,
    }
}

#[cfg(test)]
mod tests {
    use chesstypes::*;
    use search::*;
    use search::stock::{MaterialEvaluator, StdMoveGenerator};
    use utils::MoveStack;

    type Pos = StdMoveGenerator<MaterialEvaluator>;

    #[test]
    fn test_qsearch() {
        use super::qsearch;
        let mut s = MoveStack::new();
        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/6k1/6P1/8/6K1 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/6k1/6P1/8/5bK1 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   225);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/5pkp/6P1/5P1P/6K1 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/5pkp/6P1/5PKP/8 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   -100);

        let mut p = Pos::from_board(Board::from_fen("r1bqkbnr/pppp2pp/2n2p2/4p3/2N1P2B/3P1N2/PPP\
                                                     2PPP/R2QKB1R w - - 5 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("r1bqkbnr/pppp2pp/2n2p2/4N3/4P2B/3P1N2/PPP2P\
                                                     PP/R2QKB1R b - - 5 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   -100);

        let mut p = Pos::from_board(Board::from_fen("rn2kbnr/ppppqppp/8/4p3/2N1P1b1/3P1N2/PPP2PP\
                                                     P/R1BKQB1R w - - 5 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/8/7k/7q/7K w - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert!(qsearch(&mut p, -10000, 10000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0) <= -10000);
    }
}
<commit_msg>minor optimization in qsearch<commit_after>//! Implements `StdQsearch` and `StdQsearchResult`.

use std::cell::UnsafeCell;
use std::marker::PhantomData;
use uci::{SetOption, OptionDescription};
use chesstypes::*;
use search::*;
use search::quiescence::*;
use utils::MoveStack;

/// Implements the `QsearchResult` trait.
pub struct StdQsearchResult {
    value: Value,
    searched_nodes: u64,
}

impl QsearchResult for StdQsearchResult {
    #[inline(always)]
    fn new(value: Value, searched_nodes: u64) -> Self {
        debug_assert!(VALUE_EVAL_MIN <= value && value <= VALUE_EVAL_MAX);
        StdQsearchResult {
            value: value,
            searched_nodes: searched_nodes,
        }
    }

    #[inline(always)]
    fn value(&self) -> Value {
        self.value
    }

    #[inline(always)]
    fn searched_nodes(&self) -> u64 {
        self.searched_nodes
    }
}

/// Implements the `Qsearch` trait.
///
/// Performs classical quiescence search with stand pat, delta
/// pruning, static exchange evaluation, check evasions, limited
/// checks and recaptures.
pub struct StdQsearch<T: MoveGenerator> {
    phantom: PhantomData<T>,
}

impl<T: MoveGenerator> Qsearch for StdQsearch<T> {
    type MoveGenerator = T;

    type QsearchResult = StdQsearchResult;

    fn qsearch(params: QsearchParams<Self::MoveGenerator>) -> Self::QsearchResult {
        thread_local!(
            static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
        );
        let mut searched_nodes = 0;
        let value = MOVE_STACK.with(|s| unsafe {
            qsearch(params.position,
                    params.lower_bound,
                    params.upper_bound,
                    params.static_evaluation,
                    0,
                    -params.depth,
                    &mut *s.get(),
                    &mut searched_nodes)
        });
        StdQsearchResult::new(value, searched_nodes)
    }
}

impl<T: MoveGenerator> SetOption for StdQsearch<T> {
    fn options() -> Vec<(String, OptionDescription)> {
        T::options()
    }

    fn set_option(name: &str, value: &str) {
        T::set_option(name, value)
    }
}

/// A classical recursive quiescence search implementation.
fn qsearch<T: MoveGenerator>(position: &mut T,
                             mut lower_bound: Value, // alpha
                             upper_bound: Value, // beta
                             mut stand_pat: Value, // position's static evaluation
                             mut recapture_squares: Bitboard,
                             ply: i8, // the reached `qsearch` depth
                             move_stack: &mut MoveStack,
                             searched_nodes: &mut u64)
                             -> Value {
    debug_assert!(lower_bound < upper_bound);
    debug_assert!(stand_pat == VALUE_UNKNOWN ||
                  stand_pat == position.evaluator().evaluate(position.board()));
    const PIECE_VALUES: [Value; 8] = [10000, 975, 500, 325, 325, 100, 0, 0];

    let in_check = position.checkers() != 0;

    // At the beginning of quiescence, position's static evaluation
    // (`stand_pat`) is used to establish a lower bound on the
    // result. We assume that even if none of the forcing moves can
    // improve on the stand pat, there will be at least one "quiet"
    // move that will at least preserve the stand pat value. (Note
    // that this assumption is not true if the the side to move is in
    // check, because in this case all possible check evasions will be
    // tried.)
    if in_check {
        // Position's static evaluation is useless when in check.
        stand_pat = lower_bound;
    } else if stand_pat == VALUE_UNKNOWN {
        stand_pat = position.evaluator().evaluate(position.board());
    }
    if stand_pat >= upper_bound {
        return stand_pat;
    }
    if stand_pat > lower_bound {
        lower_bound = stand_pat;
    }
    let obligatory_material_gain = (lower_bound as isize) - (stand_pat as isize) -
                                   (PIECE_VALUES[KNIGHT] - 4 * PIECE_VALUES[PAWN] / 3) as isize;

    // Generate all forcing moves. (Include checks only during the
    // first ply.)
    move_stack.save();
    position.generate_forcing(ply <= 0, move_stack);

    // Consider the generated moves one by one. See if any of them
    // can raise the lower bound.
    'trymoves: while let Some(m) = move_stack.remove_best() {
        let move_type = m.move_type();
        let dest_square_bb = 1 << m.dest_square();
        let captured_piece = m.captured_piece();

        // Decide whether to try the move. Check evasions,
        // en-passant captures (for them SEE is often wrong), and
        // mandatory recaptures are always tried. (In order to
        // correct SEE errors due to pinned and overloaded pieces,
        // at least one mandatory recapture is always tried at the
        // destination squares of previous moves.) For all other
        // moves, a static exchange evaluation is performed to
        // decide if the move should be tried.
        if !in_check && move_type != MOVE_ENPASSANT && recapture_squares & dest_square_bb == 0 {
            match position.evaluate_move(m) {
                // A losing move -- do not try it.
                x if x < 0 => continue 'trymoves,

                // An even exchange -- try it only during the first few plys.
                0 if ply >= 2 && captured_piece < NO_PIECE => continue 'trymoves,

                // A safe or winning move -- try it always.
                _ => (),
            }
        }

        // Try the move.
        if position.do_move(m).is_some() {
            // If the move does not give check, ensure that
            // the immediate material gain from the move is
            // big enough.
            if position.checkers() == 0 {
                let material_gain = if move_type == MOVE_PROMOTION {
                    PIECE_VALUES[captured_piece] +
                    PIECE_VALUES[Move::piece_from_aux_data(m.aux_data())] -
                    PIECE_VALUES[PAWN]
                } else {
                    unsafe { *PIECE_VALUES.get_unchecked(captured_piece) }
                };
                if (material_gain as isize) < obligatory_material_gain {
                    position.undo_move(m);
                    continue 'trymoves;
                }
            }

            // Recursively call `qsearch`.
            *searched_nodes += 1;
            let value = -qsearch(position,
                                 -upper_bound,
                                 -lower_bound,
                                 VALUE_UNKNOWN,
                                 recapture_squares ^ dest_square_bb,
                                 ply + 1,
                                 move_stack,
                                 searched_nodes);
            position.undo_move(m);

            // Update the lower bound.
            if value >= upper_bound {
                lower_bound = value;
                break 'trymoves;
            }
            if value > lower_bound {
                lower_bound = value;
            }

            // Mark that a recapture at this square has been tried.
            recapture_squares &= !dest_square_bb;
        }
    }
    move_stack.restore();

    // Return the determined lower bound. (We should make sure
    // that the returned value is between `VALUE_EVAL_MIN` and
    // `VALUE_EVAL_MAX`, regardless of the initial bounds passed
    // to `qsearch`. If we do not take this precautions, the
    // search algorithm will abstain from checkmating the
    // opponent, seeking the huge material gain that `qsearch`
    // promised.)
    match lower_bound {
        x if x < VALUE_EVAL_MIN => VALUE_EVAL_MIN,
        x if x > VALUE_EVAL_MAX => VALUE_EVAL_MAX,
        x => x,
    }
}

#[cfg(test)]
mod tests {
    use chesstypes::*;
    use search::*;
    use search::stock::{MaterialEvaluator, StdMoveGenerator};
    use utils::MoveStack;

    type Pos = StdMoveGenerator<MaterialEvaluator>;

    #[test]
    fn test_qsearch() {
        use super::qsearch;
        let mut s = MoveStack::new();
        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/6k1/6P1/8/6K1 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/6k1/6P1/8/5bK1 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   225);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/5pkp/6P1/5P1P/6K1 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/5pkp/6P1/5PKP/8 b - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   -100);

        let mut p = Pos::from_board(Board::from_fen("r1bqkbnr/pppp2pp/2n2p2/4p3/2N1P2B/3P1N2/PPP\
                                                     2PPP/R2QKB1R w - - 5 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("r1bqkbnr/pppp2pp/2n2p2/4N3/4P2B/3P1N2/PPP2P\
                                                     PP/R2QKB1R b - - 5 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   -100);

        let mut p = Pos::from_board(Board::from_fen("rn2kbnr/ppppqppp/8/4p3/2N1P1b1/3P1N2/PPP2PP\
                                                     P/R1BKQB1R w - - 5 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert_eq!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0),
                   0);

        let mut p = Pos::from_board(Board::from_fen("8/8/8/8/8/7k/7q/7K w - - 0 1")
                                        .ok()
                                        .unwrap())
                        .unwrap();
        assert!(qsearch(&mut p, -10000, 10000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0) <= -10000);
    }
}
<|endoftext|>


<commit_before>#[allow(non_camel_case_types)]
pub mod bindgen;

#[derive(Debug)]
pub struct Cif(bindgen::ffi_cif);

#[derive(Debug)]
pub struct Arg(*mut ::std::os::raw::c_void);

#[derive(Copy, Clone, Debug)]
pub enum Type {
    Void,
    UInt8,
    SInt8,
    UInt16,
    SInt16,
    UInt32,
    SInt32,
    UInt64,
    SInt64,
    Float,
    Double,
    Pointer,
    LongDouble,
    ComplexFloat,
    ComplexDouble,
    ComplexLongDouble,
}

impl Type {
    fn as_ffi_type(self) -> *mut bindgen::ffi_type {
        unsafe {
            match self {
                Type::Void => &mut bindgen::ffi_type_void,
                Type::UInt8 => &mut bindgen::ffi_type_uint8,
                Type::SInt8 => &mut bindgen::ffi_type_sint8,
                Type::UInt16 => &mut bindgen::ffi_type_uint16,
                Type::SInt16 => &mut bindgen::ffi_type_sint16,
                Type::UInt32 => &mut bindgen::ffi_type_uint32,
                Type::SInt32 => &mut bindgen::ffi_type_sint32,
                Type::UInt64 => &mut bindgen::ffi_type_uint64,
                Type::SInt64 => &mut bindgen::ffi_type_sint64,
                Type::Float => &mut bindgen::ffi_type_float,
                Type::Double => &mut bindgen::ffi_type_double,
                Type::Pointer => &mut bindgen::ffi_type_pointer,
                Type::LongDouble => &mut bindgen::ffi_type_longdouble,
                Type::ComplexFloat => &mut bindgen::ffi_type_complex_float,
                Type::ComplexDouble => &mut bindgen::ffi_type_complex_double,
                Type::ComplexLongDouble =>
                    &mut bindgen::ffi_type_complex_double,
            }
        }
    }
}

pub fn arg<T>(r: &T) -> Arg {
    Arg(unsafe { ::std::mem::transmute(r as *const T) })
}

impl Cif {
    pub fn new(args: &[Type], result: Type) -> Self {
        let mut cif: bindgen::ffi_cif = Default::default();
        let mut real_args: Vec<_> =
            args.iter().map(|t| t.as_ffi_type()).collect();

        let result = unsafe {
            bindgen::ffi_prep_cif(&mut cif,
                                  bindgen::FFI_DEFAULT_ABI,
                                  args.len() as u32,
                                  result.as_ffi_type(),
                                  real_args.as_mut_ptr())
        };

        match result {
            bindgen::ffi_status::FFI_OK
                => (),
            bindgen::ffi_status::FFI_BAD_TYPEDEF
                => panic!("FFI_BAD_TYPEDEF"),
            bindgen::ffi_status::FFI_BAD_ABI
                => panic!("FFI_BAD_ABI"),
        }

        Cif(cif)
    }

    pub unsafe fn call<R>(&self, f: usize, values: &[Arg]) -> R {
        use std::mem;

        assert!(self.0.nargs as usize == values.len());

        let mut result: R = mem::zeroed();

        bindgen::ffi_call(
            mem::transmute(&self.0),
            mem::transmute(f),
            mem::transmute(&mut result),
            mem::transmute(values.as_ptr()));

        return result;
    }
}

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

    #[test]
    fn ffi_call() {
        let cif = Cif::new(&[Type::SInt64, Type::SInt64], Type::SInt64);

        let rc = unsafe {
            cif.call(add_it as usize, &[arg(&5i64), arg(&7i64)])
        };

        assert_eq!(12, rc);
    }

    extern "C" fn add_it(n: i64, m: i64) -> i64 {
        return n + m;
    }
}
<commit_msg>wrapping of ffi_cif passes one test.<commit_after>#[allow(non_camel_case_types)]
pub mod bindgen;

#[derive(Debug)]
pub struct Cif(bindgen::ffi_cif);

#[derive(Debug)]
pub struct Arg(*mut ::std::os::raw::c_void);

#[derive(Copy, Clone, Debug)]
pub enum Type {
    Void,
    UInt8,
    SInt8,
    UInt16,
    SInt16,
    UInt32,
    SInt32,
    UInt64,
    SInt64,
    Float,
    Double,
    Pointer,
    LongDouble,
    ComplexFloat,
    ComplexDouble,
    ComplexLongDouble,
}

impl Type {
    fn as_ffi_type(self) -> *mut bindgen::ffi_type {
        unsafe {
            match self {
                Type::Void => &mut bindgen::ffi_type_void,
                Type::UInt8 => &mut bindgen::ffi_type_uint8,
                Type::SInt8 => &mut bindgen::ffi_type_sint8,
                Type::UInt16 => &mut bindgen::ffi_type_uint16,
                Type::SInt16 => &mut bindgen::ffi_type_sint16,
                Type::UInt32 => &mut bindgen::ffi_type_uint32,
                Type::SInt32 => &mut bindgen::ffi_type_sint32,
                Type::UInt64 => &mut bindgen::ffi_type_uint64,
                Type::SInt64 => &mut bindgen::ffi_type_sint64,
                Type::Float => &mut bindgen::ffi_type_float,
                Type::Double => &mut bindgen::ffi_type_double,
                Type::Pointer => &mut bindgen::ffi_type_pointer,
                Type::LongDouble => &mut bindgen::ffi_type_longdouble,
                Type::ComplexFloat => &mut bindgen::ffi_type_complex_float,
                Type::ComplexDouble => &mut bindgen::ffi_type_complex_double,
                Type::ComplexLongDouble =>
                    &mut bindgen::ffi_type_complex_double,
            }
        }
    }
}

pub fn arg<T>(r: &T) -> Arg {
    Arg(unsafe { ::std::mem::transmute(r as *const T) })
}

impl Cif {
    pub fn new(args: &[Type], result: Type) -> Self {
        let mut cif: bindgen::ffi_cif = Default::default();
        let mut real_args: Vec<_> =
            args.iter().map(|t| t.as_ffi_type()).collect();

        let result = unsafe {
            bindgen::ffi_prep_cif(&mut cif,
                                  bindgen::FFI_DEFAULT_ABI,
                                  args.len() as u32,
                                  result.as_ffi_type(),
                                  real_args.as_mut_ptr())
        };

        match result {
            bindgen::ffi_status::FFI_OK
                => (),
            bindgen::ffi_status::FFI_BAD_TYPEDEF
                => panic!("FFI_BAD_TYPEDEF"),
            bindgen::ffi_status::FFI_BAD_ABI
                => panic!("FFI_BAD_ABI"),
        }

        Cif(cif)
    }

    pub unsafe fn call<R>(&self, f: usize, values: &[Arg]) -> R {
        use std::mem;

        assert!(self.0.nargs as usize == values.len());

        let mut result: R = mem::zeroed();

        bindgen::ffi_call(
            mem::transmute(&self.0),
            mem::transmute(f),
            mem::transmute(&mut result),
            mem::transmute(values.as_ptr()));

        return result;
    }
}

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

    #[test]
    fn ffi_call() {
        let cif = Cif::new(&[Type::SInt64, Type::SInt64], Type::SInt64);
        let f   = |m: i64, n: i64| -> i64 {
            unsafe { cif.call(add_it as usize, &[arg(&m), arg(&n)]) }
        };

        assert_eq!(12, f(5, 7));
        assert_eq!(13, f(6, 7));
        assert_eq!(15, f(8, 7));
    }

    extern "C" fn add_it(n: i64, m: i64) -> i64 {
        return n + m;
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use color::ColorAlpha;
use overlay::{Disc, Quad, Renderer, RenderInput, Text, Texture2D, Triangle, Vert};
use scene::Scene;

type Time = f32;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Viewport {
  pub x: f32,
  pub y: f32,
  pub w: f32,
  pub h: f32
}

pub struct GUI<'a> {
  // common
  renderer: Renderer,

  widgets: HashMap<String, Rc<RefCell<Widget<'a>>>>,
}

impl<'a> GUI<'a> {
  pub fn new(viewport: Viewport, scene: &mut Scene) -> Self {
    GUI {
      renderer: Renderer::new(viewport.w.ceil() as u32, viewport.h.ceil() as u32, 1024, 1024, 1024, scene),
      widgets: HashMap::new()
    }
  }

  pub fn add_widget(&mut self, id: &str, widget: &Rc<RefCell<Widget<'a>>>) {
    self.widgets.insert(id.to_owned(), widget.clone());
  }

  pub fn remove_widget(&mut self, id: &str) -> Option<Rc<RefCell<Widget<'a>>>> {
    self.widgets.remove(id)
  }

  //pub fn new_timeline(&mut self, h: f32, progress_color: ColorAlpha, inactive_color: ColorAlpha, dur_sec: f32) -> Rc<RefCell<Timeline>> {
  //  let timeline = Rc::new(RefCell::new(Timeline::new(self.viewport, h, progress_color, inactive_color, dur_sec)));

  //  self.timeline = Some(timeline.clone());
  //  timeline
  //}

  pub fn render(&self) -> &Texture2D {
    let mut tris = Vec::new();
    let mut quads = Vec::new();
    let mut discs = Vec::new();
    let mut texts = Vec::new();

    for widget in self.widgets.values() {
      for prim in &widget.borrow().unwidget() {
        match prim {
          &WidgetPrim::Triangle(ref tri) => tris.push(*tri),
          &WidgetPrim::Quad(ref quad) => quads.push(*quad),
          &WidgetPrim::Disc(ref disc) => discs.push(*disc),
          &WidgetPrim::Text(ref text) => texts.push(*text)
        }
      }
    }

    let render_input = RenderInput::new()
      .triangles(&tris)
      .quads(&quads)
      .discs(&discs)
      .texts(&texts, 1.);

    self.renderer.render(render_input)
  }
}

/// Widget primitives.
///
/// A widget primitive is used as primary a tool to build up widgets. A widget is basically just a
/// sum of widget primitives used to represent it.
pub enum WidgetPrim<'a> {
  Triangle(Triangle),
  Quad(Quad),
  Disc(Disc),
  Text(Text<'a>)
}

pub trait Widget<'a> {
  fn unwidget(&self) -> Vec<WidgetPrim<'a>>;
}

pub struct ProgressBar {
  w: f32,
  progress_quad: Quad,
  inactive_quad: Quad,
  recip_dur_sec: f32,
  listeners: Vec<Rc<RefCell<ProgressBarListener>>>
}

pub enum ProgressBarEvent {
  Set(Time),
  Toggle
}

pub trait ProgressBarListener {
  fn on(&mut self, e: ProgressBarEvent) -> bool;
}

impl ProgressBar {
  pub fn new(w: f32, h: f32, progress_color: ColorAlpha, inactive_color: ColorAlpha, dur_sec: f32) -> Rc<RefCell<Self>> {
    let pcol = *progress_color.as_ref();
    let icol = *inactive_color.as_ref();

    let progress_quad = Quad(
      Vert::new([0., 0., 0.], pcol),
      Vert::new([0., h, 0.], pcol),
      Vert::new([0., 0., 0.], pcol),
      Vert::new([0., h, 0.], pcol)
    );

    let inactive_quad = Quad(
      Vert::new([w, 0., 0.], icol),
      Vert::new([w, h, 0.], icol),
      Vert::new([0., 0., 0.], icol),
      Vert::new([0., h, 0.], icol)
    );

    Rc::new(RefCell::new(ProgressBar {
      w: w,
      progress_quad: progress_quad,
      inactive_quad: inactive_quad,
      recip_dur_sec: 1. / dur_sec,
      listeners: Vec::new()
    }))
  }

  /// Set the cursor (seconds).
  pub fn set(&mut self, cursor: f32) {
    let c = cursor * self.recip_dur_sec * self.w;

    // update the quads
    self.progress_quad.0.pos[0] = c;
    self.progress_quad.1.pos[0] = c;
    self.inactive_quad.2.pos[0] = c;
    self.inactive_quad.3.pos[0] = c;

    for l in &self.listeners {
      l.borrow_mut().on(ProgressBarEvent::Set(cursor));
    }
  }

  pub fn toggle(&mut self) {
    for l in &self.listeners {
      l.borrow_mut().on(ProgressBarEvent::Toggle);
    }
  }
}

impl<'a> Widget<'a> for ProgressBar {
  fn unwidget(&self) -> Vec<WidgetPrim<'a>> {
    vec![WidgetPrim::Quad(self.progress_quad), WidgetPrim::Quad(self.inactive_quad)]
  }
}
<commit_msg>Fixed widgets.<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use color::ColorAlpha;
use overlay::{Disc, Quad, Renderer, RenderInput, Text, Texture2D, Triangle, Vert};
use scene::Scene;

type Time = f32;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Viewport {
  pub x: f32,
  pub y: f32,
  pub w: f32,
  pub h: f32
}

pub struct GUI<'a> {
  // common
  renderer: Renderer,

  widgets: HashMap<String, Box<Widget<'a> + 'a>>,
}

impl<'a> GUI<'a> {
  pub fn new(viewport: Viewport, scene: &mut Scene) -> Self {
    GUI {
      renderer: Renderer::new(viewport.w.ceil() as u32, viewport.h.ceil() as u32, 1024, 1024, 1024, scene),
      widgets: HashMap::new()
    }
  }

  pub fn add_widget<W>(&mut self, id: &str, widget: &W) where W: 'a + Clone + Widget<'a> {
    self.widgets.insert(id.to_owned(), Box::new(widget.clone()));
  }

  pub fn remove_widget(&mut self, id: &str) {
    self.widgets.remove(id);
  }

  pub fn render(&self) -> &Texture2D {
    let mut tris = Vec::new();
    let mut quads = Vec::new();
    let mut discs = Vec::new();
    let mut texts = Vec::new();

    for widget in self.widgets.values() {
      for prim in (*widget).unwidget() {
        match prim {
          WidgetPrim::Triangle(ref tri) => tris.push(*tri),
          WidgetPrim::Quad(ref quad) => quads.push(*quad),
          WidgetPrim::Disc(ref disc) => discs.push(*disc),
          WidgetPrim::Text(ref text) => texts.push(*text)
        }
      }
    }

    let render_input = RenderInput::new()
      .triangles(&tris)
      .quads(&quads)
      .discs(&discs)
      .texts(&texts, 1.);

    self.renderer.render(render_input)
  }
}

/// Widget primitives.
///
/// A widget primitive is used as primary a tool to build up widgets. A widget is basically just a
/// sum of widget primitives used to represent it.
pub enum WidgetPrim<'a> {
  Triangle(Triangle),
  Quad(Quad),
  Disc(Disc),
  Text(Text<'a>)
}

pub trait Widget<'a> {
  fn unwidget(&self) -> Vec<WidgetPrim<'a>>;
}

pub struct ProgressBar {
  w: f32,
  progress_quad: Quad,
  inactive_quad: Quad,
  recip_dur_sec: f32,
  listeners: Vec<Rc<RefCell<ProgressBarListener>>>
}

pub enum ProgressBarEvent {
  Set(Time),
  Toggle
}

pub trait ProgressBarListener {
  fn on(&mut self, e: ProgressBarEvent) -> bool;
}

impl ProgressBar {
  pub fn new(w: f32, h: f32, progress_color: ColorAlpha, inactive_color: ColorAlpha, dur_sec: f32) -> Rc<RefCell<Self>> {
    let pcol = *progress_color.as_ref();
    let icol = *inactive_color.as_ref();

    let progress_quad = Quad(
      Vert::new([0., 0., 0.], pcol),
      Vert::new([0., h, 0.], pcol),
      Vert::new([0., 0., 0.], pcol),
      Vert::new([0., h, 0.], pcol)
    );

    let inactive_quad = Quad(
      Vert::new([w, 0., 0.], icol),
      Vert::new([w, h, 0.], icol),
      Vert::new([0., 0., 0.], icol),
      Vert::new([0., h, 0.], icol)
    );

    Rc::new(RefCell::new(ProgressBar {
      w: w,
      progress_quad: progress_quad,
      inactive_quad: inactive_quad,
      recip_dur_sec: 1. / dur_sec,
      listeners: Vec::new()
    }))
  }

  /// Set the cursor (seconds).
  pub fn set(&mut self, cursor: f32) {
    let c = cursor * self.recip_dur_sec * self.w;

    // update the quads
    self.progress_quad.0.pos[0] = c;
    self.progress_quad.1.pos[0] = c;
    self.inactive_quad.2.pos[0] = c;
    self.inactive_quad.3.pos[0] = c;

    for l in &self.listeners {
      l.borrow_mut().on(ProgressBarEvent::Set(cursor));
    }
  }

  pub fn toggle(&mut self) {
    for l in &self.listeners {
      l.borrow_mut().on(ProgressBarEvent::Toggle);
    }
  }
}

impl<'a> Widget<'a> for Rc<RefCell<ProgressBar>> {
  fn unwidget(&self) -> Vec<WidgetPrim<'a>> {
    let bar = &self.borrow();
    vec![WidgetPrim::Quad(bar.progress_quad), WidgetPrim::Quad(bar.inactive_quad)]
  }
}
<|endoftext|>


<commit_before>#![crate_name = "hiredis"]
#![crate_type = "lib"]
#![feature(globs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]

extern crate libc;

use std::mem::transmute;

pub mod api;

pub struct Reply {
    reply: *const api::Reply
}

pub enum ReplyCode {
    String = 1,
    Array,
    Integer,
    Nil,
    Status,
    Error,
    Unknown
}

impl Reply {
    unsafe fn new(reply: *const api::Reply) -> Reply {
        Reply {
            reply: reply
        }
    }

    pub fn typename(&self) -> ReplyCode {
        unsafe {
            match (*self.reply)._type {
                1 => String,
                2 => Array,
                3 => Integer,
                4 => Nil,
                5 => Status,
                6 => Error,
                _ => Unknown
            }
        }
    }
}

impl Drop for Reply {
    fn drop(&mut self) {
        unsafe {
            api::freeReplyObject(transmute(self.reply))
        }
    }
}

pub struct Redis {
    context: *const api::Context
}

impl Redis {
    pub fn new(ip: &str, port: i32) -> Redis {
        unsafe {
            Redis {
                context: api::redisConnect(
                    ip.to_c_str().as_ptr(),
                    port
                )
            }
        }
    }

    /* TODO: Move to IoResult using context.err */
    pub fn exec(&self, command: &str) -> Option<Reply> {
        command.with_c_str(|v| {
            unsafe {
                let result = api::redisCommand(self.context, v);

                /* Fail if the command errored for some reason. */
                if result == 0 as *const ::libc::c_void {
                    return None;
                }

                /* Otherwise transmute the void pointer memory into a pointer
                 * to a reply structure and return it. */
                Some(Reply::new(transmute(result)))
            }
        })
    }

    pub fn receive(&self) -> Option<Reply> {
        unsafe {
            let reply: Reply = std::mem::uninitialized();
            let result       = api::redisGetReply(self.context, transmute(&reply));

            Some(reply)
        }
    }
}

impl Drop for Redis {
    fn drop(&mut self) {
        unsafe {
            api::redisFree(self.context)
        }
    }
}
<commit_msg>Partially sort out lifetimes for Results<commit_after>#![crate_name = "hiredis"]
#![crate_type = "lib"]
#![feature(globs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]

extern crate libc;

use std::mem::transmute;

pub mod api;

pub struct Reply {
    head:  bool,
    reply: *const api::Reply
}

pub enum ReplyCode {
    String = 1,
    Array,
    Integer,
    Nil,
    Status,
    Error,
    Unknown
}

impl Reply {
    /**
     * Creates a new Reply wrapper.
     *
     * This wraps an already existing Reply returned from hiredis, it will
     * automatically free the data when the reply goes out of scope. Note that
     * this method cannot be invoked outside of the crate.
     */
    fn new(reply: *const api::Reply) -> Reply {
        Reply {
            head:  true,
            reply: reply
        }
    }

    /**
     * Redis replies with the type of data being sent, this method allows
     * peaking into it.
     *
     * TODO: Create a nicer wrapper enum that encodes the data directly with
     * Vectors and so on.
     */
    pub fn typename(&self) -> ReplyCode {
        unsafe {
            match (*self.reply)._type {
                1 => String,
                2 => Array,
                3 => Integer,
                4 => Nil,
                5 => Status,
                6 => Error,
                _ => Unknown
            }
        }
    }

    pub fn string<'r>(&'r self) -> Option<&'r [u8]> {
        match self.typename() {
            String => {
                unsafe {
                    let data = std::c_vec::CVec::new(
                        (*self.reply)._str as *mut u8,
                        (*self.reply).len as uint
                    ).as_mut_slice() as *mut [u8];

                    Some(&*data)
                }
            }

            _ => {
                None
            }
        }
    }
}

impl Drop for Reply {
    fn drop(&mut self) {
        unsafe {
            /* When Redis returns a Reply object, we wrap it in our own Reply
             * object. However, if redis returns an array, the Reply object
             * contains an array of sub Reply objects. These get wrapped too,
             * but should not be freed, as freeing the head object frees the
             * children too. This check prevents us accidentally freeing replies
             * that shouldn't be freed.
             */
            if self.head {
                api::freeReplyObject(transmute(self.reply))
            }
        }
    }
}

pub struct Redis {
    context: *const api::Context
}

impl Redis {
    /**
     * Creates a new connection to a Redis instance.
     *
     * TODO: Deal with the other redisConnect* variants, not yet sure how to
     * design this API.
     */
    pub fn new(ip: &str, port: i32) -> Redis {
        unsafe {
            Redis {
                context: api::redisConnect(
                    ip.to_c_str().as_ptr(),
                    port
                )
            }
        }
    }

    /**
     * Sends a single command to be executed, and returns the reply immediately.
     * If further replies are expected the `receive` function exists to expect
     * them. This function doesn't handle complex spaces in messages, and so the
     * `execf` function exists and uses hiredis's formatting instead.
     *
     * TODO: Move to IoResult using context.err
     */
    pub fn command(&self, command: &str) -> Option<Reply> {
        command.with_c_str(|v| {
            unsafe {
                /* This is the library call, returns (void*)0 on failure. */
                let result = api::redisCommand(self.context, v);

                /* Fail if the command errored for some reason. */
                if result == 0 as *const ::libc::c_void {
                    return None;
                }

                /* Otherwise cast the pointer to the actual struct type being
                 * returned and store it to be returned. */
                Some(Reply::new(result as *const api::Reply))
            }
        })
    }

    /**
     * Appends a command to the output buffer, allowing for pipelining.
     *
     * TODO: This needs to return a status code.
     */
    pub fn append_command(&self, command: &str) {
        command.with_c_str(|v| {
            unsafe {
                api::redisAppendCommand(self.context, v);
            }
        });
    }

    /**
     * Attempts to read a message from the input buffer. Blocking in this
     * context depends on how the connection was created.
     */
    pub fn receive(&self) -> Option<Reply> {
        unsafe {
            let reply: Reply = std::mem::uninitialized();
            let result       = api::redisGetReply(self.context, transmute(&reply));

            Some(reply)
        }
    }
}

impl Drop for Redis {
    /**
     * When a Redis instance goes out of scope, the connection needs to be
     * closed. Redis has a function for this that both closes the connection and
     * frees any resources used.
     */
    fn drop(&mut self) {
        unsafe {
            api::redisFree(self.context)
        }
    }
}
<|endoftext|>