<commit_before>// Copyright (c) IxMilia.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

use std::error;
use std::fmt;
use std::io;
use std::num;

use image;

use crate::CodePair;

#[derive(Debug)]
pub enum DxfError {
    IoError(io::Error),
    ImageError(image::ImageError),
    ParseFloatError(num::ParseFloatError, usize),
    ParseIntError(num::ParseIntError, usize),
    ParseError(usize),
    UnexpectedCode(i32, usize),
    UnexpectedCodePair(CodePair, String),
    UnexpectedByte(u8, usize),
    UnexpectedEndOfInput,
    UnexpectedEnumValue(usize),
    UnexpectedEmptySet,
    ExpectedTableType(usize),
    WrongValueType(usize),
    InvalidBinaryFile,
    MalformedString,
    WrongItemType,
}

impl From<io::Error> for DxfError {
    fn from(ioe: io::Error) -> DxfError {
        DxfError::IoError(ioe)
    }
}

impl From<::image::ImageError> for DxfError {
    fn from(ie: ::image::ImageError) -> DxfError {
        DxfError::ImageError(ie)
    }
}

impl fmt::Display for DxfError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DxfError::IoError(ref e) => write!(formatter, "{}", e),
            DxfError::ImageError(ref e) => write!(formatter, "{}", e),
            DxfError::ParseFloatError(ref e, o) => write!(formatter, "{} at line/offset {}", e, o),
            DxfError::ParseIntError(ref e, o) => write!(formatter, "{} at line/offset {}", e, o),
            DxfError::ParseError(o) => write!(
                formatter,
                "there was a general parsing error at line/offset {}",
                o
            ),
            DxfError::UnexpectedCode(c, o) => write!(
                formatter,
                "an unexpected code '{}' was encountered at line/offset {}",
                c, o
            ),
            DxfError::UnexpectedCodePair(ref cp, ref s) => write!(
                formatter,
                "the code pair '{:?}' was not expected at this time: {} at line/offset {}",
                cp, s, cp.offset
            ),
            DxfError::UnexpectedByte(ref b, o) => write!(
                formatter,
                "the byte '0x{:02x}' was not expected at this time at line/offset {}",
                b, o
            ),
            DxfError::UnexpectedEndOfInput => write!(
                formatter,
                "the input unexpectedly ended before the drawing was completely loaded"
            ),
            DxfError::UnexpectedEnumValue(o) => write!(
                formatter,
                "the specified enum value does not fall into the expected range at line/offset {}",
                o
            ),
            DxfError::UnexpectedEmptySet => {
                write!(formatter, "the set was not expected to be empty")
            }
            DxfError::ExpectedTableType(o) => write!(
                formatter,
                "a 2/<table-type> code pair was expected at line/offset {}",
                o
            ),
            DxfError::WrongValueType(o) => write!(
                formatter,
                "the CodePairValue does not contain the requested type at line/offset {}",
                o
            ),
            DxfError::InvalidBinaryFile => write!(formatter, "the binary file is invalid"),
            DxfError::MalformedString => write!(formatter, "the string is malformed"),
            DxfError::WrongItemType => write!(formatter, "the specified item type is not correct"),
        }
    }
}

impl error::Error for DxfError {
    fn description(&self) -> &str {
        match *self {
            DxfError::IoError(ref e) => e.description(),
            DxfError::ImageError(ref e) => e.description(),
            DxfError::ParseFloatError(ref e, _) => e.description(),
            DxfError::ParseIntError(ref e, _) => e.description(),
            DxfError::ParseError(_) => "there was a general parsing error",
            DxfError::UnexpectedCode(_, _) => "an unexpected code was encountered",
            DxfError::UnexpectedCodePair(_, _) => "an unexpected code pair was encountered",
            DxfError::UnexpectedByte(_, _) => "an unexpected byte was encountered",
            DxfError::UnexpectedEndOfInput => {
                "the input unexpectedly ended before the drawing was completely loaded"
            }
            DxfError::UnexpectedEnumValue(_) => {
                "the specified enum value does not fall into the expected range"
            }
            DxfError::UnexpectedEmptySet => "the set was not expected to be empty",
            DxfError::ExpectedTableType(_) => "a 2/<table-type> code pair was expected",
            DxfError::WrongValueType(_) => "the CodePairValue does not contain the requested type",
            DxfError::InvalidBinaryFile => "the binary file is invalid",
            DxfError::MalformedString => "the string is malformed",
            DxfError::WrongItemType => "the specified item type is not correct",
        }
    }
    fn cause(&self) -> Option<&dyn error::Error> {
        match *self {
            DxfError::IoError(ref e) => Some(e),
            DxfError::ImageError(ref e) => Some(e),
            DxfError::ParseFloatError(ref e, _) => Some(e),
            DxfError::ParseIntError(ref e, _) => Some(e),
            _ => None,
        }
    }
}
<commit_msg>remove unnecessary `Error::description` impl<commit_after>// Copyright (c) IxMilia.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

use std::error;
use std::fmt;
use std::io;
use std::num;

use image;

use crate::CodePair;

#[derive(Debug)]
pub enum DxfError {
    IoError(io::Error),
    ImageError(image::ImageError),
    ParseFloatError(num::ParseFloatError, usize),
    ParseIntError(num::ParseIntError, usize),
    ParseError(usize),
    UnexpectedCode(i32, usize),
    UnexpectedCodePair(CodePair, String),
    UnexpectedByte(u8, usize),
    UnexpectedEndOfInput,
    UnexpectedEnumValue(usize),
    UnexpectedEmptySet,
    ExpectedTableType(usize),
    WrongValueType(usize),
    InvalidBinaryFile,
    MalformedString,
    WrongItemType,
}

impl From<io::Error> for DxfError {
    fn from(ioe: io::Error) -> DxfError {
        DxfError::IoError(ioe)
    }
}

impl From<::image::ImageError> for DxfError {
    fn from(ie: ::image::ImageError) -> DxfError {
        DxfError::ImageError(ie)
    }
}

impl fmt::Display for DxfError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DxfError::IoError(ref e) => write!(formatter, "{}", e),
            DxfError::ImageError(ref e) => write!(formatter, "{}", e),
            DxfError::ParseFloatError(ref e, o) => write!(formatter, "{} at line/offset {}", e, o),
            DxfError::ParseIntError(ref e, o) => write!(formatter, "{} at line/offset {}", e, o),
            DxfError::ParseError(o) => write!(
                formatter,
                "there was a general parsing error at line/offset {}",
                o
            ),
            DxfError::UnexpectedCode(c, o) => write!(
                formatter,
                "an unexpected code '{}' was encountered at line/offset {}",
                c, o
            ),
            DxfError::UnexpectedCodePair(ref cp, ref s) => write!(
                formatter,
                "the code pair '{:?}' was not expected at this time: {} at line/offset {}",
                cp, s, cp.offset
            ),
            DxfError::UnexpectedByte(ref b, o) => write!(
                formatter,
                "the byte '0x{:02x}' was not expected at this time at line/offset {}",
                b, o
            ),
            DxfError::UnexpectedEndOfInput => write!(
                formatter,
                "the input unexpectedly ended before the drawing was completely loaded"
            ),
            DxfError::UnexpectedEnumValue(o) => write!(
                formatter,
                "the specified enum value does not fall into the expected range at line/offset {}",
                o
            ),
            DxfError::UnexpectedEmptySet => {
                write!(formatter, "the set was not expected to be empty")
            }
            DxfError::ExpectedTableType(o) => write!(
                formatter,
                "a 2/<table-type> code pair was expected at line/offset {}",
                o
            ),
            DxfError::WrongValueType(o) => write!(
                formatter,
                "the CodePairValue does not contain the requested type at line/offset {}",
                o
            ),
            DxfError::InvalidBinaryFile => write!(formatter, "the binary file is invalid"),
            DxfError::MalformedString => write!(formatter, "the string is malformed"),
            DxfError::WrongItemType => write!(formatter, "the specified item type is not correct"),
        }
    }
}

