<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 dom::bindings::callback::ExceptionHandling;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::MutationObserverBinding;
use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationCallback;
use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverBinding::MutationObserverMethods;
use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverInit;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::reflector::{Reflector, reflect_dom_object, DomObject};
use dom::bindings::root::DomRoot;
use dom::bindings::str::DOMString;
use dom::mutationrecord::MutationRecord;
use dom::node::Node;
use dom::window::Window;
use dom_struct::dom_struct;
use html5ever::{Namespace, LocalName};
use microtask::Microtask;
use script_thread::ScriptThread;
use std::rc::Rc;

#[dom_struct]
pub struct MutationObserver {
    reflector_: Reflector,
    #[ignore_malloc_size_of = "can't measure Rc values"]
    callback: Rc<MutationCallback>,
    record_queue: DomRefCell<Vec<DomRoot<MutationRecord>>>,
    node_list: DomRefCell<Vec<DomRoot<Node>>>,
}

pub enum Mutation<'a> {
    Attribute { name: LocalName, namespace: Namespace, old_value: Option<DOMString> },
    CharacterData { old_value: DOMString },
    ChildList { added: Option<&'a [&'a Node]>, removed: Option<&'a [&'a Node]>,
                prev: Option<&'a Node>, next: Option<&'a Node> },
}

#[derive(JSTraceable, MallocSizeOf)]
pub struct RegisteredObserver {
    pub observer: DomRoot<MutationObserver>,
    options: ObserverOptions,
}

#[derive(JSTraceable, MallocSizeOf)]
pub struct ObserverOptions {
    attribute_old_value: bool,
    attributes: bool,
    character_data: bool,
    character_data_old_value: bool,
    child_list: bool,
    subtree: bool,
    attribute_filter: Vec<DOMString>,
}

impl MutationObserver {
    fn new(global: &Window, callback: Rc<MutationCallback>) -> DomRoot<MutationObserver> {
        let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
        reflect_dom_object(boxed_observer, global, MutationObserverBinding::Wrap)
    }

    fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
        MutationObserver {
            reflector_: Reflector::new(),
            callback: callback,
            record_queue: DomRefCell::new(vec![]),
            node_list: DomRefCell::new(vec![]),
        }
    }

    pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Fallible<DomRoot<MutationObserver>> {
        global.set_exists_mut_observer();
        let observer = MutationObserver::new(global, callback);
        ScriptThread::add_mutation_observer(&*observer);
        Ok(observer)
    }

    /// <https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask>
    pub fn queue_mutation_observer_compound_microtask() {
        // Step 1
        if ScriptThread::is_mutation_observer_compound_microtask_queued() {
            return;
        }
        // Step 2
        ScriptThread::set_mutation_observer_compound_microtask_queued(true);
        // Step 3
        ScriptThread::enqueue_microtask(Microtask::NotifyMutationObservers);
    }

    /// <https://dom.spec.whatwg.org/#notify-mutation-observers>
    pub fn notify_mutation_observers() {
        // Step 1
        ScriptThread::set_mutation_observer_compound_microtask_queued(false);
        // Step 2
        let notify_list = ScriptThread::get_mutation_observers();
        // TODO: steps 3-4 (slots)
        // Step 5
        for mo in &notify_list {
            let queue: Vec<DomRoot<MutationRecord>> = mo.record_queue.borrow().clone();
            mo.record_queue.borrow_mut().clear();
            // TODO: Step 5.3 Remove all transient registered observers whose observer is mo.
            if !queue.is_empty() {
                let _ = mo.callback.Call_(&**mo, queue, &**mo, ExceptionHandling::Report);
            }
        }
        // TODO: Step 6 (slot signals)
    }

    /// <https://dom.spec.whatwg.org/#queueing-a-mutation-record>
    pub fn queue_a_mutation_record(target: &Node, attr_type: Mutation) {
        if !target.global().as_window().get_exists_mut_observer() {
            return;
        }
        // Step 1
        let mut interested_observers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];

        // Step 2 & 3
        for node in target.inclusive_ancestors() {
            for registered in &*node.registered_mutation_observers() {
                if &*node != target && !registered.options.subtree {
                    continue;
                }

                match attr_type {
                    Mutation::Attribute { ref name, ref namespace, ref old_value } => {
                        // Step 3.1
                        if !registered.options.attributes {
                            continue;
                        }
                        if !registered.options.attribute_filter.is_empty() {
                            if *namespace != ns!() {
                                continue;
                            }
                            if registered.options.attribute_filter.iter()
                                .find(|s| &**s == &**name).is_some() {
                                continue;
                            }
                        }
                        // Step 3.1.2
                        let paired_string = if registered.options.attribute_old_value {
                            old_value.clone()
                        } else {
                            None
                        };
                        // Step 3.1.1
                        let idx = interested_observers.iter().position(|&(ref o, _)|
                            &**o as *const _ == &*registered.observer as *const _);
                        if let Some(idx) = idx {
                            interested_observers[idx].1 = paired_string;
                        } else {
                            interested_observers.push((DomRoot::from_ref(&*registered.observer),
                                paired_string));
                        }
                    },
                    Mutation::CharacterData { ref old_value } => {
                        if !registered.options.character_data {
                            continue;
                        }
                        // Step 3.1.2
                        let paired_string = if registered.options.character_data_old_value {
                            Some(old_value.clone())
                        } else {
                            None
                        };
                        // Step 3.1.1
                        let idx = interested_observers.iter().position(|&(ref o, _)|
                            &**o as *const _ == &*registered.observer as *const _);
                        if let Some(idx) = idx {
                            interested_observers[idx].1 = paired_string;
                        } else {
                            interested_observers.push((DomRoot::from_ref(&*registered.observer),
                                paired_string));
                        }
                    },
                    Mutation::ChildList { .. } => {
                        if !registered.options.child_list {
                            continue;
                        }
                        interested_observers.push((DomRoot::from_ref(&*registered.observer), None));
                    }
                }
            }
        }

        // Step 4
        for &(ref observer, ref paired_string) in &interested_observers {
            // Steps 4.1-4.7
            let record = match attr_type {
                Mutation::Attribute { ref name, ref namespace, .. } => {
                    let namespace = if *namespace != ns!() {
                        Some(namespace)
                    } else {
                        None
                    };
                    MutationRecord::attribute_mutated(target, name, namespace, paired_string.clone())
                },
                Mutation::CharacterData { .. } => {
                    MutationRecord::character_data_mutated(target, paired_string.clone())
                }
                Mutation::ChildList { ref added, ref removed, ref next, ref prev } => {
                    MutationRecord::child_list_mutated(target, *added, *removed, *next, *prev)
                }
            };
            // Step 4.8
            observer.record_queue.borrow_mut().push(record);
        }

        // Step 5
        MutationObserver::queue_mutation_observer_compound_microtask();
    }

}

impl MutationObserverMethods for MutationObserver {
    /// <https://dom.spec.whatwg.org/#dom-mutationobserver-observe>
    fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
        let attribute_filter = options.attributeFilter.clone().unwrap_or(vec![]);
        let attribute_old_value = options.attributeOldValue.unwrap_or(false);
        let mut attributes = options.attributes.unwrap_or(false);
        let mut character_data = options.characterData.unwrap_or(false);
        let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
        let child_list = options.childList;
        let subtree = options.subtree;

        // Step 1
        if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
            options.attributes.is_none() {
            attributes = true;
        }

        // Step 2
        if options.characterDataOldValue.is_some() && options.characterData.is_none() {
            character_data = true;
        }

        // Step 3
        if !child_list && !attributes && !character_data {
            return Err(Error::Type("One of childList, attributes, or characterData must be true".into()));
        }

        // Step 4
        if attribute_old_value && !attributes {
            return Err(Error::Type("attributeOldValue is true but attributes is false".into()));
        }

        // Step 5
        if options.attributeFilter.is_some() && !attributes {
            return Err(Error::Type("attributeFilter is present but attributes is false".into()));
        }

        // Step 6
        if character_data_old_value && !character_data {
            return Err(Error::Type("characterDataOldValue is true but characterData is false".into()));
        }

        // Step 7
        let add_new_observer = {
            let mut replaced = false;
            for registered in &mut *target.registered_mutation_observers() {
                if &*registered.observer as *const MutationObserver != self as *const MutationObserver {
                    continue;
                }
                // TODO: remove matching transient registered observers
                registered.options.attribute_old_value = attribute_old_value;
                registered.options.attributes = attributes;
                registered.options.character_data = character_data;
                registered.options.character_data_old_value = character_data_old_value;
                registered.options.child_list = child_list;
                registered.options.subtree = subtree;
                registered.options.attribute_filter = attribute_filter.clone();
                replaced = true;
            }
            !replaced
        };

        // Step 8
        if add_new_observer {
            target.registered_mutation_observers().push(RegisteredObserver {
                observer: DomRoot::from_ref(self),
                options: ObserverOptions {
                    attributes,
                    attribute_old_value,
                    character_data,
                    character_data_old_value,
                    subtree,
                    attribute_filter,
                    child_list
                },
            });

            self.node_list.borrow_mut().push(DomRoot::from_ref(target));
        }

