<commit_before>use std::collections::HashSet;
use time::Timespec;

use conduit::{Request, Response};
use conduit_router::RequestParams;
use pg::GenericConnection;
use pg::rows::Row;

use {Model, Crate};
use db::RequestTransaction;
use util::{RequestUtils, CargoResult, ChainError};
use util::errors::NotFound;

#[derive(Clone)]
pub struct Category {
    pub id: i32,
    pub category: String,
    pub created_at: Timespec,
    pub crates_cnt: i32,
}

#[derive(RustcEncodable, RustcDecodable)]
pub struct EncodableCategory {
    pub id: String,
    pub category: String,
    pub created_at: String,
    pub crates_cnt: i32,
}

impl Category {
    pub fn find_by_category(conn: &GenericConnection, name: &str)
                            -> CargoResult<Option<Category>> {
        let stmt = try!(conn.prepare("SELECT * FROM categories \
                                      WHERE category = $1"));
        let rows = try!(stmt.query(&[&name]));
        Ok(rows.iter().next().map(|r| Model::from_row(&r)))
    }

    pub fn find_all_by_category(conn: &GenericConnection, names: &[String])
                                  -> CargoResult<Vec<Category>> {
        let stmt = try!(conn.prepare("SELECT * FROM categories \
                                      WHERE category = ANY($1)"));
        let rows = try!(stmt.query(&[&names]));
        Ok(rows.iter().map(|r| Model::from_row(&r)).collect())
    }

    pub fn encodable(self) -> EncodableCategory {
        let Category { id: _, crates_cnt, category, created_at } = self;
        EncodableCategory {
            id: category.clone(),
            created_at: ::encode_time(created_at),
            crates_cnt: crates_cnt,
            category: category,
        }
    }

    pub fn update_crate(conn: &GenericConnection,
                        krate: &Crate,
                        categories: &[String]) -> CargoResult<()> {
        let old_categories = try!(krate.categories(conn));
        let old_categories_ids: HashSet<_> = old_categories.iter().map(|cat| {
            cat.id
        }).collect();
        // If a new category specified is not in the database, filter
        // it out and don't add it.
        let new_categories = try!(
            Category::find_all_by_category(conn, categories)
        );
        let new_categories_ids: HashSet<_> = new_categories.iter().map(|cat| {
            cat.id
        }).collect();

        let to_rm: Vec<_> = old_categories_ids
                                .difference(&new_categories_ids)
                                .cloned()
                                .collect();
        let to_add: Vec<_> = new_categories_ids
                                .difference(&old_categories_ids)
                                .cloned()
                                .collect();

        if to_rm.len() > 0 {
            try!(conn.execute("DELETE FROM crates_categories \
                                WHERE category_id = ANY($1) \
                                  AND crate_id = $2",
                              &[&to_rm, &krate.id]));
        }

        if !to_add.is_empty() {
            let insert: Vec<_> = to_add.into_iter().map(|id| {
                format!("({}, {})", krate.id, id)
            }).collect();
            let insert = insert.join(", ");
            try!(conn.execute(&format!("INSERT INTO crates_categories \
                                        (crate_id, category_id) VALUES {}",
                                       insert),
                              &[]));
        }

        Ok(())
    }
}

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

/// Handles the `GET /categories` route.
pub fn index(req: &mut Request) -> CargoResult<Response> {
    let conn = try!(req.tx());
    let (offset, limit) = try!(req.pagination(10, 100));
    let query = req.query();
    let sort = query.get("sort").map(|s| &s[..]).unwrap_or("alpha");
    let sort_sql = match sort {
        "crates" => "ORDER BY crates_cnt DESC",
        _ => "ORDER BY category ASC",
    };

    // Collect all the categories
    let stmt = try!(conn.prepare(&format!("SELECT * FROM categories {} \
                                           LIMIT $1 OFFSET $2",
                                          sort_sql)));

    let categories: Vec<_> = try!(stmt.query(&[&limit, &offset]))
        .iter()
        .map(|row| {
            let category: Category = Model::from_row(&row);
            category.encodable()
        })
        .collect();

    // Query for the total count of categories
    let total = try!(Category::count(conn));

    #[derive(RustcEncodable)]
    struct R { categories: Vec<EncodableCategory>, meta: Meta }
    #[derive(RustcEncodable)]
    struct Meta { total: i64 }

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

/// Handles the `GET /categories/:category_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
    let name = &req.params()["category_id"];
    let conn = try!(req.tx());
    let cat = try!(Category::find_by_category(&*conn, &name));
    let cat = try!(cat.chain_error(|| NotFound));

    #[derive(RustcEncodable)]
    struct R { category: EncodableCategory }
    Ok(req.json(&R { category: cat.encodable() }))
}
<commit_msg>Prefer as_str over &[..]<commit_after>use std::collections::HashSet;
use time::Timespec;

use conduit::{Request, Response};
use conduit_router::RequestParams;
use pg::GenericConnection;
use pg::rows::Row;

use {Model, Crate};
use db::RequestTransaction;
use util::{RequestUtils, CargoResult, ChainError};
use util::errors::NotFound;

#[derive(Clone)]
pub struct Category {
    pub id: i32,
    pub category: String,
    pub created_at: Timespec,
    pub crates_cnt: i32,
}

#[derive(RustcEncodable, RustcDecodable)]
pub struct EncodableCategory {
    pub id: String,
    pub category: String,
    pub created_at: String,
    pub crates_cnt: i32,
}

impl Category {
    pub fn find_by_category(conn: &GenericConnection, name: &str)
                            -> CargoResult<Option<Category>> {
        let stmt = try!(conn.prepare("SELECT * FROM categories \
                                      WHERE category = $1"));
        let rows = try!(stmt.query(&[&name]));
        Ok(rows.iter().next().map(|r| Model::from_row(&r)))
    }

    pub fn find_all_by_category(conn: &GenericConnection, names: &[String])
                                  -> CargoResult<Vec<Category>> {
        let stmt = try!(conn.prepare("SELECT * FROM categories \
                                      WHERE category = ANY($1)"));
        let rows = try!(stmt.query(&[&names]));
        Ok(rows.iter().map(|r| Model::from_row(&r)).collect())
    }

    pub fn encodable(self) -> EncodableCategory {
        let Category { id: _, crates_cnt, category, created_at } = self;
        EncodableCategory {
            id: category.clone(),
            created_at: ::encode_time(created_at),
            crates_cnt: crates_cnt,
            category: category,
        }
    }

    pub fn update_crate(conn: &GenericConnection,
                        krate: &Crate,
                        categories: &[String]) -> CargoResult<()> {
        let old_categories = try!(krate.categories(conn));
        let old_categories_ids: HashSet<_> = old_categories.iter().map(|cat| {
            cat.id
        }).collect();
        // If a new category specified is not in the database, filter
        // it out and don't add it.
        let new_categories = try!(
            Category::find_all_by_category(conn, categories)
        );
        let new_categories_ids: HashSet<_> = new_categories.iter().map(|cat| {
            cat.id
        }).collect();

        let to_rm: Vec<_> = old_categories_ids
                                .difference(&new_categories_ids)
                                .cloned()
                                .collect();
        let to_add: Vec<_> = new_categories_ids
                                .difference(&old_categories_ids)
                                .cloned()
                                .collect();

        if to_rm.len() > 0 {
            try!(conn.execute("DELETE FROM crates_categories \
                                WHERE category_id = ANY($1) \
                                  AND crate_id = $2",
                              &[&to_rm, &krate.id]));
        }

        if !to_add.is_empty() {
            let insert: Vec<_> = to_add.into_iter().map(|id| {
                format!("({}, {})", krate.id, id)
            }).collect();
            let insert = insert.join(", ");
            try!(conn.execute(&format!("INSERT INTO crates_categories \
                                        (crate_id, category_id) VALUES {}",
                                       insert),
                              &[]));
        }

        Ok(())
    }
}

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

/// Handles the `GET /categories` route.
pub fn index(req: &mut Request) -> CargoResult<Response> {
    let conn = try!(req.tx());
    let (offset, limit) = try!(req.pagination(10, 100));
    let query = req.query();
    let sort = query.get("sort").map_or("alpha", String::as_str);
    let sort_sql = match sort {
        "crates" => "ORDER BY crates_cnt DESC",
        _ => "ORDER BY category ASC",
    };

    // Collect all the categories
    let stmt = try!(conn.prepare(&format!("SELECT * FROM categories {} \
                                           LIMIT $1 OFFSET $2",
                                          sort_sql)));

    let categories: Vec<_> = try!(stmt.query(&[&limit, &offset]))
        .iter()
        .map(|row| {
            let category: Category = Model::from_row(&row);
            category.encodable()
        })
        .collect();

    // Query for the total count of categories
    let total = try!(Category::count(conn));

    #[derive(RustcEncodable)]
    struct R { categories: Vec<EncodableCategory>, meta: Meta }
    #[derive(RustcEncodable)]
    struct Meta { total: i64 }

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

/// Handles the `GET /categories/:category_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
    let name = &req.params()["category_id"];
    let conn = try!(req.tx());
    let cat = try!(Category::find_by_category(&*conn, &name));
    let cat = try!(cat.chain_error(|| NotFound));

    #[derive(RustcEncodable)]
    struct R { category: EncodableCategory }
    Ok(req.json(&R { category: cat.encodable() }))
}
<|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 https://mozilla.org/MPL/2.0/. */

use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
use crate::StreamId;
use serde_json::{Map, Value};
use std::net::TcpStream;

pub struct PreferenceActor {
    name: String,
}

impl PreferenceActor {
    pub fn new(name: String) -> Self {
        Self { name }
    }
}