impl error::Error for DxfError {
    fn cause(&self) -> Option<&dyn error::Error> {
        match *self {
            DxfError::IoError(ref e) => Some(e),
            DxfError::ImageError(ref e) => Some(e),
            DxfError::ParseFloatError(ref e, _) => Some(e),
            DxfError::ParseIntError(ref e, _) => Some(e),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate rusqlite;
extern crate pibq;

use std::env;
use std::time::{Duration, Instant};
use getopts::Options;

use pibq::bluetherm;
use pibq::sql;
use pibq::models::{ConnectionStatus, Reading};

// interval between sending query packtets, in ms
const QUERY_INTERVAL: u64 = 3000;

// if no packets are returned, amount of time to wait before creating a timeout error, in ms
const TIMEOUT_INTERVAL: u64 = 6500;

const HEARTBEAT_INTERVAL: u64 = 1000;

struct Harvester {
    sql_conn: rusqlite::Connection,
    bt_conn: Option<bluetherm::Connection>,
    serial: String,
    disconnected: bool,
    disconnect_reason: Option<bluetherm::ConnectionEvent>,
    send_interval: Duration,
    timeout_interval: Duration,
    last_send: Option<Instant>,
    last_receive: Option<Instant>
}

impl Harvester {
    fn new(sql_conn: rusqlite::Connection, serial: &str) -> Harvester {
        Harvester {
            sql_conn: sql_conn,
            bt_conn: Some(Harvester::connect_bluetherm(serial)),
            serial: serial.to_string(),
            disconnected: true,
            disconnect_reason: None,
            send_interval: Duration::from_millis(QUERY_INTERVAL),
            timeout_interval: Duration::from_millis(TIMEOUT_INTERVAL),
            last_send: None,
            last_receive: None
        }
    }

    fn connect_bluetherm(serial: &str) -> bluetherm::Connection {
        bluetherm::Connection::new(serial, Some(HEARTBEAT_INTERVAL)).unwrap()
    }

    fn start(&mut self) {
        loop {
            match self.last_send {
                Some(i) if i.elapsed() < self.send_interval => {},
                _ => {
                    self.send_packet();
                    self.last_send = Some(Instant::now());
                }
            }

            let event = self.get_bt_conn().wait().unwrap();

            println!("{}", event);
            match event {
                bluetherm::ConnectionEvent::Packet(p) => {
                    self.record_packet(p);
                    self.last_receive = Some(Instant::now());
                },
                e @ bluetherm::ConnectionEvent::InvalidPacket(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::ReadError(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::BadData(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::Heartbeat => {
                    match self.last_receive {
                        None => {
                            match self.last_send {
                                None => {},
                                Some(sent) => {
                                    if sent.elapsed() > self.timeout_interval {
                                        self.bt_error(e);
                                    }
                                }
                            }
                        },
                        Some(rec) => {
                            if rec.elapsed() > self.timeout_interval {
                                self.bt_error(e);
                            }
                        }
                    }
                }
            }
        }
    }

    fn get_bt_conn(&mut self) -> &mut bluetherm::Connection {
        self.bt_conn.as_mut().unwrap()
    }

    fn record_packet(&mut self, packet: bluetherm::Packet) {
        let mut reading = Reading::new();
        reading.value1 = packet.get_sensor1_reading();
        reading.value2 = packet.get_sensor2_reading();
        sql::insert_reading(&self.sql_conn, &mut reading).unwrap();
    }

    fn send_packet(&mut self) {
        let p = bluetherm::Packet::temp_packet();
        self.get_bt_conn().send(&p).unwrap();
    }

    fn bt_error(&mut self, evt: bluetherm::ConnectionEvent) {
        let mut report_error = true;

        if self.disconnected {
            match &self.disconnect_reason {
                &None => {},
                &Some(ref reason) => {
                    match (reason, &evt) {
                        (&bluetherm::ConnectionEvent::InvalidPacket(_), &bluetherm::ConnectionEvent::InvalidPacket(_)) |
                        (&bluetherm::ConnectionEvent::ReadError(_), &bluetherm::ConnectionEvent::ReadError(_)) |
                        (&bluetherm::ConnectionEvent::BadData(_), &bluetherm::ConnectionEvent::BadData(_)) |
                        (&bluetherm::ConnectionEvent::Heartbeat, &bluetherm::ConnectionEvent::Heartbeat) => {
                            report_error = false;
                        },
                        _ => {},
                    }
                }
            }
        }

        if report_error {
            let msg = match evt {
                bluetherm::ConnectionEvent::InvalidPacket(p) => { format!("Invalid Packet: [{}]", p) },
                bluetherm::ConnectionEvent::ReadError(err) => { format!("Read Error: {}", err) },
                bluetherm::ConnectionEvent::BadData(_) => { format!("Bad Data Read") },
                bluetherm::ConnectionEvent::Heartbeat => { "Timeout".to_string() },
                _ => "Unknown Error".to_string()
            };

            let mut s = ConnectionStatus::new();
            s.is_disconnect = true;
            s.info = Some(msg);
            sql::insert_connection_status(&self.sql_conn, &mut s).unwrap();
        }

        self.disconnected = true;

        println!("error, killing old connection");

        let old = self.bt_conn.take();
        drop(old.unwrap());

        println!("old conneciton dropped; making new");

        self.bt_conn = Some(Harvester::connect_bluetherm(&self.serial));

        println!("new connection made");
    }
}

// fn harvest(sql_conn: rusqlite::Connection, serial: &str) {
//
//     loop {
//         // if disconnected {
//         //     bt_conn = bluetherm::Connection::new(serial, Some(1000)).unwrap();
//         //     let mut s = ConnectionStatus::new();
//         //     s.is_disconnect = true;
//         //     sql::insert_connection_status(&sql_conn, &mut s).unwrap();
//         // }
//
//
//     }
//
// }

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.reqopt("s", "serial", "tty serial device", "DEV");
    opts.optopt("d", "dbfile", "sqlite DB file", "FILE");
    opts.optopt("m", "migrations", "migration folder", "DIR");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            println!("{}", f.to_string());
            print_usage(&program, opts);
            return;
        }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let serial = matches.opt_str("s").unwrap();
    let dbfile = matches.opt_str("d").unwrap_or("pibq.sqlite".to_string());
    let migrations = matches.opt_str("m").unwrap_or("migrations".to_string());

    let db = sql::get_connection(&dbfile, Some(migrations)).unwrap();

    let mut h = Harvester::new(db, &serial);
    h.start();
}
<commit_msg>Fix timeouts<commit_after>extern crate getopts;
extern crate rusqlite;
extern crate pibq;

use std::env;
use std::time::{Duration, Instant};
use getopts::Options;

use pibq::bluetherm;
use pibq::sql;
use pibq::models::{ConnectionStatus, Reading};

// interval between sending query packtets, in ms
const QUERY_INTERVAL: u64 = 3000;

// if no packets are returned, amount of time to wait before creating a timeout error, in ms
const TIMEOUT_INTERVAL: u64 = 6500;

const HEARTBEAT_INTERVAL: u64 = 1000;

struct Harvester {
    sql_conn: rusqlite::Connection,
    bt_conn: Option<bluetherm::Connection>,
    serial: String,
    disconnected: bool,
    disconnect_reason: Option<bluetherm::ConnectionEvent>,
    send_interval: Duration,
    timeout_interval: Duration,
    last_send: Option<Instant>,
    last_receive: Option<Instant>
}

impl Harvester {
    fn new(sql_conn: rusqlite::Connection, serial: &str) -> Harvester {
        Harvester {
            sql_conn: sql_conn,
            bt_conn: Some(Harvester::connect_bluetherm(serial)),
            serial: serial.to_string(),
            disconnected: true,
            disconnect_reason: None,
            send_interval: Duration::from_millis(QUERY_INTERVAL),
            timeout_interval: Duration::from_millis(TIMEOUT_INTERVAL),
            last_send: None,
            last_receive: None
        }
    }

    fn connect_bluetherm(serial: &str) -> bluetherm::Connection {
        bluetherm::Connection::new(serial, Some(HEARTBEAT_INTERVAL)).unwrap()
    }

    fn start(&mut self) {
        loop {
            match self.last_send {
                Some(i) if i.elapsed() < self.send_interval => {},
                _ => {
                    self.send_packet();
                    self.last_send = Some(Instant::now());
                }
            }

            let event = self.get_bt_conn().wait().unwrap();

            println!("{}", event);
            match event {
                bluetherm::ConnectionEvent::Packet(p) => {
                    self.record_packet(p);
                    self.last_receive = Some(Instant::now());
                },
                e @ bluetherm::ConnectionEvent::InvalidPacket(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::ReadError(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::BadData(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::Heartbeat => {
                    match self.last_receive {
                        None => {
                            match self.last_send {
                                None => {},
                                Some(sent) => {
                                    if sent.elapsed() > self.timeout_interval {
                                        self.bt_error(e);
                                    }
                                }
                            }
                        },
                        Some(rec) => {
                            if rec.elapsed() > self.timeout_interval {
                                self.bt_error(e);
                            }
                        }
                    }
                }
            }
        }
    }

    fn get_bt_conn(&mut self) -> &mut bluetherm::Connection {
        self.bt_conn.as_mut().unwrap()
    }

    fn record_packet(&mut self, packet: bluetherm::Packet) {
        let mut reading = Reading::new();
        reading.value1 = packet.get_sensor1_reading();
        reading.value2 = packet.get_sensor2_reading();
        sql::insert_reading(&self.sql_conn, &mut reading).unwrap();
    }

    fn send_packet(&mut self) {
        let p = bluetherm::Packet::temp_packet();
        self.get_bt_conn().send(&p).unwrap();
    }

    fn bt_error(&mut self, evt: bluetherm::ConnectionEvent) {
        let mut report_error = true;

        if self.disconnected {
            match &self.disconnect_reason {
                &None => {},
                &Some(ref reason) => {
                    match (reason, &evt) {
                        (&bluetherm::ConnectionEvent::InvalidPacket(_), &bluetherm::ConnectionEvent::InvalidPacket(_)) |
                        (&bluetherm::ConnectionEvent::ReadError(_), &bluetherm::ConnectionEvent::ReadError(_)) |
                        (&bluetherm::ConnectionEvent::BadData(_), &bluetherm::ConnectionEvent::BadData(_)) |
                        (&bluetherm::ConnectionEvent::Heartbeat, &bluetherm::ConnectionEvent::Heartbeat) => {
                            report_error = false;
                        },
                        _ => {},
                    }
                }
            }
        }

        if report_error {
            let msg = match evt {
                bluetherm::ConnectionEvent::InvalidPacket(p) => { format!("Invalid Packet: [{}]", p) },
                bluetherm::ConnectionEvent::ReadError(err) => { format!("Read Error: {}", err) },
                bluetherm::ConnectionEvent::BadData(_) => { format!("Bad Data Read") },
                bluetherm::ConnectionEvent::Heartbeat => { "Timeout".to_string() },
                _ => "Unknown Error".to_string()
            };

            let mut s = ConnectionStatus::new();
            s.is_disconnect = true;
            s.info = Some(msg);
            sql::insert_connection_status(&self.sql_conn, &mut s).unwrap();
        }

        self.disconnected = true;
        self.last_send = None;
        self.last_receive = None;

        println!("error, killing old connection");

        let old = self.bt_conn.take();
        drop(old.unwrap());

        println!("old conneciton dropped; making new");

        self.bt_conn = Some(Harvester::connect_bluetherm(&self.serial));

        println!("new connection made");
    }
}

// fn harvest(sql_conn: rusqlite::Connection, serial: &str) {
//
//     loop {
//         // if disconnected {
//         //     bt_conn = bluetherm::Connection::new(serial, Some(1000)).unwrap();
//         //     let mut s = ConnectionStatus::new();
//         //     s.is_disconnect = true;
//         //     sql::insert_connection_status(&sql_conn, &mut s).unwrap();
//         // }
//
//
//     }
//
// }

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.reqopt("s", "serial", "tty serial device", "DEV");
    opts.optopt("d", "dbfile", "sqlite DB file", "FILE");
    opts.optopt("m", "migrations", "migration folder", "DIR");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            println!("{}", f.to_string());
            print_usage(&program, opts);
            return;
        }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let serial = matches.opt_str("s").unwrap();
    let dbfile = matches.opt_str("d").unwrap_or("pibq.sqlite".to_string());
    let migrations = matches.opt_str("m").unwrap_or("migrations".to_string());

    let db = sql::get_connection(&dbfile, Some(migrations)).unwrap();

    let mut h = Harvester::new(db, &serial);
    h.start();
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Analyze the request's headers and body.
//!
//! This module provides functions and sub-modules that allow you to easily analyze or parse the
//! request's headers and body.
//!
//! - In order to parse JSON, see [the `json` module](json/input.html).
//! - In order to parse input from HTML forms, see [the `post` module](post/input.html).
//! - In order to read a plain text body, see
//!   [the `plain_text_body` function](fn.plain_text_body.html).

pub use self::basic_http_auth::HttpAuthCredentials;
pub use self::basic_http_auth::basic_http_auth;
pub use self::cookies::cookies;
pub use self::cookies::CookiesIter;
pub use self::json::json_input;
pub use self::plain::plain_text_body;
pub use self::plain::plain_text_body_with_limit;
pub use self::priority_header::parse_priority_header;
pub use self::priority_header::priority_header_preferred;
pub use self::priority_header::PriorityHeaderIter;

pub mod json;
pub mod multipart;
pub mod post;

mod accept;
mod basic_http_auth;
mod cookies;
mod plain;
mod priority_header;
<commit_msg>Fix typo in documentation links<commit_after>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Analyze the request's headers and body.
//!
//! This module provides functions and sub-modules that allow you to easily analyze or parse the
//! request's headers and body.
//!
//! - In order to parse JSON, see [the `json` module](json/index.html).
//! - In order to parse input from HTML forms, see [the `post` module](post/index.html).
//! - In order to read a plain text body, see
//!   [the `plain_text_body` function](fn.plain_text_body.html).

pub use self::basic_http_auth::HttpAuthCredentials;
pub use self::basic_http_auth::basic_http_auth;
pub use self::cookies::cookies;
pub use self::cookies::CookiesIter;
pub use self::json::json_input;
pub use self::plain::plain_text_body;
pub use self::plain::plain_text_body_with_limit;
pub use self::priority_header::parse_priority_header;
pub use self::priority_header::priority_header_preferred;
pub use self::priority_header::PriorityHeaderIter;

pub mod json;
pub mod multipart;
pub mod post;

mod accept;
mod basic_http_auth;
mod cookies;
mod plain;
mod priority_header;
<|endoftext|>


<commit_before>use js::value::{Value, ValueData, VUndefined, VObject, ResultValue, ToValue, FromValue, to_value, from_value};
use collections::treemap::TreeMap;
use std::gc::Gc;
pub static PROTOTYPE: &'static str = "prototype";
pub static INSTANCE_PROTOTYPE: &'static str = "__proto__";
#[deriving(Clone)]
pub type ObjectData = TreeMap<StrBuf, Property>;

#[deriving(Clone)]
/// A Javascript property
pub struct Property {
	/// If the type of this can be changed and this can be deleted
	pub configurable : bool,
	/// If the property shows up in enumeration of the object
	pub enumerable: bool,
	/// If this property can be changed with an assignment
	pub writable: bool,
	/// The value associated with the property
	pub value: Value,
	/// The function serving as getter
	pub get: Value,
	/// The function serving as setter
	pub set: Value
}
impl Property {
	/// Make a new property with the given value
	pub fn new(value : Value) -> Property {
		Property {
			configurable: false,
			enumerable: false,
			writable: false,
			value: value,
			get: Gc::new(VUndefined),
			set: Gc::new(VUndefined)
		}
	}
}

impl ToValue for Property {
	fn to_value(&self) -> Value {
		let prop = ValueData::new_obj(None);
		let prop_ref = prop.borrow();
		prop_ref.set_field_slice("configurable", to_value(self.configurable));
		prop_ref.set_field_slice("enumerable", to_value(self.enumerable));
		prop_ref.set_field_slice("writable", to_value(self.writable));
		prop_ref.set_field_slice("value", self.value);
		prop_ref.set_field_slice("get", self.get);
		prop_ref.set_field_slice("set", self.set);
		prop
	}
}
impl FromValue for Property {
	fn from_value(v:Value) -> Result<Property, &'static str> {
		let vb = v.borrow();
		Ok(Property {
			configurable: from_value(vb.get_field_slice("configurable")).unwrap(),
			enumerable: from_value(vb.get_field_slice("enumerable")).unwrap(),
			writable: from_value(vb.get_field_slice("writable")).unwrap(),
			value: vb.get_field_slice("value"),
			get: vb.get_field_slice("get"),
			set: vb.get_field_slice("set")
		})
	}
}
/// Create new object
pub fn make_object(_:Value, _:Value, _:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VUndefined))
}
/// Get the prototype
pub fn get_proto_of(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let obj = args.get(0);
	Ok(obj.borrow().get_field_slice(INSTANCE_PROTOTYPE))
}
/// Set the prototype
pub fn set_proto_of(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let proto = args.get(1).clone();
	let obj = args.get(0);
	obj.borrow().set_field_slice(INSTANCE_PROTOTYPE, proto);
	Ok(*obj)
}
/// Define the property
pub fn define_prop(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let obj = args.get(0);
	let prop = from_value::<StrBuf>(*args.get(1)).unwrap();
	let desc = from_value::<Property>(*args.get(2)).unwrap();
	obj.borrow().set_prop(prop, desc);
	Ok(Gc::new(VUndefined))
}
/// To string
pub fn to_string(this:Value, _:Value, _:Vec<Value>) -> ResultValue {
	Ok(to_value(this.borrow().to_str().into_strbuf()))
}
/// Check if it has a property
pub fn has_own_prop(this:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let prop = if args.len() == 0 {
		None
	} else {
		from_value::<StrBuf>(*args.get(0)).ok()
	};
	Ok(to_value::<bool>(prop.is_some() && match *this.borrow() {
		VObject(ref obj) => obj.borrow().find(&prop.unwrap()).is_some(),
		_ => false
	}))
}
/// Create a new `Object` object
pub fn _create(global:Value) -> Value {
	let object = to_value(make_object);
	let object_ptr = object.borrow();
	let prototype = ValueData::new_obj(Some(global));
	prototype.borrow().set_field_slice("hasOwnProperty", to_value(has_own_prop));
	prototype.borrow().set_field_slice("toString", to_value(to_string));
	object_ptr.set_field_slice("length", to_value(1i32));
	object_ptr.set_field_slice(PROTOTYPE, prototype);
	object_ptr.set_field_slice("setPrototypeOf", to_value(set_proto_of));
	object_ptr.set_field_slice("getPrototypeOf", to_value(get_proto_of));
	object_ptr.set_field_slice("defineProperty", to_value(define_prop));
	object
}
/// Initialise the `Object` object on the global object
pub fn init(global:Value) {
	let global_ptr = global.borrow();
	global_ptr.set_field_slice("Object", _create(global));
}<commit_msg>Rewrite setPrototypeOf to avoid cloning<commit_after>use js::value::{Value, ValueData, VUndefined, VObject, ResultValue, ToValue, FromValue, to_value, from_value};
use collections::treemap::TreeMap;
use std::gc::Gc;
pub static PROTOTYPE: &'static str = "prototype";
pub static INSTANCE_PROTOTYPE: &'static str = "__proto__";
#[deriving(Clone)]
pub type ObjectData = TreeMap<StrBuf, Property>;

#[deriving(Clone)]
/// A Javascript property
pub struct Property {
	/// If the type of this can be changed and this can be deleted
	pub configurable : bool,
	/// If the property shows up in enumeration of the object
	pub enumerable: bool,
	/// If this property can be changed with an assignment
	pub writable: bool,
	/// The value associated with the property
	pub value: Value,
	/// The function serving as getter
	pub get: Value,
	/// The function serving as setter
	pub set: Value
}
impl Property {
	/// Make a new property with the given value
	pub fn new(value : Value) -> Property {
		Property {
			configurable: false,
			enumerable: false,
			writable: false,
			value: value,
			get: Gc::new(VUndefined),
			set: Gc::new(VUndefined)
		}
	}
}

impl ToValue for Property {
	fn to_value(&self) -> Value {
		let prop = ValueData::new_obj(None);
		let prop_ref = prop.borrow();
		prop_ref.set_field_slice("configurable", to_value(self.configurable));
		prop_ref.set_field_slice("enumerable", to_value(self.enumerable));
		prop_ref.set_field_slice("writable", to_value(self.writable));
		prop_ref.set_field_slice("value", self.value);
		prop_ref.set_field_slice("get", self.get);
		prop_ref.set_field_slice("set", self.set);
		prop
	}
}
impl FromValue for Property {
	fn from_value(v:Value) -> Result<Property, &'static str> {
		let vb = v.borrow();
		Ok(Property {
			configurable: from_value(vb.get_field_slice("configurable")).unwrap(),
			enumerable: from_value(vb.get_field_slice("enumerable")).unwrap(),
			writable: from_value(vb.get_field_slice("writable")).unwrap(),
			value: vb.get_field_slice("value"),
			get: vb.get_field_slice("get"),
			set: vb.get_field_slice("set")
		})
	}
}
/// Create new object
pub fn make_object(_:Value, _:Value, _:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VUndefined))
}
/// Get the prototype
pub fn get_proto_of(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let obj = args.get(0);
	Ok(obj.borrow().get_field_slice(INSTANCE_PROTOTYPE))
}
/// Set the prototype
pub fn set_proto_of(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let obj = *args.get(0);
	let proto = *args.get(1);
	obj.borrow().set_field_slice(INSTANCE_PROTOTYPE, proto);
	Ok(obj)
}
/// Define the property
pub fn define_prop(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let obj = args.get(0);
	let prop = from_value::<StrBuf>(*args.get(1)).unwrap();
	let desc = from_value::<Property>(*args.get(2)).unwrap();
	obj.borrow().set_prop(prop, desc);
	Ok(Gc::new(VUndefined))
}
/// To string
pub fn to_string(this:Value, _:Value, _:Vec<Value>) -> ResultValue {
	Ok(to_value(this.borrow().to_str().into_strbuf()))
}
/// Check if it has a property
pub fn has_own_prop(this:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let prop = if args.len() == 0 {
		None
	} else {
		from_value::<StrBuf>(*args.get(0)).ok()
	};
	Ok(to_value::<bool>(prop.is_some() && match *this.borrow() {
		VObject(ref obj) => obj.borrow().find(&prop.unwrap()).is_some(),
		_ => false
	}))
}
/// Create a new `Object` object
pub fn _create(global:Value) -> Value {
	let object = to_value(make_object);
	let object_ptr = object.borrow();
	let prototype = ValueData::new_obj(Some(global));
	prototype.borrow().set_field_slice("hasOwnProperty", to_value(has_own_prop));
	prototype.borrow().set_field_slice("toString", to_value(to_string));
	object_ptr.set_field_slice("length", to_value(1i32));
	object_ptr.set_field_slice(PROTOTYPE, prototype);
	object_ptr.set_field_slice("setPrototypeOf", to_value(set_proto_of));
	object_ptr.set_field_slice("getPrototypeOf", to_value(get_proto_of));
	object_ptr.set_field_slice("defineProperty", to_value(define_prop));
	object
}
/// Initialise the `Object` object on the global object
pub fn init(global:Value) {
	let global_ptr = global.borrow();
	global_ptr.set_field_slice("Object", _create(global));
}<|endoftext|>