        Ok(())
    }

    /// https://dom.spec.whatwg.org/#dom-mutationobserver-takerecords
    fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
        let records: Vec<DomRoot<MutationRecord>> = self.record_queue.borrow().clone();
        self.record_queue.borrow_mut().clear();
        records
    }

    /// https://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
    fn Disconnect(&self) {
        // Step 1
        let mut nodes = self.node_list.borrow_mut();
        for node in nodes.drain(..) {
            node.remove_mutation_observer(self);
        }

        // Step 2
        self.record_queue.borrow_mut().clear();
    }
}
<commit_msg>script: Avoid useless string clones in mutation observer stuff.<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 dom::bindings::callback::ExceptionHandling;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::MutationObserverBinding;
use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationCallback;
use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverBinding::MutationObserverMethods;
use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverInit;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::reflector::{Reflector, reflect_dom_object, DomObject};
use dom::bindings::root::DomRoot;
use dom::bindings::str::DOMString;
use dom::mutationrecord::MutationRecord;
use dom::node::Node;
use dom::window::Window;
use dom_struct::dom_struct;
use html5ever::{Namespace, LocalName};
use microtask::Microtask;
use script_thread::ScriptThread;
use std::rc::Rc;

#[dom_struct]
pub struct MutationObserver {
    reflector_: Reflector,
    #[ignore_malloc_size_of = "can't measure Rc values"]
    callback: Rc<MutationCallback>,
    record_queue: DomRefCell<Vec<DomRoot<MutationRecord>>>,
    node_list: DomRefCell<Vec<DomRoot<Node>>>,
}

pub enum Mutation<'a> {
    Attribute { name: LocalName, namespace: Namespace, old_value: Option<DOMString> },
    CharacterData { old_value: DOMString },
    ChildList { added: Option<&'a [&'a Node]>, removed: Option<&'a [&'a Node]>,
                prev: Option<&'a Node>, next: Option<&'a Node> },
}

#[derive(JSTraceable, MallocSizeOf)]
pub struct RegisteredObserver {
    pub observer: DomRoot<MutationObserver>,
    options: ObserverOptions,
}

#[derive(JSTraceable, MallocSizeOf)]
pub struct ObserverOptions {
    attribute_old_value: bool,
    attributes: bool,
    character_data: bool,
    character_data_old_value: bool,
    child_list: bool,
    subtree: bool,
    attribute_filter: Vec<DOMString>,
}

impl MutationObserver {
    fn new(global: &Window, callback: Rc<MutationCallback>) -> DomRoot<MutationObserver> {
        let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
        reflect_dom_object(boxed_observer, global, MutationObserverBinding::Wrap)
    }

    fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
        MutationObserver {
            reflector_: Reflector::new(),
            callback: callback,
            record_queue: DomRefCell::new(vec![]),
            node_list: DomRefCell::new(vec![]),
        }
    }

    pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Fallible<DomRoot<MutationObserver>> {
        global.set_exists_mut_observer();
        let observer = MutationObserver::new(global, callback);
        ScriptThread::add_mutation_observer(&*observer);
        Ok(observer)
    }

    /// <https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask>
    pub fn queue_mutation_observer_compound_microtask() {
        // Step 1
        if ScriptThread::is_mutation_observer_compound_microtask_queued() {
            return;
        }
        // Step 2
        ScriptThread::set_mutation_observer_compound_microtask_queued(true);
        // Step 3
        ScriptThread::enqueue_microtask(Microtask::NotifyMutationObservers);
    }

    /// <https://dom.spec.whatwg.org/#notify-mutation-observers>
    pub fn notify_mutation_observers() {
        // Step 1
        ScriptThread::set_mutation_observer_compound_microtask_queued(false);
        // Step 2
        let notify_list = ScriptThread::get_mutation_observers();
        // TODO: steps 3-4 (slots)
        // Step 5
        for mo in &notify_list {
            let queue: Vec<DomRoot<MutationRecord>> = mo.record_queue.borrow().clone();
            mo.record_queue.borrow_mut().clear();
            // TODO: Step 5.3 Remove all transient registered observers whose observer is mo.
            if !queue.is_empty() {
                let _ = mo.callback.Call_(&**mo, queue, &**mo, ExceptionHandling::Report);
            }
        }
        // TODO: Step 6 (slot signals)
    }

    /// <https://dom.spec.whatwg.org/#queueing-a-mutation-record>
    pub fn queue_a_mutation_record(target: &Node, attr_type: Mutation) {
        if !target.global().as_window().get_exists_mut_observer() {
            return;
        }
        // Step 1
        let mut interested_observers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];

        // Step 2 & 3
        for node in target.inclusive_ancestors() {
            for registered in &*node.registered_mutation_observers() {
                if &*node != target && !registered.options.subtree {
                    continue;
                }

                match attr_type {
                    Mutation::Attribute { ref name, ref namespace, ref old_value } => {
                        // Step 3.1
                        if !registered.options.attributes {
                            continue;
                        }
                        if !registered.options.attribute_filter.is_empty() {
                            if *namespace != ns!() {
                                continue;
                            }
                            if registered.options.attribute_filter.iter()
                                .find(|s| &**s == &**name).is_some() {
                                continue;
                            }
                        }
                        // Step 3.1.2
                        let paired_string = if registered.options.attribute_old_value {
                            old_value.clone()
                        } else {
                            None
                        };
                        // Step 3.1.1
                        let idx = interested_observers.iter().position(|&(ref o, _)|
                            &**o as *const _ == &*registered.observer as *const _);
                        if let Some(idx) = idx {
                            interested_observers[idx].1 = paired_string;
                        } else {
                            interested_observers.push((DomRoot::from_ref(&*registered.observer),
                                paired_string));
                        }
                    },
                    Mutation::CharacterData { ref old_value } => {
                        if !registered.options.character_data {
                            continue;
                        }
                        // Step 3.1.2
                        let paired_string = if registered.options.character_data_old_value {
                            Some(old_value.clone())
                        } else {
                            None
                        };
                        // Step 3.1.1
                        let idx = interested_observers.iter().position(|&(ref o, _)|
                            &**o as *const _ == &*registered.observer as *const _);
                        if let Some(idx) = idx {
                            interested_observers[idx].1 = paired_string;
                        } else {
                            interested_observers.push((DomRoot::from_ref(&*registered.observer),
                                paired_string));
                        }
                    },
                    Mutation::ChildList { .. } => {
                        if !registered.options.child_list {
                            continue;
                        }
                        interested_observers.push((DomRoot::from_ref(&*registered.observer), None));
                    }
                }
            }
        }

        // Step 4
        for (observer, paired_string) in interested_observers {
            // Steps 4.1-4.7
            let record = match attr_type {
                Mutation::Attribute { ref name, ref namespace, .. } => {
                    let namespace = if *namespace != ns!() {
                        Some(namespace)
                    } else {
                        None
                    };
                    MutationRecord::attribute_mutated(target, name, namespace, paired_string)
                },
                Mutation::CharacterData { .. } => {
                    MutationRecord::character_data_mutated(target, paired_string)
                }
                Mutation::ChildList { ref added, ref removed, ref next, ref prev } => {
                    MutationRecord::child_list_mutated(target, *added, *removed, *next, *prev)
                }
            };
            // Step 4.8
            observer.record_queue.borrow_mut().push(record);
        }

        // Step 5
        MutationObserver::queue_mutation_observer_compound_microtask();
    }

}

impl MutationObserverMethods for MutationObserver {
    /// <https://dom.spec.whatwg.org/#dom-mutationobserver-observe>
    fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
        let attribute_filter = options.attributeFilter.clone().unwrap_or(vec![]);
        let attribute_old_value = options.attributeOldValue.unwrap_or(false);
        let mut attributes = options.attributes.unwrap_or(false);
        let mut character_data = options.characterData.unwrap_or(false);
        let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
        let child_list = options.childList;
        let subtree = options.subtree;

        // Step 1
        if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
            options.attributes.is_none() {
            attributes = true;
        }

        // Step 2
        if options.characterDataOldValue.is_some() && options.characterData.is_none() {
            character_data = true;
        }

        // Step 3
        if !child_list && !attributes && !character_data {
            return Err(Error::Type("One of childList, attributes, or characterData must be true".into()));
        }

        // Step 4
        if attribute_old_value && !attributes {
            return Err(Error::Type("attributeOldValue is true but attributes is false".into()));
        }

        // Step 5
        if options.attributeFilter.is_some() && !attributes {
            return Err(Error::Type("attributeFilter is present but attributes is false".into()));
        }

        // Step 6
        if character_data_old_value && !character_data {
            return Err(Error::Type("characterDataOldValue is true but characterData is false".into()));
        }

        // Step 7
        let add_new_observer = {
            let mut replaced = false;
            for registered in &mut *target.registered_mutation_observers() {
                if &*registered.observer as *const MutationObserver != self as *const MutationObserver {
                    continue;
                }
                // TODO: remove matching transient registered observers
                registered.options.attribute_old_value = attribute_old_value;
                registered.options.attributes = attributes;
                registered.options.character_data = character_data;
                registered.options.character_data_old_value = character_data_old_value;
                registered.options.child_list = child_list;
                registered.options.subtree = subtree;
                registered.options.attribute_filter = attribute_filter.clone();
                replaced = true;
            }
            !replaced
        };

