<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::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssrule::CSSRule;
use dom::element::Element;
use dom::node::{Node, window_from_node};
use dom::window::Window;
use dom_struct::dom_struct;
use parking_lot::RwLock;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::Arc;
use style::attr::AttrValue;
use style::parser::ParserContextExtraData;
use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration, parse_style_attribute};
use style::selector_parser::PseudoElement;
use style_traits::ToCss;

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: CSSStyleOwner,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(HeapSizeOf, JSTraceable)]
#[must_root]
pub enum CSSStyleOwner {
    Element(JS<Element>),
    CSSRule(JS<CSSRule>,
            #[ignore_heap_size_of = "Arc"]
            Arc<RwLock<PropertyDeclarationBlock>>),
}

impl CSSStyleOwner {
    // Mutate the declaration block associated to this style owner, and
    // optionally indicate if it has changed (assumed to be true).
    fn mutate_associated_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut PropertyDeclarationBlock, &mut bool) -> R,
    {
        // TODO(emilio): This has some duplication just to avoid dummy clones.
        //
        // This is somewhat complex but the complexity is encapsulated.
        let mut changed = true;
        match *self {
            CSSStyleOwner::Element(ref el) => {
                let mut attr = el.style_attribute().borrow_mut().take();
                let result = if attr.is_some() {
                    let lock = attr.as_ref().unwrap();
                    let mut pdb = lock.write();
                    let result = f(&mut pdb, &mut changed);
                    result
                } else {
                    let mut pdb = PropertyDeclarationBlock {
                        important_count: 0,
                        declarations: vec![],
                    };
                    let result = f(&mut pdb, &mut changed);

                    // Here `changed` is somewhat silly, because we know the
                    // exact conditions under it changes.
                    changed = !pdb.declarations.is_empty();
                    if changed {
                        attr = Some(Arc::new(RwLock::new(pdb)));
                    }

                    result
                };

                if changed {
                    // Note that there's no need to remove the attribute here if
                    // the declaration block is empty[1], and if `attr` is
                    // `None` it means that it necessarily didn't change, so no
                    // need to go through all the set_attribute machinery.
                    //
                    // [1]: https://github.com/whatwg/html/issues/2306
                    if let Some(pdb) = attr {
                        let serialization = pdb.read().to_css_string();
                        el.set_attribute(&local_name!("style"),
                                         AttrValue::Declaration(serialization,
                                                                pdb));
                    }
                } else {
                    // Remember to put it back.
                    *el.style_attribute().borrow_mut() = attr;
                }

                result
            }
            CSSStyleOwner::CSSRule(ref rule, ref pdb) => {
                let result = f(&mut *pdb.write(), &mut changed);
                if changed {
                    rule.global().as_window().Document().invalidate_stylesheets();
                }
                result
            }
        }
    }

    fn with_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&PropertyDeclarationBlock) -> R,
    {
        match *self {
            CSSStyleOwner::Element(ref el) => {
                match *el.style_attribute().borrow() {
                    Some(ref pdb) => f(&pdb.read()),
                    None => {
                        let pdb = PropertyDeclarationBlock {
                            important_count: 0,
                            declarations: vec![],
                        };
                        f(&pdb)
                    }
                }
            }
            CSSStyleOwner::CSSRule(_, ref pdb) => {
                f(&pdb.read())
            }
        }
    }

    fn window(&self) -> Root<Window> {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el),
            CSSStyleOwner::CSSRule(ref rule, _) => Root::from_ref(rule.global().as_window()),
        }
    }

    fn base_url(&self) -> ServoUrl {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el).Document().base_url(),
            CSSStyleOwner::CSSRule(ref rule, _) => {
                rule.parent_stylesheet().style_stylesheet().base_url.clone()
            }
        }
    }
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly,
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $id:expr],)* ) => (
        $(
            fn $getter(&self) -> DOMString {
                self.get_property_value($id)
            }
            fn $setter(&self, value: DOMString) -> ErrorResult {
                self.set_property($id, value, DOMString::new())
            }
        )*
    );
);

impl CSSStyleDeclaration {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(owner: CSSStyleOwner,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess)
                         -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: owner,
            readonly: modification_access == CSSModificationAccess::Readonly,
            pseudo: pseudo,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(global: &Window,
               owner: CSSStyleOwner,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess)
               -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner,
                                                                  pseudo,
                                                                  modification_access),
                           global,
                           CSSStyleDeclarationBinding::Wrap)
    }

    fn get_computed_style(&self, property: PropertyId) -> DOMString {
        match self.owner {
            CSSStyleOwner::CSSRule(..) =>
                panic!("get_computed_style called on CSSStyleDeclaration with a CSSRule owner"),
            CSSStyleOwner::Element(ref el) => {
                let node = el.upcast::<Node>();
                if !node.is_in_doc() {
                    // TODO: Node should be matched against the style rules of this window.
                    // Firefox is currently the only browser to implement this.
                    return DOMString::new();
                }
                let addr = node.to_trusted_node_address();
                window_from_node(node).resolved_style_query(addr, self.pseudo.clone(), property)
            }
        }
    }

    fn get_property_value(&self, id: PropertyId) -> DOMString {
        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(id);
        }

        let mut string = String::new();

        self.owner.with_block(|ref pdb| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
        });

        DOMString::from(string)
    }

    fn set_property(&self, id: PropertyId, value: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            if value.is_empty() {
                // Step 4
                *changed = pdb.remove_property(&id);
                return Ok(());
            }

            // Step 5
            let importance = match &*priority {
                "" => Importance::Normal,
                p if p.eq_ignore_ascii_case("important") => Importance::Important,
                _ => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 6
            let window = self.owner.window();
            let declarations =
                parse_one_declaration(id, &value, &self.owner.base_url(),
                                      window.css_error_reporter(),
                                      ParserContextExtraData::default());

            // Step 7
            let declarations = match declarations {
                Ok(declarations) => declarations,
                Err(_) => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 8
            // Step 9
            // We could try to be better I guess?
            *changed = !declarations.is_empty();
            for declaration in declarations {
                // TODO(emilio): We could check it changed
                pdb.set_parsed_declaration(declaration.0, importance);
            }

            Ok(())
        })
    }
}

impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(&self) -> u32 {
        self.owner.with_block(|ref pdb| {
            pdb.declarations.len() as u32
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(&self, index: u32) -> DOMString {
        self.IndexedGetter(index).unwrap_or_default()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };
        self.get_property_value(id)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };

        self.owner.with_block(|ref pdb| {
            if pdb.property_priority(&id).important() {
                DOMString::from("important")
            } else {
                // Step 4
                DOMString::new()
            }
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(&self,
                   property: DOMString,
                   value: DOMString,
                   priority: DOMString)
                   -> ErrorResult {
        // Step 3
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unknown property
            return Ok(())
        };
        self.set_property(id, value, priority)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        let id = match PropertyId::parse(property.into()) {
            Ok(id) => id,
            Err(..) => return Ok(()), // Unkwown property
        };

        // Step 4
        let importance = match &*priority {
            "" => Importance::Normal,
            p if p.eq_ignore_ascii_case("important") => Importance::Important,
            _ => return Ok(()),
        };

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            // Step 5 & 6
            *changed = pdb.set_importance(&id, importance);
        });

        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, DOMString::new())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(&self, property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property, cannot be there to remove.
            return Ok(DOMString::new())
        };

        let mut string = String::new();
        self.owner.mutate_associated_block(|mut pdb, mut changed| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
            *changed = pdb.remove_property(&id);
        });

        // Step 6
        Ok(DOMString::from(string))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(&self) -> DOMString {
        self.GetPropertyValue(DOMString::from("float"))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(&self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue(DOMString::from("float"), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
        self.owner.with_block(|ref pdb| {
            pdb.declarations.get(index as usize).map(|entry| {
                let (ref declaration, importance) = *entry;
                let mut css = declaration.to_css_string();
                if importance.important() {
                    css += " !important";
                }
                DOMString::from(css)
            })
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn CssText(&self) -> DOMString {
        self.owner.with_block(|ref pdb| {
            DOMString::from(pdb.to_css_string())
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn SetCssText(&self, value: DOMString) -> ErrorResult {
        let window = self.owner.window();

        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|mut pdb, mut _changed| {
            // Step 3
            *pdb = parse_style_attribute(&value,
                                         &self.owner.base_url(),
                                         window.css_error_reporter(),
                                         ParserContextExtraData::default());
        });

        Ok(())
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute
    css_properties_accessors!(css_properties);
}
<commit_msg>Remove unnecessary `ref` in some patterns<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::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssrule::CSSRule;
use dom::element::Element;
use dom::node::{Node, window_from_node};
use dom::window::Window;
use dom_struct::dom_struct;
use parking_lot::RwLock;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::Arc;
use style::attr::AttrValue;
use style::parser::ParserContextExtraData;
use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration, parse_style_attribute};
use style::selector_parser::PseudoElement;
use style_traits::ToCss;

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: CSSStyleOwner,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(HeapSizeOf, JSTraceable)]
#[must_root]
pub enum CSSStyleOwner {
    Element(JS<Element>),
    CSSRule(JS<CSSRule>,
            #[ignore_heap_size_of = "Arc"]
            Arc<RwLock<PropertyDeclarationBlock>>),
}

impl CSSStyleOwner {
    // Mutate the declaration block associated to this style owner, and
    // optionally indicate if it has changed (assumed to be true).
    fn mutate_associated_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut PropertyDeclarationBlock, &mut bool) -> R,
    {
        // TODO(emilio): This has some duplication just to avoid dummy clones.
        //
        // This is somewhat complex but the complexity is encapsulated.
        let mut changed = true;
        match *self {
            CSSStyleOwner::Element(ref el) => {
                let mut attr = el.style_attribute().borrow_mut().take();
                let result = if attr.is_some() {
                    let lock = attr.as_ref().unwrap();
                    let mut pdb = lock.write();
                    let result = f(&mut pdb, &mut changed);
                    result
                } else {
                    let mut pdb = PropertyDeclarationBlock {
                        important_count: 0,
                        declarations: vec![],
                    };
                    let result = f(&mut pdb, &mut changed);

                    // Here `changed` is somewhat silly, because we know the
                    // exact conditions under it changes.
                    changed = !pdb.declarations.is_empty();
                    if changed {
                        attr = Some(Arc::new(RwLock::new(pdb)));
                    }

                    result
                };

                if changed {
                    // Note that there's no need to remove the attribute here if
                    // the declaration block is empty[1], and if `attr` is
                    // `None` it means that it necessarily didn't change, so no
                    // need to go through all the set_attribute machinery.
                    //
                    // [1]: https://github.com/whatwg/html/issues/2306
                    if let Some(pdb) = attr {
                        let serialization = pdb.read().to_css_string();
                        el.set_attribute(&local_name!("style"),
                                         AttrValue::Declaration(serialization,
                                                                pdb));
                    }
                } else {
                    // Remember to put it back.
                    *el.style_attribute().borrow_mut() = attr;
                }

                result
            }
            CSSStyleOwner::CSSRule(ref rule, ref pdb) => {
                let result = f(&mut *pdb.write(), &mut changed);
                if changed {
                    rule.global().as_window().Document().invalidate_stylesheets();
                }
                result
            }
        }
    }

    fn with_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&PropertyDeclarationBlock) -> R,
    {
        match *self {
            CSSStyleOwner::Element(ref el) => {
                match *el.style_attribute().borrow() {
                    Some(ref pdb) => f(&pdb.read()),
                    None => {
                        let pdb = PropertyDeclarationBlock {
                            important_count: 0,
                            declarations: vec![],
                        };
                        f(&pdb)
                    }
                }
            }
            CSSStyleOwner::CSSRule(_, ref pdb) => {
                f(&pdb.read())
            }
        }
    }

    fn window(&self) -> Root<Window> {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el),
            CSSStyleOwner::CSSRule(ref rule, _) => Root::from_ref(rule.global().as_window()),
        }
    }

    fn base_url(&self) -> ServoUrl {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el).Document().base_url(),
            CSSStyleOwner::CSSRule(ref rule, _) => {
                rule.parent_stylesheet().style_stylesheet().base_url.clone()
            }
        }
    }
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly,
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $id:expr],)* ) => (
        $(
            fn $getter(&self) -> DOMString {
                self.get_property_value($id)
            }
            fn $setter(&self, value: DOMString) -> ErrorResult {
                self.set_property($id, value, DOMString::new())
            }
        )*
    );
);

impl CSSStyleDeclaration {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(owner: CSSStyleOwner,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess)
                         -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: owner,
            readonly: modification_access == CSSModificationAccess::Readonly,
            pseudo: pseudo,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(global: &Window,
               owner: CSSStyleOwner,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess)
               -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner,
                                                                  pseudo,
                                                                  modification_access),
                           global,
                           CSSStyleDeclarationBinding::Wrap)
    }

    fn get_computed_style(&self, property: PropertyId) -> DOMString {
        match self.owner {
            CSSStyleOwner::CSSRule(..) =>
                panic!("get_computed_style called on CSSStyleDeclaration with a CSSRule owner"),
            CSSStyleOwner::Element(ref el) => {
                let node = el.upcast::<Node>();
                if !node.is_in_doc() {
                    // TODO: Node should be matched against the style rules of this window.
                    // Firefox is currently the only browser to implement this.
                    return DOMString::new();
                }
                let addr = node.to_trusted_node_address();
                window_from_node(node).resolved_style_query(addr, self.pseudo.clone(), property)
            }
        }
    }

    fn get_property_value(&self, id: PropertyId) -> DOMString {
        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(id);
        }

        let mut string = String::new();

        self.owner.with_block(|pdb| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
        });

        DOMString::from(string)
    }

    fn set_property(&self, id: PropertyId, value: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            if value.is_empty() {
                // Step 4
                *changed = pdb.remove_property(&id);
                return Ok(());
            }

            // Step 5
            let importance = match &*priority {
                "" => Importance::Normal,
                p if p.eq_ignore_ascii_case("important") => Importance::Important,
                _ => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 6
            let window = self.owner.window();
            let declarations =
                parse_one_declaration(id, &value, &self.owner.base_url(),
                                      window.css_error_reporter(),
                                      ParserContextExtraData::default());

            // Step 7
            let declarations = match declarations {
                Ok(declarations) => declarations,
                Err(_) => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 8
            // Step 9
            // We could try to be better I guess?
            *changed = !declarations.is_empty();
            for declaration in declarations {
                // TODO(emilio): We could check it changed
                pdb.set_parsed_declaration(declaration.0, importance);
            }

            Ok(())
        })
    }
}

impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(&self) -> u32 {
        self.owner.with_block(|pdb| {
            pdb.declarations.len() as u32
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(&self, index: u32) -> DOMString {
        self.IndexedGetter(index).unwrap_or_default()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };
        self.get_property_value(id)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };

        self.owner.with_block(|pdb| {
            if pdb.property_priority(&id).important() {
                DOMString::from("important")
            } else {
                // Step 4
                DOMString::new()
            }
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(&self,
                   property: DOMString,
                   value: DOMString,
                   priority: DOMString)
                   -> ErrorResult {
        // Step 3
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unknown property
            return Ok(())
        };
        self.set_property(id, value, priority)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        let id = match PropertyId::parse(property.into()) {
            Ok(id) => id,
            Err(..) => return Ok(()), // Unkwown property
        };

        // Step 4
        let importance = match &*priority {
            "" => Importance::Normal,
            p if p.eq_ignore_ascii_case("important") => Importance::Important,
            _ => return Ok(()),
        };

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            // Step 5 & 6
            *changed = pdb.set_importance(&id, importance);
        });

        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, DOMString::new())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(&self, property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property, cannot be there to remove.
            return Ok(DOMString::new())
        };

        let mut string = String::new();
        self.owner.mutate_associated_block(|mut pdb, mut changed| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
            *changed = pdb.remove_property(&id);
        });

        // Step 6
        Ok(DOMString::from(string))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(&self) -> DOMString {
        self.GetPropertyValue(DOMString::from("float"))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(&self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue(DOMString::from("float"), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
        self.owner.with_block(|pdb| {
            pdb.declarations.get(index as usize).map(|entry| {
                let (ref declaration, importance) = *entry;
                let mut css = declaration.to_css_string();
                if importance.important() {
                    css += " !important";
                }
                DOMString::from(css)
            })
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn CssText(&self) -> DOMString {
        self.owner.with_block(|pdb| {
            DOMString::from(pdb.to_css_string())
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn SetCssText(&self, value: DOMString) -> ErrorResult {
        let window = self.owner.window();

        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|mut pdb, mut _changed| {
            // Step 3
            *pdb = parse_style_attribute(&value,
                                         &self.owner.base_url(),
                                         window.css_error_reporter(),
                                         ParserContextExtraData::default());
        });

        Ok(())
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute
    css_properties_accessors!(css_properties);
}
<|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/. */

//! Generic types for CSS handling of specified and computed values of
//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)

use crate::One;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};

/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    Deserialize,
    MallocSizeOf,
    PartialEq,
    Serialize,
    SpecifiedValueInfo,
    ToAnimatedValue,
    ToAnimatedZero,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct GenericPosition<H, V> {
    /// The horizontal component of position.
    pub horizontal: H,
    /// The vertical component of position.
    pub vertical: V,
}

impl<H, V> PositionComponent for Position<H, V>
where
    H: PositionComponent,
    V: PositionComponent,
{
    #[inline]
    fn is_center(&self) -> bool {
        self.horizontal.is_center() && self.vertical.is_center()
    }
}

pub use self::GenericPosition as Position;

impl<H, V> Position<H, V> {
    /// Returns a new position.
    pub fn new(horizontal: H, vertical: V) -> Self {
        Self {
            horizontal,
            vertical,
        }
    }
}

/// Implements a method that checks if the position is centered.
pub trait PositionComponent {
    /// Returns if the position component is 50% or center.
    /// For pixel lengths, it always returns false.
    fn is_center(&self) -> bool;
}

/// A generic type for representing an `Auto | <position>`.
/// This is used by <offset-anchor> for now.
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
/// cbindgen:private-default-tagged-enum-constructor=false
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    Deserialize,
    MallocSizeOf,
    Parse,
    PartialEq,
    Serialize,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum GenericPositionOrAuto<Pos> {
    /// The <position> value.
    Position(Pos),
    /// The keyword `auto`.
    Auto,
}

pub use self::GenericPositionOrAuto as PositionOrAuto;

impl<Pos> PositionOrAuto<Pos> {
    /// Return `auto`.
    #[inline]
    pub fn auto() -> Self {
        PositionOrAuto::Auto
    }
}

/// A generic value for the `z-index` property.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    Parse,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum GenericZIndex<I> {
    /// An integer value.
    Integer(I),
    /// The keyword `auto`.
    Auto,
}

pub use self::GenericZIndex as ZIndex;

impl<Integer> ZIndex<Integer> {
    /// Returns `auto`
    #[inline]
    pub fn auto() -> Self {
        ZIndex::Auto
    }

    /// Returns whether `self` is `auto`.
    #[inline]
    pub fn is_auto(self) -> bool {
        matches!(self, ZIndex::Auto)
    }

    /// Returns the integer value if it is an integer, or `auto`.
    #[inline]
    pub fn integer_or(self, auto: Integer) -> Integer {
        match self {
            ZIndex::Integer(n) => n,
            ZIndex::Auto => auto,
        }
    }
}

/// A generic value for the `<ratio>` value.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct Ratio<N>(pub N, pub N);

impl<N> ToCss for Ratio<N>
where
    N: ToCss + One + std::cmp::PartialEq,
{
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.0.to_css(dest)?;
        // The second defaults to 1. So if it is one, we omit it in serialization.
        if !self.1.is_one() {
            dest.write_str(" / ")?;
            self.1.to_css(dest)?;
        }
        Ok(())
    }
}

/// Ratio or None.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum PreferredRatio<N> {
    /// Without specified ratio
    #[css(skip)]
    None,
    /// With specified ratio
    Ratio(#[css(field_bound)] Ratio<N>),
}

/// A generic value for the `aspect-ratio` property, the value is `auto || <ratio>`.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct GenericAspectRatio<N> {
    /// Specifiy auto or not.
    #[animation(constant)]
    #[css(represents_keyword)]
    pub auto: bool,
    /// The preferred aspect-ratio value.
    #[css(field_bound)]
    pub ratio: PreferredRatio<N>,
}

pub use self::GenericAspectRatio as AspectRatio;

impl<N> AspectRatio<N> {
    /// Returns `auto`
    #[inline]
    pub fn auto() -> Self {
        AspectRatio {
            auto: true,
            ratio: PreferredRatio::None,
        }
    }
}
<commit_msg>style: Always serialize ratio with both components.<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/. */

//! Generic types for CSS handling of specified and computed values of
//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)

use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};

/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    Deserialize,
    MallocSizeOf,
    PartialEq,
    Serialize,
    SpecifiedValueInfo,
    ToAnimatedValue,
    ToAnimatedZero,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct GenericPosition<H, V> {
    /// The horizontal component of position.
    pub horizontal: H,
    /// The vertical component of position.
    pub vertical: V,
}

impl<H, V> PositionComponent for Position<H, V>
where
    H: PositionComponent,
    V: PositionComponent,
{
    #[inline]
    fn is_center(&self) -> bool {
        self.horizontal.is_center() && self.vertical.is_center()
    }
}

pub use self::GenericPosition as Position;

impl<H, V> Position<H, V> {
    /// Returns a new position.
    pub fn new(horizontal: H, vertical: V) -> Self {
        Self {
            horizontal,
            vertical,
        }
    }
}

/// Implements a method that checks if the position is centered.
pub trait PositionComponent {
    /// Returns if the position component is 50% or center.
    /// For pixel lengths, it always returns false.
    fn is_center(&self) -> bool;
}

/// A generic type for representing an `Auto | <position>`.
/// This is used by <offset-anchor> for now.
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
/// cbindgen:private-default-tagged-enum-constructor=false
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    Deserialize,
    MallocSizeOf,
    Parse,
    PartialEq,
    Serialize,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum GenericPositionOrAuto<Pos> {
    /// The <position> value.
    Position(Pos),
    /// The keyword `auto`.
    Auto,
}

pub use self::GenericPositionOrAuto as PositionOrAuto;

impl<Pos> PositionOrAuto<Pos> {
    /// Return `auto`.
    #[inline]
    pub fn auto() -> Self {
        PositionOrAuto::Auto
    }
}

/// A generic value for the `z-index` property.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    Parse,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum GenericZIndex<I> {
    /// An integer value.
    Integer(I),
    /// The keyword `auto`.
    Auto,
}

pub use self::GenericZIndex as ZIndex;

impl<Integer> ZIndex<Integer> {
    /// Returns `auto`
    #[inline]
    pub fn auto() -> Self {
        ZIndex::Auto
    }

    /// Returns whether `self` is `auto`.
    #[inline]
    pub fn is_auto(self) -> bool {
        matches!(self, ZIndex::Auto)
    }

    /// Returns the integer value if it is an integer, or `auto`.
    #[inline]
    pub fn integer_or(self, auto: Integer) -> Integer {
        match self {
            ZIndex::Integer(n) => n,
            ZIndex::Auto => auto,
        }
    }
}

/// A generic value for the `<ratio>` value.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct Ratio<N>(pub N, pub N);

impl<N> ToCss for Ratio<N>
where
    N: ToCss
{
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.0.to_css(dest)?;
        // Even though 1 could be omitted, we don't per
        // https://drafts.csswg.org/css-values-4/#ratio-value:
        //
        //     The second <number> is optional, defaulting to 1. However,
        //     <ratio> is always serialized with both components.
        //
        // And for compat reasons, see bug 1669742.
        //
        // We serialize with spaces for consistency with all other
        // slash-delimited things, see
        // https://github.com/w3c/csswg-drafts/issues/4282
        dest.write_str(" / ")?;
        self.1.to_css(dest)?;
        Ok(())
    }
}