<commit_before>// requires nightly toolchain!
//
// compile-flags:-C panic=abort
#![no_std]
#![feature(alloc, lang_items, start, core_intrinsics, oom, panic_implementation)]
#![allow(dead_code)]
use core::intrinsics;
use core::panic::PanicInfo;

// Pull in the system libc library for what crt0.o likely requires.
// extern crate libc;

#[macro_use]
extern crate derive_builder;
extern crate alloc;

#[derive(Builder)]
#[builder(no_std)]
struct IgnoreEmptyStruct {}

#[derive(Builder, PartialEq, Debug)]
#[builder(no_std)]
struct Foo {
    #[builder(default)]
    defaulted: u32,
    #[builder(setter(skip), try_setter)]
    skipped: u32,
}

fn main() {
    let foo = FooBuilder::default()
        .build()
        .unwrap();

    assert_eq!(foo, Foo {
        defaulted: 0,
        skipped: 0,
    })
}

///////////////////////////////////////////////////////////////
// some no_std-boilerplate
// from https://doc.rust-lang.org/book/no-stdlib.html
///////////////////////////////////////////////////////////////

// These functions and traits are used by the compiler, but not
// for a bare-bones hello world. These are normally
// provided by libstd.
#[lang = "eh_personality"]
#[no_mangle]
pub extern  fn eh_personality() {}