        // Step 8
        if add_new_observer {
            target.registered_mutation_observers().push(RegisteredObserver {
                observer: DomRoot::from_ref(self),
                options: ObserverOptions {
                    attributes,
                    attribute_old_value,
                    character_data,
                    character_data_old_value,
                    subtree,
                    attribute_filter,
                    child_list
                },
            });

            self.node_list.borrow_mut().push(DomRoot::from_ref(target));
        }

        Ok(())
    }

    /// https://dom.spec.whatwg.org/#dom-mutationobserver-takerecords
    fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
        let records: Vec<DomRoot<MutationRecord>> = self.record_queue.borrow().clone();
        self.record_queue.borrow_mut().clear();
        records
    }

    /// https://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
    fn Disconnect(&self) {
        // Step 1
        let mut nodes = self.node_list.borrow_mut();
        for node in nodes.drain(..) {
            node.remove_mutation_observer(self);
        }

        // Step 2
        self.record_queue.borrow_mut().clear();
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::{Location, Named, WithLocation};
use graphql_syntax::{FloatValue, OperationKind};
use interner::StringKey;
use schema::Schema;
use schema::{FieldID, Type, TypeReference};
use std::fmt;
use std::sync::Arc;
// Definitions

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ExecutableDefinition {
    Operation(OperationDefinition),
    Fragment(FragmentDefinition),
}

/// A fully-typed mutation, query, or subscription definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct OperationDefinition {
    pub kind: OperationKind,
    pub name: WithLocation<StringKey>,
    pub type_: Type,
    pub variable_definitions: Vec<VariableDefinition>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// A fully-typed fragment definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentDefinition {
    pub name: WithLocation<StringKey>,
    pub variable_definitions: Vec<VariableDefinition>,
    pub used_global_variables: Vec<VariableDefinition>,
    pub type_condition: Type,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// A variable definition of an operation or fragment
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VariableDefinition {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
    pub default_value: Option<ConstantValue>,
    pub directives: Vec<Directive>,
}

impl VariableDefinition {
    pub fn has_non_null_default_value(&self) -> bool {
        match &self.default_value {
            Some(value) => value.is_non_null(),
            _ => false,
        }
    }
}

impl Named for VariableDefinition {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

// Selections

/// A selection within an operation or fragment
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum Selection {
    FragmentSpread(Arc<FragmentSpread>),
    InlineFragment(Arc<InlineFragment>),
    LinkedField(Arc<LinkedField>),
    ScalarField(Arc<ScalarField>),
    Condition(Arc<Condition>),
}

impl Selection {
    /// Get selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn directives(&self) -> &[Directive] {
        match self {
            Selection::FragmentSpread(node) => &node.directives,
            Selection::InlineFragment(node) => &node.directives,
            Selection::ScalarField(node) => &node.directives,
            Selection::LinkedField(node) => &node.directives,
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        }
    }

    /// Update Selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn set_directives(&mut self, directives: Vec<Directive>) {
        match self {
            Selection::FragmentSpread(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::InlineFragment(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::ScalarField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::LinkedField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        };
    }
}

impl fmt::Debug for Selection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Selection::FragmentSpread(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::InlineFragment(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::LinkedField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::ScalarField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::Condition(node) => f.write_fmt(format_args!("{:#?}", node)),
        }
    }
}

/// ... Name
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentSpread {
    pub fragment: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

/// ... SelectionSet
/// ... on Type SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct InlineFragment {
    pub type_condition: Option<Type>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// Name Arguments? SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LinkedField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

impl LinkedField {
    pub fn alias_or_name(&self, schema: &Schema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ScalarField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

impl ScalarField {
    pub fn alias_or_name(&self, schema: &Schema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// https://spec.graphql.org/June2018/#sec--skip
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Condition {
    pub selections: Vec<Selection>,
    pub value: ConditionValue,
    pub passing_value: bool,
}

// Associated Types

/// @ Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Directive {
    pub name: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
}
impl Named for Directive {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

/// Name : Value
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Argument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<Value>,
}
impl Named for Argument {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Value {
    Constant(ConstantValue),
    Variable(Variable),
    List(Vec<Value>),
    Object(Vec<Argument>),
}
impl Value {
    /// If the value is a constant string literal, return the value, otherwise None.
    pub fn get_string_literal(&self) -> Option<StringKey> {
        if let Value::Constant(ConstantValue::String(val)) = self {
            Some(*val)
        } else {
            None
        }
    }

    /// Return the constant string literal of this value.
    /// Panics if the value is not a constant string literal.
    pub fn expect_string_literal(&self) -> StringKey {
        self.get_string_literal().unwrap_or_else(|| {
            panic!("expected a string literal, got {:?}", self);
        })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Variable {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
}

/// Name : Value[Const]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ConstantArgument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<ConstantValue>,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConstantValue {
    Int(i64),
    Float(FloatValue),
    String(StringKey),
    Boolean(bool),
    Null(),
    Enum(StringKey),
    List(Vec<ConstantValue>),
    Object(Vec<ConstantArgument>),
}

impl ConstantValue {
    pub fn is_null(&self) -> bool {
        match self {
            ConstantValue::Null() => true,
            _ => false,
        }
    }

    pub fn is_non_null(&self) -> bool {
        !self.is_null()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConditionValue {
    Constant(bool),
    Variable(Variable),
}
<commit_msg>Add `ExecutableDefinition.has_directive` to GraphQL IR<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::{Location, Named, WithLocation};
use graphql_syntax::{FloatValue, OperationKind};
use interner::StringKey;
use schema::Schema;
use schema::{FieldID, Type, TypeReference};
use std::fmt;
use std::sync::Arc;
// Definitions

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ExecutableDefinition {
    Operation(OperationDefinition),
    Fragment(FragmentDefinition),
}

impl ExecutableDefinition {
    pub fn has_directive(&self, directive_name: StringKey) -> bool {
        match self {
            ExecutableDefinition::Operation(node) => node
                .directives
                .iter()
                .any(|d| d.name.item == directive_name),
            ExecutableDefinition::Fragment(node) => node
                .directives
                .iter()
                .any(|d| d.name.item == directive_name),
        }
    }
}

/// A fully-typed mutation, query, or subscription definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct OperationDefinition {
    pub kind: OperationKind,
    pub name: WithLocation<StringKey>,
    pub type_: Type,
    pub variable_definitions: Vec<VariableDefinition>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// A fully-typed fragment definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentDefinition {
    pub name: WithLocation<StringKey>,
    pub variable_definitions: Vec<VariableDefinition>,
    pub used_global_variables: Vec<VariableDefinition>,
    pub type_condition: Type,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// A variable definition of an operation or fragment
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VariableDefinition {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
    pub default_value: Option<ConstantValue>,
    pub directives: Vec<Directive>,
}

impl VariableDefinition {
    pub fn has_non_null_default_value(&self) -> bool {
        match &self.default_value {
            Some(value) => value.is_non_null(),
            _ => false,
        }
    }
}

impl Named for VariableDefinition {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

// Selections

/// A selection within an operation or fragment
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum Selection {
    FragmentSpread(Arc<FragmentSpread>),
    InlineFragment(Arc<InlineFragment>),
    LinkedField(Arc<LinkedField>),
    ScalarField(Arc<ScalarField>),
    Condition(Arc<Condition>),
}

impl Selection {
    /// Get selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn directives(&self) -> &[Directive] {
        match self {
            Selection::FragmentSpread(node) => &node.directives,
            Selection::InlineFragment(node) => &node.directives,
            Selection::ScalarField(node) => &node.directives,
            Selection::LinkedField(node) => &node.directives,
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        }
    }

    /// Update Selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn set_directives(&mut self, directives: Vec<Directive>) {
        match self {
            Selection::FragmentSpread(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::InlineFragment(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::ScalarField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::LinkedField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        };
    }
}

impl fmt::Debug for Selection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Selection::FragmentSpread(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::InlineFragment(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::LinkedField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::ScalarField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::Condition(node) => f.write_fmt(format_args!("{:#?}", node)),
        }
    }
}

/// ... Name
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentSpread {
    pub fragment: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

/// ... SelectionSet
/// ... on Type SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct InlineFragment {
    pub type_condition: Option<Type>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// Name Arguments? SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LinkedField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

impl LinkedField {
    pub fn alias_or_name(&self, schema: &Schema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ScalarField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

impl ScalarField {
    pub fn alias_or_name(&self, schema: &Schema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// https://spec.graphql.org/June2018/#sec--skip
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Condition {
    pub selections: Vec<Selection>,
    pub value: ConditionValue,
    pub passing_value: bool,
}

// Associated Types

/// @ Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Directive {
    pub name: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
}
impl Named for Directive {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

/// Name : Value
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Argument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<Value>,
}
impl Named for Argument {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Value {
    Constant(ConstantValue),
    Variable(Variable),
    List(Vec<Value>),
    Object(Vec<Argument>),
}
impl Value {
    /// If the value is a constant string literal, return the value, otherwise None.
    pub fn get_string_literal(&self) -> Option<StringKey> {
        if let Value::Constant(ConstantValue::String(val)) = self {
            Some(*val)
        } else {
            None
        }
    }

    /// Return the constant string literal of this value.
    /// Panics if the value is not a constant string literal.
    pub fn expect_string_literal(&self) -> StringKey {
        self.get_string_literal().unwrap_or_else(|| {
            panic!("expected a string literal, got {:?}", self);
        })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Variable {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
}

/// Name : Value[Const]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ConstantArgument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<ConstantValue>,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConstantValue {
    Int(i64),
    Float(FloatValue),
    String(StringKey),
    Boolean(bool),
    Null(),
    Enum(StringKey),
    List(Vec<ConstantValue>),
    Object(Vec<ConstantArgument>),
}

impl ConstantValue {
    pub fn is_null(&self) -> bool {
        match self {
            ConstantValue::Null() => true,
            _ => false,
        }
    }

    pub fn is_non_null(&self) -> bool {
        !self.is_null()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConditionValue {
    Constant(bool),
    Variable(Variable),
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fs::File;
use std::str::FromStr;
use std::io::{Read, Seek, SeekFrom, BufReader};

use byteorder::{NativeEndian, ReadBytesExt};

// FIXME make configurable or associated constants?
const PAGE_SIZE: usize = 256;
const IMAGE_SIZE: usize = 64;

#[derive(Debug)]
pub struct StyleFile {
    pub header: StyleFileHeader,
    pub tiles: Vec<Vec<u8>>,
}

impl StyleFile {
    pub fn from_file(file: &File) -> StyleFile {
        let mut buf_reader = BufReader::new(file);

        let header = read_header(&mut buf_reader);
        let chunks = match read_chunks(&mut buf_reader) {
            Some(c) => c,
            None => panic!("Error while reading chunks."),
        };

        StyleFile {
            header,
            tiles: chunks.tiles,
        }
    }
}

#[derive(Debug)]
pub struct StyleFileHeader {
    file_type: String,
    version: u16,
}

fn read_header<T: Read>(buf_reader: &mut T) -> StyleFileHeader {
    let mut buffer = [0; 4];

    buf_reader.read_exact(&mut buffer);
    let file_type = String::from_utf8(buffer.to_vec()).unwrap();

    let version = buf_reader.read_u16::<NativeEndian>().unwrap();

    StyleFileHeader { file_type, version }
}

#[derive(Debug)]
struct StyleFileChunks {
    tiles: Vec<Vec<u8>>,
}

#[derive(Debug)]
struct PaletteIndex {
    physical_palettes: Vec<u16>,
}

enum StyleFileChunkTypes {
    PaletteIndex,
    PhysicalPalettes,
    PaletteBase,
    Tiles,
    SpriteGraphics,
    SpriteIndex,
    SpritesBases,
    DeltaStore,
    DeltaIndex,
    FontBase,
    CarInfo,
    MapObjectInfo,
    PSXTiles,
    CarRecyclingInfo,
}

// FIXME rename to something more expressive
struct StyleChunkParseError();

impl FromStr for StyleFileChunkTypes {
    type Err = StyleChunkParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PALX" => Ok(StyleFileChunkTypes::PaletteIndex),
            "PPAL" => Ok(StyleFileChunkTypes::PhysicalPalettes),
            "PALB" => Ok(StyleFileChunkTypes::PaletteBase),
            "TILE" => Ok(StyleFileChunkTypes::Tiles),
            "SPRG" => Ok(StyleFileChunkTypes::SpriteGraphics),
            "SPRX" => Ok(StyleFileChunkTypes::SpriteIndex),
            "SPRB" => Ok(StyleFileChunkTypes::SpritesBases),
            "DELS" => Ok(StyleFileChunkTypes::DeltaStore),
            "DELX" => Ok(StyleFileChunkTypes::DeltaIndex),
            "FONB" => Ok(StyleFileChunkTypes::FontBase),
            "CARI" => Ok(StyleFileChunkTypes::CarInfo),
            "OBJI" => Ok(StyleFileChunkTypes::MapObjectInfo),
            "PSXT" => Ok(StyleFileChunkTypes::PSXTiles),
            "RECY" => Ok(StyleFileChunkTypes::CarRecyclingInfo),
            _ => Err(StyleChunkParseError()),
        }
    }
}

fn read_chunks<T: Read + Seek>(mut buf_reader: &mut T) -> Option<StyleFileChunks> {
    let mut buffer = [0; 4];

    loop {
        buf_reader.read_exact(&mut buffer);

        let chunk_type = match String::from_utf8(buffer.to_vec()) {
            Ok(s) => s,
            Err(_) => return None,
        };

        let size = match buf_reader.read_u32::<NativeEndian>() {
            Ok(s) => s,
            Err(_) => return None,
        };

        match StyleFileChunkTypes::from_str(&chunk_type) {
            Ok(StyleFileChunkTypes::Tiles) => {
                let tiles = load_tiles(&mut buf_reader);
                return Some(StyleFileChunks { tiles });
            }
            Ok(_) => {}
            Err(_) => println!("Tile parse error."),
        }

        buf_reader.seek(SeekFrom::Current(size as i64)).unwrap();
    }
}

fn load_tiles<T: Read + Seek>(buf_reader: &mut T) -> Vec<Vec<u8>> {
    let mut tiles: Vec<Vec<u8>> = Vec::with_capacity(16); // one page

    // load page
    let mut page: [u8; PAGE_SIZE * PAGE_SIZE] = [0; PAGE_SIZE * PAGE_SIZE];
    for pixel in page.iter_mut() {
        *pixel = buf_reader.read_u8().unwrap();
    }
    let page = page;

    for id in 0..16 {
        let y_start = (id / 4) * IMAGE_SIZE;
        let y_end = y_start + IMAGE_SIZE;
        let mut tile = Vec::with_capacity(IMAGE_SIZE * IMAGE_SIZE);

        for y in y_start..y_end {
            let x_start = (id % 4) * IMAGE_SIZE;
            let x_end = x_start + IMAGE_SIZE;

            for x in x_start..x_end {
                tile.push(page[(y * PAGE_SIZE) + x]);
            }
        }
        tiles.push(tile);
    }

    tiles
}
<commit_msg>Refactor chunk loading<commit_after>use std::fmt;
use std::fs::File;
use std::str::FromStr;
use std::io::{Read, Seek, SeekFrom, BufReader};

use byteorder::{NativeEndian, ReadBytesExt};

// FIXME make configurable or associated constants?
const PAGE_SIZE: usize = 256;
const IMAGE_SIZE: usize = 64;

#[derive(Debug)]
pub struct StyleFile {
    // FIXME remove pub
    pub header: StyleFileHeader,
    pub tiles: Vec<Vec<u8>>,
}

impl StyleFile {
    pub fn from_file(file: &File) -> StyleFile {
        let mut buf_reader = BufReader::new(file);

        let header = read_header(&mut buf_reader);
        let chunks = match read_chunks(&mut buf_reader) {
            Some(c) => c,
            None => panic!("Error while reading chunks."),
        };

        StyleFile {
            header,
            tiles: chunks.tiles,
        }
    }
}

enum ChunkBuilderError {
    MissingTilesChunkError,
}

struct ChunkBuilder {
    tiles: Option<Vec<Vec<u8>>>,
}

impl ChunkBuilder {
    pub fn new() -> ChunkBuilder {
        ChunkBuilder { tiles: None }
    }

    pub fn load_chunk<T: Read + Seek>(&mut self,
                                      chunk_type: ChunkTypes,
                                      mut buf_reader: &mut T)
                                      -> &mut ChunkBuilder {
        match chunk_type {
            ChunkTypes::Tiles => self.tiles(load_tiles(buf_reader)),
            _ => unimplemented!(),
        }
    }

    pub fn tiles(&mut self, tiles: Vec<Vec<u8>>) -> &mut ChunkBuilder {
        self.tiles = Some(tiles);
        self
    }

    pub fn build(self) -> Result<StyleFileChunks, ChunkBuilderError> {
        let tiles = try!(self.tiles.ok_or(ChunkBuilderError::MissingTilesChunkError));

        let chunks = StyleFileChunks { tiles };

        Ok(chunks)
    }
}

#[derive(Debug)]
pub struct StyleFileHeader {
    file_type: String,
    version: u16,
}

fn read_header<T: Read>(buf_reader: &mut T) -> StyleFileHeader {
    let mut buffer = [0; 4];

    buf_reader.read_exact(&mut buffer);
    let file_type = String::from_utf8(buffer.to_vec()).unwrap();

    let version = buf_reader.read_u16::<NativeEndian>().unwrap();

    StyleFileHeader { file_type, version }
}

#[derive(Debug)]
struct StyleFileChunks {
    tiles: Vec<Vec<u8>>,
}

#[derive(Debug)]
struct PaletteIndex {
    physical_palettes: Vec<u16>,
}

enum ChunkTypes {
    PaletteIndex,
    PhysicalPalettes,
    PaletteBase,
    Tiles,
    SpriteGraphics,
    SpriteIndex,
    SpritesBases,
    DeltaStore,
    DeltaIndex,
    FontBase,
    CarInfo,
    MapObjectInfo,
    PSXTiles,
    CarRecyclingInfo,
}

// FIXME rename to something more expressive
#[derive(Debug)]
enum StyleFileParseError {
    UnknownChunkTypeError(String),
}

impl FromStr for ChunkTypes {
    type Err = StyleFileParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PALX" => Ok(ChunkTypes::PaletteIndex),
            "PPAL" => Ok(ChunkTypes::PhysicalPalettes),
            "PALB" => Ok(ChunkTypes::PaletteBase),
            "TILE" => Ok(ChunkTypes::Tiles),
            "SPRG" => Ok(ChunkTypes::SpriteGraphics),
            "SPRX" => Ok(ChunkTypes::SpriteIndex),
            "SPRB" => Ok(ChunkTypes::SpritesBases),
            "DELS" => Ok(ChunkTypes::DeltaStore),
            "DELX" => Ok(ChunkTypes::DeltaIndex),
            "FONB" => Ok(ChunkTypes::FontBase),
            "CARI" => Ok(ChunkTypes::CarInfo),
            "OBJI" => Ok(ChunkTypes::MapObjectInfo),
            "PSXT" => Ok(ChunkTypes::PSXTiles),
            "RECY" => Ok(ChunkTypes::CarRecyclingInfo),
            s => Err(StyleFileParseError::UnknownChunkTypeError(String::from(s))),
        }
    }
}

fn read_chunks<T: Read + Seek>(mut buf_reader: &mut T) -> Option<StyleFileChunks> {
    let mut chunk_builder = ChunkBuilder::new();
    let mut buffer = [0; 4];

    loop {
        buf_reader.read_exact(&mut buffer);

        let chunk_type = match String::from_utf8(buffer.to_vec()) {
            Ok(s) => s,
            Err(_) => break,
        };

        let size = match buf_reader.read_u32::<NativeEndian>() {
            Ok(s) => s,
            Err(_) => break,
        };

        let chunk_type = ChunkTypes::from_str(&chunk_type).unwrap();
        chunk_builder.load_chunk(chunk_type, &mut buf_reader);

        buf_reader.seek(SeekFrom::Current(size as i64)).unwrap();
    }

    match chunk_builder.build() {
        Ok(chunk) => Some(chunk),
        Err(_) => None,
    }
}

fn load_tiles<T: Read + Seek>(buf_reader: &mut T) -> Vec<Vec<u8>> {
    let mut tiles: Vec<Vec<u8>> = Vec::with_capacity(16); // one page

    // load page
    let mut page: [u8; PAGE_SIZE * PAGE_SIZE] = [0; PAGE_SIZE * PAGE_SIZE];
    for pixel in page.iter_mut() {
        *pixel = buf_reader.read_u8().unwrap();
    }
    let page = page;

    for id in 0..16 {
        let y_start = (id / 4) * IMAGE_SIZE;
        let y_end = y_start + IMAGE_SIZE;
        let mut tile = Vec::with_capacity(IMAGE_SIZE * IMAGE_SIZE);

        for y in y_start..y_end {
            let x_start = (id % 4) * IMAGE_SIZE;
            let x_end = x_start + IMAGE_SIZE;

            for x in x_start..x_end {
                tile.push(page[(y * PAGE_SIZE) + x]);
            }
        }
        tiles.push(tile);
    }

    tiles
}
<|endoftext|>


<commit_before>use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::cmp::{min, max, Ordering};
use std::default::Default;
use std::fs;
use std::io::Read;
use std::ops::Range;
use std::path::Path;

use cfg::ColorScheme;
use gfx::MouseCursor;
use glyph::Metrics;
use graphics::character::CharacterCache;

use ui::{BB, Px};
use ui::layout::{RectBB, RectBounded, Layout};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

use ide::highlight;

pub struct Editor {
    bb: RectBB,
    font: text::Mono,
    font_bold: text::MonoBold,
    font_metrics: Cell<Metrics>,
    over: Cell<bool>,
    down: Cell<bool>,

    // Caret is visible between [0, 0.5) and hidden between [0.5, 1).
    blink_phase: Cell<f32>,
    scroll_start: Cell<usize>,

    selection_start: Cell<Caret>,
    caret: Cell<Caret>,

    lines: RefCell<Vec<Line>>
}

#[derive(Copy, Clone)]
struct Caret {
    row: usize,
    col: usize,
    offset: usize
}

impl Caret {
    fn advance(&mut self, c: char, forward: bool) {
        let w = if c == '\t' {
            // FIXME this won't work backwards.
            7 - (self.col + 7) % 8
        } else {
            c.width(false).unwrap_or(1)
        };
        let l = c.len_utf8();

        if forward {
            self.col += w;
            self.offset += l;
        } else {
            self.col -= w;
            self.offset -= l;
        }
    }
}

impl PartialEq for Caret {
    fn eq(&self, other: &Caret) -> bool {
        self.row == other.row && self.col == other.col
    }
}

impl Eq for Caret {}

impl PartialOrd for Caret {
    fn partial_cmp(&self, other: &Caret) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Caret {
    fn cmp(&self, other: &Caret) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

#[derive(Debug)]
struct Line {
    data: String,
    hl_depth: usize,
    ranges: Vec<(usize, highlight::Style)>
}

impl Editor {
    pub fn open<P: AsRef<Path>>(path: P) -> Editor {
        let mut data = String::new();
        fs::File::open(path).unwrap().read_to_string(&mut data).unwrap();
        let lines = data.split('\n').map(|line| Line {
            data: line.to_owned(),
            hl_depth: 1,
            ranges: vec![]
        }).collect();

        let caret = Caret {
            row: 0,
            col: 0,
            offset: 0
        };

        let editor = Editor {
            bb: RectBB::default(),
            font: text::Mono,
            font_bold: text::MonoBold,
            font_metrics: Cell::new(Metrics::default()),
            over: Cell::new(false),
            down: Cell::new(false),
            scroll_start: Cell::new(0),

            blink_phase: Cell::new(0.0),
            selection_start: Cell::new(caret),
            caret: Cell::new(caret),

            lines: RefCell::new(lines)
        };

        let num_lines = editor.lines.borrow().len();
        editor.update_hl(0..num_lines);

        editor
    }

    fn pos_to_caret(&self, [x, y]: [Px; 2]) -> Option<Caret> {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return None;
        }

        let bb = self.bb();
        if x > bb.x2 || y > bb.y2 {
            return None;
        }
        let [x, y] = [x - bb.x1, y - bb.y1];
        if x < 0.0 || y < 0.0 {
            return None;
        }

        let mut k = Caret {
            row:  (y / metrics.height) as usize + self.scroll_start.get(),
            col: 0,
            offset: 0
        };

        let lines = self.lines.borrow();
        if k.row >= lines.len() {
            return None;
        }

        for c in lines[k.row].data.chars() {
            let prev_k = k;
            k.advance(c, true);
            if k.col as Px * metrics.width > x {
                return Some(prev_k);
            }
        }
        Some(k)
    }

            }
            col += w;
            offset += c.len_utf8();
        }
        Some(Caret { row: row, col: col, offset: offset })
    }

    fn move_to(&self, caret: Caret) {
        self.selection_start.set(caret);
        self.caret.set(caret);
        self.blink_phase.set(0.0);
    }

    fn update_hl(&self, mut range: Range<usize>) {
        let mut lines = self.lines.borrow_mut();
        while lines[range.start].hl_depth > 0 && range.start > 0 {
            range.start -= 1;
        }
        while lines[range.end - 1].hl_depth > 0 && range.end < lines.len() {
            range.end += 1;
        }

        let (d, mut hl) = highlight::Rust::run(lines[range.clone()].iter().map(|line| &line.data[..]));

        // Fallback to re-highlight everything until the end.
        if d > 0 {
            hl = highlight::Rust::run(lines.iter().map(|line| &line.data[..])).1;
            range = 0..lines.len();
        }

        for (line, (hl_depth, ranges)) in lines[range].iter_mut().zip(hl.into_iter()) {
            line.hl_depth = hl_depth;
            line.ranges = ranges;
        }
    }

    fn remove(&self, range: Range<Caret>) {
        let (s1, s2) = (range.start, range.end);
        let mut lines = self.lines.borrow_mut();

        // Remove part of the first line.
        if s1.row == s2.row {
            let line = &mut lines[s1.row].data;

            let final_len = line.len() - (s2.offset - s1.offset);
            while line.len() > final_len {
                line.remove(s1.offset);
            }
        } else {
            lines[s1.row].data.truncate(s1.offset);
        }

        // Add part of last line to first line (if range has at least 2 lines).
        if s1.row < s2.row {
            let (dest, src) = lines[s1.row..].split_at_mut(1);
            let dest = &mut dest[0].data;
            let src = &mut src[s2.row - s1.row - 1].data;

            dest.push_str(&src[s2.offset..]);
        }

        // Remove all other lines.
        for _ in s1.row+1..s2.row+1 {
            lines.remove(s1.row + 1);
        }
    }

    fn insert(&self, data: &str) {
        let (s1, s2) = (self.selection_start.get(), self.caret.get());
        let (s1, s2) = (min(s1, s2), max(s1, s2));

        if s1 != s2 {
            self.remove(s1..s2);
        }

        let mut k = s1;
        {
            let mut lines = self.lines.borrow_mut();
            let line = &mut lines[k.row].data;
            for ch in data.chars() {
                line.insert(k.offset, ch);
                k.offset += ch.len_utf8();
                k.col += 1;
            }
        }

        self.update_hl(k.row..k.row+1);
        self.move_to(k);
    }
}

impl RectBounded for Editor {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<editor>" }
}

impl Draw for Editor {
    fn draw(&self, cx: &mut DrawCx) {
        let mut metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            metrics = cx.fonts.metrics(self.font);
            self.font_metrics.set(metrics);
        }

        if self.over.get() {
            cx.cursor = MouseCursor::Text;
        }

        let bb = self.bb();
        let start = self.scroll_start.get();
        let end = start + (((bb.y2 - bb.y1) / metrics.height) as usize);
        let lines = self.lines.borrow();
        let lines = if end < lines.len() {
            &lines[start..end]
        } else {
            &lines[start..]
        };

        cx.rect(bb, ColorScheme.back_view());

        let s1 = self.selection_start.get();
        let s2 = self.caret.get();
        let k = s2;
        if start <= s2.row && s2.row <= end {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x2, y2: y + metrics.height
            }, ColorScheme.back_view_alt());
        }

        // First line of the selection.
        let (s1, s2) = (min(s1, s2), max(s1, s2));
        if start <= s1.row && s1.row <= end {
            let y = bb.y1 + ((s1.row - start) as Px * metrics.height);
            let x1 = bb.x1 + (s1.col as Px) * metrics.width;
            let x2 = if s1.row == s2.row {
                bb.x1 + (s2.col as Px) * metrics.width
            } else {
                bb.x2
            };
            cx.rect(BB {
                x1: x1, y1: y,
                x2: x2, y2: y + metrics.height
            }, ColorScheme.focus());
        }
        // Subsequent lines.
        for row in (s1.row + 1)..s2.row {
            if start <= row && row <= end {
                let y = bb.y1 + ((row - start) as Px * metrics.height);
                cx.rect(BB {
                    x1: bb.x1, y1: y,
                    x2: bb.x2, y2: y + metrics.height
                }, ColorScheme.focus());
            }
        }
        // Last line (if selection has at least 2 lines).
        if start <= s2.row && s2.row <= end && s1.row < s2.row {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x1 + (s2.col as Px) * metrics.width, y2: y + metrics.height
            }, ColorScheme.focus());
        }

        // The actual text in each line.
        for (i, line) in lines.iter().enumerate() {
            let y = bb.y1 + i as Px * metrics.height;
            let mut draw_k = Caret {
                row: 0,
                col: 0,
                offset: 0
            };
            for &(len, style) in &line.ranges {
                let x = bb.x1 + (draw_k.col as Px) * metrics.width;
                let data = &line.data[draw_k.offset..draw_k.offset+len];
                if style.bold {
                    self.font_bold.draw(cx, [x, y], style.color, data);
                } else {
                    self.font.draw(cx, [x, y], style.color, data);
                }
                for c in data.chars() {
                    draw_k.advance(c, true);
                }
            }
        }

        // Caret on top of everything else.
        if self.blink_phase.get() < BLINK_SPACING && start <= k.row && k.row <= end {
            let y = bb.y1 + ((k.row - start) as Px * metrics.height);

            // TODO proper BB scissoring.
            let x = bb.x1 + (k.col as Px) * metrics.width;
            let w = 2.0;
            if bb.x1 <= x && x + w <= bb.x2 {
                cx.rect(BB {
                    x1: x, y1: y,
                    x2: x + w, y2: y + metrics.height
                }, ColorScheme.normal());
            }
        }
    }
}

impl Dispatch<MouseDown> for Editor {
    fn dispatch(&self, ev: &MouseDown) -> bool {
        self.down.set(true);

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            self.move_to(caret);
            true
        } else {
            false
        }
    }
}

impl Dispatch<MouseUp> for Editor {
    fn dispatch(&self, _: &MouseUp) -> bool {
        self.down.set(false);
        false
    }
}

impl Dispatch<MouseMove> for Editor {
    fn dispatch(&self, ev: &MouseMove) -> bool {
        let over = self.bb().contains([ev.x, ev.y]);
        let mut dirty = false;
        if over != self.over.get() {
            self.over.set(over);
            dirty = true;
        }

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            if self.down.get() {
                self.caret.set(caret);
                dirty = true;
            }
        }

        dirty
    }
}

impl Dispatch<MouseScroll> for Editor {
    fn dispatch(&self, ev: &MouseScroll) -> bool {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return false;
        }

        let [_, dy] = ev.delta();
        if dy == 0.0 {
            return false;
        }

        let dy = -dy;
        let bb = self.bb();
        let sy = self.scroll_start.get();
        let new_sy = if dy < 0.0 {
            let dy = -dy as usize;
            if sy < dy { sy } else { sy - dy }
        } else {
            let dy = dy as usize;
            if ((self.lines.borrow().len() as Px)  - ((sy + dy) as Px)) * metrics.height <= (bb.y2 - bb.y1) {
                sy
            } else {
                sy + dy
            }
        };
        self.scroll_start.set(new_sy);
        new_sy != sy
    }
}

const BLINK_SPACING: f32 = 0.5;

impl Dispatch<Update> for Editor {
    fn dispatch(&self, &Update(dt): &Update) -> bool {
        let mut dirty = false;

        let blink = (self.blink_phase.get() + dt) % (BLINK_SPACING * 2.0);
        dirty |= (blink >= BLINK_SPACING) != (self.blink_phase.get() >= BLINK_SPACING);
        self.blink_phase.set(blink);

        dirty
    }
}

impl<'a> Dispatch<TextInput<'a>> for Editor {
    fn dispatch(&self, ev: &TextInput) -> bool {
        self.insert(ev.0);
        true
    }
}
<commit_msg>editor: moving the caret should always keep it visible.<commit_after>use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::cmp::{min, max, Ordering};
use std::default::Default;
use std::fs;
use std::io::Read;
use std::ops::Range;
use std::path::Path;

use cfg::ColorScheme;
use gfx::MouseCursor;
use glyph::Metrics;
use graphics::character::CharacterCache;

use ui::{BB, Px};
use ui::layout::{RectBB, RectBounded, Layout};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

use ide::highlight;

pub struct Editor {
    bb: RectBB,
    font: text::Mono,
    font_bold: text::MonoBold,
    font_metrics: Cell<Metrics>,
    over: Cell<bool>,
    down: Cell<bool>,

    // Caret is visible between [0, 0.5) and hidden between [0.5, 1).
    blink_phase: Cell<f32>,
    scroll_start: Cell<usize>,

    selection_start: Cell<Caret>,
    caret: Cell<Caret>,

    lines: RefCell<Vec<Line>>
}

#[derive(Copy, Clone)]
struct Caret {
    row: usize,
    col: usize,
    offset: usize
}

impl Caret {
    fn advance(&mut self, c: char, forward: bool) {
        let w = if c == '\t' {
            // FIXME this won't work backwards.
            7 - (self.col + 7) % 8
        } else {
            c.width(false).unwrap_or(1)
        };
        let l = c.len_utf8();

        if forward {
            self.col += w;
            self.offset += l;
        } else {
            self.col -= w;
            self.offset -= l;
        }
    }
}

impl PartialEq for Caret {
    fn eq(&self, other: &Caret) -> bool {
        self.row == other.row && self.col == other.col
    }
}

impl Eq for Caret {}

impl PartialOrd for Caret {
    fn partial_cmp(&self, other: &Caret) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Caret {
    fn cmp(&self, other: &Caret) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

#[derive(Debug)]
struct Line {
    data: String,
    hl_depth: usize,
    ranges: Vec<(usize, highlight::Style)>
}

impl Editor {
    pub fn open<P: AsRef<Path>>(path: P) -> Editor {
        let mut data = String::new();
        fs::File::open(path).unwrap().read_to_string(&mut data).unwrap();
        let lines = data.split('\n').map(|line| Line {
            data: line.to_owned(),
            hl_depth: 1,
            ranges: vec![]
        }).collect();

        let caret = Caret {
            row: 0,
            col: 0,
            offset: 0
        };

        let editor = Editor {
            bb: RectBB::default(),
            font: text::Mono,
            font_bold: text::MonoBold,
            font_metrics: Cell::new(Metrics::default()),
            over: Cell::new(false),
            down: Cell::new(false),
            scroll_start: Cell::new(0),

            blink_phase: Cell::new(0.0),
            selection_start: Cell::new(caret),
            caret: Cell::new(caret),

            lines: RefCell::new(lines)
        };

        let num_lines = editor.lines.borrow().len();
        editor.update_hl(0..num_lines);

        editor
    }

    fn pos_to_caret(&self, [x, y]: [Px; 2]) -> Option<Caret> {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return None;
        }

        let bb = self.bb();
        if x > bb.x2 || y > bb.y2 {
            return None;
        }
        let [x, y] = [x - bb.x1, y - bb.y1];
        if x < 0.0 || y < 0.0 {
            return None;
        }

        let mut k = Caret {
            row:  (y / metrics.height) as usize + self.scroll_start.get(),
            col: 0,
            offset: 0
        };

        let lines = self.lines.borrow();
        if k.row >= lines.len() {
            return None;
        }

        for c in lines[k.row].data.chars() {
            let prev_k = k;
            k.advance(c, true);
            if k.col as Px * metrics.width > x {
                return Some(prev_k);
            }
        }
        Some(k)
    }

            }
            col += w;
            offset += c.len_utf8();
        }
        Some(Caret { row: row, col: col, offset: offset })
    }

    fn move_to(&self, k: Caret, hold: bool) {
        if !hold {
            self.selection_start.set(k);
        }
        self.caret.set(k);
        self.blink_phase.set(0.0);

        // Make sure the caret stays in the viewport.
        let scroll_start = self.scroll_start.get();
        if k.row < scroll_start {
            self.scroll_start.set(k.row);
        } else {
            let metrics = self.font_metrics.get();
            let bb = self.bb();
            let h = bb.y2 - bb.y1;
            if metrics.height != 0.0 && h > 0.0 {
                let rows = (h / metrics.height) as usize;
                if k.row >= scroll_start + rows {
                    self.scroll_start.set(k.row - rows + 1);
                }
            }
        }
    }

    fn update_hl(&self, mut range: Range<usize>) {
        let mut lines = self.lines.borrow_mut();
        while lines[range.start].hl_depth > 0 && range.start > 0 {
            range.start -= 1;
        }
        while lines[range.end - 1].hl_depth > 0 && range.end < lines.len() {
            range.end += 1;
        }

        let (d, mut hl) = highlight::Rust::run(lines[range.clone()].iter().map(|line| &line.data[..]));

        // Fallback to re-highlight everything until the end.
        if d > 0 {
            hl = highlight::Rust::run(lines.iter().map(|line| &line.data[..])).1;
            range = 0..lines.len();
        }

        for (line, (hl_depth, ranges)) in lines[range].iter_mut().zip(hl.into_iter()) {
            line.hl_depth = hl_depth;
            line.ranges = ranges;
        }
    }

    fn remove(&self, range: Range<Caret>) {
        let (s1, s2) = (range.start, range.end);
        let mut lines = self.lines.borrow_mut();

        // Remove part of the first line.
        if s1.row == s2.row {
            let line = &mut lines[s1.row].data;

            let final_len = line.len() - (s2.offset - s1.offset);
            while line.len() > final_len {
                line.remove(s1.offset);
            }
        } else {
            lines[s1.row].data.truncate(s1.offset);
        }

        // Add part of last line to first line (if range has at least 2 lines).
        if s1.row < s2.row {
            let (dest, src) = lines[s1.row..].split_at_mut(1);
            let dest = &mut dest[0].data;
            let src = &mut src[s2.row - s1.row - 1].data;

            dest.push_str(&src[s2.offset..]);
        }

        // Remove all other lines.
        for _ in s1.row+1..s2.row+1 {
            lines.remove(s1.row + 1);
        }
    }

    fn insert(&self, data: &str) {
        let (s1, s2) = (self.selection_start.get(), self.caret.get());
        let (s1, s2) = (min(s1, s2), max(s1, s2));

        if s1 != s2 {
            self.remove(s1..s2);
        }

        let mut k = s1;
        {
            let mut lines = self.lines.borrow_mut();
            let line = &mut lines[k.row].data;
            for ch in data.chars() {
                line.insert(k.offset, ch);
                k.offset += ch.len_utf8();
                k.col += 1;
            }
        }

        self.update_hl(k.row..k.row+1);
        self.move_to(k);
    }
}

impl RectBounded for Editor {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<editor>" }
}

impl Draw for Editor {
    fn draw(&self, cx: &mut DrawCx) {
        let mut metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            metrics = cx.fonts.metrics(self.font);
            self.font_metrics.set(metrics);
        }

        if self.over.get() {
            cx.cursor = MouseCursor::Text;
        }

        let bb = self.bb();
        let start = self.scroll_start.get();
        let end = start + (((bb.y2 - bb.y1) / metrics.height) as usize);
        let lines = self.lines.borrow();
        let lines = if end < lines.len() {
            &lines[start..end]
        } else {
            &lines[start..]
        };

        cx.rect(bb, ColorScheme.back_view());

        let s1 = self.selection_start.get();
        let s2 = self.caret.get();
        let k = s2;
        if start <= s2.row && s2.row <= end {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x2, y2: y + metrics.height
            }, ColorScheme.back_view_alt());
        }

        // First line of the selection.
        let (s1, s2) = (min(s1, s2), max(s1, s2));
        if start <= s1.row && s1.row <= end {
            let y = bb.y1 + ((s1.row - start) as Px * metrics.height);
            let x1 = bb.x1 + (s1.col as Px) * metrics.width;
            let x2 = if s1.row == s2.row {
                bb.x1 + (s2.col as Px) * metrics.width
            } else {
                bb.x2
            };
            cx.rect(BB {
                x1: x1, y1: y,
                x2: x2, y2: y + metrics.height
            }, ColorScheme.focus());
        }
        // Subsequent lines.
        for row in (s1.row + 1)..s2.row {
            if start <= row && row <= end {
                let y = bb.y1 + ((row - start) as Px * metrics.height);
                cx.rect(BB {
                    x1: bb.x1, y1: y,
                    x2: bb.x2, y2: y + metrics.height
                }, ColorScheme.focus());
            }
        }
        // Last line (if selection has at least 2 lines).
        if start <= s2.row && s2.row <= end && s1.row < s2.row {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x1 + (s2.col as Px) * metrics.width, y2: y + metrics.height
            }, ColorScheme.focus());
        }

        // The actual text in each line.
        for (i, line) in lines.iter().enumerate() {
            let y = bb.y1 + i as Px * metrics.height;
            let mut draw_k = Caret {
                row: 0,
                col: 0,
                offset: 0
            };
            for &(len, style) in &line.ranges {
                let x = bb.x1 + (draw_k.col as Px) * metrics.width;
                let data = &line.data[draw_k.offset..draw_k.offset+len];
                if style.bold {
                    self.font_bold.draw(cx, [x, y], style.color, data);
                } else {
                    self.font.draw(cx, [x, y], style.color, data);
                }
                for c in data.chars() {
                    draw_k.advance(c, true);
                }
            }
        }

        // Caret on top of everything else.
        if self.blink_phase.get() < BLINK_SPACING && start <= k.row && k.row <= end {
            let y = bb.y1 + ((k.row - start) as Px * metrics.height);

            // TODO proper BB scissoring.
            let x = bb.x1 + (k.col as Px) * metrics.width;
            let w = 2.0;
            if bb.x1 <= x && x + w <= bb.x2 {
                cx.rect(BB {
                    x1: x, y1: y,
                    x2: x + w, y2: y + metrics.height
                }, ColorScheme.normal());
            }
        }
    }
}