/// Ratio or None.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C, u8)]
pub enum PreferredRatio<N> {
    /// Without specified ratio
    #[css(skip)]
    None,
    /// With specified ratio
    Ratio(#[css(field_bound)] Ratio<N>),
}

/// A generic value for the `aspect-ratio` property, the value is `auto || <ratio>`.
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToAnimatedZero,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct GenericAspectRatio<N> {
    /// Specifiy auto or not.
    #[animation(constant)]
    #[css(represents_keyword)]
    pub auto: bool,
    /// The preferred aspect-ratio value.
    #[css(field_bound)]
    pub ratio: PreferredRatio<N>,
}

pub use self::GenericAspectRatio as AspectRatio;

impl<N> AspectRatio<N> {
    /// Returns `auto`
    #[inline]
    pub fn auto() -> Self {
        AspectRatio {
            auto: true,
            ratio: PreferredRatio::None,
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::env;
use std::fs;

pub fn shorten(path: &str, path_aliases_file: &str) -> String {
    let path_canonical = make_canonical_path(path, path_aliases_file);
    path_shorten(&path_canonical)
}

fn path_shorten(path_canonical: &str) -> String {
    if path_canonical.chars().count() < 2 {
        return path_canonical.to_string();
    }

    let mut dirs: Vec<String> = Vec::new();

    let num_dirs = path_canonical.split("/").count();

    // Construct the shortened path. As we consider each directory that makes up
    // the path, shorten it if using it as-is would result in >30 characters
    // used.
    let mut path_len = 0;
    let mut path_len_rem = path_canonical.len();
    for (i, dir) in path_canonical.split("/").enumerate() {
        // Handle special case where a leading slash, e.g., "/a/b" results in
        // iterating over ["", "a", "b"]. We skip over the first element.
        if i == 0 && dir.len() == 0 {
            path_len += 1;
            path_len_rem -= 1;
            continue;
        }

        let dir_long = dir.to_string();
        let dir_long_len = dir.len();

        let dir_short = dir.chars().next().unwrap().to_string();

        if path_len + path_len_rem > 30 && !dir.contains("~") && i != num_dirs - 1 {
            dirs.push(dir_short);
            path_len += 1;
            path_len_rem -= dir_long_len;
        } else {
            dirs.push(dir_long);
            path_len += dir_long_len;
            path_len_rem -= dir_long_len;
        }
        // Only add length of "/" separator if we're not at the end.
        if i != num_dirs - 1 {
            path_len += 1;
            path_len_rem -= 1;
        }
    }

    if path_canonical.chars().next().unwrap() == '/' {
        format!("/{}", dirs.join("/"))
    } else {
        dirs.join("/")
    }
}

fn make_canonical_path(path: &str, path_aliases_file: &str) -> String {
    // The '?' operator short-circuits if we don't have a HOME variable defined
    // in the environment.
    let home_dir = env::var("HOME").expect("$HOME not set");

    let path_aliases_contents = fs::read_to_string(path_aliases_file).unwrap_or_default();
    let path_aliases = make_path_aliases(&path_aliases_contents, &home_dir);

    let path_canonical = match get_matching_path_alias(path, &path_aliases) {
        // Find the longest matching expanded path in the path aliases. If there
        // is a match, then we use "~ALIAS" (the leading "~" does not mean $HOME
        // and just signifies that the word that immediately follows it is a
        // path alias).
        Some((expanded_path, path_alias)) => {
            let new_path = path.replacen(&expanded_path, &path_alias, 1);
            format!("~{}", new_path)
        }
        // If there is no match, just replace whatever $HOME is with "~".
        None => {
            if path.starts_with(&home_dir) {
                format!("{}", path.replacen(&home_dir, "~", 1))
            } else {
                format!("{}", path)
            }
        }
    };

    path_canonical
}

fn make_path_aliases(path_aliases_contents: &str, home_dir: &String) -> HashMap<String, String> {
    let mut path_aliases = HashMap::new();

    for line in path_aliases_contents.lines() {
        if line.starts_with("hash") {
            for word in line.split_ascii_whitespace() {
                if word.contains("=") {
                    let mut k_or_v = word.split("=");
                    let v = k_or_v.next().unwrap_or("").to_string();
                    let k = k_or_v.next().unwrap_or("").to_string();
                    let k_final = k.replacen("$HOME", &home_dir, 1);
                    path_aliases.insert(k_final, v);
                }
            }
        }
    }

    path_aliases
}

fn get_matching_path_alias(
    path: &str,
    path_aliases: &HashMap<String, String>,
) -> Option<(String, String)> {
    let mut keys = path_aliases.keys().collect::<Vec<_>>();
    keys.sort();
    keys.reverse();

    for k in keys {
        if path.starts_with(k) {
            let v = path_aliases.get(k).unwrap();
            return Some((k.to_string(), v.to_string()));
        }
    }

    None
}

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

    #[test]
    fn test_path_shorten() {
        assert_eq!(path_shorten(""), "");
        assert_eq!(path_shorten("~"), "~");
        assert_eq!(path_shorten("/"), "/");
        //assert_eq!("/a/b".split("/").collect::<Vec<&str>>(), [""]);
        assert_eq!(path_shorten("/a"), "/a");
        assert_eq!(path_shorten("/a/b/c"), "/a/b/c");
        assert_eq!(path_shorten("a"), "a");
        assert_eq!(path_shorten("a/b/c"), "a/b/c");
        // If the path is exactly 30 characters, we should not shorten anything.
        assert_eq!(
            path_shorten("/a23456789/b23456789/c23456789"),
            "/a23456789/b23456789/c23456789"
        );
        // If the path is just over 30 characters, we should shorten the first
        // directory.
        assert_eq!(
            path_shorten("/a23456789/123456789/123456789a"),
            "/a/123456789/123456789a"
        );
        // Some longer directories.
        assert_eq!(
            path_shorten("/a23456789/b23456789/123456789/123456789"),
            "/a/b/123456789/123456789"
        );
        assert_eq!(
            path_shorten("a23456789/b23456789/123456789/123456789"),
            "a/b/123456789/123456789"
        );
        // Shortening of aliases (directories with "~") in them are forbidden.
        assert_eq!(
            path_shorten("~123456789/123456789/123456789/123456789"),
            "~123456789/1/1/123456789"
        );
        // Realistic example (last directory remains untouched).
        assert_eq!(
            path_shorten("~/prog/foreign/git/contrib/thunderbird-patch-inline"),
            "~/p/f/g/c/thunderbird-patch-inline"
        );
        // Extreme example.
        assert_eq!(
            path_shorten(
                "~/aaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbb/cccccccccccccccccccccc/hello"
            ),
            "~/a/b/c/hello"
        );
    }
}
<commit_msg>path_shorten: add unicode support<commit_after>use std::collections::HashMap;
use std::env;
use std::fs;

pub fn shorten(path: &str, path_aliases_file: &str) -> String {
    let path_canonical = make_canonical_path(path, path_aliases_file);
    path_shorten(&path_canonical)
}

fn path_shorten(path_canonical: &str) -> String {
    // Don't shorten paths that are 30 characters or less in length.
    if path_canonical.chars().count() <= 30 {
        return path_canonical.to_string();
    }

    // Don't bother shortening anything if there is only 1 directory.
    let parts_count = path_canonical.split("/").count();
    if parts_count == 1 {
        return path_canonical.to_string();
    }
    let first_char = path_canonical.chars().next().unwrap();
    if first_char == '/' && parts_count == 2 {
        return path_canonical.to_string();
    }

    // Determine overall "search" area of possible directories within the path
    // to shorten to 1 character. We exclude from the search the very first and
    // last directories.
    let (j, shortenable_dirs) = match first_char {
        // Do not shorten leading directories that start with '~', and also do
        // not consider the root directory '/'.
        '/' | '~' => (1, 1..(parts_count - 1)),
        _ => (0, 0..(parts_count - 1)),
    };

    // Construct a set of ranges, using shortenable_dirs. E.g., if
    // shortenable_dirs is (1..3), then construct:
    //   (1..2)
    //   (1..3)
    //   (1..4).
    // We use these ranges to denote directories that should be shortened. As
    // these ranges include more and more numbers, we shorten more and more
    // directories until we are satisified with how much we've shortened
    // path_canonical.
    let mut ranges: Vec<std::ops::Range<usize>> = Vec::new();

    for i in shortenable_dirs {
        ranges.push(j..i + 1);
    }

    let mut candidate_best: Option<String> = None;
    for range in ranges {
        // Construct shortened path candidate with all directories in the range
        // shortened.
        let mut candidate: Vec<String> = Vec::new();
        for (part_idx, part) in path_canonical.split("/").enumerate() {
            if range.contains(&part_idx) {
                // Add shortened version.
                candidate.push(part.chars().next().unwrap().to_string());
            } else {
                // Add as-is.
                candidate.push(part.to_string());
            }
        }
        let shortened = candidate.join("/");
        // If a better (shorter) candidate is found, prefer it over the previous
        // candidate.
        if candidate_best.is_none()
            || shortened.chars().count() < candidate_best.as_ref().unwrap().chars().count()
        {
            candidate_best = Some(shortened);
        };

        // If a candidate is already under 30 characters, stop searching.
        if candidate_best.is_some() && candidate_best.as_ref().unwrap().chars().count() <= 30 {
            break;
        }
    }

    if candidate_best.is_none() {
        path_canonical.to_string()
    } else {
        candidate_best.unwrap().to_string()
    }
}

fn make_canonical_path(path: &str, path_aliases_file: &str) -> String {
    // The '?' operator short-circuits if we don't have a HOME variable defined
    // in the environment.
    let home_dir = env::var("HOME").expect("$HOME not set");

    let path_aliases_contents = fs::read_to_string(path_aliases_file).unwrap_or_default();
    let path_aliases = make_path_aliases(&path_aliases_contents, &home_dir);

    let path_canonical = match get_matching_path_alias(path, &path_aliases) {
        // Find the longest matching expanded path in the path aliases. If there
        // is a match, then we use "~ALIAS" (the leading "~" does not mean $HOME
        // and just signifies that the word that immediately follows it is a
        // path alias).
        Some((expanded_path, path_alias)) => {
            let new_path = path.replacen(&expanded_path, &path_alias, 1);
            format!("~{}", new_path)
        }
        // If there is no match, just replace whatever $HOME is with "~".
        None => {
            if path.starts_with(&home_dir) {
                format!("{}", path.replacen(&home_dir, "~", 1))
            } else {
                format!("{}", path)
            }
        }
    };

    path_canonical
}

fn make_path_aliases(path_aliases_contents: &str, home_dir: &String) -> HashMap<String, String> {
    let mut path_aliases = HashMap::new();

    for line in path_aliases_contents.lines() {
        if line.starts_with("hash") {
            for word in line.split_ascii_whitespace() {
                if word.contains("=") {
                    let mut k_or_v = word.split("=");
                    let v = k_or_v.next().unwrap_or("").to_string();
                    let k = k_or_v.next().unwrap_or("").to_string();
                    let k_final = k.replacen("$HOME", &home_dir, 1);
                    path_aliases.insert(k_final, v);
                }
            }
        }
    }

    path_aliases
}

fn get_matching_path_alias(
    path: &str,
    path_aliases: &HashMap<String, String>,
) -> Option<(String, String)> {
    let mut keys = path_aliases.keys().collect::<Vec<_>>();
    keys.sort();
    keys.reverse();

    for k in keys {
        if path.starts_with(k) {
            let v = path_aliases.get(k).unwrap();
            return Some((k.to_string(), v.to_string()));
        }
    }

    None
}

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

    #[test]
    fn test_path_shorten() {
        assert_eq!(path_shorten(""), "");
        assert_eq!(path_shorten("~"), "~");
        assert_eq!(path_shorten("/"), "/");
        assert_eq!(path_shorten("/a"), "/a");
        assert_eq!(path_shorten("/a/b/c"), "/a/b/c");
        assert_eq!(path_shorten("a"), "a");
        assert_eq!(path_shorten("a/b/c"), "a/b/c");
        // If the path is exactly 30 characters, we should not shorten anything.
        assert_eq!(
            path_shorten("/a23456789/b23456789/c23456789"),
            "/a23456789/b23456789/c23456789"
        );
        // If the path is just over 30 characters, we should shorten the first
        // directory.
        assert_eq!(
            path_shorten("/a23456789/b23456789/c23456789d"),
            "/a/b23456789/c23456789d"
        );
        // Some longer directories.
        assert_eq!(
            path_shorten("/a23456789/b23456789/c23456789/d23456789"),
            "/a/b/c23456789/d23456789"
        );
        assert_eq!(
            path_shorten("a23456789/b23456789/c23456789/d23456789"),
            "a/b/c23456789/d23456789"
        );
        // Shortening of aliases (directories with "~") in them are forbidden.
        assert_eq!(
            path_shorten("~a23456789/b23456789/c23456789/d23456789"),
            "~a23456789/b/c/d23456789"
        );
        // Realistic example (last directory remains untouched).
        assert_eq!(
            path_shorten("~/prog/foreign/git/contrib/thunderbird-patch-inline"),
            "~/p/f/g/c/thunderbird-patch-inline"
        );
        // Extreme cases.
        assert_eq!(
            path_shorten(
                "~/aaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbb/cccccccccccccccccccccc/hello"
            ),
            "~/a/b/c/hello"
        );
        // Unusual case of just 2 directories, where both are very long.
        assert_eq!(
            path_shorten(
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
            ),
            "a/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        );
        // Non-ASCII (exactly 30 characters).
        assert_eq!(
            path_shorten("/일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구"),
            "/일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구"
        );
        assert_eq!(
            path_shorten("일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구a"),
            "일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구a"
        );
        assert_eq!(
            path_shorten("~일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구"),
            "~일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구"
        );
        // Non-ASCII (over 30 characters).
        assert_eq!(
            path_shorten("/일이삼사오육칠팔구/일이삼사오육칠팔구/일이삼사오육칠팔구/a"),
            "/일/일이삼사오육칠팔구/일이삼사오육칠팔구/a"
        );
        assert_eq!(
            path_shorten("~/일일일일일일일일일일일일일일일일일일일일/이이이이이이이이이이이이이이이이이이이이/삼삼삼삼삼삼삼삼삼삼삼삼삼삼삼삼삼삼삼삼/hello"),
            "~/일/이/삼/hello"
        );
    }
}
<|endoftext|>


<commit_before>extern crate phone_number as phone;

fn to_some_string(s: &str) -> Option<String> {
    Some(s.to_string())
}

#[test]
fn test_number_cleans() {
    assert_eq!(phone::number("(123) 456-7890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_number_cleans_with_dots() {
    assert_eq!(phone::number("123.456.7890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_valid_when_11_digits_and_first_is_1() {
    assert_eq!(phone::number("11234567890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_invalid_when_11_digits() {
    assert_eq!(phone::number("21234567890"), None);
}

#[test]
#[ignore]
fn test_invalid_when_9_digits() {
    assert_eq!(phone::number("123456789"), None);
}

#[test]
#[ignore]
fn test_invalid_when_empty() {
    assert_eq!(phone::number(""), None);
}

#[test]
#[ignore]
fn test_invalid_when_no_digits_present() {
    assert_eq!(phone::number(" (-) "), None);
}

#[test]
#[ignore]
fn test_valid_with_leading_characters() {
    assert_eq!(phone::number("my number is 123 456 7890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_valid_with_trailing_characters() {
    assert_eq!(phone::number("123 456 7890 - bob"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_area_code() {
    assert_eq!(phone::area_code("1234567890"), to_some_string("123"));
}

#[test]
#[ignore]
fn test_area_code_with_full_us_phone_number() {
    assert_eq!(phone::area_code("18234567890"), to_some_string("823"));
}

#[test]
#[ignore]
fn test_area_code_with_invalid() {
    assert_eq!(phone::area_code("1234161567890"), None);
}

#[test]
fn test_pretty_print() {
    assert_eq!(phone::pretty_print("1234567890"), "(123) 456-7890");
}

#[test]
fn test_pretty_print_with_full_us_phone_number() {
    assert_eq!(phone::pretty_print("11234567890"), "(123) 456-7890");
}

#[test]
fn test_pretty_print_with_invalid() {
    assert_eq!(phone::pretty_print("1186234567890"), "invalid");
}
<commit_msg>Fixes Phone Number problem to ignore all but one test<commit_after>extern crate phone_number as phone;

fn to_some_string(s: &str) -> Option<String> {
    Some(s.to_string())
}

#[test]
fn test_number_cleans() {
    assert_eq!(phone::number("(123) 456-7890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_number_cleans_with_dots() {
    assert_eq!(phone::number("123.456.7890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_valid_when_11_digits_and_first_is_1() {
    assert_eq!(phone::number("11234567890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_invalid_when_11_digits() {
    assert_eq!(phone::number("21234567890"), None);
}

#[test]
#[ignore]
fn test_invalid_when_9_digits() {
    assert_eq!(phone::number("123456789"), None);
}

#[test]
#[ignore]
fn test_invalid_when_empty() {
    assert_eq!(phone::number(""), None);
}

#[test]
#[ignore]
fn test_invalid_when_no_digits_present() {
    assert_eq!(phone::number(" (-) "), None);
}

#[test]
#[ignore]
fn test_valid_with_leading_characters() {
    assert_eq!(phone::number("my number is 123 456 7890"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_valid_with_trailing_characters() {
    assert_eq!(phone::number("123 456 7890 - bob"), to_some_string("1234567890"));
}

#[test]
#[ignore]
fn test_area_code() {
    assert_eq!(phone::area_code("1234567890"), to_some_string("123"));
}

#[test]
#[ignore]
fn test_area_code_with_full_us_phone_number() {
    assert_eq!(phone::area_code("18234567890"), to_some_string("823"));
}

#[test]
#[ignore]
fn test_area_code_with_invalid() {
    assert_eq!(phone::area_code("1234161567890"), None);
}

#[test]
#[ignore]
fn test_pretty_print() {
    assert_eq!(phone::pretty_print("1234567890"), "(123) 456-7890");
}

#[test]
#[ignore]
fn test_pretty_print_with_full_us_phone_number() {
    assert_eq!(phone::pretty_print("11234567890"), "(123) 456-7890");
}

#[test]
#[ignore]
fn test_pretty_print_with_invalid() {
    assert_eq!(phone::pretty_print("1186234567890"), "invalid");
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use anyhow::bail;
use aptos_rest_client::Client;
use aptos_types::account_address::AccountAddress;
use framework::natives::code::{ModuleMetadata, PackageMetadata, PackageRegistry, UpgradePolicy};
use framework::unzip_metadata_str;
use move_package::compilation::package_layout::CompiledPackageLayout;
use reqwest::Url;
use std::fs;
use std::path::Path;

// TODO: this is a first naive implementation of the package registry. Before mainnet
// we need to use tables for the package registry.

/// Represents the package registry at a given account.
pub struct CachedPackageRegistry {
    inner: PackageRegistry,
}

/// Represents the package metadata found in an registry.
pub struct CachedPackageMetadata<'a> {
    metadata: &'a PackageMetadata,
}

/// Represents the package metadata found in an registry.
pub struct CachedModuleMetadata<'a> {
    metadata: &'a ModuleMetadata,
}

impl CachedPackageRegistry {
    /// Creates a new registry.
    pub async fn create(url: Url, addr: AccountAddress) -> anyhow::Result<Self> {
        let client = Client::new(url);
        // Need to use a different type to deserialize JSON
        let inner = client
            .get_account_resource_bcs::<PackageRegistry>(addr, "0x1::code::PackageRegistry")
            .await?
            .into_inner();
        Ok(Self { inner })
    }

    /// Returns the list of packages in this registry by name.
    pub fn package_names(&self) -> Vec<&str> {
        self.inner
            .packages
            .iter()
            .map(|p| p.name.as_str())
            .collect()
    }

    /// Finds the metadata for the given module in the registry by its unique name.
    pub async fn get_module<'a>(
        &self,
        name: impl AsRef<str>,
    ) -> anyhow::Result<CachedModuleMetadata<'_>> {
        let name = name.as_ref();
        for package in &self.inner.packages {
            for module in &package.modules {
                if module.name == name {
                    return Ok(CachedModuleMetadata { metadata: module });
                }
            }
        }
        bail!("module `{}` not found", name)
    }

    /// Finds the metadata for the given package in the registry by its unique name.
    pub async fn get_package<'a>(
        &self,
        name: impl AsRef<str>,
    ) -> anyhow::Result<CachedPackageMetadata<'_>> {
        let name = name.as_ref();
        for package in &self.inner.packages {
            if package.name == name {
                return Ok(CachedPackageMetadata { metadata: package });
            }
        }
        bail!("package `{}` not found", name)
    }
}

impl<'a> CachedPackageMetadata<'a> {
    pub fn name(&self) -> &str {
        &self.metadata.name
    }

    pub fn upgrade_policy(&self) -> UpgradePolicy {
        self.metadata.upgrade_policy
    }

    pub fn upgrade_number(&self) -> u64 {
        self.metadata.upgrade_number
    }

    pub fn source_digest(&self) -> &str {
        &self.metadata.source_digest
    }

    pub fn manifest(&self) -> anyhow::Result<String> {
        unzip_metadata_str(&self.metadata.manifest)
    }

    pub fn module_names(&self) -> Vec<&str> {
        self.metadata
            .modules
            .iter()
            .map(|s| s.name.as_str())
            .collect()
    }

    pub fn module(&self, name: impl AsRef<str>) -> anyhow::Result<CachedModuleMetadata<'_>> {
        let name = name.as_ref();
        for module in &self.metadata.modules {
            if module.name == name {
                return Ok(CachedModuleMetadata { metadata: module });
            }
        }
        bail!("module `{}` not found", name)
    }

    pub fn save_package_to_disk(&self, path: &Path) -> anyhow::Result<()> {
        fs::create_dir_all(path)?;
        fs::write(
            path.join("Move.toml"),
            unzip_metadata_str(&self.metadata.manifest)?,
        )?;
        let sources_dir = path.join(CompiledPackageLayout::Sources.path());
        fs::create_dir_all(&sources_dir)?;
        for module in &self.metadata.modules {
            let source = match module.source.is_empty() {
                true => {
                    print!("module without code: {}\n", module.name);
                    "".into()
                },
                false => unzip_metadata_str(&module.source)?
            };
            fs::write(sources_dir.join(format!("{}.move", module.name)), source)?;
        }
        Ok(())
    }

    pub fn verify(&self, package_metadata: &PackageMetadata) -> anyhow::Result<()> {
        let self_metadata = self.metadata;

        if self_metadata.name != package_metadata.name {
            bail!(
                "Package name doesn't match {} : {}",
                package_metadata.name,
                self_metadata.name
            )
        } else if self_metadata.deps != package_metadata.deps {
            bail!(
                "Dependencies don't match {:?} : {:?}",
                package_metadata.deps,
                self_metadata.deps
            )
        } else if self_metadata.modules != package_metadata.modules {
            bail!(
                "Modules don't match {:?} : {:?}",
                package_metadata.modules,
                self_metadata.modules
            )
        } else if self_metadata.manifest != package_metadata.manifest {
            bail!(
                "Manifest doesn't match {:?} : {:?}",
                package_metadata.manifest,
                self_metadata.manifest
            )
        } else if self_metadata.upgrade_policy != package_metadata.upgrade_policy {
            bail!(
                "Upgrade policy doesn't match {:?} : {:?}",
                package_metadata.upgrade_policy,
                self_metadata.upgrade_policy
            )
        } else if self_metadata.upgrade_number != package_metadata.upgrade_number {
            bail!(
                "Upgrade number doesn't match {:?} : {:?}",
                package_metadata.upgrade_number,
                self_metadata.upgrade_number
            )
        } else if self_metadata.extension != package_metadata.extension {
            bail!(
                "Extensions doesn't match {:?} : {:?}",
                package_metadata.extension,
                self_metadata.extension
            )
        } else if self_metadata.source_digest != package_metadata.source_digest {
            bail!(
                "Source digests doesn't match {:?} : {:?}",
                package_metadata.source_digest,
                self_metadata.source_digest
            )
        }

        Ok(())
    }
}

impl<'a> CachedModuleMetadata<'a> {
    pub fn name(&self) -> &str {
        &self.metadata.name
    }

    pub fn zipped_source(&self) -> &[u8] {
        &self.metadata.source
    }

    pub fn zipped_source_map_raw(&self) -> &[u8] {
        &self.metadata.source_map
    }
}
<commit_msg>[cli] Fix lint error<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use anyhow::bail;
use aptos_rest_client::Client;
use aptos_types::account_address::AccountAddress;
use framework::natives::code::{ModuleMetadata, PackageMetadata, PackageRegistry, UpgradePolicy};
use framework::unzip_metadata_str;
use move_package::compilation::package_layout::CompiledPackageLayout;
use reqwest::Url;
use std::fs;
use std::path::Path;

// TODO: this is a first naive implementation of the package registry. Before mainnet
// we need to use tables for the package registry.

/// Represents the package registry at a given account.
pub struct CachedPackageRegistry {
    inner: PackageRegistry,
}

/// Represents the package metadata found in an registry.
pub struct CachedPackageMetadata<'a> {
    metadata: &'a PackageMetadata,
}

/// Represents the package metadata found in an registry.
pub struct CachedModuleMetadata<'a> {
    metadata: &'a ModuleMetadata,
}

impl CachedPackageRegistry {
    /// Creates a new registry.
    pub async fn create(url: Url, addr: AccountAddress) -> anyhow::Result<Self> {
        let client = Client::new(url);
        // Need to use a different type to deserialize JSON
        let inner = client
            .get_account_resource_bcs::<PackageRegistry>(addr, "0x1::code::PackageRegistry")
            .await?
            .into_inner();
        Ok(Self { inner })
    }

    /// Returns the list of packages in this registry by name.
    pub fn package_names(&self) -> Vec<&str> {
        self.inner
            .packages
            .iter()
            .map(|p| p.name.as_str())
            .collect()
    }

    /// Finds the metadata for the given module in the registry by its unique name.
    pub async fn get_module<'a>(
        &self,
        name: impl AsRef<str>,
    ) -> anyhow::Result<CachedModuleMetadata<'_>> {
        let name = name.as_ref();
        for package in &self.inner.packages {
            for module in &package.modules {
                if module.name == name {
                    return Ok(CachedModuleMetadata { metadata: module });
                }
            }
        }
        bail!("module `{}` not found", name)
    }

    /// Finds the metadata for the given package in the registry by its unique name.
    pub async fn get_package<'a>(
        &self,
        name: impl AsRef<str>,
    ) -> anyhow::Result<CachedPackageMetadata<'_>> {
        let name = name.as_ref();
        for package in &self.inner.packages {
            if package.name == name {
                return Ok(CachedPackageMetadata { metadata: package });
            }
        }
        bail!("package `{}` not found", name)
    }
}

impl<'a> CachedPackageMetadata<'a> {
    pub fn name(&self) -> &str {
        &self.metadata.name
    }

    pub fn upgrade_policy(&self) -> UpgradePolicy {
        self.metadata.upgrade_policy
    }

    pub fn upgrade_number(&self) -> u64 {
        self.metadata.upgrade_number
    }

    pub fn source_digest(&self) -> &str {
        &self.metadata.source_digest
    }

    pub fn manifest(&self) -> anyhow::Result<String> {
        unzip_metadata_str(&self.metadata.manifest)
    }

    pub fn module_names(&self) -> Vec<&str> {
        self.metadata
            .modules
            .iter()
            .map(|s| s.name.as_str())
            .collect()
    }

    pub fn module(&self, name: impl AsRef<str>) -> anyhow::Result<CachedModuleMetadata<'_>> {
        let name = name.as_ref();
        for module in &self.metadata.modules {
            if module.name == name {
                return Ok(CachedModuleMetadata { metadata: module });
            }
        }
        bail!("module `{}` not found", name)
    }

    pub fn save_package_to_disk(&self, path: &Path) -> anyhow::Result<()> {
        fs::create_dir_all(path)?;
        fs::write(
            path.join("Move.toml"),
            unzip_metadata_str(&self.metadata.manifest)?,
        )?;
        let sources_dir = path.join(CompiledPackageLayout::Sources.path());
        fs::create_dir_all(&sources_dir)?;
        for module in &self.metadata.modules {
            let source = match module.source.is_empty() {
                true => {
                    println!("module without code: {}", module.name);
                    "".into()
                }
                false => unzip_metadata_str(&module.source)?,
            };
            fs::write(sources_dir.join(format!("{}.move", module.name)), source)?;
        }
        Ok(())
    }

    pub fn verify(&self, package_metadata: &PackageMetadata) -> anyhow::Result<()> {
        let self_metadata = self.metadata;

        if self_metadata.name != package_metadata.name {
            bail!(
                "Package name doesn't match {} : {}",
                package_metadata.name,
                self_metadata.name
            )
        } else if self_metadata.deps != package_metadata.deps {
            bail!(
                "Dependencies don't match {:?} : {:?}",
                package_metadata.deps,
                self_metadata.deps
            )
        } else if self_metadata.modules != package_metadata.modules {
            bail!(
                "Modules don't match {:?} : {:?}",
                package_metadata.modules,
                self_metadata.modules
            )
        } else if self_metadata.manifest != package_metadata.manifest {
            bail!(
                "Manifest doesn't match {:?} : {:?}",
                package_metadata.manifest,
                self_metadata.manifest
            )
        } else if self_metadata.upgrade_policy != package_metadata.upgrade_policy {
            bail!(
                "Upgrade policy doesn't match {:?} : {:?}",
                package_metadata.upgrade_policy,
                self_metadata.upgrade_policy
            )
        } else if self_metadata.upgrade_number != package_metadata.upgrade_number {
            bail!(
                "Upgrade number doesn't match {:?} : {:?}",
                package_metadata.upgrade_number,
                self_metadata.upgrade_number
            )
        } else if self_metadata.extension != package_metadata.extension {
            bail!(
                "Extensions doesn't match {:?} : {:?}",
                package_metadata.extension,
                self_metadata.extension
            )
        } else if self_metadata.source_digest != package_metadata.source_digest {
            bail!(
                "Source digests doesn't match {:?} : {:?}",
                package_metadata.source_digest,
                self_metadata.source_digest
            )
        }

        Ok(())
    }
}

impl<'a> CachedModuleMetadata<'a> {
    pub fn name(&self) -> &str {
        &self.metadata.name
    }

    pub fn zipped_source(&self) -> &[u8] {
        &self.metadata.source
    }

    pub fn zipped_source_map_raw(&self) -> &[u8] {
        &self.metadata.source_map
    }
}
<|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/. */

//! Value information for devtools.

use servo_arc::Arc;
use std::ops::Range;
use std::sync::Arc as StdArc;

/// Type of value that a property supports. This is used by Gecko's
/// devtools to make sense about value it parses, and types listed
/// here should match TYPE_* constants in InspectorUtils.webidl.
///
/// XXX This should really be a bitflags rather than a namespace mod,
/// but currently we cannot use bitflags in const.
#[allow(non_snake_case)]
pub mod CssType {
    /// <color>
    pub const COLOR: u8 = 1 << 0;
    /// <gradient>
    pub const GRADIENT: u8 = 1 << 1;
    /// <timing-function>
    pub const TIMING_FUNCTION: u8 = 1 << 2;
}

/// See SpecifiedValueInfo::collect_completion_keywords.
pub type KeywordsCollectFn<'a> = &'a mut FnMut(&[&'static str]);

/// Information of values of a given specified value type.
///
/// This trait is derivable with `#[derive(SpecifiedValueInfo)]`.
///
/// The algorithm traverses the type definition. For `SUPPORTED_TYPES`,
/// it puts an or'ed value of `SUPPORTED_TYPES` of all types it finds.
/// For `collect_completion_keywords`, it recursively invokes this
/// method on types found, and lists all keyword values and function
/// names following the same rule as `ToCss` in that method.
///
/// Some attributes of `ToCss` can affect the behavior, specifically:
/// * If `#[css(function)]` is found, the content inside the annotated
///   variant (or the whole type) isn't traversed, only the function
///   name is listed in `collect_completion_keywords`.
/// * If `#[css(skip)]` is found, the content inside the variant or
///   field is ignored.
/// * Values listed in `#[css(if_empty)]`, `#[parse(aliases)]`, and
///   `#[css(keyword)]` are added into `collect_completion_keywords`.
///
/// In addition to `css` attributes, it also has `value_info` helper
/// attributes, including:
/// * `#[value_info(ty = "TYPE")]` can be used to specify a constant
///   from `CssType` to `SUPPORTED_TYPES`.
/// * `#[value_info(other_values = "value1,value2")]` can be used to
///   add other values related to a field, variant, or the type itself
///   into `collect_completion_keywords`.
/// * `#[value_info(starts_with_keyword)]` can be used on variants to
///   add the name of a non-unit variant (serialized like `ToCss`) into
///   `collect_completion_keywords`.
pub trait SpecifiedValueInfo {
    /// Supported CssTypes by the given value type.
    ///
    /// XXX This should be typed CssType when that becomes a bitflags.
    /// Currently we cannot do so since bitflags cannot be used in constant.
    const SUPPORTED_TYPES: u8 = 0;

    /// Collect value starting words for the given specified value type.
    /// This includes keyword and function names which can appear at the
    /// beginning of a value of this type.
    ///
    /// Caller should pass in a callback function to accept the list of
    /// values. The callback function can be called multiple times, and
    /// some values passed to the callback may be duplicate.
    fn collect_completion_keywords(_f: KeywordsCollectFn) {}
}

impl SpecifiedValueInfo for bool {}
impl SpecifiedValueInfo for f32 {}
impl SpecifiedValueInfo for i8 {}
impl SpecifiedValueInfo for i32 {}
impl SpecifiedValueInfo for u8 {}
impl SpecifiedValueInfo for u16 {}
impl SpecifiedValueInfo for u32 {}
impl SpecifiedValueInfo for str {}
impl SpecifiedValueInfo for String {}

#[cfg(feature = "servo")]
impl SpecifiedValueInfo for ::servo_atoms::Atom {}
#[cfg(feature = "servo")]
impl SpecifiedValueInfo for ::servo_url::ServoUrl {}

impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {
    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
    fn collect_completion_keywords(f: KeywordsCollectFn) {
        T::collect_completion_keywords(f);
    }
}

impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {
    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
    fn collect_completion_keywords(f: KeywordsCollectFn) {
        T::collect_completion_keywords(f);
    }
}

macro_rules! impl_generic_specified_value_info {
    ($ty:ident<$param:ident>) => {
        impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> {
            const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES;
            fn collect_completion_keywords(f: KeywordsCollectFn) {
                $param::collect_completion_keywords(f);
            }
        }
    };
}
impl_generic_specified_value_info!(Option<T>);
impl_generic_specified_value_info!(Vec<T>);
impl_generic_specified_value_info!(Arc<T>);
impl_generic_specified_value_info!(StdArc<T>);
impl_generic_specified_value_info!(Range<Idx>);

impl<T1, T2> SpecifiedValueInfo for (T1, T2)
where
    T1: SpecifiedValueInfo,
    T2: SpecifiedValueInfo,
{
    const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;

    fn collect_completion_keywords(f: KeywordsCollectFn) {
        T1::collect_completion_keywords(f);
        T2::collect_completion_keywords(f);
    }
}
<commit_msg>style: Make cssPropertySupportsType take an enum.<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/. */

//! Value information for devtools.

use servo_arc::Arc;
use std::ops::Range;
use std::sync::Arc as StdArc;

/// Type of value that a property supports. This is used by Gecko's
/// devtools to make sense about value it parses, and types listed
/// here should match InspectorPropertyType in InspectorUtils.webidl.
///
/// XXX This should really be a bitflags rather than a namespace mod,
/// but currently we cannot use bitflags in const.
#[allow(non_snake_case)]
pub mod CssType {
    /// <color>
    pub const COLOR: u8 = 1 << 0;
    /// <gradient>
    pub const GRADIENT: u8 = 1 << 1;
    /// <timing-function>
    pub const TIMING_FUNCTION: u8 = 1 << 2;
}

/// See SpecifiedValueInfo::collect_completion_keywords.
pub type KeywordsCollectFn<'a> = &'a mut FnMut(&[&'static str]);

/// Information of values of a given specified value type.
///
/// This trait is derivable with `#[derive(SpecifiedValueInfo)]`.
///
/// The algorithm traverses the type definition. For `SUPPORTED_TYPES`,
/// it puts an or'ed value of `SUPPORTED_TYPES` of all types it finds.
/// For `collect_completion_keywords`, it recursively invokes this
/// method on types found, and lists all keyword values and function
/// names following the same rule as `ToCss` in that method.
///
/// Some attributes of `ToCss` can affect the behavior, specifically:
/// * If `#[css(function)]` is found, the content inside the annotated
///   variant (or the whole type) isn't traversed, only the function
///   name is listed in `collect_completion_keywords`.
/// * If `#[css(skip)]` is found, the content inside the variant or
///   field is ignored.
/// * Values listed in `#[css(if_empty)]`, `#[parse(aliases)]`, and
///   `#[css(keyword)]` are added into `collect_completion_keywords`.
///
/// In addition to `css` attributes, it also has `value_info` helper
/// attributes, including:
/// * `#[value_info(ty = "TYPE")]` can be used to specify a constant
///   from `CssType` to `SUPPORTED_TYPES`.
/// * `#[value_info(other_values = "value1,value2")]` can be used to
///   add other values related to a field, variant, or the type itself
///   into `collect_completion_keywords`.
/// * `#[value_info(starts_with_keyword)]` can be used on variants to
///   add the name of a non-unit variant (serialized like `ToCss`) into
///   `collect_completion_keywords`.
pub trait SpecifiedValueInfo {
    /// Supported CssTypes by the given value type.
    ///
    /// XXX This should be typed CssType when that becomes a bitflags.
    /// Currently we cannot do so since bitflags cannot be used in constant.
    const SUPPORTED_TYPES: u8 = 0;

    /// Collect value starting words for the given specified value type.
    /// This includes keyword and function names which can appear at the
    /// beginning of a value of this type.
    ///
    /// Caller should pass in a callback function to accept the list of
    /// values. The callback function can be called multiple times, and
    /// some values passed to the callback may be duplicate.
    fn collect_completion_keywords(_f: KeywordsCollectFn) {}
}

impl SpecifiedValueInfo for bool {}
impl SpecifiedValueInfo for f32 {}
impl SpecifiedValueInfo for i8 {}
impl SpecifiedValueInfo for i32 {}
impl SpecifiedValueInfo for u8 {}
impl SpecifiedValueInfo for u16 {}
impl SpecifiedValueInfo for u32 {}
impl SpecifiedValueInfo for str {}
impl SpecifiedValueInfo for String {}

#[cfg(feature = "servo")]
impl SpecifiedValueInfo for ::servo_atoms::Atom {}
#[cfg(feature = "servo")]
impl SpecifiedValueInfo for ::servo_url::ServoUrl {}

impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {
    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
    fn collect_completion_keywords(f: KeywordsCollectFn) {
        T::collect_completion_keywords(f);
    }
}

impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {
    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
    fn collect_completion_keywords(f: KeywordsCollectFn) {
        T::collect_completion_keywords(f);
    }
}

macro_rules! impl_generic_specified_value_info {
    ($ty:ident<$param:ident>) => {
        impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> {
            const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES;
            fn collect_completion_keywords(f: KeywordsCollectFn) {
                $param::collect_completion_keywords(f);
            }
        }
    };
}
impl_generic_specified_value_info!(Option<T>);
impl_generic_specified_value_info!(Vec<T>);
impl_generic_specified_value_info!(Arc<T>);
impl_generic_specified_value_info!(StdArc<T>);
impl_generic_specified_value_info!(Range<Idx>);

impl<T1, T2> SpecifiedValueInfo for (T1, T2)
where
    T1: SpecifiedValueInfo,
    T2: SpecifiedValueInfo,
{
    const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;

    fn collect_completion_keywords(f: KeywordsCollectFn) {
        T1::collect_completion_keywords(f);
        T2::collect_completion_keywords(f);
    }
}
<|endoftext|>


<commit_before>//! [PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid)

use ruma_api::ruma_api;
use ruma_events::EventType;
use ruma_identifiers::{EventId, RoomId};
use serde_json::value::RawValue as RawJsonValue;

ruma_api! {
    metadata: {
        description: "Send a message event to a room.",
        method: PUT,
        name: "create_message_event",
        path: "/_matrix/client/r0/rooms/:room_id/send/:event_type/:txn_id",
        rate_limited: false,
        requires_authentication: true,
    }

    request: {
        /// The room to send the event to.
        #[ruma_api(path)]
        pub room_id: RoomId,

        /// The type of event to send.
        #[ruma_api(path)]
        pub event_type: EventType,

        /// The transaction ID for this event.
        ///
        /// Clients should generate an ID unique across requests with the
        /// same access token; it will be used by the server to ensure
        /// idempotency of requests.
        #[ruma_api(path)]
        pub txn_id: String,

        /// The event's content. The type for this field will be updated in a
        /// future release, until then you can create a value using
        /// `serde_json::value::to_raw_value`.
        #[ruma_api(body)]
        pub data: Box<RawJsonValue>,
    }

    response: {
        /// A unique identifier for the event.
        pub event_id: EventId,
    }

    error: crate::Error
}
<commit_msg>Borrow strings and identifiers in send_message_event<commit_after>//! [PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid)

use ruma_api::ruma_api;
use ruma_events::EventType;
use ruma_identifiers::{EventId, RoomId};
use serde_json::value::RawValue as RawJsonValue;

ruma_api! {
    metadata: {
        description: "Send a message event to a room.",
        method: PUT,
        name: "create_message_event",
        path: "/_matrix/client/r0/rooms/:room_id/send/:event_type/:txn_id",
        rate_limited: false,
        requires_authentication: true,
    }

    request: {
        /// The room to send the event to.
        #[ruma_api(path)]
        pub room_id: &'a RoomId,

        /// The type of event to send.
        #[ruma_api(path)]
        pub event_type: EventType,

        /// The transaction ID for this event.
        ///
        /// Clients should generate an ID unique across requests with the
        /// same access token; it will be used by the server to ensure
        /// idempotency of requests.
        #[ruma_api(path)]
        pub txn_id: &'a str,

        /// The event's content. The type for this field will be updated in a
        /// future release, until then you can create a value using
        /// `serde_json::value::to_raw_value`.
        #[ruma_api(body)]
        pub data: Box<RawJsonValue>,
    }

    response: {
        /// A unique identifier for the event.
        pub event_id: &'a EventId,
    }

    error: crate::Error
}
<|endoftext|>