// This function may be needed based on the compilation target.
#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern fn rust_eh_unwind_resume() {
}

#[panic_implementation]
#[no_mangle]
fn panic(_info: &PanicInfo) -> ! {
    unsafe { intrinsics::abort() }
}

#[lang = "oom"]
#[no_mangle]
pub extern fn rust_oom() {
    unsafe { intrinsics::abort() }
}

#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
    unsafe { intrinsics::abort() }
}

// Entry point for this program
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    main();
    0
}
<commit_msg>Update no_std to use new oom lang item<commit_after>// requires nightly toolchain!
//
// compile-flags:-C panic=abort
#![no_std]
#![feature(alloc, allocator_api, lang_items, start, core_intrinsics, oom, panic_implementation)]
#![allow(dead_code)]
use core::intrinsics;
use core::panic::PanicInfo;

// Pull in the system libc library for what crt0.o likely requires.
// extern crate libc;

#[macro_use]
extern crate derive_builder;
extern crate alloc;

#[derive(Builder)]
#[builder(no_std)]
struct IgnoreEmptyStruct {}

#[derive(Builder, PartialEq, Debug)]
#[builder(no_std)]
struct Foo {
    #[builder(default)]
    defaulted: u32,
    #[builder(setter(skip), try_setter)]
    skipped: u32,
}

fn main() {
    let foo = FooBuilder::default()
        .build()
        .unwrap();

    assert_eq!(foo, Foo {
        defaulted: 0,
        skipped: 0,
    })
}

///////////////////////////////////////////////////////////////
// some no_std-boilerplate
// from https://doc.rust-lang.org/book/no-stdlib.html
///////////////////////////////////////////////////////////////

// These functions and traits are used by the compiler, but not
// for a bare-bones hello world. These are normally
// provided by libstd.
#[lang = "eh_personality"]
#[no_mangle]
pub extern  fn eh_personality() {}

// This function may be needed based on the compilation target.
#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern fn rust_eh_unwind_resume() {
}

#[panic_implementation]
#[no_mangle]
fn panic(_info: &PanicInfo) -> ! {
    unsafe { intrinsics::abort() }
}

#[lang = "oom"]
#[no_mangle]
pub extern fn rust_oom(_: core::alloc::Layout) -> ! {
    unsafe { intrinsics::abort() }
}

#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
    unsafe { intrinsics::abort() }
}

// Entry point for this program
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    main();
    0
}
<|endoftext|>


<commit_before>use crate::err::{self, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use snafu::{ensure, OptionExt, ResultExt};
use std::collections::HashMap;
use std::fs;
use std::ops::Deref;
use std::path::Path;

#[cfg(feature = "rusqlite-support")]
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Value, ValueRef};

/// A regex pattern to parse episode files.
#[derive(Debug, Default)]
pub struct EpisodeMatcher(Option<Regex>);

impl EpisodeMatcher {
    /// The default regex pattern to match episodes in several common formats, such as:
    ///
    /// * [Group] Series Name - 01.mkv
    /// * [Group]_Series_Name_-_01.mkv
    /// * [Group].Series.Name.-.01.mkv
    /// * [Group] Series Name - 01 [tag 1][tag 2].mkv
    /// * [Group]_Series_Name_-_01_[tag1][tag2].mkv
    /// * [Group].Series.Name.-.01.[tag1][tag2].mkv
    /// * Series Name - 01.mkv
    /// * Series_Name_-_01.mkv
    /// * Series.Name.-.01.mkv
    pub const DEFAULT_PATTERN: &'static str = r"(?:\[.+?\](?:_+|\.+|\s*))?(?P<title>.+)(?:\s*|_*|\.*)(?:-|\.|_).*?(?P<episode>\d+)(?:\s*?\(|\s*?\[|\.mkv|\.mp4|\.avi)";

    /// Create a new `EpisodeMatcher` with the default matcher.
    #[inline]
    pub fn new() -> EpisodeMatcher {
        EpisodeMatcher(None)
    }

    /// Create a new `EpisodeMatcher` with a specified regex pattern.
    ///
    /// The pattern must have 2 groups named `title` and `episode`. If they
    /// are not present, a `MissingCustomMatcherGroup` error will be returned.
    ///
    /// # Example
    ///
    /// ```
    /// use anime::local::EpisodeMatcher;
    ///
    /// let pattern = r"(?P<title>.+?) - (?P<episode>\d+)";
    /// let matcher = EpisodeMatcher::from_pattern(pattern).unwrap();
    ///
    /// assert_eq!(matcher.get().as_str(), pattern);
    /// ```
    #[inline]
    pub fn from_pattern<S>(pattern: S) -> Result<EpisodeMatcher>
    where
        S: AsRef<str>,
    {
        let pattern = pattern.as_ref();

        ensure!(
            pattern.contains("(?P<title>"),
            err::MissingCustomMatcherGroup { group: "title" }
        );

        ensure!(
            pattern.contains("(?P<episode>"),
            err::MissingCustomMatcherGroup { group: "episode" }
        );

        let regex = Regex::new(pattern).context(err::Regex { pattern })?;
        Ok(EpisodeMatcher(Some(regex)))
    }

    /// Returns a reference to the inner `Regex` for the `EpisodeMatcher`.
    ///
    /// # Example
    ///
    /// ```
    /// use anime::local::EpisodeMatcher;
    ///
    /// let default_matcher = EpisodeMatcher::new();
    /// let custom_matcher = EpisodeMatcher::from_pattern(r"(?P<title>.+?) - (?P<episode>\d+)").unwrap();
    ///
    /// assert_eq!(default_matcher.get().as_str(), EpisodeMatcher::DEFAULT_PATTERN);
    /// assert_eq!(custom_matcher.get().as_str(), r"(?P<title>.+?) - (?P<episode>\d+)");
    /// ```
    #[inline]
    pub fn get(&self) -> &Regex {
        static DEFAULT_MATCHER: Lazy<Regex> =
            Lazy::new(|| Regex::new(EpisodeMatcher::DEFAULT_PATTERN).unwrap());

        match &self.0 {
            Some(matcher) => matcher,
            None => &DEFAULT_MATCHER,
        }
    }
}

#[cfg(feature = "rusqlite-support")]
impl FromSql for EpisodeMatcher {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        use std::str;

        match value {
            ValueRef::Null => Ok(EpisodeMatcher::new()),
            ValueRef::Text(bytes) => {
                let pattern =
                    str::from_utf8(bytes).map_err(|err| FromSqlError::Other(Box::new(err)))?;

                let matcher = EpisodeMatcher::from_pattern(pattern)
                    .map_err(|err| FromSqlError::Other(Box::new(err)))?;

                Ok(matcher)
            }
            _ => Err(FromSqlError::InvalidType),
        }
    }
}

#[cfg(feature = "rusqlite-support")]
impl ToSql for EpisodeMatcher {
    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
        let result = match &self.0 {
            Some(matcher) => matcher.as_str().as_bytes().into(),
            None => ToSqlOutput::Owned(Value::Null),
        };

        Ok(result)
    }
}

/// Episode of an anime series on disk.
#[derive(Debug)]
pub struct Episode {
    /// The detected title of the anime series.
    pub series_name: String,
    /// The detected episode number.
    pub num: u32,
}

impl Episode {
    pub fn parse<'a, S>(name: S, matcher: &EpisodeMatcher) -> Result<Episode>
    where
        S: AsRef<str> + Into<String> + 'a,
    {
        let name = name.as_ref();

        let caps = matcher
            .get()
            .captures(name)
            .context(err::NoEpMatches { name })?;

        let series_name = caps
            .name("title")
            .context(err::NoEpisodeTitle { name })?
            .as_str()
            .trim()
            .to_string();

        let num = caps
            .name("episode")
            .and_then(|val| val.as_str().parse::<u32>().ok())
            .context(err::ExpectedEpNumber { name })?;

        Ok(Episode { series_name, num })
    }
}

/// A mapping between episode numbers and their filename.
#[derive(Debug, Default)]
pub struct EpisodeMap(HashMap<u32, String>);

impl EpisodeMap {
    /// Detect all episodes in `dir` with the specified `matcher` and return them in a new `EpisodeMap`.
    pub fn parse<P>(dir: P, matcher: &EpisodeMatcher) -> Result<EpisodeMap>
    where
        P: AsRef<Path>,
    {
        let dir = dir.as_ref();
        let entries = fs::read_dir(dir).context(err::FileIO { path: dir })?;
        let mut results = HashMap::new();
        let mut last_title: Option<String> = None;

        for entry in entries {
            let entry = entry.context(err::EntryIO { dir })?;
            let entry_type = entry.file_type().context(err::EntryIO { dir })?;

            if entry_type.is_dir() {
                continue;
            }

            let filename = entry.file_name();
            let filename = filename.to_string_lossy();

            // The .part extension is commonly used to indicate that a file is incomplete
            if filename.ends_with(".part") {
                continue;
            }

            let episode = Episode::parse(filename.as_ref(), matcher)?;

            match &mut last_title {
                Some(last_title) => ensure!(
                    *last_title == episode.series_name,
                    err::MultipleTitles {
                        expecting: last_title.clone(),
                        found: episode.series_name
                    }
                ),
                None => last_title = Some(episode.series_name.clone()),
            }

            results.insert(episode.num, filename.into_owned());
        }

        Ok(EpisodeMap(results))
    }
}