impl Dispatch<MouseDown> for Editor {
    fn dispatch(&self, ev: &MouseDown) -> bool {
        if !self.bb().contains([ev.x, ev.y]) {
            return false;
        }

        self.down.set(true);

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            self.move_to(caret, false);
            true
        } else {
            false
        }
    }
}

impl Dispatch<MouseUp> for Editor {
    fn dispatch(&self, _: &MouseUp) -> bool {
        self.down.set(false);
        false
    }
}

impl Dispatch<MouseMove> for Editor {
    fn dispatch(&self, ev: &MouseMove) -> bool {
        let over = self.bb().contains([ev.x, ev.y]);
        let mut dirty = false;
        if over != self.over.get() {
            self.over.set(over);
            dirty = true;
        }

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            if self.down.get() {
                self.move_to(caret, true);
                dirty = true;
            }
        }

        dirty
    }
}

impl Dispatch<MouseScroll> for Editor {
    fn dispatch(&self, ev: &MouseScroll) -> bool {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return false;
        }

        let [_, dy] = ev.delta();
        if dy == 0.0 {
            return false;
        }

        let dy = -dy;
        let bb = self.bb();
        let sy = self.scroll_start.get();
        let new_sy = if dy < 0.0 {
            let dy = -dy as usize;
            if sy < dy { sy } else { sy - dy }
        } else {
            let dy = dy as usize;
            if ((self.lines.borrow().len() as Px)  - ((sy + dy) as Px)) * metrics.height <= (bb.y2 - bb.y1) {
                sy
            } else {
                sy + dy
            }
        };
        self.scroll_start.set(new_sy);
        new_sy != sy
    }
}