impl Actor for PreferenceActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(
        &self,
        _registry: &ActorRegistry,
        msg_type: &str,
        _msg: &Map<String, Value>,
        stream: &mut TcpStream,
        _id: StreamId,
    ) -> Result<ActorMessageStatus, ()> {
        Ok(match msg_type {
            "getBoolPref" => {
                let reply = BoolReply {
                    from: self.name(),
                    value: false,
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },

            "getCharPref" => {
                let reply = CharReply {
                    from: self.name(),
                    value: "".to_owned(),
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },

            "getIntPref" => {
                let reply = IntReply {
                    from: self.name(),
                    value: 0,
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },

            _ => ActorMessageStatus::Ignored,
        })
    }
}

#[derive(Serialize)]
struct BoolReply {
    from: String,
    value: bool,
}

#[derive(Serialize)]
struct CharReply {
    from: String,
    value: String,
}

#[derive(Serialize)]
struct IntReply {
    from: String,
    value: i32,
}
<commit_msg>preferences actor now returns real values.<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 https://mozilla.org/MPL/2.0/. */

use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
use crate::StreamId;
use serde_json::{Map, Value};
use servo_config::pref_util::PrefValue;
use servo_config::prefs::pref_map;
use std::net::TcpStream;

pub struct PreferenceActor {
    name: String,
}

impl PreferenceActor {
    pub fn new(name: String) -> Self {
        Self { name }
    }
}

impl Actor for PreferenceActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(
        &self,
        _registry: &ActorRegistry,
        msg_type: &str,
        _msg: &Map<String, Value>,
        stream: &mut TcpStream,
        _id: StreamId,
    ) -> Result<ActorMessageStatus, ()> {
        let pref_value = pref_map().get(msg_type);
        Ok(match pref_value {
            PrefValue::Float(value) => {
                let reply = FloatReply {
                    from: self.name(),
                    value: value,
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },
            PrefValue::Int(value) => {
                let reply = IntReply {
                    from: self.name(),
                    value: value,
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },
            PrefValue::Str(value) => {
                let reply = CharReply {
                    from: self.name(),
                    value: value,
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },
            PrefValue::Bool(value) => {
                let reply = BoolReply {
                    from: self.name(),
                    value: value,
                };
                let _ = stream.write_json_packet(&reply);
                ActorMessageStatus::Processed
            },
            PrefValue::Missing => ActorMessageStatus::Ignored,
        })
    }
}

#[derive(Serialize)]
struct BoolReply {
    from: String,
    value: bool,
}

#[derive(Serialize)]
struct CharReply {
    from: String,
    value: String,
}

#[derive(Serialize)]
struct IntReply {
    from: String,
    value: i64,
}

#[derive(Serialize)]
struct FloatReply {
    from: String,
    value: f64,
}
<|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 cssparser::Parser as CssParser;
use document_loader::LoadType;
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding;
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference};
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::{AttributeMutation, Element, ElementCreator};
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use encoding::EncodingRef;
use encoding::all::UTF_8;
use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
use network_listener::{NetworkListener, PreInvoke};
use script_layout_interface::message::Msg;
use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Cell;
use std::default::Default;
use std::mem;
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use style::attr::AttrValue;
use style::media_queries::{MediaQueryList, parse_media_query_list};
use style::parser::ParserContextExtraData;
use style::servo::Stylesheet;
use style::stylesheets::Origin;
use url::Url;
use util::str::HTML_SPACE_CHARACTERS;

no_jsmanaged_fields!(Stylesheet);

#[dom_struct]
pub struct HTMLLinkElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableHeap<JS<DOMTokenList>>,
    stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>,

    /// https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts
    parser_inserted: Cell<bool>,
}

impl HTMLLinkElement {
    fn new_inherited(localName: Atom, prefix: Option<DOMString>, document: &Document,
                     creator: ElementCreator) -> HTMLLinkElement {
        HTMLLinkElement {
            htmlelement: HTMLElement::new_inherited(localName, prefix, document),
            rel_list: Default::default(),
            parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
            stylesheet: DOMRefCell::new(None),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: Atom,
               prefix: Option<DOMString>,
               document: &Document,
               creator: ElementCreator) -> Root<HTMLLinkElement> {
        let element = HTMLLinkElement::new_inherited(localName, prefix, document, creator);
        Node::reflect_node(box element, document, HTMLLinkElementBinding::Wrap)
    }

    pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
        self.stylesheet.borrow().clone()
    }
}

fn get_attr(element: &Element, local_name: &Atom) -> Option<String> {
    let elem = element.get_attribute(&ns!(), local_name);
    elem.map(|e| {
        let value = e.value();
        (**value).to_owned()
    })
}

fn string_is_stylesheet(value: &Option<String>) -> bool {
    match *value {
        Some(ref value) => {
            let mut found_stylesheet = false;
            for s in value.split(HTML_SPACE_CHARACTERS).into_iter() {
                if s.eq_ignore_ascii_case("alternate") {
                    return false;
                }

                if s.eq_ignore_ascii_case("stylesheet") {
                    found_stylesheet = true;
                }
            }
            found_stylesheet
        },
        None => false,
    }
}

/// Favicon spec usage in accordance with CEF implementation:
/// only url of icon is required/used
/// https://html.spec.whatwg.org/multipage/#rel-icon
fn is_favicon(value: &Option<String>) -> bool {
    match *value {
        Some(ref value) => {
            value.split(HTML_SPACE_CHARACTERS)
                .any(|s| s.eq_ignore_ascii_case("icon") || s.eq_ignore_ascii_case("apple-touch-icon"))
        },
        None => false,
    }
}

impl VirtualMethods for HTMLLinkElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        if !self.upcast::<Node>().is_in_doc() || mutation == AttributeMutation::Removed {
            return;
        }

        let rel = get_attr(self.upcast(), &atom!("rel"));
        match attr.local_name() {
            &atom!("href") => {
                if string_is_stylesheet(&rel) {
                    self.handle_stylesheet_url(&attr.value());
                } else if is_favicon(&rel) {
                    let sizes = get_attr(self.upcast(), &atom!("sizes"));
                    self.handle_favicon_url(rel.as_ref().unwrap(), &attr.value(), &sizes);
                }
            },
            &atom!("sizes") => {
                if is_favicon(&rel) {
                    if let Some(ref href) = get_attr(self.upcast(), &atom!("href")) {
                        self.handle_favicon_url(rel.as_ref().unwrap(), href, &Some(attr.value().to_string()));
                    }
                }
            },
            &atom!("media") => {
                if string_is_stylesheet(&rel) {
                    self.handle_stylesheet_url(&attr.value());
                }
            },
            _ => {},
        }
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        if tree_in_doc {
            let element = self.upcast();

            let rel = get_attr(element, &atom!("rel"));
            let href = get_attr(element, &atom!("href"));
            let sizes = get_attr(self.upcast(), &atom!("sizes"));

            match href {
                Some(ref href) if string_is_stylesheet(&rel) => {
                    self.handle_stylesheet_url(href);
                }
                Some(ref href) if is_favicon(&rel) => {
                    self.handle_favicon_url(rel.as_ref().unwrap(), href, &sizes);
                }
                _ => {}
            }
        }
    }
}

impl HTMLLinkElement {
    fn handle_stylesheet_url(&self, href: &str) {
        let document = document_from_node(self);
        match document.base_url().join(href) {
            Ok(url) => {
                let element = self.upcast::<Element>();

                let mq_attribute = element.get_attribute(&ns!(), &atom!("media"));
                let value = mq_attribute.r().map(|a| a.value());
                let mq_str = match value {
                    Some(ref value) => &***value,
                    None => "",
                };
                let mut css_parser = CssParser::new(&mq_str);
                let media = parse_media_query_list(&mut css_parser);

                // TODO: #8085 - Don't load external stylesheets if the node's mq doesn't match.
                let elem = Trusted::new(self);

                let context = Arc::new(Mutex::new(StylesheetContext {
                    elem: elem,
                    media: Some(media),
                    data: vec!(),
                    metadata: None,
                    url: url.clone(),
                }));

                let (action_sender, action_receiver) = ipc::channel().unwrap();
                let listener = NetworkListener {
                    context: context,
                    script_chan: document.window().networking_task_source(),
                };
                let response_target = AsyncResponseTarget {
                    sender: action_sender,
                };
                ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
                    listener.notify_action(message.to().unwrap());
                });

                if self.parser_inserted.get() {
                    document.increment_script_blocking_stylesheet_count();
                }
                document.load_async(LoadType::Stylesheet(url), response_target);
            }
            Err(e) => debug!("Parsing url {} failed: {}", href, e)
        }
    }

    fn handle_favicon_url(&self, rel: &str, href: &str, sizes: &Option<String>) {
        let document = document_from_node(self);
        match document.base_url().join(href) {
            Ok(url) => {
                let event = ConstellationMsg::NewFavicon(url.clone());
                document.window().constellation_chan().send(event).unwrap();

                let mozbrowser_event = match *sizes {
                    Some(ref sizes) => MozBrowserEvent::IconChange(rel.to_owned(), url.to_string(), sizes.to_owned()),
                    None => MozBrowserEvent::IconChange(rel.to_owned(), url.to_string(), "".to_owned())
                };
                document.trigger_mozbrowser_event(mozbrowser_event);
            }
            Err(e) => debug!("Parsing url {} failed: {}", href, e)
        }
    }
}

/// The context required for asynchronously loading an external stylesheet.
struct StylesheetContext {
    /// The element that initiated the request.
    elem: Trusted<HTMLLinkElement>,
    media: Option<MediaQueryList>,
    /// The response body received to date.
    data: Vec<u8>,
    /// The response metadata received to date.
    metadata: Option<Metadata>,
    /// The initial URL requested.
    url: Url,
}

impl PreInvoke for StylesheetContext {}

impl AsyncResponseListener for StylesheetContext {
    fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
        self.metadata = metadata.ok();
        if let Some(ref meta) = self.metadata {
            if let Some(ContentType(Mime(TopLevel::Text, SubLevel::Css, _))) = meta.content_type {
            } else {
                self.elem.root().upcast::<EventTarget>().fire_simple_event("error");
            }
        }
    }

    fn data_available(&mut self, payload: Vec<u8>) {
        let mut payload = payload;
        self.data.append(&mut payload);
    }

    fn response_complete(&mut self, status: Result<(), NetworkError>) {
        let elem = self.elem.root();
        let document = document_from_node(&*elem);

        if status.is_err() {
            self.elem.root().upcast::<EventTarget>().fire_simple_event("error");
        } else {
            let data = mem::replace(&mut self.data, vec!());
            let metadata = match self.metadata.take() {
                Some(meta) => meta,
                None => return,
            };
            // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
            let environment_encoding = UTF_8 as EncodingRef;
            let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s);
            let final_url = metadata.final_url;

            let win = window_from_node(&*elem);

            let mut sheet = Stylesheet::from_bytes(&data, final_url, protocol_encoding_label,
                                                   Some(environment_encoding), Origin::Author,
                                                   win.css_error_reporter(),
                                                   ParserContextExtraData::default());
            let media = self.media.take().unwrap();
            sheet.set_media(Some(media));
            let sheet = Arc::new(sheet);

            let elem = elem.r();
            let document = document.r();

            let win = window_from_node(elem);
            win.layout_chan().send(Msg::AddStylesheet(sheet.clone())).unwrap();

            *elem.stylesheet.borrow_mut() = Some(sheet);
            document.invalidate_stylesheets();
        }

        if elem.parser_inserted.get() {
            document.decrement_script_blocking_stylesheet_count();
        }

        document.finish_load(LoadType::Stylesheet(self.url.clone()));
    }
}

impl HTMLLinkElementMethods for HTMLLinkElement {
    // https://html.spec.whatwg.org/multipage/#dom-link-href
    make_url_getter!(Href, "href");