impl Deref for EpisodeMap {
    type Target = HashMap<u32, String>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

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

    #[test]
    #[should_panic]
    fn episode_matcher_detect_no_group() {
        EpisodeMatcher::from_pattern("useless").unwrap();
    }

    #[test]
    #[should_panic]
    fn episode_matcher_detect_no_title_group() {
        EpisodeMatcher::from_pattern(r"(.+?) - (?P<episode>\d+)").unwrap();
    }

    #[test]
    #[should_panic]
    fn episode_matcher_detect_no_episode_group() {
        EpisodeMatcher::from_pattern(r"(?P<title>.+?) - \d+").unwrap();
    }
}
<commit_msg>anime: also parse blobs when parsing an EpisodeMatcher from SQLite<commit_after>use crate::err::{self, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use snafu::{ensure, OptionExt, ResultExt};
use std::collections::HashMap;
use std::fs;
use std::ops::Deref;
use std::path::Path;

#[cfg(feature = "rusqlite-support")]
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Value, ValueRef};

/// A regex pattern to parse episode files.
#[derive(Debug, Default)]
pub struct EpisodeMatcher(Option<Regex>);

impl EpisodeMatcher {
    /// The default regex pattern to match episodes in several common formats, such as:
    ///
    /// * [Group] Series Name - 01.mkv
    /// * [Group]_Series_Name_-_01.mkv
    /// * [Group].Series.Name.-.01.mkv
    /// * [Group] Series Name - 01 [tag 1][tag 2].mkv
    /// * [Group]_Series_Name_-_01_[tag1][tag2].mkv
    /// * [Group].Series.Name.-.01.[tag1][tag2].mkv
    /// * Series Name - 01.mkv
    /// * Series_Name_-_01.mkv
    /// * Series.Name.-.01.mkv
    pub const DEFAULT_PATTERN: &'static str = r"(?:\[.+?\](?:_+|\.+|\s*))?(?P<title>.+)(?:\s*|_*|\.*)(?:-|\.|_).*?(?P<episode>\d+)(?:\s*?\(|\s*?\[|\.mkv|\.mp4|\.avi)";

    /// Create a new `EpisodeMatcher` with the default matcher.
    #[inline]
    pub fn new() -> EpisodeMatcher {
        EpisodeMatcher(None)
    }

    /// Create a new `EpisodeMatcher` with a specified regex pattern.
    ///
    /// The pattern must have 2 groups named `title` and `episode`. If they
    /// are not present, a `MissingCustomMatcherGroup` error will be returned.
    ///
    /// # Example
    ///
    /// ```
    /// use anime::local::EpisodeMatcher;
    ///
    /// let pattern = r"(?P<title>.+?) - (?P<episode>\d+)";
    /// let matcher = EpisodeMatcher::from_pattern(pattern).unwrap();
    ///
    /// assert_eq!(matcher.get().as_str(), pattern);
    /// ```
    #[inline]
    pub fn from_pattern<S>(pattern: S) -> Result<EpisodeMatcher>
    where
        S: AsRef<str>,
    {
        let pattern = pattern.as_ref();

        ensure!(
            pattern.contains("(?P<title>"),
            err::MissingCustomMatcherGroup { group: "title" }
        );

        ensure!(
            pattern.contains("(?P<episode>"),
            err::MissingCustomMatcherGroup { group: "episode" }
        );

        let regex = Regex::new(pattern).context(err::Regex { pattern })?;
        Ok(EpisodeMatcher(Some(regex)))
    }

    /// Returns a reference to the inner `Regex` for the `EpisodeMatcher`.
    ///
    /// # Example
    ///
    /// ```
    /// use anime::local::EpisodeMatcher;
    ///
    /// let default_matcher = EpisodeMatcher::new();
    /// let custom_matcher = EpisodeMatcher::from_pattern(r"(?P<title>.+?) - (?P<episode>\d+)").unwrap();
    ///
    /// assert_eq!(default_matcher.get().as_str(), EpisodeMatcher::DEFAULT_PATTERN);
    /// assert_eq!(custom_matcher.get().as_str(), r"(?P<title>.+?) - (?P<episode>\d+)");
    /// ```
    #[inline]
    pub fn get(&self) -> &Regex {
        static DEFAULT_MATCHER: Lazy<Regex> =
            Lazy::new(|| Regex::new(EpisodeMatcher::DEFAULT_PATTERN).unwrap());

        match &self.0 {
            Some(matcher) => matcher,
            None => &DEFAULT_MATCHER,
        }
    }
}

#[cfg(feature = "rusqlite-support")]
impl FromSql for EpisodeMatcher {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        use std::str;

        match value {
            ValueRef::Null => Ok(EpisodeMatcher::new()),
            ValueRef::Text(bytes) | ValueRef::Blob(bytes) => {
                let pattern =
                    str::from_utf8(bytes).map_err(|err| FromSqlError::Other(Box::new(err)))?;

                let matcher = EpisodeMatcher::from_pattern(pattern)
                    .map_err(|err| FromSqlError::Other(Box::new(err)))?;

                Ok(matcher)
            }
            _ => Err(FromSqlError::InvalidType),
        }
    }
}

#[cfg(feature = "rusqlite-support")]
impl ToSql for EpisodeMatcher {
    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
        let result = match &self.0 {
            Some(matcher) => matcher.as_str().as_bytes().into(),
            None => ToSqlOutput::Owned(Value::Null),
        };

        Ok(result)
    }
}

/// Episode of an anime series on disk.
#[derive(Debug)]
pub struct Episode {
    /// The detected title of the anime series.
    pub series_name: String,
    /// The detected episode number.
    pub num: u32,
}

impl Episode {
    pub fn parse<'a, S>(name: S, matcher: &EpisodeMatcher) -> Result<Episode>
    where
        S: AsRef<str> + Into<String> + 'a,
    {
        let name = name.as_ref();

        let caps = matcher
            .get()
            .captures(name)
            .context(err::NoEpMatches { name })?;

        let series_name = caps
            .name("title")
            .context(err::NoEpisodeTitle { name })?
            .as_str()
            .trim()
            .to_string();

        let num = caps
            .name("episode")
            .and_then(|val| val.as_str().parse::<u32>().ok())
            .context(err::ExpectedEpNumber { name })?;

        Ok(Episode { series_name, num })
    }
}

/// A mapping between episode numbers and their filename.
#[derive(Debug, Default)]
pub struct EpisodeMap(HashMap<u32, String>);

impl EpisodeMap {
    /// Detect all episodes in `dir` with the specified `matcher` and return them in a new `EpisodeMap`.
    pub fn parse<P>(dir: P, matcher: &EpisodeMatcher) -> Result<EpisodeMap>
    where
        P: AsRef<Path>,
    {
        let dir = dir.as_ref();
        let entries = fs::read_dir(dir).context(err::FileIO { path: dir })?;
        let mut results = HashMap::new();
        let mut last_title: Option<String> = None;

        for entry in entries {
            let entry = entry.context(err::EntryIO { dir })?;
            let entry_type = entry.file_type().context(err::EntryIO { dir })?;

            if entry_type.is_dir() {
                continue;
            }

            let filename = entry.file_name();
            let filename = filename.to_string_lossy();

            // The .part extension is commonly used to indicate that a file is incomplete
            if filename.ends_with(".part") {
                continue;
            }

            let episode = Episode::parse(filename.as_ref(), matcher)?;

            match &mut last_title {
                Some(last_title) => ensure!(
                    *last_title == episode.series_name,
                    err::MultipleTitles {
                        expecting: last_title.clone(),
                        found: episode.series_name
                    }
                ),
                None => last_title = Some(episode.series_name.clone()),
            }

            results.insert(episode.num, filename.into_owned());
        }

        Ok(EpisodeMap(results))
    }
}

impl Deref for EpisodeMap {
    type Target = HashMap<u32, String>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

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

    #[test]
    #[should_panic]
    fn episode_matcher_detect_no_group() {
        EpisodeMatcher::from_pattern("useless").unwrap();
    }

    #[test]
    #[should_panic]
    fn episode_matcher_detect_no_title_group() {
        EpisodeMatcher::from_pattern(r"(.+?) - (?P<episode>\d+)").unwrap();
    }

    #[test]
    #[should_panic]
    fn episode_matcher_detect_no_episode_group() {
        EpisodeMatcher::from_pattern(r"(?P<title>.+?) - \d+").unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::vga::color::ColorCode;
use crate::vga::VGA_BUFFER;
use core::fmt;
use volatile::Volatile;

pub const BUFFER_HEIGHT: usize = 25;
pub const BUFFER_WIDTH: usize = 80;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct ScreenChar {
    ascii_char: u8,
    color: ColorCode,
}

impl ScreenChar {
    pub fn new(ascii_char: u8, color: ColorCode) -> Self {
        ScreenChar { ascii_char, color }
    }
}

impl Default for ScreenChar {
    fn default() -> Self {
        ScreenChar::new(b' ', ColorCode::default())
    }
}

pub struct Buffer {
    chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

pub struct Writer {
    column: usize,
    color: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    pub fn new(color: ColorCode) -> Self {
        Writer {
            column: 0,
            color,
            buffer: unsafe { &mut *(VGA_BUFFER as *mut Buffer) },
        }
    }
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column;

                let color = self.color;
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_char: byte,
                    color,
                });
                self.column += 1;
            }
        }
    }

    fn new_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let character = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(character);
            }
        }
        self.clear_row(BUFFER_HEIGHT - 1);
        self.column = 0;
    }

    fn clear_row(&mut self, row: usize) {
        let blank = ScreenChar::new(b' ', self.color);
        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[row][col].write(blank);
        }
    }
}

impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for byte in s.bytes() {
            match byte {
                // Printable
                0x20...0x7e | b'\n' => self.write_byte(byte),
                // Not printable
                _ => self.write_byte(0xfe),
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use array_init::array_init;
    use std::boxed::Box;
    use volatile::Volatile;

    fn construct_buffer() -> Buffer {
        Buffer {
            chars: array_init(|_| array_init(|_| Volatile::new(ScreenChar::default()))),
        }
    }

    fn construct_writer() -> Writer {
        let buffer = construct_buffer();
        Writer {
            column: 0,
            color: ColorCode::default(),
            buffer: Box::leak(Box::new(buffer)),
        }
    }

    #[test]
    fn write_byte() {
        let mut writer = construct_writer();
        writer.write_byte(b'X');
        writer.write_byte(b'Y');

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 1 && j == 0 {
                    assert_eq!(screen_char.ascii_char, b'X');
                    assert_eq!(screen_char.color, writer.color);
                } else if i == BUFFER_HEIGHT - 1 && j == 1 {
                    assert_eq!(screen_char.ascii_char, b'Y');
                    assert_eq!(screen_char.color, writer.color);
                } else {
                    assert_eq!(screen_char, ScreenChar::default());
                }
            }
        }
    }
}
<commit_msg>Added formatting test<commit_after>use crate::vga::color::ColorCode;
use crate::vga::VGA_BUFFER;
use core::fmt;
use volatile::Volatile;

pub const BUFFER_HEIGHT: usize = 25;
pub const BUFFER_WIDTH: usize = 80;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct ScreenChar {
    ascii_char: u8,
    color: ColorCode,
}

impl ScreenChar {
    pub fn new(ascii_char: u8, color: ColorCode) -> Self {
        ScreenChar { ascii_char, color }
    }
}

impl Default for ScreenChar {
    fn default() -> Self {
        ScreenChar::new(b' ', ColorCode::default())
    }
}

pub struct Buffer {
    chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

pub struct Writer {
    column: usize,
    color: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    pub fn new(color: ColorCode) -> Self {
        Writer {
            column: 0,
            color,
            buffer: unsafe { &mut *(VGA_BUFFER as *mut Buffer) },
        }
    }
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column;

                let color = self.color;
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_char: byte,
                    color,
                });
                self.column += 1;
            }
        }
    }

    fn new_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let character = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(character);
            }
        }
        self.clear_row(BUFFER_HEIGHT - 1);
        self.column = 0;
    }

    fn clear_row(&mut self, row: usize) {
        let blank = ScreenChar::new(b' ', self.color);
        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[row][col].write(blank);
        }
    }
}

impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for byte in s.bytes() {
            match byte {
                // Printable
                0x20...0x7e | b'\n' => self.write_byte(byte),
                // Not printable
                _ => self.write_byte(0xfe),
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use array_init::array_init;
    use std::boxed::Box;
    use volatile::Volatile;

    fn construct_buffer() -> Buffer {
        Buffer {
            chars: array_init(|_| array_init(|_| Volatile::new(ScreenChar::default()))),
        }
    }

    fn construct_writer() -> Writer {
        let buffer = construct_buffer();
        Writer {
            column: 0,
            color: ColorCode::default(),
            buffer: Box::leak(Box::new(buffer)),
        }
    }

    #[test]
    fn write_byte() {
        let mut writer = construct_writer();
        writer.write_byte(b'X');
        writer.write_byte(b'Y');

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 1 && j == 0 {
                    assert_eq!(screen_char.ascii_char, b'X');
                    assert_eq!(screen_char.color, writer.color);
                } else if i == BUFFER_HEIGHT - 1 && j == 1 {
                    assert_eq!(screen_char.ascii_char, b'Y');
                    assert_eq!(screen_char.color, writer.color);
                } else {
                    assert_eq!(screen_char, ScreenChar::default());
                }
            }
        }
    }

    #[test]
    fn write_formatted() {
        use core::fmt::Write;

        let mut writer = construct_writer();
        writeln!(&mut writer, "a").unwrap();
        writeln!(&mut writer, "b{}", "c").unwrap();

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 3 && j == 0 {
                    assert_eq!(screen_char.ascii_char, b'a');
                    assert_eq!(screen_char.color, writer.color);
                } else if i == BUFFER_HEIGHT - 2 && j == 0 {
                    assert_eq!(screen_char.ascii_char, b'b');
                    assert_eq!(screen_char.color, writer.color);
                } else if i == BUFFER_HEIGHT - 2 && j == 1 {
                    assert_eq!(screen_char.ascii_char, b'c');
                    assert_eq!(screen_char.color, writer.color);
                } else if i >= BUFFER_HEIGHT - 2 {
                    assert_eq!(screen_char.ascii_char, b' ');
                    assert_eq!(screen_char.color, writer.color);
                } else {
                    assert_eq!(screen_char, ScreenChar::default());
                }
            }
        }
    }
}
<|endoftext|>


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

use app_units::Au;
use std::collections::HashMap;
use webrender_traits::{FontKey, ColorU, FontRenderMode, GlyphDimensions, GlyphOptions};
use gamma_lut::{GammaLut, Color as ColorLut};

use dwrote;

lazy_static! {
    static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
        family_name: "Arial".to_owned(),
        weight: dwrote::FontWeight::Regular,
        stretch: dwrote::FontStretch::Normal,
        style: dwrote::FontStyle::Normal,
    };
}

#[allow(dead_code)]
pub struct FontContext {
    fonts: HashMap<FontKey, dwrote::FontFace>,
    gamma_lut: GammaLut,
    gdi_gamma_lut: GammaLut,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn dwrite_texture_type(render_mode: FontRenderMode) ->
                       dwrote::DWRITE_TEXTURE_TYPE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
    }
}

fn dwrite_measure_mode(render_mode: FontRenderMode) ->
                       dwrote::DWRITE_MEASURING_MODE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
        FontRenderMode::Alpha => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
        FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC,
    }
}

fn dwrite_render_mode(render_mode: FontRenderMode) ->
                      dwrote::DWRITE_RENDERING_MODE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
        FontRenderMode::Alpha => dwrote::DWRITE_RENDERING_MODE_GDI_NATURAL,
        FontRenderMode::Subpixel => dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC,
    }
}

fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
                                      texture_type: dwrote::DWRITE_TEXTURE_TYPE)
                                      -> GlyphDimensions {
    let bounds = analysis.get_alpha_texture_bounds(texture_type);

    let width = (bounds.right - bounds.left) as u32;
    let height = (bounds.bottom - bounds.top) as u32;
    GlyphDimensions {
        left: bounds.left,
        top: -bounds.top,
        width: width,
        height: height,
    }
}

impl FontContext {
    pub fn new() -> FontContext {
        // These are the default values we use in Gecko.
        // We use a gamma value of 2.3 for gdi fonts
        // TODO: Fetch this data from Gecko itself.
        let contrast = 1.0;
        let gamma = 1.8;
        let gdi_gamma = 2.3;
        FontContext {
            fonts: HashMap::new(),
            gamma_lut: GammaLut::new(contrast, gamma, gamma),
            gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8]) {
        if self.fonts.contains_key(font_key) {
            return
        }

        if let Some(font_file) = dwrote::FontFile::new_from_data(data) {
            let face = font_file.create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
            self.fonts.insert((*font_key).clone(), face);
        } else {
            // XXX add_raw_font needs to have a way to return an error
            debug!("DWrite WR failed to load font from data, using Arial instead");
            self.add_native_font(font_key, DEFAULT_FONT_DESCRIPTOR.clone());
        }
    }

    pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: dwrote::FontDescriptor) {
        if self.fonts.contains_key(font_key) {
            return
        }

        let system_fc = dwrote::FontCollection::system();
        let font = system_fc.get_font_from_descriptor(&font_handle).unwrap();
        let face = font.create_font_face();
        self.fonts.insert((*font_key).clone(), face);
    }

    // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
    // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
    #[allow(dead_code)]
    fn print_glyph_data(&self, data: &Vec<u8>, width: usize, height: usize) {
        // Rust doesn't have step_by support on stable :(
        for i in 0..height {
            let current_height = i * width * 3;

            for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
                let r = pixel[0];
                let g = pixel[1];
                let b = pixel[2];
                print!("({}, {}, {}) ", r, g, b, );
            }
            println!("");
        }
    }

    fn create_glyph_analysis(&self, font_key: FontKey,
                            size: Au, glyph: u32,
                            render_mode: FontRenderMode) ->
                            dwrote::GlyphRunAnalysis {
        let face = self.fonts.get(&font_key).unwrap();
        let glyph = glyph as u16;

        let glyph = glyph as u16;
        let advance = 0.0f32;
        let offset = dwrote::GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 };

        let glyph_run = dwrote::DWRITE_GLYPH_RUN {
            fontFace: unsafe { face.as_ptr() },
            fontEmSize: size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
            glyphCount: 1,
            glyphIndices: &glyph,
            glyphAdvances: &advance,
            glyphOffsets: &offset,
            isSideways: 0,
            bidiLevel: 0,
        };

        let dwrite_render_mode = dwrite_render_mode(render_mode);
        let dwrite_measure_mode = dwrite_measure_mode(render_mode);

        // XX use the xform to handle subpixel positioning (what skia does), I believe that keeps
        //let xform = dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0, dx: 0.0, dy: 0.0 };
        dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, None,
                                         dwrite_render_mode,
                                         dwrite_measure_mode,
                                         0.0, 0.0)
    }

    pub fn get_glyph_dimensions(&self,
                                font_key: FontKey,
                                size: Au,
                                glyph: u32) -> Option<GlyphDimensions> {
        // Probably have to default to something else here.
        let render_mode = FontRenderMode::Subpixel;
        let analysis = self.create_glyph_analysis(font_key, size, glyph, render_mode);

        let texture_type = dwrite_texture_type(render_mode);
        Some(get_glyph_dimensions_with_analysis(analysis, texture_type))
    }

    // DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
    // TODO: Decide whether all fonts should return RGB or BGR
    fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
        match render_mode {
            FontRenderMode::Mono => {
                let mut rgba_pixels: Vec<u8> = Vec::with_capacity(pixels.len() * 4);
                for i in 0..pixels.len() {
                    rgba_pixels[i*4+0] = pixels[i];
                    rgba_pixels[i*4+1] = pixels[i];
                    rgba_pixels[i*4+2] = pixels[i];
                    rgba_pixels[i*4+3] = pixels[i];
                }
                rgba_pixels
            }
            FontRenderMode::Alpha => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = Vec::with_capacity(length * 4);
                for i in 0..length {
                    // TODO(vlad): we likely need to do something smarter
                    // This is what skia does
                    let alpha = ((pixels[i*3+0] as u32 +
                                pixels[i*3+1] as u32 +
                                pixels[i*3+2] as u32)
                                / 3) as u8;

                    rgba_pixels[i*4+0] = alpha;
                    rgba_pixels[i*4+1] = alpha;
                    rgba_pixels[i*4+2] = alpha;
                    rgba_pixels[i*4+3] = alpha;
                }
                rgba_pixels
            }
            FontRenderMode::Subpixel => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = Vec::with_capacity(length * 4);
                for i in 0..length {
                    rgba_pixels[i*4+0] = pixels[i*3+0];
                    rgba_pixels[i*4+1] = pixels[i*3+1];
                    rgba_pixels[i*4+2] = pixels[i*3+2];
                    rgba_pixels[i*4+3] = 0xff;
                }
                rgba_pixels
            }
        }
    }

    pub fn rasterize_glyph(&mut self,
                           font_key: FontKey,
                           size: Au,
                           color: ColorU,
                           glyph: u32,
                           render_mode: FontRenderMode,
                           glyph_options: Option<GlyphOptions>) -> Option<RasterizedGlyph> {
        let analysis = self.create_glyph_analysis(font_key, size, glyph, render_mode);
        let texture_type = dwrite_texture_type(render_mode);

        let bounds = analysis.get_alpha_texture_bounds(texture_type);
        let width = (bounds.right - bounds.left) as usize;
        let height = (bounds.bottom - bounds.top) as usize;

        let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
        self.gamma_lut.preblend_rgb(&mut pixels, width, height,
                                    ColorLut::new(color.r, color.g, color.b, color.a));

        let rgba_pixels = self.convert_to_rgba(&mut pixels, render_mode);

        Some(RasterizedGlyph {
            width: width as u32,
            height: height as u32,
            bytes: rgba_pixels,
        })
    }
}
<commit_msg>Properly allocate rgba pixels<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use std::collections::HashMap;
use webrender_traits::{FontKey, ColorU, FontRenderMode, GlyphDimensions, GlyphOptions};
use gamma_lut::{GammaLut, Color as ColorLut};