const BLINK_SPACING: f32 = 0.5;

impl Dispatch<Update> for Editor {
    fn dispatch(&self, &Update(dt): &Update) -> bool {
        let mut dirty = false;

        let blink = (self.blink_phase.get() + dt) % (BLINK_SPACING * 2.0);
        dirty |= (blink >= BLINK_SPACING) != (self.blink_phase.get() >= BLINK_SPACING);
        self.blink_phase.set(blink);

        dirty
    }
}

impl<'a> Dispatch<TextInput<'a>> for Editor {
    fn dispatch(&self, ev: &TextInput) -> bool {
        self.insert(ev.0);
        true
    }
}
<|endoftext|>


<commit_before>//! Reading proc-macro rustc version information from binary data

use std::{
    fs::File,
    io::{self, Read},
};

use memmap2::Mmap;
use object::read::{File as BinaryFile, Object, ObjectSection};
use paths::AbsPath;
use snap::read::FrameDecoder as SnapDecoder;

#[derive(Debug)]
pub struct RustCInfo {
    pub version: (usize, usize, usize),
    pub channel: String,
    pub commit: String,
    pub date: String,
}

/// Read rustc dylib information
pub fn read_dylib_info(dylib_path: &AbsPath) -> io::Result<RustCInfo> {
    macro_rules! err {
        ($e:literal) => {
            io::Error::new(io::ErrorKind::InvalidData, $e)
        };
    }

    let ver_str = read_version(dylib_path)?;
    let mut items = ver_str.split_whitespace();
    let tag = items.next().ok_or_else(|| err!("version format error"))?;
    if tag != "rustc" {
        return Err(err!("version format error (No rustc tag)"));
    }

    let version_part = items.next().ok_or_else(|| err!("no version string"))?;
    let mut version_parts = version_part.split('-');
    let version = version_parts.next().ok_or_else(|| err!("no version"))?;
    let channel = version_parts.next().unwrap_or_default().to_string();

    let commit = items.next().ok_or_else(|| err!("no commit info"))?;
    // remove (
    if commit.len() == 0 {
        return Err(err!("commit format error"));
    }
    let commit = commit[1..].to_string();
    let date = items.next().ok_or_else(|| err!("no date info"))?;
    // remove )
    if date.len() == 0 {
        return Err(err!("date format error"));
    }
    let date = date[0..date.len() - 2].to_string();

    let version_numbers = version
        .split('.')
        .map(|it| it.parse::<usize>())
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| err!("version number error"))?;

    if version_numbers.len() != 3 {
        return Err(err!("version number format error"));
    }
    let version = (version_numbers[0], version_numbers[1], version_numbers[2]);

    Ok(RustCInfo { version, channel, commit, date })
}