    // https://html.spec.whatwg.org/multipage/#dom-link-href
    make_setter!(SetHref, "href");

    // https://html.spec.whatwg.org/multipage/#dom-link-rel
    make_getter!(Rel, "rel");

    // https://html.spec.whatwg.org/multipage/#dom-link-rel
    make_setter!(SetRel, "rel");

    // https://html.spec.whatwg.org/multipage/#dom-link-media
    make_getter!(Media, "media");

    // https://html.spec.whatwg.org/multipage/#dom-link-media
    make_setter!(SetMedia, "media");

    // https://html.spec.whatwg.org/multipage/#dom-link-hreflang
    make_getter!(Hreflang, "hreflang");

    // https://html.spec.whatwg.org/multipage/#dom-link-hreflang
    make_setter!(SetHreflang, "hreflang");

    // https://html.spec.whatwg.org/multipage/#dom-link-type
    make_getter!(Type, "type");

    // https://html.spec.whatwg.org/multipage/#dom-link-type
    make_setter!(SetType, "type");

    // https://html.spec.whatwg.org/multipage/#dom-link-rellist
    fn RelList(&self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| DOMTokenList::new(self.upcast(), &atom!("rel")))
    }

    // https://html.spec.whatwg.org/multipage/#dom-link-charset
    make_getter!(Charset, "charset");

    // https://html.spec.whatwg.org/multipage/#dom-link-charset
    make_setter!(SetCharset, "charset");

    // https://html.spec.whatwg.org/multipage/#dom-link-rev
    make_getter!(Rev, "rev");

    // https://html.spec.whatwg.org/multipage/#dom-link-rev
    make_setter!(SetRev, "rev");

    // https://html.spec.whatwg.org/multipage/#dom-link-target
    make_getter!(Target, "target");

    // https://html.spec.whatwg.org/multipage/#dom-link-target
    make_setter!(SetTarget, "target");
}
<commit_msg>Avoid loading stylesheets in inactive documents.<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 cssparser::Parser as CssParser;
use document_loader::LoadType;
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding;
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference};
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::{AttributeMutation, Element, ElementCreator};
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use encoding::EncodingRef;
use encoding::all::UTF_8;
use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
use network_listener::{NetworkListener, PreInvoke};
use script_layout_interface::message::Msg;
use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Cell;
use std::default::Default;
use std::mem;
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use style::attr::AttrValue;
use style::media_queries::{MediaQueryList, parse_media_query_list};
use style::parser::ParserContextExtraData;
use style::servo::Stylesheet;
use style::stylesheets::Origin;
use url::Url;
use util::str::HTML_SPACE_CHARACTERS;

no_jsmanaged_fields!(Stylesheet);

#[dom_struct]
pub struct HTMLLinkElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableHeap<JS<DOMTokenList>>,
    stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>,

    /// https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts
    parser_inserted: Cell<bool>,
}

impl HTMLLinkElement {
    fn new_inherited(localName: Atom, prefix: Option<DOMString>, document: &Document,
                     creator: ElementCreator) -> HTMLLinkElement {
        HTMLLinkElement {
            htmlelement: HTMLElement::new_inherited(localName, prefix, document),
            rel_list: Default::default(),
            parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
            stylesheet: DOMRefCell::new(None),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: Atom,
               prefix: Option<DOMString>,
               document: &Document,
               creator: ElementCreator) -> Root<HTMLLinkElement> {
        let element = HTMLLinkElement::new_inherited(localName, prefix, document, creator);
        Node::reflect_node(box element, document, HTMLLinkElementBinding::Wrap)
    }

    pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
        self.stylesheet.borrow().clone()
    }
}

fn get_attr(element: &Element, local_name: &Atom) -> Option<String> {
    let elem = element.get_attribute(&ns!(), local_name);
    elem.map(|e| {
        let value = e.value();
        (**value).to_owned()
    })
}

fn string_is_stylesheet(value: &Option<String>) -> bool {
    match *value {
        Some(ref value) => {
            let mut found_stylesheet = false;
            for s in value.split(HTML_SPACE_CHARACTERS).into_iter() {
                if s.eq_ignore_ascii_case("alternate") {
                    return false;
                }

                if s.eq_ignore_ascii_case("stylesheet") {
                    found_stylesheet = true;
                }
            }
            found_stylesheet
        },
        None => false,
    }
}

/// Favicon spec usage in accordance with CEF implementation:
/// only url of icon is required/used
/// https://html.spec.whatwg.org/multipage/#rel-icon
fn is_favicon(value: &Option<String>) -> bool {
    match *value {
        Some(ref value) => {
            value.split(HTML_SPACE_CHARACTERS)
                .any(|s| s.eq_ignore_ascii_case("icon") || s.eq_ignore_ascii_case("apple-touch-icon"))
        },
        None => false,
    }
}

impl VirtualMethods for HTMLLinkElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        if !self.upcast::<Node>().is_in_doc() || mutation == AttributeMutation::Removed {
            return;
        }

        let rel = get_attr(self.upcast(), &atom!("rel"));
        match attr.local_name() {
            &atom!("href") => {
                if string_is_stylesheet(&rel) {
                    self.handle_stylesheet_url(&attr.value());
                } else if is_favicon(&rel) {
                    let sizes = get_attr(self.upcast(), &atom!("sizes"));
                    self.handle_favicon_url(rel.as_ref().unwrap(), &attr.value(), &sizes);
                }
            },
            &atom!("sizes") => {
                if is_favicon(&rel) {
                    if let Some(ref href) = get_attr(self.upcast(), &atom!("href")) {
                        self.handle_favicon_url(rel.as_ref().unwrap(), href, &Some(attr.value().to_string()));
                    }
                }
            },
            &atom!("media") => {
                if string_is_stylesheet(&rel) {
                    self.handle_stylesheet_url(&attr.value());
                }
            },
            _ => {},
        }
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        if tree_in_doc {
            let element = self.upcast();

            let rel = get_attr(element, &atom!("rel"));
            let href = get_attr(element, &atom!("href"));
            let sizes = get_attr(self.upcast(), &atom!("sizes"));

            match href {
                Some(ref href) if string_is_stylesheet(&rel) => {
                    self.handle_stylesheet_url(href);
                }
                Some(ref href) if is_favicon(&rel) => {
                    self.handle_favicon_url(rel.as_ref().unwrap(), href, &sizes);
                }
                _ => {}
            }
        }
    }
}

impl HTMLLinkElement {
    fn handle_stylesheet_url(&self, href: &str) {
        let document = document_from_node(self);
        if document.browsing_context().is_none() {
            return;
        }

        match document.base_url().join(href) {
            Ok(url) => {
                let element = self.upcast::<Element>();

                let mq_attribute = element.get_attribute(&ns!(), &atom!("media"));
                let value = mq_attribute.r().map(|a| a.value());
                let mq_str = match value {
                    Some(ref value) => &***value,
                    None => "",
                };
                let mut css_parser = CssParser::new(&mq_str);
                let media = parse_media_query_list(&mut css_parser);

                // TODO: #8085 - Don't load external stylesheets if the node's mq doesn't match.
                let elem = Trusted::new(self);

                let context = Arc::new(Mutex::new(StylesheetContext {
                    elem: elem,
                    media: Some(media),
                    data: vec!(),
                    metadata: None,
                    url: url.clone(),
                }));

                let (action_sender, action_receiver) = ipc::channel().unwrap();
                let listener = NetworkListener {
                    context: context,
                    script_chan: document.window().networking_task_source(),
                };
                let response_target = AsyncResponseTarget {
                    sender: action_sender,
                };
                ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
                    listener.notify_action(message.to().unwrap());
                });

                if self.parser_inserted.get() {
                    document.increment_script_blocking_stylesheet_count();
                }
                document.load_async(LoadType::Stylesheet(url), response_target);
            }
            Err(e) => debug!("Parsing url {} failed: {}", href, e)
        }
    }

    fn handle_favicon_url(&self, rel: &str, href: &str, sizes: &Option<String>) {
        let document = document_from_node(self);
        match document.base_url().join(href) {
            Ok(url) => {
                let event = ConstellationMsg::NewFavicon(url.clone());
                document.window().constellation_chan().send(event).unwrap();

                let mozbrowser_event = match *sizes {
                    Some(ref sizes) => MozBrowserEvent::IconChange(rel.to_owned(), url.to_string(), sizes.to_owned()),
                    None => MozBrowserEvent::IconChange(rel.to_owned(), url.to_string(), "".to_owned())
                };
                document.trigger_mozbrowser_event(mozbrowser_event);
            }
            Err(e) => debug!("Parsing url {} failed: {}", href, e)
        }
    }
}

/// The context required for asynchronously loading an external stylesheet.
struct StylesheetContext {
    /// The element that initiated the request.
    elem: Trusted<HTMLLinkElement>,
    media: Option<MediaQueryList>,
    /// The response body received to date.
    data: Vec<u8>,
    /// The response metadata received to date.
    metadata: Option<Metadata>,
    /// The initial URL requested.
    url: Url,
}

impl PreInvoke for StylesheetContext {}

impl AsyncResponseListener for StylesheetContext {
    fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
        self.metadata = metadata.ok();
        if let Some(ref meta) = self.metadata {
            if let Some(ContentType(Mime(TopLevel::Text, SubLevel::Css, _))) = meta.content_type {
            } else {
                self.elem.root().upcast::<EventTarget>().fire_simple_event("error");
            }
        }
    }

    fn data_available(&mut self, payload: Vec<u8>) {
        let mut payload = payload;
        self.data.append(&mut payload);
    }

    fn response_complete(&mut self, status: Result<(), NetworkError>) {
        let elem = self.elem.root();
        let document = document_from_node(&*elem);

        if status.is_err() {
            self.elem.root().upcast::<EventTarget>().fire_simple_event("error");
        } else {
            let data = mem::replace(&mut self.data, vec!());
            let metadata = match self.metadata.take() {
                Some(meta) => meta,
                None => return,
            };
            // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
            let environment_encoding = UTF_8 as EncodingRef;
            let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s);
            let final_url = metadata.final_url;

            let win = window_from_node(&*elem);

            let mut sheet = Stylesheet::from_bytes(&data, final_url, protocol_encoding_label,
                                                   Some(environment_encoding), Origin::Author,
                                                   win.css_error_reporter(),
                                                   ParserContextExtraData::default());
            let media = self.media.take().unwrap();
            sheet.set_media(Some(media));
            let sheet = Arc::new(sheet);

            let elem = elem.r();
            let document = document.r();

            let win = window_from_node(elem);
            win.layout_chan().send(Msg::AddStylesheet(sheet.clone())).unwrap();

            *elem.stylesheet.borrow_mut() = Some(sheet);
            document.invalidate_stylesheets();
        }

        if elem.parser_inserted.get() {
            document.decrement_script_blocking_stylesheet_count();
        }

        document.finish_load(LoadType::Stylesheet(self.url.clone()));
    }
}