use dwrote;

lazy_static! {
    static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
        family_name: "Arial".to_owned(),
        weight: dwrote::FontWeight::Regular,
        stretch: dwrote::FontStretch::Normal,
        style: dwrote::FontStyle::Normal,
    };
}

#[allow(dead_code)]
pub struct FontContext {
    fonts: HashMap<FontKey, dwrote::FontFace>,
    gamma_lut: GammaLut,
    gdi_gamma_lut: GammaLut,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn dwrite_texture_type(render_mode: FontRenderMode) ->
                       dwrote::DWRITE_TEXTURE_TYPE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
    }
}

fn dwrite_measure_mode(render_mode: FontRenderMode) ->
                       dwrote::DWRITE_MEASURING_MODE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
        FontRenderMode::Alpha => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
        FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC,
    }
}

fn dwrite_render_mode(render_mode: FontRenderMode) ->
                      dwrote::DWRITE_RENDERING_MODE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
        FontRenderMode::Alpha => dwrote::DWRITE_RENDERING_MODE_GDI_NATURAL,
        FontRenderMode::Subpixel => dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC,
    }
}

fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
                                      texture_type: dwrote::DWRITE_TEXTURE_TYPE)
                                      -> GlyphDimensions {
    let bounds = analysis.get_alpha_texture_bounds(texture_type);

    let width = (bounds.right - bounds.left) as u32;
    let height = (bounds.bottom - bounds.top) as u32;
    GlyphDimensions {
        left: bounds.left,
        top: -bounds.top,
        width: width,
        height: height,
    }
}

impl FontContext {
    pub fn new() -> FontContext {
        // These are the default values we use in Gecko.
        // We use a gamma value of 2.3 for gdi fonts
        // TODO: Fetch this data from Gecko itself.
        let contrast = 1.0;
        let gamma = 1.8;
        let gdi_gamma = 2.3;
        FontContext {
            fonts: HashMap::new(),
            gamma_lut: GammaLut::new(contrast, gamma, gamma),
            gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8]) {
        if self.fonts.contains_key(font_key) {
            return
        }

        if let Some(font_file) = dwrote::FontFile::new_from_data(data) {
            let face = font_file.create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
            self.fonts.insert((*font_key).clone(), face);
        } else {
            // XXX add_raw_font needs to have a way to return an error
            debug!("DWrite WR failed to load font from data, using Arial instead");
            self.add_native_font(font_key, DEFAULT_FONT_DESCRIPTOR.clone());
        }
    }

    pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: dwrote::FontDescriptor) {
        if self.fonts.contains_key(font_key) {
            return
        }

        let system_fc = dwrote::FontCollection::system();
        let font = system_fc.get_font_from_descriptor(&font_handle).unwrap();
        let face = font.create_font_face();
        self.fonts.insert((*font_key).clone(), face);
    }

    // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
    // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
    #[allow(dead_code)]
    fn print_glyph_data(&self, data: &Vec<u8>, width: usize, height: usize) {
        // Rust doesn't have step_by support on stable :(
        for i in 0..height {
            let current_height = i * width * 3;

            for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
                let r = pixel[0];
                let g = pixel[1];
                let b = pixel[2];
                print!("({}, {}, {}) ", r, g, b, );
            }
            println!("");
        }
    }

    fn create_glyph_analysis(&self, font_key: FontKey,
                            size: Au, glyph: u32,
                            render_mode: FontRenderMode) ->
                            dwrote::GlyphRunAnalysis {
        let face = self.fonts.get(&font_key).unwrap();
        let glyph = glyph as u16;

        let glyph = glyph as u16;
        let advance = 0.0f32;
        let offset = dwrote::GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 };

        let glyph_run = dwrote::DWRITE_GLYPH_RUN {
            fontFace: unsafe { face.as_ptr() },
            fontEmSize: size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
            glyphCount: 1,
            glyphIndices: &glyph,
            glyphAdvances: &advance,
            glyphOffsets: &offset,
            isSideways: 0,
            bidiLevel: 0,
        };

        let dwrite_render_mode = dwrite_render_mode(render_mode);
        let dwrite_measure_mode = dwrite_measure_mode(render_mode);

        // XX use the xform to handle subpixel positioning (what skia does), I believe that keeps
        //let xform = dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0, dx: 0.0, dy: 0.0 };
        dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, None,
                                         dwrite_render_mode,
                                         dwrite_measure_mode,
                                         0.0, 0.0)
    }

    pub fn get_glyph_dimensions(&self,
                                font_key: FontKey,
                                size: Au,
                                glyph: u32) -> Option<GlyphDimensions> {
        // Probably have to default to something else here.
        let render_mode = FontRenderMode::Subpixel;
        let analysis = self.create_glyph_analysis(font_key, size, glyph, render_mode);

        let texture_type = dwrite_texture_type(render_mode);
        Some(get_glyph_dimensions_with_analysis(analysis, texture_type))
    }

    // DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
    // TODO: Decide whether all fonts should return RGB or BGR
    fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
        match render_mode {
            FontRenderMode::Mono => {
                let mut rgba_pixels: Vec<u8> = vec![0; pixels.len() * 4];
                for i in 0..pixels.len() {
                    rgba_pixels[i*4+0] = pixels[i];
                    rgba_pixels[i*4+1] = pixels[i];
                    rgba_pixels[i*4+2] = pixels[i];
                    rgba_pixels[i*4+3] = pixels[i];
                }
                rgba_pixels
            }
            FontRenderMode::Alpha => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                for i in 0..length {
                    // TODO(vlad): we likely need to do something smarter
                    // This is what skia does
                    let alpha = ((pixels[i*3+0] as u32 +
                                pixels[i*3+1] as u32 +
                                pixels[i*3+2] as u32)
                                / 3) as u8;

                    rgba_pixels[i*4+0] = alpha;
                    rgba_pixels[i*4+1] = alpha;
                    rgba_pixels[i*4+2] = alpha;
                    rgba_pixels[i*4+3] = alpha;
                }
                rgba_pixels
            }
            FontRenderMode::Subpixel => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                for i in 0..length {
                    rgba_pixels[i*4+0] = pixels[i*3+0];
                    rgba_pixels[i*4+1] = pixels[i*3+1];
                    rgba_pixels[i*4+2] = pixels[i*3+2];
                    rgba_pixels[i*4+3] = 0xff;
                }
                rgba_pixels
            }
        }
    }

    pub fn rasterize_glyph(&mut self,
                           font_key: FontKey,
                           size: Au,
                           color: ColorU,
                           glyph: u32,
                           render_mode: FontRenderMode,
                           glyph_options: Option<GlyphOptions>) -> Option<RasterizedGlyph> {
        let analysis = self.create_glyph_analysis(font_key, size, glyph, render_mode);
        let texture_type = dwrite_texture_type(render_mode);

        let bounds = analysis.get_alpha_texture_bounds(texture_type);
        let width = (bounds.right - bounds.left) as usize;
        let height = (bounds.bottom - bounds.top) as usize;

        let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
        self.gamma_lut.preblend_rgb(&mut pixels, width, height,
                                    ColorLut::new(color.r, color.g, color.b, color.a));

        let rgba_pixels = self.convert_to_rgba(&mut pixels, render_mode);

        Some(RasterizedGlyph {
            width: width as u32,
            height: height as u32,
            bytes: rgba_pixels,
        })
    }
}
<|endoftext|>


<commit_before>//extern crate serde_json;
use rustc_serialize::json;

use std::error::Error;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io::{ self, BufReader, BufRead, Read };
use std::path::{ Path, PathBuf };
use std::str::FromStr;

use download;
use verify;