/// This is used inside read_version() to locate the ".rustc" section
/// from a proc macro crate's binary file.
fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'a [u8]> {
    BinaryFile::parse(dylib_binary)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
        .section_by_name(section_name)
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "section read error"))?
        .data()
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}

/// Check the version of rustc that was used to compile a proc macro crate's
///
/// binary file.
/// A proc macro crate binary's ".rustc" section has following byte layout:
/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes
/// * ff060000 734e6150 is followed, it's the snappy format magic bytes,
///   means bytes from here(including this sequence) are compressed in
///   snappy compression format. Version info is inside here, so decompress
///   this.
/// The bytes you get after decompressing the snappy format portion has
/// following layout:
/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
/// * [crate root bytes] next 4 bytes is to store crate root position,
///   according to rustc's source code comment
/// * [length byte] next 1 byte tells us how many bytes we should read next
///   for the version string's utf8 bytes
/// * [version string bytes encoded in utf8] <- GET THIS BOI
/// * [some more bytes that we don really care but still there] :-)
/// Check this issue for more about the bytes layout:
/// <https://github.com/rust-analyzer/rust-analyzer/issues/6174>
fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
    let dylib_file = File::open(dylib_path)?;
    let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;

    let dot_rustc = read_section(&dylib_mmaped, ".rustc")?;

    let header = &dot_rustc[..8];
    const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5];
    // check if header is valid
    if header != EXPECTED_HEADER {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("only metadata version 5 is supported, section header was: {:?}", header),
        ));
    }

    let snappy_portion = &dot_rustc[8..];

    let mut snappy_decoder = SnapDecoder::new(snappy_portion);

    // the bytes before version string bytes, so this basically is:
    // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5]
    // 4 bytes for [crate root bytes]
    // 1 byte for length of version string
    // so 13 bytes in total, and we should check the 13th byte
    // to know the length
    let mut bytes_before_version = [0u8; 13];
    snappy_decoder.read_exact(&mut bytes_before_version)?;
    let length = bytes_before_version[12]; // what? can't use -1 indexing?

    let mut version_string_utf8 = vec![0u8; length as usize];
    snappy_decoder.read_exact(&mut version_string_utf8)?;
    let version_string = String::from_utf8(version_string_utf8);
    version_string.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