impl HTMLLinkElementMethods for HTMLLinkElement {
    // https://html.spec.whatwg.org/multipage/#dom-link-href
    make_url_getter!(Href, "href");

    // https://html.spec.whatwg.org/multipage/#dom-link-href
    make_setter!(SetHref, "href");

    // https://html.spec.whatwg.org/multipage/#dom-link-rel
    make_getter!(Rel, "rel");

    // https://html.spec.whatwg.org/multipage/#dom-link-rel
    make_setter!(SetRel, "rel");

    // https://html.spec.whatwg.org/multipage/#dom-link-media
    make_getter!(Media, "media");

    // https://html.spec.whatwg.org/multipage/#dom-link-media
    make_setter!(SetMedia, "media");

    // https://html.spec.whatwg.org/multipage/#dom-link-hreflang
    make_getter!(Hreflang, "hreflang");

    // https://html.spec.whatwg.org/multipage/#dom-link-hreflang
    make_setter!(SetHreflang, "hreflang");

    // https://html.spec.whatwg.org/multipage/#dom-link-type
    make_getter!(Type, "type");

    // https://html.spec.whatwg.org/multipage/#dom-link-type
    make_setter!(SetType, "type");

    // https://html.spec.whatwg.org/multipage/#dom-link-rellist
    fn RelList(&self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| DOMTokenList::new(self.upcast(), &atom!("rel")))
    }

    // https://html.spec.whatwg.org/multipage/#dom-link-charset
    make_getter!(Charset, "charset");

    // https://html.spec.whatwg.org/multipage/#dom-link-charset
    make_setter!(SetCharset, "charset");

    // https://html.spec.whatwg.org/multipage/#dom-link-rev
    make_getter!(Rev, "rev");

    // https://html.spec.whatwg.org/multipage/#dom-link-rev
    make_setter!(SetRev, "rev");

    // https://html.spec.whatwg.org/multipage/#dom-link-target
    make_getter!(Target, "target");

    // https://html.spec.whatwg.org/multipage/#dom-link-target
    make_setter!(SetTarget, "target");
}
<|endoftext|>


<commit_before>//! Node attributes.

use std::io::Read;

use parser::binary::BinaryParser;
use parser::binary::error::{Result, Warning};
use parser::binary::event::NodeHeader;
pub use self::array::ArrayAttribute;
pub use self::special::{SpecialAttribute, SpecialAttributeType};

mod array;
mod special;

/// Node attribute.
#[derive(Debug)]
pub struct Attributes<'a, R: 'a> {
    /// Number of all attributes.
    num_attributes: u64,
    /// Number of rest attributes.
    rest_attributes: u64,
    /// Parser.
    parser: &'a mut BinaryParser<R>,
}

impl<'a, R: 'a + Read> Attributes<'a, R> {
    /// Returns number of all attributes.
    pub fn num_attributes(&self) -> u64 {
        self.num_attributes
    }

    /// Returns number of rest attributes.
    pub fn rest_attributes(&self) -> u64 {
        self.rest_attributes
    }

    /// Returns the next attribute if available.
    pub fn next_attribute(&mut self) -> Result<Option<Attribute<R>>> {
        if self.rest_attributes == 0 {
            return Ok(None);
        }

        self.rest_attributes -= 1;
        let type_code = try!(self.parser.source.read_u8());
        let position = self.parser.source.count();
        match type_code {
            b'C' => {
                let raw = try!(self.parser.source.read_u8());
                let val = (raw & 0x01) == 1;
                if raw != b'T' && raw != b'Y' {
                    self.parser.warn(Warning::InvalidBooleanAttributeValue {
                        got: raw,
                        assumed: val,
                        position: position,
                    });
                }
                Ok(Some(Attribute::Primitive(PrimitiveAttribute::Bool(val))))
            },
            b'Y' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I16(try!(self.parser.source.read_i16()))))),
            b'I' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I32(try!(self.parser.source.read_i32()))))),
            b'L' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I64(try!(self.parser.source.read_i64()))))),
            b'F' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::F32(try!(self.parser.source.read_f32()))))),
            b'D' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::F64(try!(self.parser.source.read_f64()))))),
            _ => unimplemented!(),
        }
    }
}

/// Creates a new `Attributes`.
pub fn new_attributes<'a, R: 'a>(
    parser: &'a mut BinaryParser<R>,
    header: &NodeHeader
) -> Attributes<'a, R> {
    Attributes {
        num_attributes: header.num_attributes,
        rest_attributes: header.num_attributes,
        parser: parser,
    }
}

/// Node attribute.
#[derive(Debug)]
pub enum Attribute<'a, R: 'a> {
    /// Primitive type value.
    Primitive(PrimitiveAttribute),
    /// Array type value.
    Array(ArrayAttribute<'a, R>),
    /// Special type value.
    Special(SpecialAttribute<'a, R>),
}

/// Attribute type of primitive value.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PrimitiveAttribute {
    /// Single `bool`.
    Bool(bool),
    /// Single `i16`.
    I16(i16),
    /// Single `i32`.
    I32(i32),
    /// Single `i64`.
    I64(i64),
    /// Single `f32`.
    F32(f32),
    /// Single `f64`.
    F64(f64),
}

impl PrimitiveAttribute {
    /// Converts floating-point number into `f32`.
    pub fn as_f32(&self) -> Option<f32> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v),
            PrimitiveAttribute::F64(v) => Some(v as f32),
            _ => None,
        }
    }

    /// Converts floating-point number into `f32`.
    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v as f64),
            PrimitiveAttribute::F64(v) => Some(v),
            _ => None,
        }
    }
}
<commit_msg>Prepare for reading variable length attributes<commit_after>//! Node attributes.

use std::io::Read;

use parser::binary::BinaryParser;
use parser::binary::error::{Result, Warning};
use parser::binary::event::NodeHeader;
pub use self::array::ArrayAttribute;
pub use self::special::{SpecialAttribute, SpecialAttributeType};

mod array;
mod special;

/// Node attribute.
#[derive(Debug)]
pub struct Attributes<'a, R: 'a> {
    /// Number of all attributes.
    num_attributes: u64,
    /// Number of rest attributes.
    rest_attributes: u64,
    /// End offset of the previous attribute.
    prev_attr_end: Option<u64>,
    /// Parser.
    parser: &'a mut BinaryParser<R>,
}

impl<'a, R: 'a + Read> Attributes<'a, R> {
    /// Returns number of all attributes.
    pub fn num_attributes(&self) -> u64 {
        self.num_attributes
    }

    /// Returns number of rest attributes.
    pub fn rest_attributes(&self) -> u64 {
        self.rest_attributes
    }

    /// Returns the next attribute if available.
    pub fn next_attribute(&mut self) -> Result<Option<Attribute<R>>> {
        if self.rest_attributes == 0 {
            return Ok(None);
        }

        // Skip unread part of the previous attribute if available.
        if let Some(prev_attr_end) = self.prev_attr_end {
            try!(self.parser.source.skip_to(prev_attr_end));
            self.prev_attr_end = None;
        }

        self.rest_attributes -= 1;
        let type_code = try!(self.parser.source.read_u8());
        let position = self.parser.source.count();
        match type_code {
            b'C' => {
                let raw = try!(self.parser.source.read_u8());
                let val = (raw & 0x01) == 1;
                if raw != b'T' && raw != b'Y' {
                    self.parser.warn(Warning::InvalidBooleanAttributeValue {
                        got: raw,
                        assumed: val,
                        position: position,
                    });
                }
                Ok(Some(Attribute::Primitive(PrimitiveAttribute::Bool(val))))
            },
            b'Y' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I16(try!(self.parser.source.read_i16()))))),
            b'I' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I32(try!(self.parser.source.read_i32()))))),
            b'L' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I64(try!(self.parser.source.read_i64()))))),
            b'F' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::F32(try!(self.parser.source.read_f32()))))),
            b'D' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::F64(try!(self.parser.source.read_f64()))))),
            _ => unimplemented!(),
        }
    }
}

/// Creates a new `Attributes`.
pub fn new_attributes<'a, R: 'a>(
    parser: &'a mut BinaryParser<R>,
    header: &NodeHeader
) -> Attributes<'a, R> {
    Attributes {
        num_attributes: header.num_attributes,
        rest_attributes: header.num_attributes,
        prev_attr_end: None,
        parser: parser,
    }
}

/// Node attribute.
#[derive(Debug)]
pub enum Attribute<'a, R: 'a> {
    /// Primitive type value.
    Primitive(PrimitiveAttribute),
    /// Array type value.
    Array(ArrayAttribute<'a, R>),
    /// Special type value.
    Special(SpecialAttribute<'a, R>),
}

/// Attribute type of primitive value.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PrimitiveAttribute {
    /// Single `bool`.
    Bool(bool),
    /// Single `i16`.
    I16(i16),
    /// Single `i32`.
    I32(i32),
    /// Single `i64`.
    I64(i64),
    /// Single `f32`.
    F32(f32),
    /// Single `f64`.
    F64(f64),
}

impl PrimitiveAttribute {
    /// Converts floating-point number into `f32`.
    pub fn as_f32(&self) -> Option<f32> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v),
            PrimitiveAttribute::F64(v) => Some(v as f32),
            _ => None,
        }
    }

    /// Converts floating-point number into `f32`.
    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v as f64),
            PrimitiveAttribute::F64(v) => Some(v),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>pub trait GerberCode {
    fn to_code(&self) -> String;
}

#[derive(Debug)]
pub enum Command {
    FunctionCode(FunctionCode),
    ExtendedCode(ExtendedCode),
}

#[derive(Debug)]
pub enum FunctionCode {
    DCode(DCode),
    GCode(GCode),
    MCode(MCode),
}

#[derive(Debug)]
pub enum ExtendedCode {
    FS,
    MO,
    AD,
    AM,
    SR,
    LP,
    TF,
    TA,
    TD,
}

#[derive(Debug)]
pub enum DCode {
    Operation,
    SelectAperture,
}

#[derive(Debug)]
pub enum InterpolationMode {
    Linear,
    ClockwiseCircular,
    CounterclockwiseCircular,
}

#[derive(Debug)]
pub enum QuadrantMode {
    Single,
    Multi,
}

#[derive(Debug)]
pub enum GCode {
    InterpolationMode(InterpolationMode),
    RegionMode(bool),
    QuadrantMode(QuadrantMode),
    Comment(String),
}

#[derive(Debug)]
pub enum MCode {
    EndOfFile,
}