macro_rules! custom_try {
    ($x:expr) => (match $x {
        Ok(x) => x,
        Err(why) => return Err(format!("An error occured during package finding. {}", why)),
    });
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Package {
    pub name: String,
    pub version: String,
    pub crc32: u32,
    pub url: String,
    pub dependencies: Vec<Package>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PackageList {
    packages: Vec<Package>,
    version: i32
}

pub fn update_package_list(url: &str, path: &Path) -> Result<bool, String> {
    if (path.extension().is_some() &&
        path.extension().unwrap_or(OsStr::new("")) != ".json") ||
       (path.extension().is_none())
    {
        return Err(String::from(
            format!("Extension of package list is not .json: {}", path.to_str().unwrap_or("Invalid File"))
        ));
    }

    let mut path_temp = PathBuf::new();
    path_temp.push(path);
    path_temp.set_extension("json.tmp");

    if download::download_file(url, path_temp.as_path()).is_err() {
        return Err(String::from("Error occured while downloading package list"))
    }

    if verify::verify_file_crc32(&custom_try!(File::open(&path_temp))) ==
       verify::verify_file_crc32(&custom_try!(File::open(&path)))
    {
        fs::remove_file(path_temp);
        return Ok(false)
    } else {
        fs::remove_file(path);
        fs::rename(path_temp, path);
        return Ok(true)
    }
}

pub fn find_package(name: &str, path:&Path) -> Result<Vec<Package>, String> {
    let mut list = custom_try!(File::open(path));
    let mut reader = BufReader::new(list);
    let mut buffer = String::new();

    reader.read_to_string(&mut buffer);
    let package_list: PackageList = custom_try!(json::decode(&buffer));

    let filtered = package_list.packages
                    .into_iter()
                    .filter(|x| !x.name.contains(name))
                    .collect::<Vec<_>>();

    match filtered.len() {
        0 => Err(String::from("Package not found")),
        _ => Ok(filtered)
    }
}

pub fn download_and_find(name: &str, url:&str, path:&Path) -> Result<Vec<Package>, String>{
    if update_package_list(url, path).is_ok() {
        return find_package(name, path)
    } else {
        return Err(String::from("Error occured while downloading package list"))
    }
}

pub fn select_package(name: &str, path: &Path) -> Result<Package, String>{
    let found_packages = find_package(name, path)?;

    if found_packages.len() > 1 {
        for (i, package) in found_packages.iter().enumerate() {
            println!("Package {}: {}", i, package.name);
        }

        let mut input = String::new();
        custom_try!(io::stdin().read_line(&mut input));
        let input: u16 = custom_try!(input.trim().parse::<u16>());

        if input > found_packages.len() as u16 {
            return Err(String::from("Invalid input"))
        } else {
            return Ok(found_packages[input as usize].clone())
        }
    } else {
        return Ok(found_packages[0].clone())
    }
}<commit_msg>Implement trait Clone for Package<commit_after>//extern crate serde_json;
use rustc_serialize::json;

use std::error::Error;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io::{ self, BufReader, BufRead, Read };
use std::path::{ Path, PathBuf };
use std::str::FromStr;

use download;
use verify;

macro_rules! custom_try {
    ($x:expr) => (match $x {
        Ok(x) => x,
        Err(why) => return Err(format!("An error occured during package finding. {}", why)),
    });
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct Package {
    pub name: String,
    pub version: String,
    pub crc32: u32,
    pub url: String,
    pub dependencies: Vec<Package>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PackageList {
    packages: Vec<Package>,
    version: i32
}

pub fn update_package_list(url: &str, path: &Path) -> Result<bool, String> {
    if (path.extension().is_some() &&
        path.extension().unwrap_or(OsStr::new("")) != ".json") ||
       (path.extension().is_none())
    {
        return Err(String::from(
            format!("Extension of package list is not .json: {}", path.to_str().unwrap_or("Invalid File"))
        ));
    }

    let mut path_temp = PathBuf::new();
    path_temp.push(path);
    path_temp.set_extension("json.tmp");

    if download::download_file(url, path_temp.as_path()).is_err() {
        return Err(String::from("Error occured while downloading package list"))
    }

    if verify::verify_file_crc32(&custom_try!(File::open(&path_temp))) ==
       verify::verify_file_crc32(&custom_try!(File::open(&path)))
    {
        fs::remove_file(path_temp);
        return Ok(false)
    } else {
        fs::remove_file(path);
        fs::rename(path_temp, path);
        return Ok(true)
    }
}

pub fn find_package(name: &str, path:&Path) -> Result<Vec<Package>, String> {
    let mut list = custom_try!(File::open(path));
    let mut reader = BufReader::new(list);
    let mut buffer = String::new();

    reader.read_to_string(&mut buffer);
    let package_list: PackageList = custom_try!(json::decode(&buffer));

    let filtered = package_list.packages
                    .into_iter()
                    .filter(|x| !x.name.contains(name))
                    .collect::<Vec<_>>();

    match filtered.len() {
        0 => Err(String::from("Package not found")),
        _ => Ok(filtered)
    }
}

pub fn download_and_find(name: &str, url:&str, path:&Path) -> Result<Vec<Package>, String>{
    if update_package_list(url, path).is_ok() {
        return find_package(name, path)
    } else {
        return Err(String::from("Error occured while downloading package list"))
    }
}

pub fn select_package(name: &str, path: &Path) -> Result<Package, String>{
    let found_packages = find_package(name, path)?;

    if found_packages.len() > 1 {
        for (i, package) in found_packages.iter().enumerate() {
            println!("Package {}: {}", i, package.name);
        }

        let mut input = String::new();
        custom_try!(io::stdin().read_line(&mut input));
        let input: u16 = custom_try!(input.trim().parse::<u16>());

        if input > found_packages.len() as u16 {
            return Err(String::from("Invalid input"))
        } else {
            return Ok(found_packages[input as usize].clone())
        }
    } else {
        return Ok(found_packages[0].clone())
    }
}<|endoftext|>


<commit_before>use std::cell::RefCell;

use proc_macro2::{Delimiter, Span};

use buffer::Cursor;
use error::{self, Error};
use span::IntoSpans;
use token::Token;

/// Support for checking the next token in a stream to decide how to parse.
///
/// Use [`ParseStream::lookahead1`] to construct this object.
///
/// [`ParseStream::lookahead1`]: struct.ParseBuffer.html#method.lookahead1
pub struct Lookahead1<'a> {
    scope: Span,
    cursor: Cursor<'a>,
    comparisons: RefCell<Vec<String>>,
}

pub fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
    Lookahead1 {
        scope: scope,
        cursor: cursor,
        comparisons: RefCell::new(Vec::new()),
    }
}

impl<'a> Lookahead1<'a> {
    pub fn peek<T: Peek>(&self, token: T) -> bool {
        let _ = token;
        if T::Token::peek(self) {
            return true;
        }
        self.comparisons.borrow_mut().push(T::Token::display());
        false
    }

    pub fn error(self) -> Error {
        let comparisons = self.comparisons.borrow();
        match comparisons.len() {
            0 => if self.cursor.eof() {
                Error::new(self.scope, "unexpected end of input")
            } else {
                Error::new(self.cursor.span(), "unexpected token")
            },
            1 => {
                let message = format!("expected {}", comparisons[0]);
                error::new_at(self.scope, self.cursor, message)
            }
            _ => {
                let join = comparisons.join(", ");
                let message = format!("expected one of: {}", join);
                error::new_at(self.scope, self.cursor, message)
            }
        }
    }

    pub fn cursor(&self) -> Cursor<'a> {
        self.cursor
    }
}

/// Types that can be parsed by looking at just one token.
///
/// Use [`ParseStream::peek`] to peek one of these types in a parse stream
/// without consuming it from the stream.
///
/// This trait is sealed and cannot be implemented for types outside of Syn.
///
/// [`ParseStream::peek`]: struct.ParseBuffer.html#method.peek
pub trait Peek: private::Sealed {
    // Not public API.
    #[doc(hidden)]
    type Token: Token;
}

impl<F: FnOnce(TokenMarker) -> T, T: Token> Peek for F {
    type Token = T;
}

pub enum TokenMarker {}

impl<S> IntoSpans<S> for TokenMarker {
    fn into_spans(self) -> S {
        match self {}
    }
}

pub fn is_delimiter(lookahead: &Lookahead1, delimiter: Delimiter) -> bool {
    lookahead.cursor.group(delimiter).is_some()
}

mod private {
    use super::{Token, TokenMarker};
    pub trait Sealed {}
    impl<F: FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
}
<commit_msg>Explain Lookahead1 vs ParseStream peek<commit_after>use std::cell::RefCell;

use proc_macro2::{Delimiter, Span};

use buffer::Cursor;
use error::{self, Error};
use span::IntoSpans;
use token::Token;

/// Support for checking the next token in a stream to decide how to parse.
///
/// An important advantage over [`ParseStream::peek`] is that here we
/// automatically construct an appropriate error message based on the token
/// alternatives that get peeked. If you are producing your own error message,
/// go ahead and use `ParseStream::peek` instead.
///
/// Use [`ParseStream::lookahead1`] to construct this object.
///
/// [`ParseStream::peek`]: struct.ParseBuffer.html#method.peek
/// [`ParseStream::lookahead1`]: struct.ParseBuffer.html#method.lookahead1
pub struct Lookahead1<'a> {
    scope: Span,
    cursor: Cursor<'a>,
    comparisons: RefCell<Vec<String>>,
}

pub fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
    Lookahead1 {
        scope: scope,
        cursor: cursor,
        comparisons: RefCell::new(Vec::new()),
    }
}

impl<'a> Lookahead1<'a> {
    pub fn peek<T: Peek>(&self, token: T) -> bool {
        let _ = token;
        if T::Token::peek(self) {
            return true;
        }
        self.comparisons.borrow_mut().push(T::Token::display());
        false
    }

    pub fn error(self) -> Error {
        let comparisons = self.comparisons.borrow();
        match comparisons.len() {
            0 => if self.cursor.eof() {
                Error::new(self.scope, "unexpected end of input")
            } else {
                Error::new(self.cursor.span(), "unexpected token")
            },
            1 => {
                let message = format!("expected {}", comparisons[0]);
                error::new_at(self.scope, self.cursor, message)
            }
            _ => {
                let join = comparisons.join(", ");
                let message = format!("expected one of: {}", join);
                error::new_at(self.scope, self.cursor, message)
            }
        }
    }

    pub fn cursor(&self) -> Cursor<'a> {
        self.cursor
    }
}

/// Types that can be parsed by looking at just one token.
///
/// Use [`ParseStream::peek`] to peek one of these types in a parse stream
/// without consuming it from the stream.
///
/// This trait is sealed and cannot be implemented for types outside of Syn.
///
/// [`ParseStream::peek`]: struct.ParseBuffer.html#method.peek
pub trait Peek: private::Sealed {
    // Not public API.
    #[doc(hidden)]
    type Token: Token;
}

impl<F: FnOnce(TokenMarker) -> T, T: Token> Peek for F {
    type Token = T;
}

pub enum TokenMarker {}

impl<S> IntoSpans<S> for TokenMarker {
    fn into_spans(self) -> S {
        match self {}
    }
}

pub fn is_delimiter(lookahead: &Lookahead1, delimiter: Delimiter) -> bool {
    lookahead.cursor.group(delimiter).is_some()
}

mod private {
    use super::{Token, TokenMarker};
    pub trait Sealed {}
    impl<F: FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
}
<|endoftext|>