<commit_msg>proc_macro_api: make commit & date suffix of binary version optional<commit_after>//! Reading proc-macro rustc version information from binary data

use std::{
    fs::File,
    io::{self, Read},
};

use memmap2::Mmap;
use object::read::{File as BinaryFile, Object, ObjectSection};
use paths::AbsPath;
use snap::read::FrameDecoder as SnapDecoder;

#[derive(Debug)]
pub struct RustCInfo {
    pub version: (usize, usize, usize),
    pub channel: String,
    pub commit: Option<String>,
    pub date: Option<String>,
}

/// Read rustc dylib information
pub fn read_dylib_info(dylib_path: &AbsPath) -> io::Result<RustCInfo> {
    macro_rules! err {
        ($e:literal) => {
            io::Error::new(io::ErrorKind::InvalidData, $e)
        };
    }

    let ver_str = read_version(dylib_path)?;
    let mut items = ver_str.split_whitespace();
    let tag = items.next().ok_or_else(|| err!("version format error"))?;
    if tag != "rustc" {
        return Err(err!("version format error (No rustc tag)"));
    }

    let version_part = items.next().ok_or_else(|| err!("no version string"))?;
    let mut version_parts = version_part.split('-');
    let version = version_parts.next().ok_or_else(|| err!("no version"))?;
    let channel = version_parts.next().unwrap_or_default().to_string();

    let commit = match items.next() {
        Some(commit) => {
            match commit.len() {
                0 => None,
                _ => Some(commit[1..].to_string() /* remove ( */)
            }
        },
        None => None
    };
    let date = match items.next() {
        Some(date) => {
            match date.len() {
                0 => None,
                _ => Some(date[0..date.len() - 2].to_string() /* remove ) */)
            }
        },
        None => None
    };

    let version_numbers = version
        .split('.')
        .map(|it| it.parse::<usize>())
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| err!("version number error"))?;

    if version_numbers.len() != 3 {
        return Err(err!("version number format error"));
    }
    let version = (version_numbers[0], version_numbers[1], version_numbers[2]);

    Ok(RustCInfo { version, channel, commit, date })
}