impl GerberCode for GCode {
    fn to_code(&self) -> String {
        match self {
            &GCode::InterpolationMode(ref mode) => {
                match *mode {
                    InterpolationMode::Linear => "G01*",
                    InterpolationMode::ClockwiseCircular => "G02*",
                    InterpolationMode::CounterclockwiseCircular => "G03*",
                }.to_string()
            },
            &GCode::RegionMode(enabled) => {
                match enabled {
                    true => "G36*",
                    false => "G37*",
                }.to_string()
            },
            &GCode::QuadrantMode(ref mode) => {
                match *mode {
                    QuadrantMode::Single => "G74*",
                    QuadrantMode::Multi => "G75*",
                }.to_string()
            },
            &GCode::Comment(ref comment) => format!("G04 {} *", comment),
        }
    }
}

impl<T: GerberCode> GerberCode for Vec<T> {
    fn to_code(&self) -> String {
        self.iter()
            .map(|g| g.to_code())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

#[cfg(test)]
mod test {
    use super::{Command, FunctionCode, DCode};
    use super::{GCode, InterpolationMode, QuadrantMode};
    use super::GerberCode;

    #[test]
    fn test_gcode_debug() {
        let c = Command::FunctionCode(FunctionCode::GCode(GCode::Comment("test".to_string())));
        let debug = format!("{:?}", c);
        assert_eq!(debug, "FunctionCode(GCode(Comment(\"test\")))");
    }

    #[test]
    fn test_to_code() {
        let comment = GCode::Comment("testcomment".to_string());
        assert_eq!(comment.to_code(), "G04 testcomment *".to_string());
    }

    #[test]
    fn test_vec_to_code() {
        let mut v = Vec::new();
        v.push(GCode::Comment("comment 1".to_string()));
        v.push(GCode::Comment("another one".to_string()));
        assert_eq!(v.to_code(), "G04 comment 1 *\nG04 another one *".to_string());
    }

    #[test]
    fn test_interpolation_mode() {
        let mut commands = Vec::new();
        let c1 = GCode::InterpolationMode(InterpolationMode::Linear);
        let c2 = GCode::InterpolationMode(InterpolationMode::ClockwiseCircular);
        let c3 = GCode::InterpolationMode(InterpolationMode::CounterclockwiseCircular);
        commands.push(c1);
        commands.push(c2);
        commands.push(c3);
        assert_eq!(commands.to_code(), "G01*\nG02*\nG03*".to_string());
    }

    #[test]
    fn test_region_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::RegionMode(true));
        commands.push(GCode::RegionMode(false));
        assert_eq!(commands.to_code(), "G36*\nG37*".to_string());
    }

    #[test]
    fn test_quadrant_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::QuadrantMode(QuadrantMode::Single));
        commands.push(GCode::QuadrantMode(QuadrantMode::Multi));
        assert_eq!(commands.to_code(), "G74*\nG75*".to_string());
    }

}
<commit_msg>Implement M-codes<commit_after>pub trait GerberCode {
    fn to_code(&self) -> String;
}

#[derive(Debug)]
pub enum Command {
    FunctionCode(FunctionCode),
    ExtendedCode(ExtendedCode),
}

#[derive(Debug)]
pub enum FunctionCode {
    DCode(DCode),
    GCode(GCode),
    MCode(MCode),
}

#[derive(Debug)]
pub enum ExtendedCode {
    FS,
    MO,
    AD,
    AM,
    SR,
    LP,
    TF,
    TA,
    TD,
}

#[derive(Debug)]
pub enum DCode {
    Operation,
    SelectAperture,
}

#[derive(Debug)]
pub enum InterpolationMode {
    Linear,
    ClockwiseCircular,
    CounterclockwiseCircular,
}

#[derive(Debug)]
pub enum QuadrantMode {
    Single,
    Multi,
}

#[derive(Debug)]
pub enum GCode {
    InterpolationMode(InterpolationMode),
    RegionMode(bool),
    QuadrantMode(QuadrantMode),
    Comment(String),
}

impl GerberCode for GCode {
    fn to_code(&self) -> String {
        match self {
            &GCode::InterpolationMode(ref mode) => {
                match *mode {
                    InterpolationMode::Linear => "G01*",
                    InterpolationMode::ClockwiseCircular => "G02*",
                    InterpolationMode::CounterclockwiseCircular => "G03*",
                }.to_string()
            },
            &GCode::RegionMode(enabled) => {
                match enabled {
                    true => "G36*",
                    false => "G37*",
                }.to_string()
            },
            &GCode::QuadrantMode(ref mode) => {
                match *mode {
                    QuadrantMode::Single => "G74*",
                    QuadrantMode::Multi => "G75*",
                }.to_string()
            },
            &GCode::Comment(ref comment) => format!("G04 {} *", comment),
        }
    }
}

#[derive(Debug)]
pub enum MCode {
    EndOfFile,
}

impl GerberCode for MCode {
    fn to_code(&self) -> String {
        match *self {
            MCode::EndOfFile => "M02*",
        }.to_string()
    }
}

/// Implement GerberCode for Vectors of types that implement GerberCode.
impl<T: GerberCode> GerberCode for Vec<T> {
    fn to_code(&self) -> String {
        self.iter()
            .map(|g| g.to_code())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

#[cfg(test)]
mod test {
    use super::{Command, FunctionCode, DCode};
    use super::{GCode, InterpolationMode, QuadrantMode};
    use super::{MCode};
    use super::GerberCode;

    #[test]
    fn test_debug() {
        //! The debug representation should work properly.
        let c = Command::FunctionCode(FunctionCode::GCode(GCode::Comment("test".to_string())));
        let debug = format!("{:?}", c);
        assert_eq!(debug, "FunctionCode(GCode(Comment(\"test\")))");
    }

    #[test]
    fn test_to_code() {
        //! The to_code method of the GerberCode trait should generate strings.
        let comment = GCode::Comment("testcomment".to_string());
        assert_eq!(comment.to_code(), "G04 testcomment *".to_string());
    }

    #[test]
    fn test_vec_to_code() {
        //! A `Vec<T: GerberCode>` should also implement `GerberCode`.
        let mut v = Vec::new();
        v.push(GCode::Comment("comment 1".to_string()));
        v.push(GCode::Comment("another one".to_string()));
        assert_eq!(v.to_code(), "G04 comment 1 *\nG04 another one *".to_string());
    }

    #[test]
    fn test_interpolation_mode() {
        let mut commands = Vec::new();
        let c1 = GCode::InterpolationMode(InterpolationMode::Linear);
        let c2 = GCode::InterpolationMode(InterpolationMode::ClockwiseCircular);
        let c3 = GCode::InterpolationMode(InterpolationMode::CounterclockwiseCircular);
        commands.push(c1);
        commands.push(c2);
        commands.push(c3);
        assert_eq!(commands.to_code(), "G01*\nG02*\nG03*".to_string());
    }

    #[test]
    fn test_region_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::RegionMode(true));
        commands.push(GCode::RegionMode(false));
        assert_eq!(commands.to_code(), "G36*\nG37*".to_string());
    }

    #[test]
    fn test_quadrant_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::QuadrantMode(QuadrantMode::Single));
        commands.push(GCode::QuadrantMode(QuadrantMode::Multi));
        assert_eq!(commands.to_code(), "G74*\nG75*".to_string());
    }

    #[test]
    fn test_end_of_file() {
        let c = MCode::EndOfFile;
        assert_eq!(c.to_code(), "M02*".to_string());
    }

}
<|endoftext|>


<commit_before>//! Data consumers
//!
//! The goal of data producers is to parse data depending on the previous result.
//! It can be used to selectively seek in a file.
//!
//! ## Example
//!
//! This consumer will take 4 samples from the input, print them, then stop
//!
//! ```rust
//!  use nom::{IResult,Needed,MemProducer,Consumer,ConsumerState};
//!  use std::str;
//!
//!  struct TestPrintConsumer {
//!    counter: usize
//!  }
//!
//!  impl TestPrintConsumer {
//!    fn new() -> TestPrintConsumer {
//!      TestPrintConsumer { counter: 0 }
//!    }
//!  }
//!
//!  fn take4(i:&[u8]) -> IResult<&[u8], &[u8]>{
//!    if i.len() < 4 {
//!      IResult::Incomplete(Needed::Size(4))
//!    } else {
//!      IResult::Done(&i[4..],&i[0..4])
//!    }
//!  }
//!
//!  // Return ConsumerState::Await if it needs more data, or ConsumerDone when it ends
//!  impl Consumer for TestPrintConsumer {
//!    fn consume(&mut self, input: &[u8]) -> ConsumerState {
//!      match take4(input) {
//!        IResult::Error(a)      => ConsumerState::ConsumerError(0),
//!        IResult::Incomplete(a) => ConsumerState::Await(0, 4),
//!        IResult::Done(i, o)    => {
//!          println!("{} -> {}", self.counter, str::from_utf8(o).unwrap());
//!          self.counter = self.counter + 1;
//!          if self.counter <= 4 {
//!            ConsumerState::Await(4, 4)
//!          } else {
//!            ConsumerState::ConsumerDone
//!          }
//!        }
//!      }
//!    }
//!
//!    fn failed(&mut self, error_code: u32) {
//!      println!("failed with error code {}", error_code);
//!    }
//!
//!    fn end(&mut self) {
//!      println!("finished");
//!    }
//!  }
//!
//!  // It can consume data directly from a producer
//!  let mut p = MemProducer::new(b"abcdefghijklmnopqrstuvwx", 4);
//!  let mut c = TestPrintConsumer::new();
//!  c.run(&mut p);
//! ```

use self::ConsumerState::*;
use producer::Producer;
use producer::ProducerState::*;
use std::io::SeekFrom;

/// Holds the current state of the consumer
///
/// * Await(consumed, needed input data size) if more data is needed
///
/// * Seek(consumed, new position, needed input data size) if the consumer must move back or forth
///
/// * Incomplete if there is not enough data but not enough information for Await or Seek
///
/// * ConsumerDone if the consumer does not need anymore data to be parsed
///
/// * ConsumerError(error code) when something went wrong
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
pub enum ConsumerState {
  Await(
    usize,    // consumed
    usize     // needed buffer size
  ),
  Seek(
    usize,    // consumed
    SeekFrom, // new position
    usize     // needed buffer size
  ),
  Incomplete,
  ConsumerDone,
  ConsumerError(u32)
}

/// Implement the consume method, taking a byte array as input and returning a consumer state
///
/// The run function takes care of continuing or not
pub trait Consumer {
  fn consume(&mut self, input: &[u8]) -> ConsumerState;
  fn failed(&mut self, error_code: u32);
  fn end(&mut self);

  fn run(&mut self, producer: &mut Producer) {
    let mut acc: Vec<u8>       = Vec::new();
    let mut position           = 0;
    let mut should_seek        = false;
    let mut consumed:usize     = 0;
    let mut needed:usize       = 0;
    let mut seek_from:SeekFrom = SeekFrom::Current(0);
    let mut eof = false;
    let mut end = false;
    let mut err: Option<u32> = None;

    loop {
      //self.getDataFromProducer(producer, seek_from, needed, acc);
      if !should_seek && acc.len() - consumed >= needed {
        //println!("buffer is large enough, skipping");
        let mut tmp = Vec::new();
        //println!("before:\n{}", acc.to_hex(16));
        //println!("after:\n{}", (&acc[consumed..acc.len()]).to_hex(16));
        tmp.extend(acc[consumed..acc.len()].iter().cloned());
        acc.clear();
        acc = tmp;
      } else {
        if should_seek {
          let _ = producer.seek(seek_from);
          //println!("seeking: {:?}", pos);
          should_seek = false;
          acc.clear();
        } else {
          let mut tmp = Vec::new();
          tmp.extend(acc[consumed..acc.len()].iter().cloned());
          acc.clear();
          acc = tmp;
        }

        loop {
          let state   = producer.produce();
          match state {
            Data(v) => {
              //println!("got data: {} bytes", v.len());
              acc.extend(v.iter().cloned());
              position = position + v.len();
            },
            Eof(v) => {
              if v.is_empty() {
                //println!("eof empty");
                //eof = true;
                self.end();
                return
              } else {
                //println!("eof with {} bytes", v.len());
                eof = true;
                acc.extend(v.iter().cloned());
                position = position + v.len();
                break;
              }
            }
            ProducerError(_) => {break;}
            Continue => { continue; }
          }
          //println!("acc size: {}", acc.len());
          if acc.len() >= needed { break; }
        }
      }

      //println!("full:\n{}", acc.to_hex(8));
      //println!("truncated:\n{}", (&acc[0..needed]).to_hex(16));
      match self.consume(&acc[0..needed]) {
        ConsumerError(e) => {
          //println!("consumer error, stopping: {}", e);
          err = Some(e);
        },
        ConsumerDone => {
          //println!("data, done");
          end = true;
        },
        Seek(consumed_bytes, sf, needed_bytes) => {
          //println!("Seek: consumed {} bytes, got {:?} and asked {} bytes", consumed_bytes, sf, needed_bytes);
          seek_from = match sf {
            SeekFrom::Current(i) => SeekFrom::Current(i - (acc.len() - needed) as i64),
            a => a
          };
          should_seek = true;
          consumed = consumed_bytes;
          needed   = needed_bytes;
        },
        Await(consumed_bytes, needed_bytes) => {
          //println!("consumed: {} bytes | needed: {} bytes", consumed_bytes, needed_bytes);
          consumed = consumed_bytes;
          needed   = needed_bytes;
        },
        Incomplete => {
          //println!("incomplete");
        }
      }
      if let Some(e) = err {
        self.failed(e);
        break;
      }
      if eof {
        self.end();
        break;
      }
      if end {
        self.end();
        break;
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use super::ConsumerState::*;
  use producer::MemProducer;
  use internal::{Needed,IResult};
  use std::str;

#[macro_export]
macro_rules! take(
  ($name:ident $count:expr) => (
    fn $name(i:&[u8]) -> IResult<&[u8], &[u8]>{
      if i.len() < $count {
        IResult::Incomplete(Needed::Size($count))
      } else {
        IResult::Done(&i[$count..],&i[0..$count])
      }
    }
  )
);

  struct TestPrintConsumer {
    counter: usize,
    ended:   bool
  }

  impl TestPrintConsumer {
    fn new() -> TestPrintConsumer {
      TestPrintConsumer { counter: 0, ended: false }
    }
  }

  take!(take4 4);

  impl Consumer for TestPrintConsumer {
    fn consume(&mut self, input: &[u8]) -> ConsumerState {
      match take4(input) {
        IResult::Error(_)      => ConsumerError(0),
        IResult::Incomplete(_) => Await(0, 4),
        IResult::Done(_, o)    => {
          println!("{} -> {}", self.counter, str::from_utf8(o).unwrap());
          self.counter = self.counter + 1;
          if self.counter <= 4 {
            Await(4, 4)
          } else {
            ConsumerDone
          }
        }
      }
    }

    fn end(&mut self) {
      assert_eq!(self.counter, 5);
      self.ended = true;
    }

    fn failed(&mut self, error_code: u32) {
      println!("failed with error code: {}", error_code);
    }
  }

  #[test]
  fn pull() {
    let mut p = MemProducer::new(&b"abcdefghijklmnopqrstuvwx"[..], 4);
    let mut c = TestPrintConsumer::new();
    c.run(&mut p);

    assert!(c.ended);
  }
}
<commit_msg>Fix for seeking from the end of the input.<commit_after>//! Data consumers
//!
//! The goal of data producers is to parse data depending on the previous result.
//! It can be used to selectively seek in a file.
//!
//! ## Example
//!
//! This consumer will take 4 samples from the input, print them, then stop
//!
//! ```rust
//!  use nom::{IResult,Needed,MemProducer,Consumer,ConsumerState};
//!  use std::str;
//!
//!  struct TestPrintConsumer {
//!    counter: usize
//!  }
//!
//!  impl TestPrintConsumer {
//!    fn new() -> TestPrintConsumer {
//!      TestPrintConsumer { counter: 0 }
//!    }
//!  }
//!
//!  fn take4(i:&[u8]) -> IResult<&[u8], &[u8]>{
//!    if i.len() < 4 {
//!      IResult::Incomplete(Needed::Size(4))
//!    } else {
//!      IResult::Done(&i[4..],&i[0..4])
//!    }
//!  }
//!
//!  // Return ConsumerState::Await if it needs more data, or ConsumerDone when it ends
//!  impl Consumer for TestPrintConsumer {
//!    fn consume(&mut self, input: &[u8]) -> ConsumerState {
//!      match take4(input) {
//!        IResult::Error(a)      => ConsumerState::ConsumerError(0),
//!        IResult::Incomplete(a) => ConsumerState::Await(0, 4),
//!        IResult::Done(i, o)    => {
//!          println!("{} -> {}", self.counter, str::from_utf8(o).unwrap());
//!          self.counter = self.counter + 1;
//!          if self.counter <= 4 {
//!            ConsumerState::Await(4, 4)
//!          } else {
//!            ConsumerState::ConsumerDone
//!          }
//!        }
//!      }
//!    }
//!
//!    fn failed(&mut self, error_code: u32) {
//!      println!("failed with error code {}", error_code);
//!    }
//!
//!    fn end(&mut self) {
//!      println!("finished");
//!    }
//!  }
//!
//!  // It can consume data directly from a producer
//!  let mut p = MemProducer::new(b"abcdefghijklmnopqrstuvwx", 4);
//!  let mut c = TestPrintConsumer::new();
//!  c.run(&mut p);
//! ```

use self::ConsumerState::*;
use producer::Producer;
use producer::ProducerState::*;
use std::io::SeekFrom;

/// Holds the current state of the consumer
///
/// * Await(consumed, needed input data size) if more data is needed
///
/// * Seek(consumed, new position, needed input data size) if the consumer must move back or forth
///
/// * Incomplete if there is not enough data but not enough information for Await or Seek
///
/// * ConsumerDone if the consumer does not need anymore data to be parsed
///
/// * ConsumerError(error code) when something went wrong
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
pub enum ConsumerState {
  Await(
    usize,    // consumed
    usize     // needed buffer size
  ),
  Seek(
    usize,    // consumed
    SeekFrom, // new position
    usize     // needed buffer size
  ),
  Incomplete,
  ConsumerDone,
  ConsumerError(u32)
}

/// Implement the consume method, taking a byte array as input and returning a consumer state
///
/// The run function takes care of continuing or not
pub trait Consumer {
  fn consume(&mut self, input: &[u8]) -> ConsumerState;
  fn failed(&mut self, error_code: u32);
  fn end(&mut self);

  fn run(&mut self, producer: &mut Producer) {
    let mut acc: Vec<u8>       = Vec::new();
    let mut position           = 0;
    let mut should_seek        = false;
    let mut consumed:usize     = 0;
    let mut needed:usize       = 0;
    let mut seek_from:SeekFrom = SeekFrom::Current(0);
    let mut eof = false;
    let mut end = false;
    let mut err: Option<u32> = None;

    loop {
      //self.getDataFromProducer(producer, seek_from, needed, acc);
      if !should_seek && acc.len() - consumed >= needed {
        //println!("buffer is large enough, skipping");
        let mut tmp = Vec::new();
        //println!("before:\n{}", acc.to_hex(16));
        //println!("after:\n{}", (&acc[consumed..acc.len()]).to_hex(16));
        tmp.extend(acc[consumed..acc.len()].iter().cloned());
        acc.clear();
        acc = tmp;
      } else {
        if should_seek {
          let _ = producer.seek(seek_from);
          //println!("seeking: {:?}", pos);
          should_seek = false;
          eof = false;
          acc.clear();
        } else {
          let mut tmp = Vec::new();
          tmp.extend(acc[consumed..acc.len()].iter().cloned());
          acc.clear();
          acc = tmp;
        }

        loop {
          let state   = producer.produce();
          match state {
            Data(v) => {
              //println!("got data: {} bytes", v.len());
              acc.extend(v.iter().cloned());
              position = position + v.len();
            },
            Eof(v) => {
              if v.is_empty() {
                //println!("eof empty");
                //eof = true;
                self.end();
                return
              } else {
                //println!("eof with {} bytes", v.len());
                eof = true;
                acc.extend(v.iter().cloned());
                position = position + v.len();
                break;
              }
            }
            ProducerError(_) => {break;}
            Continue => { continue; }
          }
          //println!("acc size: {}", acc.len());
          if acc.len() >= needed { break; }
        }
      }

      //println!("full:\n{}", acc.to_hex(8));
      //println!("truncated:\n{}", (&acc[0..needed]).to_hex(16));
      match self.consume(&acc[0..needed]) {
        ConsumerError(e) => {
          //println!("consumer error, stopping: {}", e);
          err = Some(e);
        },
        ConsumerDone => {
          //println!("data, done");
          end = true;
        },
        Seek(consumed_bytes, sf, needed_bytes) => {
          //println!("Seek: consumed {} bytes, got {:?} and asked {} bytes", consumed_bytes, sf, needed_bytes);
          seek_from = match sf {
            SeekFrom::Current(i) => SeekFrom::Current(i - (acc.len() - needed) as i64),
            a => a
          };
          should_seek = true;
          consumed = consumed_bytes;
          needed   = needed_bytes;
        },
        Await(consumed_bytes, needed_bytes) => {
          //println!("consumed: {} bytes | needed: {} bytes", consumed_bytes, needed_bytes);
          consumed = consumed_bytes;
          needed   = needed_bytes;
        },
        Incomplete => {
          //println!("incomplete");
        }
      }
      if let Some(e) = err {
        self.failed(e);
        break;
      }
      if eof && !should_seek {
        self.end();
        break;
      }
      if end {
        self.end();
        break;
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use super::ConsumerState::*;
  use producer::MemProducer;
  use internal::{Needed,IResult};
  use std::str;
  use std::io::SeekFrom;

#[macro_export]
macro_rules! take(
  ($name:ident $count:expr) => (
    fn $name(i:&[u8]) -> IResult<&[u8], &[u8]>{
      if i.len() < $count {
        IResult::Incomplete(Needed::Size($count))
      } else {
        IResult::Done(&i[$count..],&i[0..$count])
      }
    }
  )
);

  struct TestPrintConsumer {
    counter: usize,
    ended:   bool
  }

  impl TestPrintConsumer {
    fn new() -> TestPrintConsumer {
      TestPrintConsumer { counter: 0, ended: false }
    }
  }

  take!(take4 4);

  impl Consumer for TestPrintConsumer {
    fn consume(&mut self, input: &[u8]) -> ConsumerState {
      match take4(input) {
        IResult::Error(_)      => ConsumerError(0),
        IResult::Incomplete(_) => Await(0, 4),
        IResult::Done(_, o)    => {
          println!("{} -> {}", self.counter, str::from_utf8(o).unwrap());
          self.counter = self.counter + 1;
          if self.counter <= 4 {
            Await(4, 4)
          } else {
            ConsumerDone
          }
        }
      }
    }

    fn end(&mut self) {
      assert_eq!(self.counter, 5);
      self.ended = true;
    }

    fn failed(&mut self, error_code: u32) {
      println!("failed with error code: {}", error_code);
    }
  }

  #[test]
  fn pull() {
    let mut p = MemProducer::new(&b"abcdefghijklmnopqrstuvwx"[..], 4);
    let mut c = TestPrintConsumer::new();
    c.run(&mut p);

    assert!(c.ended);
  }

  #[derive(Debug,PartialEq,Eq,Clone,Copy)]
  enum TestSeekState {
      Start,
      ToEnd,
      ToStart,
      Further
  }

  #[derive(Debug)]
  struct TestSeekConsumer {
      state: TestSeekState,
      result: [u8;3],
      ended: bool
  }

  impl TestSeekConsumer {

      fn new() -> TestSeekConsumer {
          TestSeekConsumer { state: TestSeekState::Start,
                            result: [0x00, 0x00, 0x00],
                             ended: false
           }
      }
      fn result(&self) -> [u8;3] {
          self.result
      }

      fn ended(&self) -> bool {
          self.ended
      }
  }

  impl Consumer for TestSeekConsumer {

    fn consume(&mut self, input: &[u8]) -> ConsumerState {
        let s = self.state;
        match s {
            TestSeekState::Start => {
                self.state = TestSeekState::ToEnd;
                ConsumerState::Seek(0,SeekFrom::End(-1), 1)
            },
            TestSeekState::ToEnd => {
                self.state = TestSeekState::ToStart;
                self.result[0] = input[0];
                ConsumerState::Seek(1,SeekFrom::Start(0), 1)
            },
            TestSeekState::ToStart => {
                self.state = TestSeekState::Further;
                self.result[1] = input[0];
                ConsumerState::Await(1,1)
            }
            TestSeekState::Further => {
                self.result[2] = input[0];
                ConsumerState::ConsumerDone
            }

        }
    }

    fn end(&mut self) {
      assert_eq!(self.state, TestSeekState::Further);
      self.ended = true;
    }

    fn failed(&mut self, error_code: u32) {
      println!("failed with error code: {}", error_code);
    }
  }

  #[test]
  fn seeking() {
    let mut p = MemProducer::new(&b"abc"[..], 1);
    let mut c = TestSeekConsumer::new();
    c.run(&mut p);
    let res = c.result();
    assert_eq!(Ok("cab"),str::from_utf8(&res));
    assert!(c.ended());
  }
}
<|endoftext|>


<commit_before>/*
Each client talks to a single consumer object, which references the shared log.

Consumer:
Client LWM -------------- Sent point -------------- Client HWM
*/

use data::Seqno;
use replica::{Log, Outputs};
use mio;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        InvalidRange(lwm: Option<Seqno>, sent: Option<Seqno>, mark: Seqno)
    }
}

#[derive(Debug)]
pub struct Consumer {
    low_water_mark: Option<Seqno>,
    sent: Option<Seqno>,
}

impl Consumer {
    pub fn new() -> Consumer {
        Consumer {
            low_water_mark: None,
            sent: None,
        }
    }

    pub fn consume_requested(&mut self, mark: Seqno) -> Result<(), Error> {
        // It would be odd for a client to request an item they have not seen
        // in the current session.
        let lwm = match (self.low_water_mark, self.sent) {
            (None, None) => Some(mark),
            (Some(m), None) if mark >= m => {
                Some(m)
            },
            (Some(m), Some(s)) if mark >= m && mark <= s => {
                Some(mark)
            },

            (None, Some(_)) => unreachable!(),
            (m, s) => return Err(Error::InvalidRange(m, s, mark)),
        };
        debug!("consume_requested: {:?}; mark:{:?} => {:?}", self, mark, lwm);
        self.low_water_mark = lwm;
        Ok(())
    }

    pub fn process<L: Log, O: Outputs>(&mut self,
            out: &mut O, token: mio::Token, log: &L) -> bool {
        let mut changed = false;
        let next = self.low_water_mark;
        let committed = log.read_committed();
        trace!("Consumer#process: committed: {:?}; next: {:?}", committed, next);

        if let (Some(next), Some(committed)) = (next, committed) {
            for i in next.upto(&committed) {
                debug!("Consuming seq:{:?}", i);
                if let Some(op) = log.read(i) {
                    debug!("Consume seq:{:?}/{:?}; ds/seqno: {:?}", i, op, self.low_water_mark);
                    out.consumer_message(token, i, op.into());
                    self.sent = Some(i);
                    self.low_water_mark = Some(i.succ());
                    changed = true
                } else {
                    panic!("Attempted to consume item not in log: {:?}", i);
                }
            }
        }
        changed
    }
}

#[cfg(test)]
mod test {
    use consumer::Consumer;
    use data::Seqno;
    use replica::Log;
    use replica::test::{Outs,OutMessage};
    use replication_log::test::{VecLog,TestLog, LogCommand, arbitrary_given, hash};
    use slab::Index;
    use mio;
    use quickcheck::{self, Arbitrary, Gen, TestResult};
    use std::cmp;

    #[derive(Debug, Clone, Hash)]
    enum ConsOp {
        RequestMark(Seqno),
        Log(LogCommand),
        RunProcess,
    }

    impl Arbitrary for ConsOp {
        fn arbitrary<G: Gen>(g: &mut G) -> ConsOp {
            let case = u64::arbitrary(g) % 100;
            let res = match case {
                0 ... 20 => ConsOp::RequestMark(Arbitrary::arbitrary(g)),
                20 ... 50 => ConsOp::Log(Arbitrary::arbitrary(g)),
                _ => ConsOp::RunProcess,
            };
            res
        }
        fn shrink(&self) -> Box<Iterator<Item = Self> + 'static> {
            let h = hash(self);
            trace!("ConsOp#shrink {:x}: {:?}", h, self);
            match self {
                &ConsOp::RequestMark(ref s) => Box::new(s.shrink().map(ConsOp::RequestMark).inspect(move |it| trace!("ConsOp#shrink {:x}: => {:?}", h, it))),
                &ConsOp::Log(ref cmd) => Box::new(cmd.shrink().map(ConsOp::Log).inspect(move |it| trace!("ConsOp#shrink {:x}: => {:?}", h, it))),
                &ConsOp::RunProcess => Box::new(vec![].into_iter().inspect(move |it| debug!("ConsOp#shrank {:x}: => {:?}", h, it))),
            }
        }
    }

    impl ConsOp {
        pub fn apply_to<L: Log>(&self, model: &mut L) {
            match self {
                &ConsOp::Log(ref c) => c.apply_to(model),
                _ => (),
            }
        }

        fn satisfies_precondition(&self, model: &VecLog) -> bool {
            let ret = match self {
                &ConsOp::Log(ref c) => c.satisfies_precondition(model),
                &ConsOp::RequestMark(m) => Some(m) <= model.read_committed(),
                _ => true,
            };
            trace!("ConsOp#satisfies_precondition: {:?}/{:?} -> {:?}", self, model, ret);
            ret
        }
    }

    #[derive(Debug, Clone, Hash)]
    struct Commands(Vec<ConsOp>);

    impl Commands {
        fn validate_commands(&self) -> bool {
            let (_, okayp) = self.0.iter().fold((VecLog::new(move |_| ()), true),
                    |(model_log, okayp), cmd| {
                if !okayp {
                    (model_log, false)
                } else  {
                    let okayp = cmd.satisfies_precondition(&model_log);
                    (model_log, okayp)
                }
            });

            okayp
        }
    }

    impl Arbitrary for Commands {
        fn arbitrary<G: Gen>(g: &mut G) -> Commands {
            let sz = usize::arbitrary(g);
            let mut commands : Vec<ConsOp> = Vec::with_capacity(sz);
            let mut model_log = VecLog::new(move |seq| ());

            for _ in 0..sz {
                let cmd = arbitrary_given(g, |cmd: &ConsOp| cmd.satisfies_precondition(&model_log));
                debug!("Generated command: {:?}", cmd);
                cmd.apply_to(&mut model_log);
                commands.push(cmd);
            };
            Commands(commands)
        }
        fn shrink(&self) -> Box<Iterator<Item = Self> + 'static> {
            let h = hash(self);
            trace!("Commands#shrink {:x}: {:?}", h, self);
            let ret = Arbitrary::shrink(&self.0).map(Commands).filter(Commands::validate_commands).inspect(move |it| trace!("Commands#shrink {:x}: => {:?}", h, it));
            Box::new(ret)
        }
    }

    fn should_do_the_thing_prop<L: TestLog>(cmds: Commands) -> TestResult {
        debug!("commands {:x}: {:?}", hash(&cmds), cmds);
        let Commands(cmds) = cmds;
        let mut actual = Consumer::new();
        let mut log = VecLog::new(move |_| ());

        let token = mio::Token::from_usize(0);
        let mut observed = Outs::new();

        let mut min_seq = None;
        let mut prev_seq = None;

        for cmd in cmds {
            debug!("apply: {:?}", cmd);
            match cmd {
                ConsOp::RequestMark(s) => {
                    let res = actual.consume_requested(s);
                    min_seq = Some(min_seq.unwrap_or(s));
                    if Some(s) < prev_seq { assert!(res.is_err()) }

                    if let Some(sent) = observed.borrow().iter().rev().filter_map(|x| {
                        match x {
                            &OutMessage::ConsumerMessage(_, seq, _) => Some(seq),
                            _ => None,
                        }
                    }).next() {
                        debug!("found sent: {:?}; mark: {:?}", sent, s);
                        if s > sent { assert!(res.is_err()) }
                    }
                    prev_seq = Some(s)

                },
                ConsOp::Log(cmd) => {
                    cmd.apply_to(&mut log)
                },
                ConsOp::RunProcess => {
                    actual.process(&mut observed, token, &log);
                }
            }
        }

        while actual.process(&mut observed, token, &log) { /* noop */ }

        log.quiesce();

        let expected_msgs = if let (Some(min), Some(committed)) = (min_seq, log.read_committed()) {
                min.upto(&committed)
                .inspect(|s| debug!("Read: {:?} -> {:?}", s, log.read(*s)))
                .map(|s| (s, hash(&log.read(s).expect("read item"))))
                .collect::<Vec<_>>()
        } else {
            vec![]
        };

        let observed = observed.inner();
        debug!("obs: {:?}", observed);

        let consumer_msgs = observed.into_iter().filter_map(|x| match x {
            OutMessage::ConsumerMessage(_tok, seq, m) => Some((seq, hash(&m))),
            _ => None,
        }).collect::<Vec<_>>();

        debug!("Expected: {:?}", expected_msgs);
        debug!("observed: {:?}", consumer_msgs);
        assert_eq!(expected_msgs, consumer_msgs);

        TestResult::passed()
    }

    #[test]
    fn should_do_the_thing() {
        use env_logger;

        env_logger::init().unwrap_or(());
        quickcheck::quickcheck(should_do_the_thing_prop::<VecLog> as fn(Commands) -> TestResult);
    }
}

<commit_msg>Encouragement for tests.<commit_after>/*
Each client talks to a single consumer object, which references the shared log.

Consumer:
Client LWM -------------- Sent point -------------- Client HWM
*/

use data::Seqno;
use replica::{Log, Outputs};
use mio;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        InvalidRange(lwm: Option<Seqno>, sent: Option<Seqno>, mark: Seqno)
    }
}