/// This is used inside read_version() to locate the ".rustc" section
/// from a proc macro crate's binary file.
fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'a [u8]> {
    BinaryFile::parse(dylib_binary)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
        .section_by_name(section_name)
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "section read error"))?
        .data()
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}

/// Check the version of rustc that was used to compile a proc macro crate's
///
/// binary file.
/// A proc macro crate binary's ".rustc" section has following byte layout:
/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes
/// * ff060000 734e6150 is followed, it's the snappy format magic bytes,
///   means bytes from here(including this sequence) are compressed in
///   snappy compression format. Version info is inside here, so decompress
///   this.
/// The bytes you get after decompressing the snappy format portion has
/// following layout:
/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
/// * [crate root bytes] next 4 bytes is to store crate root position,
///   according to rustc's source code comment
/// * [length byte] next 1 byte tells us how many bytes we should read next
///   for the version string's utf8 bytes
/// * [version string bytes encoded in utf8] <- GET THIS BOI
/// * [some more bytes that we don really care but still there] :-)
/// Check this issue for more about the bytes layout:
/// <https://github.com/rust-analyzer/rust-analyzer/issues/6174>
fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
    let dylib_file = File::open(dylib_path)?;
    let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;

    let dot_rustc = read_section(&dylib_mmaped, ".rustc")?;

    let header = &dot_rustc[..8];
    const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5];
    // check if header is valid
    if header != EXPECTED_HEADER {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("only metadata version 5 is supported, section header was: {:?}", header),
        ));
    }

    let snappy_portion = &dot_rustc[8..];

    let mut snappy_decoder = SnapDecoder::new(snappy_portion);

    // the bytes before version string bytes, so this basically is:
    // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5]
    // 4 bytes for [crate root bytes]
    // 1 byte for length of version string
    // so 13 bytes in total, and we should check the 13th byte
    // to know the length
    let mut bytes_before_version = [0u8; 13];
    snappy_decoder.read_exact(&mut bytes_before_version)?;
    let length = bytes_before_version[12]; // what? can't use -1 indexing?

    let mut version_string_utf8 = vec![0u8; length as usize];
    snappy_decoder.read_exact(&mut version_string_utf8)?;
    let version_string = String::from_utf8(version_string_utf8);
    version_string.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
<|endoftext|>