#[derive(Debug)]
pub struct Consumer {
    low_water_mark: Option<Seqno>,
    sent: Option<Seqno>,
}

impl Consumer {
    pub fn new() -> Consumer {
        Consumer {
            low_water_mark: None,
            sent: None,
        }
    }

    pub fn consume_requested(&mut self, mark: Seqno) -> Result<(), Error> {
        // It would be odd for a client to request an item they have not seen
        // in the current session.
        let lwm = match (self.low_water_mark, self.sent) {
            (None, None) => Some(mark),
            (Some(m), None) if mark >= m => {
                Some(m)
            },
            (Some(m), Some(s)) if mark >= m && mark <= s => {
                Some(mark)
            },

            (None, Some(_)) => unreachable!(),
            (m, s) => return Err(Error::InvalidRange(m, s, mark)),
        };
        debug!("consume_requested: {:?}; mark:{:?} => {:?}", self, mark, lwm);
        self.low_water_mark = lwm;
        Ok(())
    }

    pub fn process<L: Log, O: Outputs>(&mut self,
            out: &mut O, token: mio::Token, log: &L) -> bool {
        let mut changed = false;
        let next = self.low_water_mark;
        let committed = log.read_committed();
        trace!("Consumer#process: committed: {:?}; next: {:?}", committed, next);

        if let (Some(next), Some(committed)) = (next, committed) {
            for i in next.upto(&committed) {
                debug!("Consuming seq:{:?}", i);
                if let Some(op) = log.read(i) {
                    debug!("Consume seq:{:?}/{:?}; ds/seqno: {:?}", i, op, self.low_water_mark);
                    out.consumer_message(token, i, op.into());
                    self.sent = Some(i);
                    self.low_water_mark = Some(i.succ());
                    changed = true
                } else {
                    panic!("Attempted to consume item not in log: {:?}", i);
                }
            }
        }
        changed
    }
}

#[cfg(test)]
mod test {
    use consumer::Consumer;
    use data::Seqno;
    use replica::Log;
    use replica::test::{Outs,OutMessage};
    use replication_log::test::{VecLog,TestLog, LogCommand, arbitrary_given, hash};
    use slab::Index;
    use mio;
    use quickcheck::{self, Arbitrary, Gen, TestResult};
    use std::cmp;

    #[derive(Debug, Clone, Hash)]
    enum ConsOp {
        RequestMark(Seqno),
        Log(LogCommand),
        RunProcess,
    }

    impl Arbitrary for ConsOp {
        fn arbitrary<G: Gen>(g: &mut G) -> ConsOp {
            let case = u64::arbitrary(g) % 100;
            let res = match case {
                0 ... 20 => ConsOp::RequestMark(Arbitrary::arbitrary(g)),
                20 ... 50 => ConsOp::Log(Arbitrary::arbitrary(g)),
                _ => ConsOp::RunProcess,
            };
            res
        }
        fn shrink(&self) -> Box<Iterator<Item = Self> + 'static> {
            let h = hash(self);
            trace!("ConsOp#shrink {:x}: {:?}", h, self);
            match self {
                &ConsOp::RequestMark(ref s) => Box::new(s.shrink().map(ConsOp::RequestMark).inspect(move |it| trace!("ConsOp#shrink {:x}: => {:?}", h, it))),
                &ConsOp::Log(ref cmd) => Box::new(cmd.shrink().map(ConsOp::Log).inspect(move |it| trace!("ConsOp#shrink {:x}: => {:?}", h, it))),
                &ConsOp::RunProcess => Box::new(vec![].into_iter().inspect(move |it| debug!("ConsOp#shrank {:x}: => {:?}", h, it))),
            }
        }
    }

    impl ConsOp {
        pub fn apply_to<L: Log>(&self, model: &mut L) {
            match self {
                &ConsOp::Log(ref c) => c.apply_to(model),
                _ => (),
            }
        }

        fn satisfies_precondition(&self, model: &VecLog) -> bool {
            let ret = match self {
                &ConsOp::Log(ref c) => c.satisfies_precondition(model),
                &ConsOp::RequestMark(m) => Some(m) <= model.read_committed(),
                _ => true,
            };
            trace!("ConsOp#satisfies_precondition: {:?}/{:?} -> {:?}", self, model, ret);
            ret
        }
    }

    #[derive(Debug, Clone, Hash)]
    struct Commands(Vec<ConsOp>);

    impl Commands {
        fn validate_commands(&self) -> bool {
            let (_, okayp) = self.0.iter().fold((VecLog::new(move |_| ()), true),
                    |(model_log, okayp), cmd| {
                if !okayp {
                    (model_log, false)
                } else  {
                    let okayp = cmd.satisfies_precondition(&model_log);
                    (model_log, okayp)
                }
            });

            okayp
        }
    }

    impl Arbitrary for Commands {
        fn arbitrary<G: Gen>(g: &mut G) -> Commands {
            let sz = usize::arbitrary(g);
            let mut commands : Vec<ConsOp> = Vec::with_capacity(sz);
            let mut model_log = VecLog::new(move |seq| ());

            for _ in 0..sz {
                let cmd = arbitrary_given(g, |cmd: &ConsOp| cmd.satisfies_precondition(&model_log));
                debug!("Generated command: {:?}", cmd);
                cmd.apply_to(&mut model_log);
                commands.push(cmd);
            };
            Commands(commands)
        }
        fn shrink(&self) -> Box<Iterator<Item = Self> + 'static> {
            let h = hash(self);
            trace!("Commands#shrink {:x}: {:?}", h, self);
            let ret = Arbitrary::shrink(&self.0).map(Commands).filter(Commands::validate_commands).inspect(move |it| trace!("Commands#shrink {:x}: => {:?}", h, it));
            Box::new(ret)
        }
    }

    fn can_totally_do_the_thing_prop<L: TestLog>(cmds: Commands) -> TestResult {
        debug!("commands {:x}: {:?}", hash(&cmds), cmds);
        let Commands(cmds) = cmds;
        let mut actual = Consumer::new();
        let mut log = VecLog::new(move |_| ());

        let token = mio::Token::from_usize(0);
        let mut observed = Outs::new();

        let mut min_seq = None;
        let mut prev_seq = None;

        for cmd in cmds {
            debug!("apply: {:?}", cmd);
            match cmd {
                ConsOp::RequestMark(s) => {
                    let res = actual.consume_requested(s);
                    min_seq = Some(min_seq.unwrap_or(s));
                    if Some(s) < prev_seq { assert!(res.is_err()) }

                    if let Some(sent) = observed.borrow().iter().rev().filter_map(|x| {
                        match x {
                            &OutMessage::ConsumerMessage(_, seq, _) => Some(seq),
                            _ => None,
                        }
                    }).next() {
                        debug!("found sent: {:?}; mark: {:?}", sent, s);
                        if s > sent { assert!(res.is_err()) }
                    }
                    prev_seq = Some(s)

                },
                ConsOp::Log(cmd) => {
                    cmd.apply_to(&mut log)
                },
                ConsOp::RunProcess => {
                    actual.process(&mut observed, token, &log);
                }
            }
        }

        while actual.process(&mut observed, token, &log) { /* noop */ }

        log.quiesce();

        let expected_msgs = if let (Some(min), Some(committed)) = (min_seq, log.read_committed()) {
                min.upto(&committed)
                .inspect(|s| debug!("Read: {:?} -> {:?}", s, log.read(*s)))
                .map(|s| (s, hash(&log.read(s).expect("read item"))))
                .collect::<Vec<_>>()
        } else {
            vec![]
        };

        let observed = observed.inner();
        debug!("obs: {:?}", observed);

        let consumer_msgs = observed.into_iter().filter_map(|x| match x {
            OutMessage::ConsumerMessage(_tok, seq, m) => Some((seq, hash(&m))),
            _ => None,
        }).collect::<Vec<_>>();

        debug!("Expected: {:?}", expected_msgs);
        debug!("observed: {:?}", consumer_msgs);
        assert_eq!(expected_msgs, consumer_msgs);

        TestResult::passed()
    }

    #[test]
    fn can_totally_do_the_thing() {
        use env_logger;

        env_logger::init().unwrap_or(());
        quickcheck::quickcheck(can_totally_do_the_thing_prop::<VecLog> as fn(Commands) -> TestResult);
    }
}

<|endoftext|>