<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/. */

//! [Media queries][mq].
//!
//! [mq]: https://drafts.csswg.org/mediaqueries/

use Atom;
use app_units::Au;
use cssparser::{Delimiter, Parser, Token};
use euclid::size::{Size2D, TypedSize2D};
use serialize_comma_separated_list;
use std::ascii::AsciiExt;
use std::fmt;
use style_traits::{ToCss, ViewportPx};
use values::computed::{self, ToComputedValue};
use values::specified;

#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaList {
    pub media_queries: Vec<MediaQuery>
}

impl ToCss for MediaList {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write
    {
        serialize_comma_separated_list(dest, &self.media_queries)
    }
}

impl Default for MediaList {
    fn default() -> MediaList {
        MediaList { media_queries: vec![] }
    }
}

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Range<T> {
    Min(T),
    Max(T),
    Eq(T),
}

impl Range<specified::Length> {
    fn to_computed_range(&self, viewport_size: Size2D<Au>) -> Range<Au> {
        // http://dev.w3.org/csswg/mediaqueries3/#units
        // em units are relative to the initial font-size.
        let context = computed::Context::initial(viewport_size, false);

        match *self {
            Range::Min(ref width) => Range::Min(width.to_computed_value(&context)),
            Range::Max(ref width) => Range::Max(width.to_computed_value(&context)),
            Range::Eq(ref width) => Range::Eq(width.to_computed_value(&context))
        }
    }
}

impl<T: Ord> Range<T> {
    fn evaluate(&self, value: T) -> bool {
        match *self {
            Range::Min(ref width) => { value >= *width },
            Range::Max(ref width) => { value <= *width },
            Range::Eq(ref width) => { value == *width },
        }
    }
}

/// http://dev.w3.org/csswg/mediaqueries-3/#media1
#[derive(PartialEq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Expression {
    /// http://dev.w3.org/csswg/mediaqueries-3/#width
    Width(Range<specified::Length>),
}

/// http://dev.w3.org/csswg/mediaqueries-3/#media0
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Qualifier {
    Only,
    Not,
}

#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaQuery {
    pub qualifier: Option<Qualifier>,
    pub media_type: MediaQueryType,
    pub expressions: Vec<Expression>,
}

impl MediaQuery {
    /// Return a media query that never matches, used for when we fail to parse
    /// a given media query.
    fn never_matching() -> Self {
        Self::new(Some(Qualifier::Not), MediaQueryType::All, vec![])
    }

    pub fn new(qualifier: Option<Qualifier>, media_type: MediaQueryType,
               expressions: Vec<Expression>) -> MediaQuery {
        MediaQuery {
            qualifier: qualifier,
            media_type: media_type,
            expressions: expressions,
        }
    }
}

impl ToCss for MediaQuery {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write
    {
        if self.qualifier == Some(Qualifier::Not) {
            try!(write!(dest, "not "));
        }

        match self.media_type {
            MediaQueryType::All => try!(write!(dest, "all")),
            MediaQueryType::Known(MediaType::Screen) => try!(write!(dest, "screen")),
            MediaQueryType::Known(MediaType::Print) => try!(write!(dest, "print")),
            MediaQueryType::Unknown(ref desc) => try!(write!(dest, "{}", desc)),
        };

        if self.expressions.is_empty() {
            return Ok(());
        }

        if self.media_type != MediaQueryType::All || self.qualifier == Some(Qualifier::Not) {
            try!(write!(dest, " and "));
        }

        for (i, &e) in self.expressions.iter().enumerate() {
            try!(write!(dest, "("));
            let (mm, l) = match e {
                Expression::Width(Range::Min(ref l)) => ("min-", l),
                Expression::Width(Range::Max(ref l)) => ("max-", l),
                Expression::Width(Range::Eq(ref l)) => ("", l),
            };
            try!(write!(dest, "{}width: ", mm));
            try!(l.to_css(dest));
            if i == self.expressions.len() - 1 {
                try!(write!(dest, ")"));
            } else {
                try!(write!(dest, ") and "));
            }
        }
        Ok(())
    }
}

/// http://dev.w3.org/csswg/mediaqueries-3/#media0
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaQueryType {
    All,  // Always true
    Known(MediaType),
    Unknown(Atom),
}

impl MediaQueryType {
    fn parse(ident: &str) -> Self {
        if ident.eq_ignore_ascii_case("all") {
            return MediaQueryType::All;
        }

        match MediaType::parse(ident) {
            Some(media_type) => MediaQueryType::Known(media_type),
            None => MediaQueryType::Unknown(Atom::from(ident)),
        }
    }

    fn matches(&self, other: &MediaType) -> bool {
        match *self {
            MediaQueryType::All => true,
            MediaQueryType::Known(ref known_type) => known_type == other,
            MediaQueryType::Unknown(..) => false,
        }
    }
}

#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaType {
    Screen,
    Print,
}

impl MediaType {
    fn parse(name: &str) -> Option<Self> {
        Some(match_ignore_ascii_case! { name,
            "screen" => MediaType::Screen,
            "print" => MediaType::Print,
            _ => return None
        })
    }
}

#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Device {
    pub media_type: MediaType,
    pub viewport_size: TypedSize2D<f32, ViewportPx>,
}

impl Device {
    pub fn new(media_type: MediaType, viewport_size: TypedSize2D<f32, ViewportPx>) -> Device {
        Device {
            media_type: media_type,
            viewport_size: viewport_size,
        }
    }

    #[inline]
    pub fn au_viewport_size(&self) -> Size2D<Au> {
        Size2D::new(Au::from_f32_px(self.viewport_size.width),
                    Au::from_f32_px(self.viewport_size.height))
    }

}

impl Expression {
    fn parse(input: &mut Parser) -> Result<Expression, ()> {
        try!(input.expect_parenthesis_block());
        input.parse_nested_block(|input| {
            let name = try!(input.expect_ident());
            try!(input.expect_colon());
            // TODO: Handle other media features
            match_ignore_ascii_case! { name,
                "min-width" => {
                    Ok(Expression::Width(Range::Min(try!(specified::Length::parse_non_negative(input)))))
                },
                "max-width" => {
                    Ok(Expression::Width(Range::Max(try!(specified::Length::parse_non_negative(input)))))
                },
                "width" => {
                    Ok(Expression::Width(Range::Eq(try!(specified::Length::parse_non_negative(input)))))
                },
                _ => Err(())
            }
        })
    }
}

impl MediaQuery {
    pub fn parse(input: &mut Parser) -> Result<MediaQuery, ()> {
        let mut expressions = vec![];

        let qualifier = if input.try(|input| input.expect_ident_matching("only")).is_ok() {
            Some(Qualifier::Only)
        } else if input.try(|input| input.expect_ident_matching("not")).is_ok() {
            Some(Qualifier::Not)
        } else {
            None
        };

        let media_type = match input.try(|input| input.expect_ident()) {
            Ok(ident) => MediaQueryType::parse(&*ident),
            Err(()) => {
                // Media type is only optional if qualifier is not specified.
                if qualifier.is_some() {
                    return Err(())
                }

                // Without a media type, require at least one expression.
                expressions.push(try!(Expression::parse(input)));

                MediaQueryType::All
            }
        };

        // Parse any subsequent expressions
        loop {
            if input.try(|input| input.expect_ident_matching("and")).is_err() {
                return Ok(MediaQuery::new(qualifier, media_type, expressions))
            }
            expressions.push(try!(Expression::parse(input)))
        }
    }
}

pub fn parse_media_query_list(input: &mut Parser) -> MediaList {
    if input.is_exhausted() {
        return Default::default()
    }

    let mut media_queries = vec![];
    loop {
        media_queries.push(
            input.parse_until_before(Delimiter::Comma, MediaQuery::parse).ok()
                 .unwrap_or_else(MediaQuery::never_matching));
        match input.next() {
            Ok(Token::Comma) => {},
            Ok(_) => unreachable!(),
            Err(()) => break,
        }
    }
    MediaList {
        media_queries: media_queries,
    }
}

impl MediaList {
    pub fn evaluate(&self, device: &Device) -> bool {
        let viewport_size = device.au_viewport_size();

        // Check if it is an empty media query list or any queries match (OR condition)
        // https://drafts.csswg.org/mediaqueries-4/#mq-list
        self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
            let media_match = mq.media_type.matches(&device.media_type);

            // Check if all conditions match (AND condition)
            let query_match = media_match && mq.expressions.iter().all(|expression| {
                match *expression {
                    Expression::Width(ref value) =>
                        value.to_computed_range(viewport_size).evaluate(viewport_size.width),
                }
            });

            // Apply the logical NOT qualifier to the result
            match mq.qualifier {
                Some(Qualifier::Not) => !query_match,
                _ => query_match,
            }
        })
    }

    pub fn is_empty(&self) -> bool {
        self.media_queries.is_empty()
    }
}
<commit_msg>style: Serialize also "only" qualifier, and fix serialization of all media expressions without explicit qualifiers.<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/. */

//! [Media queries][mq].
//!
//! [mq]: https://drafts.csswg.org/mediaqueries/

use Atom;
use app_units::Au;
use cssparser::{Delimiter, Parser, Token};
use euclid::size::{Size2D, TypedSize2D};
use serialize_comma_separated_list;
use std::ascii::AsciiExt;
use std::fmt;
use style_traits::{ToCss, ViewportPx};
use values::computed::{self, ToComputedValue};
use values::specified;

#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaList {
    pub media_queries: Vec<MediaQuery>
}

impl ToCss for MediaList {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write
    {
        serialize_comma_separated_list(dest, &self.media_queries)
    }
}

impl Default for MediaList {
    fn default() -> MediaList {
        MediaList { media_queries: vec![] }
    }
}

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Range<T> {
    Min(T),
    Max(T),
    Eq(T),
}

impl Range<specified::Length> {
    fn to_computed_range(&self, viewport_size: Size2D<Au>) -> Range<Au> {
        // http://dev.w3.org/csswg/mediaqueries3/#units
        // em units are relative to the initial font-size.
        let context = computed::Context::initial(viewport_size, false);

        match *self {
            Range::Min(ref width) => Range::Min(width.to_computed_value(&context)),
            Range::Max(ref width) => Range::Max(width.to_computed_value(&context)),
            Range::Eq(ref width) => Range::Eq(width.to_computed_value(&context))
        }
    }
}

impl<T: Ord> Range<T> {
    fn evaluate(&self, value: T) -> bool {
        match *self {
            Range::Min(ref width) => { value >= *width },
            Range::Max(ref width) => { value <= *width },
            Range::Eq(ref width) => { value == *width },
        }
    }
}

/// http://dev.w3.org/csswg/mediaqueries-3/#media1
#[derive(PartialEq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Expression {
    /// http://dev.w3.org/csswg/mediaqueries-3/#width
    Width(Range<specified::Length>),
}

/// http://dev.w3.org/csswg/mediaqueries-3/#media0
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Qualifier {
    Only,
    Not,
}

impl ToCss for Qualifier {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write
    {
        match *self {
            Qualifier::Not => write!(dest, "not"),
            Qualifier::Only => write!(dest, "only"),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaQuery {
    pub qualifier: Option<Qualifier>,
    pub media_type: MediaQueryType,
    pub expressions: Vec<Expression>,
}

impl MediaQuery {
    /// Return a media query that never matches, used for when we fail to parse
    /// a given media query.
    fn never_matching() -> Self {
        Self::new(Some(Qualifier::Not), MediaQueryType::All, vec![])
    }

    pub fn new(qualifier: Option<Qualifier>, media_type: MediaQueryType,
               expressions: Vec<Expression>) -> MediaQuery {
        MediaQuery {
            qualifier: qualifier,
            media_type: media_type,
            expressions: expressions,
        }
    }
}

impl ToCss for MediaQuery {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write
    {
        if let Some(qual) = self.qualifier {
            try!(qual.to_css(dest));
            try!(write!(dest, " "));
        }

        match self.media_type {
            MediaQueryType::All => {
                // We need to print "all" if there's a qualifier, or there's
                // just an empty list of expressions.
                //
                // Otherwise, we'd serialize media queries like "(min-width:
                // 40px)" in "all (min-width: 40px)", which is unexpected.
                if self.qualifier.is_some() || self.expressions.is_empty() {
                    try!(write!(dest, "all"));
                }
            },
            MediaQueryType::Known(MediaType::Screen) => try!(write!(dest, "screen")),
            MediaQueryType::Known(MediaType::Print) => try!(write!(dest, "print")),
            MediaQueryType::Unknown(ref desc) => try!(write!(dest, "{}", desc)),
        }

        if self.expressions.is_empty() {
            return Ok(());
        }

        if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
            try!(write!(dest, " and "));
        }

        for (i, &e) in self.expressions.iter().enumerate() {
            try!(write!(dest, "("));
            let (mm, l) = match e {
                Expression::Width(Range::Min(ref l)) => ("min-", l),
                Expression::Width(Range::Max(ref l)) => ("max-", l),
                Expression::Width(Range::Eq(ref l)) => ("", l),
            };
            try!(write!(dest, "{}width: ", mm));
            try!(l.to_css(dest));
            try!(write!(dest, ")"));
            if i != self.expressions.len() - 1 {
                try!(write!(dest, " and "));
            }
        }
        Ok(())
    }
}

/// http://dev.w3.org/csswg/mediaqueries-3/#media0
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaQueryType {
    All,  // Always true
    Known(MediaType),
    Unknown(Atom),
}

impl MediaQueryType {
    fn parse(ident: &str) -> Self {
        if ident.eq_ignore_ascii_case("all") {
            return MediaQueryType::All;
        }

        match MediaType::parse(ident) {
            Some(media_type) => MediaQueryType::Known(media_type),
            None => MediaQueryType::Unknown(Atom::from(ident)),
        }
    }

    fn matches(&self, other: &MediaType) -> bool {
        match *self {
            MediaQueryType::All => true,
            MediaQueryType::Known(ref known_type) => known_type == other,
            MediaQueryType::Unknown(..) => false,
        }
    }
}

#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaType {
    Screen,
    Print,
}

impl MediaType {
    fn parse(name: &str) -> Option<Self> {
        Some(match_ignore_ascii_case! { name,
            "screen" => MediaType::Screen,
            "print" => MediaType::Print,
            _ => return None
        })
    }
}

#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Device {
    pub media_type: MediaType,
    pub viewport_size: TypedSize2D<f32, ViewportPx>,
}

impl Device {
    pub fn new(media_type: MediaType, viewport_size: TypedSize2D<f32, ViewportPx>) -> Device {
        Device {
            media_type: media_type,
            viewport_size: viewport_size,
        }
    }

    #[inline]
    pub fn au_viewport_size(&self) -> Size2D<Au> {
        Size2D::new(Au::from_f32_px(self.viewport_size.width),
                    Au::from_f32_px(self.viewport_size.height))
    }

}

impl Expression {
    fn parse(input: &mut Parser) -> Result<Expression, ()> {
        try!(input.expect_parenthesis_block());
        input.parse_nested_block(|input| {
            let name = try!(input.expect_ident());
            try!(input.expect_colon());
            // TODO: Handle other media features
            match_ignore_ascii_case! { name,
                "min-width" => {
                    Ok(Expression::Width(Range::Min(try!(specified::Length::parse_non_negative(input)))))
                },
                "max-width" => {
                    Ok(Expression::Width(Range::Max(try!(specified::Length::parse_non_negative(input)))))
                },
                "width" => {
                    Ok(Expression::Width(Range::Eq(try!(specified::Length::parse_non_negative(input)))))
                },
                _ => Err(())
            }
        })
    }
}

impl MediaQuery {
    pub fn parse(input: &mut Parser) -> Result<MediaQuery, ()> {
        let mut expressions = vec![];

        let qualifier = if input.try(|input| input.expect_ident_matching("only")).is_ok() {
            Some(Qualifier::Only)
        } else if input.try(|input| input.expect_ident_matching("not")).is_ok() {
            Some(Qualifier::Not)
        } else {
            None
        };

        let media_type = match input.try(|input| input.expect_ident()) {
            Ok(ident) => MediaQueryType::parse(&*ident),
            Err(()) => {
                // Media type is only optional if qualifier is not specified.
                if qualifier.is_some() {
                    return Err(())
                }

                // Without a media type, require at least one expression.
                expressions.push(try!(Expression::parse(input)));

                MediaQueryType::All
            }
        };

        // Parse any subsequent expressions
        loop {
            if input.try(|input| input.expect_ident_matching("and")).is_err() {
                return Ok(MediaQuery::new(qualifier, media_type, expressions))
            }
            expressions.push(try!(Expression::parse(input)))
        }
    }
}

pub fn parse_media_query_list(input: &mut Parser) -> MediaList {
    if input.is_exhausted() {
        return Default::default()
    }

    let mut media_queries = vec![];
    loop {
        media_queries.push(
            input.parse_until_before(Delimiter::Comma, MediaQuery::parse).ok()
                 .unwrap_or_else(MediaQuery::never_matching));
        match input.next() {
            Ok(Token::Comma) => {},
            Ok(_) => unreachable!(),
            Err(()) => break,
        }
    }
    MediaList {
        media_queries: media_queries,
    }
}

impl MediaList {
    pub fn evaluate(&self, device: &Device) -> bool {
        let viewport_size = device.au_viewport_size();

        // Check if it is an empty media query list or any queries match (OR condition)
        // https://drafts.csswg.org/mediaqueries-4/#mq-list
        self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
            let media_match = mq.media_type.matches(&device.media_type);

            // Check if all conditions match (AND condition)
            let query_match = media_match && mq.expressions.iter().all(|expression| {
                match *expression {
                    Expression::Width(ref value) =>
                        value.to_computed_range(viewport_size).evaluate(viewport_size.width),
                }
            });

            // Apply the logical NOT qualifier to the result
            match mq.qualifier {
                Some(Qualifier::Not) => !query_match,
                _ => query_match,
            }
        })
    }

    pub fn is_empty(&self) -> bool {
        self.media_queries.is_empty()
    }
}
<|endoftext|>


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

//! Helper types and traits for the handling of CSS values.

use app_units::Au;
use cssparser::UnicodeRange;
use std::fmt;

/// The real `ToCss` trait can't be implemented for types in crates that don't
/// depend on each other.
pub trait ToCss {
    /// Serialize `self` in CSS syntax, writing to `dest`.
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write;

    /// Serialize `self` in CSS syntax and return a string.
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn to_css_string(&self) -> String {
        let mut s = String::new();
        self.to_css(&mut s).unwrap();
        s
    }
}

/// Marker trait to automatically implement ToCss for Vec<T>.
pub trait OneOrMoreCommaSeparated {}

impl OneOrMoreCommaSeparated for UnicodeRange {}

impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreCommaSeparated {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.iter();
        iter.next().unwrap().to_css(dest)?;
        for item in iter {
            dest.write_str(", ")?;
            item.to_css(dest)?;
        }
        Ok(())
    }
}

impl<T: ToCss> ToCss for Box<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        (**self).to_css(dest)
    }
}

impl ToCss for Au {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}px", self.to_f64_px())
    }
}

macro_rules! impl_to_css_for_predefined_type {
    ($name: ty) => {
        impl<'a> ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                ::cssparser::ToCss::to_css(self, dest)
            }
        }
    };
}

impl_to_css_for_predefined_type!(f32);
impl_to_css_for_predefined_type!(i32);
impl_to_css_for_predefined_type!(u32);
impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
impl_to_css_for_predefined_type!(::cssparser::RGBA);
impl_to_css_for_predefined_type!(::cssparser::Color);
impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);

#[macro_export]
macro_rules! define_css_keyword_enum {
    ($name: ident: $( $css: expr => $variant: ident ),+,) => {
        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]);
    };
    ($name: ident: $( $css: expr => $variant: ident ),+) => {
        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]);
    };
}

#[cfg(feature = "servo")]
#[macro_export]
macro_rules! __define_css_keyword_enum__add_optional_traits {
    ($name: ident [ $( $css: expr => $variant: ident ),+ ]) => {
        __define_css_keyword_enum__actual! {
            $name [ Deserialize, Serialize, HeapSizeOf ] [ $( $css => $variant ),+ ]
        }
    };
}

#[cfg(not(feature = "servo"))]
#[macro_export]
macro_rules! __define_css_keyword_enum__add_optional_traits {
    ($name: ident [ $( $css: expr => $variant: ident ),+ ]) => {
        __define_css_keyword_enum__actual! {
            $name [] [ $( $css => $variant ),+ ]
        }
    };
}

#[macro_export]
macro_rules! __define_css_keyword_enum__actual {
    ($name: ident [ $( $derived_trait: ident),* ] [ $( $css: expr => $variant: ident ),+ ]) => {
        #[allow(non_camel_case_types, missing_docs)]
        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq $(, $derived_trait )* )]
        pub enum $name {
            $( $variant ),+
        }

        impl $name {
            /// Parse this property from a CSS input stream.
            pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
                let ident = input.expect_ident()?;
                match_ignore_ascii_case! { &ident,
                                           $( $css => Ok($name::$variant), )+
                                           _ => Err(())
                }
            }
        }

        impl ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
                where W: ::std::fmt::Write
            {
                match *self {
                    $( $name::$variant => dest.write_str($css) ),+
                }
            }
        }
    }
}

/// Helper types for the handling of specified values.
pub mod specified {
    use app_units::Au;

    /// Whether to allow negative values or not.
    #[repr(u8)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum AllowedNumericType {
        /// Allow all kind of numeric values.
        All,
        /// Allow only non-negative values.
        NonNegative
    }

    impl AllowedNumericType {
        /// Whether value is valid for this allowed numeric type.
        #[inline]
        pub fn is_ok(&self, value: f32) -> bool {
            match *self {
                AllowedNumericType::All => true,
                AllowedNumericType::NonNegative => value >= 0.,
            }
        }

        /// Clamp the value following the rules of this numeric type.
        #[inline]
        pub fn clamp(&self, val: Au) -> Au {
            use std::cmp;
            match *self {
                AllowedNumericType::All => val,
                AllowedNumericType::NonNegative => cmp::max(Au(0), val),
            }
        }
    }
}
<commit_msg>style: Add <keyword>::from_ident helper to avoid tokenizing the same value multiple times.<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/. */

//! Helper types and traits for the handling of CSS values.

use app_units::Au;
use cssparser::UnicodeRange;
use std::fmt;

/// The real `ToCss` trait can't be implemented for types in crates that don't
/// depend on each other.
pub trait ToCss {
    /// Serialize `self` in CSS syntax, writing to `dest`.
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write;

    /// Serialize `self` in CSS syntax and return a string.
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn to_css_string(&self) -> String {
        let mut s = String::new();
        self.to_css(&mut s).unwrap();
        s
    }
}

/// Marker trait to automatically implement ToCss for Vec<T>.
pub trait OneOrMoreCommaSeparated {}

impl OneOrMoreCommaSeparated for UnicodeRange {}

impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreCommaSeparated {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.iter();
        iter.next().unwrap().to_css(dest)?;
        for item in iter {
            dest.write_str(", ")?;
            item.to_css(dest)?;
        }
        Ok(())
    }
}

impl<T: ToCss> ToCss for Box<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        (**self).to_css(dest)
    }
}

impl ToCss for Au {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}px", self.to_f64_px())
    }
}

macro_rules! impl_to_css_for_predefined_type {
    ($name: ty) => {
        impl<'a> ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                ::cssparser::ToCss::to_css(self, dest)
            }
        }
    };
}

impl_to_css_for_predefined_type!(f32);
impl_to_css_for_predefined_type!(i32);
impl_to_css_for_predefined_type!(u32);
impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
impl_to_css_for_predefined_type!(::cssparser::RGBA);
impl_to_css_for_predefined_type!(::cssparser::Color);
impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);

#[macro_export]
macro_rules! define_css_keyword_enum {
    ($name: ident: $( $css: expr => $variant: ident ),+,) => {
        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]);
    };
    ($name: ident: $( $css: expr => $variant: ident ),+) => {
        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]);
    };
}

#[cfg(feature = "servo")]
#[macro_export]
macro_rules! __define_css_keyword_enum__add_optional_traits {
    ($name: ident [ $( $css: expr => $variant: ident ),+ ]) => {
        __define_css_keyword_enum__actual! {
            $name [ Deserialize, Serialize, HeapSizeOf ] [ $( $css => $variant ),+ ]
        }
    };
}

#[cfg(not(feature = "servo"))]
#[macro_export]
macro_rules! __define_css_keyword_enum__add_optional_traits {
    ($name: ident [ $( $css: expr => $variant: ident ),+ ]) => {
        __define_css_keyword_enum__actual! {
            $name [] [ $( $css => $variant ),+ ]
        }
    };
}

#[macro_export]
macro_rules! __define_css_keyword_enum__actual {
    ($name: ident [ $( $derived_trait: ident),* ] [ $( $css: expr => $variant: ident ),+ ]) => {
        #[allow(non_camel_case_types, missing_docs)]
        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq $(, $derived_trait )* )]
        pub enum $name {
            $( $variant ),+
        }

        impl $name {
            /// Parse this property from a CSS input stream.
            pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
                let ident = input.expect_ident()?;
                Self::from_ident(&ident)
            }

            /// Parse this property from an already-tokenized identifier.
            pub fn from_ident(ident: &str) -> Result<$name, ()> {
                match_ignore_ascii_case! { ident,
                                           $( $css => Ok($name::$variant), )+
                                           _ => Err(())
                }
            }
        }

        impl ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
                where W: ::std::fmt::Write
            {
                match *self {
                    $( $name::$variant => dest.write_str($css) ),+
                }
            }
        }
    }
}

/// Helper types for the handling of specified values.
pub mod specified {
    use app_units::Au;

    /// Whether to allow negative values or not.
    #[repr(u8)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum AllowedNumericType {
        /// Allow all kind of numeric values.
        All,
        /// Allow only non-negative values.
        NonNegative
    }

    impl AllowedNumericType {
        /// Whether value is valid for this allowed numeric type.
        #[inline]
        pub fn is_ok(&self, value: f32) -> bool {
            match *self {
                AllowedNumericType::All => true,
                AllowedNumericType::NonNegative => value >= 0.,
            }
        }

        /// Clamp the value following the rules of this numeric type.
        #[inline]
        pub fn clamp(&self, val: Au) -> Au {
            use std::cmp;
            match *self {
                AllowedNumericType::All => val,
                AllowedNumericType::NonNegative => cmp::max(Au(0), val),
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate rocket;

mod database;
mod domain;

use std::env;
use std::path::Path;

use aws_sdk_s3::types::ByteStream;
use aws_sdk_s3::Client;
use chrono::{DateTime, Datelike, SecondsFormat, Utc};
use dotenv::dotenv;
use rocket::data::ToByteUnit;
use rocket::http::ContentType;
use rocket::outcome::{try_outcome, Outcome};
use rocket::request::FromRequest;
use rocket::request::{self, FromParam};
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use rocket::{Data, Request};
use rocket_db_pools::{Connection, Database};

use database::Db;
use domain::entities::{ApiKeyId, DropId, User, UserId};
use domain::ApiKeyValue;

impl<'a> FromParam<'a> for DropId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        DropId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a drop ID: {}", param, err))
    }
}

impl<'a> FromParam<'a> for UserId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        UserId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a user ID: {}", param, err))
    }
}

pub struct ApiKeyBearer {
    user: User,
}

struct ApiKeyLookup {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKeyBearer {
    type Error = String;

    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
        let mut db = try_outcome!(req
            .guard::<Connection<Db>>()
            .await
            .map_failure(|f| (f.0, "failed to acquire database connection".to_string())));

        match req.headers().get_one("Authorization") {
            Some(authorization) => {
                let token = authorization.replace("Bearer ", "");

                let api_key = sqlx::query_as!(
                    ApiKeyLookup,
                    "
                    select user.id, user.username, user.full_name from api_key
                    inner join user on user.id = api_key.user_id
                    where api_key.value = $1
                    ",
                    token
                )
                .fetch_one(&mut *db)
                .await
                .unwrap();

                Outcome::Success(ApiKeyBearer {
                    user: User {
                        id: UserId::try_from(api_key.id).unwrap(),
                        username: api_key.username,
                        full_name: api_key.full_name,
                    },
                })
            }
            _ => Outcome::Forward(()),
        }
    }
}

#[get("/")]
async fn index() -> &'static str {
    "滴"
}

#[post("/drops", data = "<drop>")]
async fn upload_drop(_bearer: ApiKeyBearer, drop: Data<'_>) -> std::io::Result<String> {
    let id = DropId::new();

    let temp_filepath = Path::new("temp").join(id.to_string());

    drop.open(2.mebibytes()).into_file(&temp_filepath).await?;

    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let body = ByteStream::from_path(&temp_filepath).await?;

    client
        .put_object()
        .bucket(env::var("AWS_S3_BUCKET").unwrap())
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .body(body)
        .send()
        .await
        .expect("failed to upload to S3");

    tokio::fs::remove_file(temp_filepath).await.ok();

    Ok(id.to_string())
}

#[get("/drops/<id>")]
async fn get_drop(id: DropId) -> (ContentType, Vec<u8>) {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env::var("AWS_S3_BUCKET").unwrap())
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    let bytes = object
        .body
        .collect()
        .await
        .map(|data| data.into_bytes())
        .unwrap()
        .to_vec();

    let mime_type = tree_magic_mini::from_u8(&bytes);

    let content_type = ContentType::parse_flexible(mime_type).unwrap_or(ContentType::Binary);

    (content_type, bytes)
}

#[derive(Debug, Serialize)]
pub struct DropMetadata {
    pub created_at: DateTime<Utc>,
    pub size_in_bytes: i64,
}

#[get("/drops/<id>/meta")]
async fn get_drop_metadata(id: DropId) -> Json<DropMetadata> {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env::var("AWS_S3_BUCKET").unwrap())
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    Json(DropMetadata {
        created_at: id.created_at(),
        size_in_bytes: object.content_length,
    })
}

#[derive(Debug, Serialize)]
pub struct UserJson {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[post("/users")]
async fn create_user(
    _bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<UserJson>> {
    let id = UserId::new();

    let username = "maxdeviant";
    let full_name = "Marshall Bowers";

    let unprefixed_id = id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into user (id, created_at, updated_at, username, full_name)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        username,
        full_name
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(UserJson {
        id: id.to_string(),
        username: username.to_string(),
        full_name: Some(full_name.to_string()),
    }))
}

#[derive(Debug, Serialize)]
pub struct ApiKeyJson {
    pub id: String,
    pub value: String,
}

#[post("/keys")]
async fn generate_api_key(
    bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<ApiKeyJson>> {
    let id = ApiKeyId::new();
    let value = ApiKeyValue::new();

    let unprefixed_id = id.unprefixed();
    let unprefixed_user_id = bearer.user.id.unprefixed();
    let key_value = value.to_string();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        unprefixed_user_id,
        key_value
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(ApiKeyJson {
        id: id.to_string(),
        value: value.to_string(),
    }))
}

async fn ensure_root_user_exists() {
    use sqlx::{Connection, SqliteConnection};

    let root_username = env::var("ROOT_USERNAME").unwrap();
    let root_api_key = env::var("ROOT_API_KEY").unwrap();

    let database_url = env::var("DATABASE_URL").unwrap();
    let mut connection = SqliteConnection::connect(&database_url).await.unwrap();

    let user_id = UserId::new();
    let unprefixed_user_id = user_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    #[derive(Debug)]
    struct RootUser {
        id: String,
    }

    let root_user = sqlx::query_as!(
        RootUser,
        "
        insert into user (id, created_at, updated_at, username)
        values (?, ?, ?, ?)
        -- This is a no-op update just so we can get the `id` returned.
        on conflict (username) do update set updated_at = updated_at
        returning id
        ",
        unprefixed_user_id,
        now,
        now,
        root_username
    )
    .fetch_one(&mut connection)
    .await
    .expect("failed to insert root user");

    println!("Root user: {:?}", root_user);

    let key_id = ApiKeyId::new();
    let unprefixed_key_id = key_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        on conflict (value) do nothing
        ",
        unprefixed_key_id,
        now,
        now,
        root_user.id,
        root_api_key
    )
    .execute(&mut connection)
    .await
    .unwrap();
}

#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
    dotenv().ok();

    ensure_root_user_exists().await;

    let rocket = rocket::build()
        .mount(
            "/",
            routes![index, upload_drop, get_drop, get_drop_metadata,],
        )
        .mount("/x/", routes![create_user, generate_api_key])
        .attach(Db::init())
        .ignite()
        .await?;

    let _ = rocket.launch().await?;

    Ok(())
}
<commit_msg>Ensure temp directory exists<commit_after>#[macro_use]
extern crate rocket;

mod database;
mod domain;

use std::env;
use std::path::Path;

use aws_sdk_s3::types::ByteStream;
use aws_sdk_s3::Client;
use chrono::{DateTime, Datelike, SecondsFormat, Utc};
use dotenv::dotenv;
use rocket::data::ToByteUnit;
use rocket::http::ContentType;
use rocket::outcome::{try_outcome, Outcome};
use rocket::request::FromRequest;
use rocket::request::{self, FromParam};
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use rocket::{Data, Request};
use rocket_db_pools::{Connection, Database};
use tokio::fs;

use database::Db;
use domain::entities::{ApiKeyId, DropId, User, UserId};
use domain::ApiKeyValue;

impl<'a> FromParam<'a> for DropId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        DropId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a drop ID: {}", param, err))
    }
}

impl<'a> FromParam<'a> for UserId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        UserId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a user ID: {}", param, err))
    }
}

pub struct ApiKeyBearer {
    user: User,
}

struct ApiKeyLookup {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKeyBearer {
    type Error = String;

    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
        let mut db = try_outcome!(req
            .guard::<Connection<Db>>()
            .await
            .map_failure(|f| (f.0, "failed to acquire database connection".to_string())));

        match req.headers().get_one("Authorization") {
            Some(authorization) => {
                let token = authorization.replace("Bearer ", "");

                let api_key = sqlx::query_as!(
                    ApiKeyLookup,
                    "
                    select user.id, user.username, user.full_name from api_key
                    inner join user on user.id = api_key.user_id
                    where api_key.value = $1
                    ",
                    token
                )
                .fetch_one(&mut *db)
                .await
                .unwrap();

                Outcome::Success(ApiKeyBearer {
                    user: User {
                        id: UserId::try_from(api_key.id).unwrap(),
                        username: api_key.username,
                        full_name: api_key.full_name,
                    },
                })
            }
            _ => Outcome::Forward(()),
        }
    }
}

#[get("/")]
async fn index() -> &'static str {
    "滴"
}

#[post("/drops", data = "<drop>")]
async fn upload_drop(_bearer: ApiKeyBearer, drop: Data<'_>) -> std::io::Result<String> {
    let id = DropId::new();

    let temp_dir = Path::new("temp");
    fs::create_dir_all(temp_dir).await.unwrap();

    let temp_filepath = temp_dir.join(id.to_string());

    drop.open(2.mebibytes()).into_file(&temp_filepath).await?;

    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let body = ByteStream::from_path(&temp_filepath).await?;

    client
        .put_object()
        .bucket(env::var("AWS_S3_BUCKET").unwrap())
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .body(body)
        .send()
        .await
        .expect("failed to upload to S3");

    tokio::fs::remove_file(temp_filepath).await.ok();

    Ok(id.to_string())
}

#[get("/drops/<id>")]
async fn get_drop(id: DropId) -> (ContentType, Vec<u8>) {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env::var("AWS_S3_BUCKET").unwrap())
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    let bytes = object
        .body
        .collect()
        .await
        .map(|data| data.into_bytes())
        .unwrap()
        .to_vec();

    let mime_type = tree_magic_mini::from_u8(&bytes);

    let content_type = ContentType::parse_flexible(mime_type).unwrap_or(ContentType::Binary);

    (content_type, bytes)
}

#[derive(Debug, Serialize)]
pub struct DropMetadata {
    pub created_at: DateTime<Utc>,
    pub size_in_bytes: i64,
}

#[get("/drops/<id>/meta")]
async fn get_drop_metadata(id: DropId) -> Json<DropMetadata> {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env::var("AWS_S3_BUCKET").unwrap())
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    Json(DropMetadata {
        created_at: id.created_at(),
        size_in_bytes: object.content_length,
    })
}

#[derive(Debug, Serialize)]
pub struct UserJson {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[post("/users")]
async fn create_user(
    _bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<UserJson>> {
    let id = UserId::new();

    let username = "maxdeviant";
    let full_name = "Marshall Bowers";

    let unprefixed_id = id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into user (id, created_at, updated_at, username, full_name)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        username,
        full_name
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(UserJson {
        id: id.to_string(),
        username: username.to_string(),
        full_name: Some(full_name.to_string()),
    }))
}

#[derive(Debug, Serialize)]
pub struct ApiKeyJson {
    pub id: String,
    pub value: String,
}

#[post("/keys")]
async fn generate_api_key(
    bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<ApiKeyJson>> {
    let id = ApiKeyId::new();
    let value = ApiKeyValue::new();

    let unprefixed_id = id.unprefixed();
    let unprefixed_user_id = bearer.user.id.unprefixed();
    let key_value = value.to_string();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        unprefixed_user_id,
        key_value
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(ApiKeyJson {
        id: id.to_string(),
        value: value.to_string(),
    }))
}

async fn ensure_root_user_exists() {
    use sqlx::{Connection, SqliteConnection};

    let root_username = env::var("ROOT_USERNAME").unwrap();
    let root_api_key = env::var("ROOT_API_KEY").unwrap();

    let database_url = env::var("DATABASE_URL").unwrap();
    let mut connection = SqliteConnection::connect(&database_url).await.unwrap();

    let user_id = UserId::new();
    let unprefixed_user_id = user_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    #[derive(Debug)]
    struct RootUser {
        id: String,
    }

    let root_user = sqlx::query_as!(
        RootUser,
        "
        insert into user (id, created_at, updated_at, username)
        values (?, ?, ?, ?)
        -- This is a no-op update just so we can get the `id` returned.
        on conflict (username) do update set updated_at = updated_at
        returning id
        ",
        unprefixed_user_id,
        now,
        now,
        root_username
    )
    .fetch_one(&mut connection)
    .await
    .expect("failed to insert root user");

    println!("Root user: {:?}", root_user);

    let key_id = ApiKeyId::new();
    let unprefixed_key_id = key_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        on conflict (value) do nothing
        ",
        unprefixed_key_id,
        now,
        now,
        root_user.id,
        root_api_key
    )
    .execute(&mut connection)
    .await
    .unwrap();
}

#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
    dotenv().ok();

    ensure_root_user_exists().await;

    let rocket = rocket::build()
        .mount(
            "/",
            routes![index, upload_drop, get_drop, get_drop_metadata,],
        )
        .mount("/x/", routes![create_user, generate_api_key])
        .attach(Db::init())
        .ignite()
        .await?;

    let _ = rocket.launch().await?;

    Ok(())
}
<|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/. */

//! Gecko's media-query device and expression representation.

use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::media_queries::MediaType;
use crate::properties::ComputedValues;
use crate::string_cache::Atom;
use crate::values::specified::font::FONT_MEDIUM_PX;
use crate::values::{CustomIdent, KeyframesName};
use app_units::{Au, AU_PER_PX};
use cssparser::RGBA;
use euclid::default::Size2D;
use euclid::{Scale, SideOffsets2D};
use servo_arc::Arc;
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel};

/// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state.
pub struct Device {
    /// NB: The document owns the styleset, who owns the stylist, and thus the
    /// `Device`, so having a raw document pointer here is fine.
    document: *const structs::Document,
    default_values: Arc<ComputedValues>,
    /// The font size of the root element
    /// This is set when computing the style of the root
    /// element, and used for rem units in other elements.
    ///
    /// When computing the style of the root element, there can't be any
    /// other style being computed at the same time, given we need the style of
    /// the parent to compute everything else. So it is correct to just use
    /// a relaxed atomic here.
    root_font_size: AtomicIsize,
    /// The body text color, stored as an `nscolor`, used for the "tables
    /// inherit from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    body_text_color: AtomicUsize,
    /// Whether any styles computed in the document relied on the root font-size
    /// by using rem units.
    used_root_font_size: AtomicBool,
    /// Whether any styles computed in the document relied on the viewport size
    /// by using vw/vh/vmin/vmax units.
    used_viewport_size: AtomicBool,
    /// The CssEnvironment object responsible of getting CSS environment
    /// variables.
    environment: CssEnvironment,
}

impl fmt::Debug for Device {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use nsstring::nsCString;

        let mut doc_uri = nsCString::new();
        unsafe {
            bindings::Gecko_nsIURI_Debug(
                (*self.document()).mDocumentURI.raw::<structs::nsIURI>(),
                &mut doc_uri,
            )
        };

        f.debug_struct("Device")
            .field("document_url", &doc_uri)
            .finish()
    }
}

unsafe impl Sync for Device {}
unsafe impl Send for Device {}

impl Device {
    /// Trivially constructs a new `Device`.
    pub fn new(document: *const structs::Document) -> Self {
        assert!(!document.is_null());
        let doc = unsafe { &*document };
        let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
        Device {
            document,
            default_values: ComputedValues::default_values(doc),
            // FIXME(bz): Seems dubious?
            root_font_size: AtomicIsize::new(Au::from_px(FONT_MEDIUM_PX as i32).0 as isize),
            body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
            used_root_font_size: AtomicBool::new(false),
            used_viewport_size: AtomicBool::new(false),
            environment: CssEnvironment,
        }
    }

    /// Get the relevant environment to resolve `env()` functions.
    #[inline]
    pub fn environment(&self) -> &CssEnvironment {
        &self.environment
    }

    /// Tells the device that a new viewport rule has been found, and stores the
    /// relevant viewport constraints.
    pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
        unreachable!("Gecko doesn't support @viewport");
    }

    /// Whether any animation name may be referenced from the style of any
    /// element.
    pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return false,
        };

        unsafe {
            bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
        }
    }

    /// Returns the default computed values as a reference, in order to match
    /// Servo.
    pub fn default_computed_values(&self) -> &ComputedValues {
        &self.default_values
    }

    /// Returns the default computed values as an `Arc`.
    pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {
        &self.default_values
    }

    /// Get the font size of the root element (for rem)
    pub fn root_font_size(&self) -> Au {
        self.used_root_font_size.store(true, Ordering::Relaxed);
        Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
    }

    /// Set the font size of the root element (for rem)
    pub fn set_root_font_size(&self, size: Au) {
        self.root_font_size
            .store(size.0 as isize, Ordering::Relaxed)
    }

    /// Sets the body text color for the "inherit color from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    pub fn set_body_text_color(&self, color: RGBA) {
        self.body_text_color
            .store(convert_rgba_to_nscolor(&color) as usize, Ordering::Relaxed)
    }

    /// Returns the body text color.
    pub fn body_text_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32)
    }

    /// Gets the document pointer.
    #[inline]
    pub fn document(&self) -> &structs::Document {
        unsafe { &*self.document }
    }

    /// Gets the pres context associated with this document.
    #[inline]
    pub fn pres_context(&self) -> Option<&structs::nsPresContext> {
        unsafe {
            self.document()
                .mPresShell
                .as_ref()?
                .mPresContext
                .mRawPtr
                .as_ref()
        }
    }

    /// Gets the preference stylesheet prefs for our document.
    #[inline]
    pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {
        unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }
    }

    /// Recreates the default computed values.
    pub fn reset_computed_values(&mut self) {
        self.default_values = ComputedValues::default_values(self.document());
    }

    /// Rebuild all the cached data.
    pub fn rebuild_cached_data(&mut self) {
        self.reset_computed_values();
        self.used_root_font_size.store(false, Ordering::Relaxed);
        self.used_viewport_size.store(false, Ordering::Relaxed);
    }

    /// Returns whether we ever looked up the root font size of the Device.
    pub fn used_root_font_size(&self) -> bool {
        self.used_root_font_size.load(Ordering::Relaxed)
    }

    /// Recreates all the temporary state that the `Device` stores.
    ///
    /// This includes the viewport override from `@viewport` rules, and also the
    /// default computed values.
    pub fn reset(&mut self) {
        self.reset_computed_values();
    }

    /// Returns the current media type of the device.
    pub fn media_type(&self) -> MediaType {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return MediaType::screen(),
        };

        // Gecko allows emulating random media with mMediaEmulationData.mMedium.
        let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() {
            pc.mMediaEmulationData.mMedium.mRawPtr
        } else {
            pc.mMedium as *const structs::nsAtom as *mut _
        };

        MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
    }

    /// Returns the current viewport size in app units.
    pub fn au_viewport_size(&self) -> Size2D<Au> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Size2D::new(Au(0), Au(0)),
        };
        let area = &pc.mVisibleArea;
        Size2D::new(Au(area.width), Au(area.height))
    }

    /// Returns the current viewport size in app units, recording that it's been
    /// used for viewport unit resolution.
    pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
        self.used_viewport_size.store(true, Ordering::Relaxed);

        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Size2D::new(Au(0), Au(0)),
        };
        let size = &pc.mSizeForViewportUnits;
        Size2D::new(Au(size.width), Au(size.height))
    }

    /// Returns whether we ever looked up the viewport size of the Device.
    pub fn used_viewport_size(&self) -> bool {
        self.used_viewport_size.load(Ordering::Relaxed)
    }

    /// Returns the device pixel ratio.
    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Scale::new(1.),
        };

        if pc.mMediaEmulationData.mDPPX > 0.0 {
            return Scale::new(pc.mMediaEmulationData.mDPPX);
        }

        let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
        let au_per_px = AU_PER_PX as f32;
        Scale::new(au_per_px / au_per_dpx)
    }

    /// Returns whether document colors are enabled.
    #[inline]
    pub fn use_document_colors(&self) -> bool {
        let doc = self.document();
        if doc.mIsBeingUsedAsImage() {
            return true;
        }
        self.pref_sheet_prefs().mUseDocumentColors
    }

    /// Returns the default background color.
    pub fn default_background_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
    }

    /// Returns the default foreground color.
    pub fn default_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultColor)
    }

    /// Returns the current effective text zoom.
    #[inline]
    fn effective_text_zoom(&self) -> f32 {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return 1.,
        };
        pc.mEffectiveTextZoom
    }

    /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
    #[inline]
    pub fn zoom_text(&self, size: Au) -> Au {
        size.scale_by(self.effective_text_zoom())
    }

    /// Un-apply text zoom.
    #[inline]
    pub fn unzoom_text(&self, size: Au) -> Au {
        size.scale_by(1. / self.effective_text_zoom())
    }

    /// Returns safe area insets
    pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
        SideOffsets2D::zero()
    }
}
<commit_msg>style: Get safe area insets from Gecko.<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/. */

//! Gecko's media-query device and expression representation.

use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::media_queries::MediaType;
use crate::properties::ComputedValues;
use crate::string_cache::Atom;
use crate::values::specified::font::FONT_MEDIUM_PX;
use crate::values::{CustomIdent, KeyframesName};
use app_units::{Au, AU_PER_PX};
use cssparser::RGBA;
use euclid::default::Size2D;
use euclid::{Scale, SideOffsets2D};
use servo_arc::Arc;
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel};

/// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state.
pub struct Device {
    /// NB: The document owns the styleset, who owns the stylist, and thus the
    /// `Device`, so having a raw document pointer here is fine.
    document: *const structs::Document,
    default_values: Arc<ComputedValues>,
    /// The font size of the root element
    /// This is set when computing the style of the root
    /// element, and used for rem units in other elements.
    ///
    /// When computing the style of the root element, there can't be any
    /// other style being computed at the same time, given we need the style of
    /// the parent to compute everything else. So it is correct to just use
    /// a relaxed atomic here.
    root_font_size: AtomicIsize,
    /// The body text color, stored as an `nscolor`, used for the "tables
    /// inherit from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    body_text_color: AtomicUsize,
    /// Whether any styles computed in the document relied on the root font-size
    /// by using rem units.
    used_root_font_size: AtomicBool,
    /// Whether any styles computed in the document relied on the viewport size
    /// by using vw/vh/vmin/vmax units.
    used_viewport_size: AtomicBool,
    /// The CssEnvironment object responsible of getting CSS environment
    /// variables.
    environment: CssEnvironment,
}

impl fmt::Debug for Device {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use nsstring::nsCString;

        let mut doc_uri = nsCString::new();
        unsafe {
            bindings::Gecko_nsIURI_Debug(
                (*self.document()).mDocumentURI.raw::<structs::nsIURI>(),
                &mut doc_uri,
            )
        };

        f.debug_struct("Device")
            .field("document_url", &doc_uri)
            .finish()
    }
}

unsafe impl Sync for Device {}
unsafe impl Send for Device {}

impl Device {
    /// Trivially constructs a new `Device`.
    pub fn new(document: *const structs::Document) -> Self {
        assert!(!document.is_null());
        let doc = unsafe { &*document };
        let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
        Device {
            document,
            default_values: ComputedValues::default_values(doc),
            // FIXME(bz): Seems dubious?
            root_font_size: AtomicIsize::new(Au::from_px(FONT_MEDIUM_PX as i32).0 as isize),
            body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
            used_root_font_size: AtomicBool::new(false),
            used_viewport_size: AtomicBool::new(false),
            environment: CssEnvironment,
        }
    }

    /// Get the relevant environment to resolve `env()` functions.
    #[inline]
    pub fn environment(&self) -> &CssEnvironment {
        &self.environment
    }

    /// Tells the device that a new viewport rule has been found, and stores the
    /// relevant viewport constraints.
    pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
        unreachable!("Gecko doesn't support @viewport");
    }

    /// Whether any animation name may be referenced from the style of any
    /// element.
    pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return false,
        };

        unsafe {
            bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
        }
    }

    /// Returns the default computed values as a reference, in order to match
    /// Servo.
    pub fn default_computed_values(&self) -> &ComputedValues {
        &self.default_values
    }

    /// Returns the default computed values as an `Arc`.
    pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {
        &self.default_values
    }

    /// Get the font size of the root element (for rem)
    pub fn root_font_size(&self) -> Au {
        self.used_root_font_size.store(true, Ordering::Relaxed);
        Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
    }

    /// Set the font size of the root element (for rem)
    pub fn set_root_font_size(&self, size: Au) {
        self.root_font_size
            .store(size.0 as isize, Ordering::Relaxed)
    }

    /// Sets the body text color for the "inherit color from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    pub fn set_body_text_color(&self, color: RGBA) {
        self.body_text_color
            .store(convert_rgba_to_nscolor(&color) as usize, Ordering::Relaxed)
    }

    /// Returns the body text color.
    pub fn body_text_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32)
    }

    /// Gets the document pointer.
    #[inline]
    pub fn document(&self) -> &structs::Document {
        unsafe { &*self.document }
    }

    /// Gets the pres context associated with this document.
    #[inline]
    pub fn pres_context(&self) -> Option<&structs::nsPresContext> {
        unsafe {
            self.document()
                .mPresShell
                .as_ref()?
                .mPresContext
                .mRawPtr
                .as_ref()
        }
    }

    /// Gets the preference stylesheet prefs for our document.
    #[inline]
    pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {
        unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }
    }

    /// Recreates the default computed values.
    pub fn reset_computed_values(&mut self) {
        self.default_values = ComputedValues::default_values(self.document());
    }

    /// Rebuild all the cached data.
    pub fn rebuild_cached_data(&mut self) {
        self.reset_computed_values();
        self.used_root_font_size.store(false, Ordering::Relaxed);
        self.used_viewport_size.store(false, Ordering::Relaxed);
    }

    /// Returns whether we ever looked up the root font size of the Device.
    pub fn used_root_font_size(&self) -> bool {
        self.used_root_font_size.load(Ordering::Relaxed)
    }

    /// Recreates all the temporary state that the `Device` stores.
    ///
    /// This includes the viewport override from `@viewport` rules, and also the
    /// default computed values.
    pub fn reset(&mut self) {
        self.reset_computed_values();
    }

    /// Returns the current media type of the device.
    pub fn media_type(&self) -> MediaType {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return MediaType::screen(),
        };

        // Gecko allows emulating random media with mMediaEmulationData.mMedium.
        let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() {
            pc.mMediaEmulationData.mMedium.mRawPtr
        } else {
            pc.mMedium as *const structs::nsAtom as *mut _
        };

        MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
    }

    /// Returns the current viewport size in app units.
    pub fn au_viewport_size(&self) -> Size2D<Au> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Size2D::new(Au(0), Au(0)),
        };
        let area = &pc.mVisibleArea;
        Size2D::new(Au(area.width), Au(area.height))
    }

    /// Returns the current viewport size in app units, recording that it's been
    /// used for viewport unit resolution.
    pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
        self.used_viewport_size.store(true, Ordering::Relaxed);

        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Size2D::new(Au(0), Au(0)),
        };
        let size = &pc.mSizeForViewportUnits;
        Size2D::new(Au(size.width), Au(size.height))
    }

    /// Returns whether we ever looked up the viewport size of the Device.
    pub fn used_viewport_size(&self) -> bool {
        self.used_viewport_size.load(Ordering::Relaxed)
    }

    /// Returns the device pixel ratio.
    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Scale::new(1.),
        };

        if pc.mMediaEmulationData.mDPPX > 0.0 {
            return Scale::new(pc.mMediaEmulationData.mDPPX);
        }

        let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
        let au_per_px = AU_PER_PX as f32;
        Scale::new(au_per_px / au_per_dpx)
    }

    /// Returns whether document colors are enabled.
    #[inline]
    pub fn use_document_colors(&self) -> bool {
        let doc = self.document();
        if doc.mIsBeingUsedAsImage() {
            return true;
        }
        self.pref_sheet_prefs().mUseDocumentColors
    }

    /// Returns the default background color.
    pub fn default_background_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
    }

    /// Returns the default foreground color.
    pub fn default_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultColor)
    }

    /// Returns the current effective text zoom.
    #[inline]
    fn effective_text_zoom(&self) -> f32 {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return 1.,
        };
        pc.mEffectiveTextZoom
    }

    /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
    #[inline]
    pub fn zoom_text(&self, size: Au) -> Au {
        size.scale_by(self.effective_text_zoom())
    }

    /// Un-apply text zoom.
    #[inline]
    pub fn unzoom_text(&self, size: Au) -> Au {
        size.scale_by(1. / self.effective_text_zoom())
    }

    /// Returns safe area insets
    pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return SideOffsets2D::zero(),
        };
        let mut top = 0.0;
        let mut right = 0.0;
        let mut bottom = 0.0;
        let mut left = 0.0;
        unsafe { bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left) };
        SideOffsets2D::new(top, right, bottom, left)
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io;
use std::str;

pub struct InfoWriter;

impl io::Write for InfoWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        info!("{}", str::from_utf8(&buf[..]).unwrap());
        Ok(buf.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/*
  This is a hack until FnBox gets stabilized, see issue #28796.
  TODO(idolf): When #28796 gets stabalized, remove this and use the
               the library implementation. Then FnOnce gets a proper
               implementation, use that.
*/
pub trait FnBox<A, B> : Send {
    fn call(self: Box<Self>, args: A) -> B;
}

impl<A, B, F> FnBox<A, B> for F where F: FnOnce(A) -> B + Send {
    fn call(self: Box<F>, args: A) -> B {
        self(args)
    }
}
<commit_msg>Fixed a comment based on feedback from brinchj<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io;
use std::str;

pub struct InfoWriter;

impl io::Write for InfoWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        info!("{}", str::from_utf8(&buf[..]).unwrap());
        Ok(buf.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/*
  TODO(idolf): This is a hack until FnBox gets stabilized, see issue
               rust-lang/rust#28796.  When this issue gets stabilized, remove
               this and use the the library implementation. Then FnOnce gets a
               proper implementation, use that.
*/
pub trait FnBox<A, B> : Send {
    fn call(self: Box<Self>, args: A) -> B;
}

impl<A, B, F> FnBox<A, B> for F where F: FnOnce(A) -> B + Send {
    fn call(self: Box<F>, args: A) -> B {
        self(args)
    }
}
<|endoftext|>


<commit_before>pub use self::os::{CODESET};
pub use self::os::{D_T_FMT};
pub use self::os::{D_FMT};
pub use self::os::{T_FMT};
pub use self::os::{T_FMT_AMPM};
pub use self::os::{AM_STR};
pub use self::os::{PM_STR};
pub use self::os::{DAY_1};
pub use self::os::{DAY_2};
pub use self::os::{DAY_3};
pub use self::os::{DAY_4};
pub use self::os::{DAY_5};
pub use self::os::{DAY_6};
pub use self::os::{DAY_7};
pub use self::os::{ABDAY_1};
pub use self::os::{ABDAY_2};
pub use self::os::{ABDAY_3};
pub use self::os::{ABDAY_4};
pub use self::os::{ABDAY_5};
pub use self::os::{ABDAY_6};
pub use self::os::{ABDAY_7};
pub use self::os::{MON_1};
pub use self::os::{MON_2};
pub use self::os::{MON_3};
pub use self::os::{MON_4};
pub use self::os::{MON_5};
pub use self::os::{MON_6};
pub use self::os::{MON_7};
pub use self::os::{MON_8};
pub use self::os::{MON_9};
pub use self::os::{MON_10};
pub use self::os::{MON_11};
pub use self::os::{MON_12};
pub use self::os::{ABMON_1};
pub use self::os::{ABMON_2};
pub use self::os::{ABMON_3};
pub use self::os::{ABMON_4};
pub use self::os::{ABMON_5};
pub use self::os::{ABMON_6};
pub use self::os::{ABMON_7};
pub use self::os::{ABMON_8};
pub use self::os::{ABMON_9};
pub use self::os::{ABMON_10};
pub use self::os::{ABMON_11};
pub use self::os::{ABMON_12};
pub use self::os::{ERA};
pub use self::os::{ERA_D_FMT};
pub use self::os::{ERA_D_T_FMT};
pub use self::os::{ERA_T_FMT};
pub use self::os::{ALT_DIGITS};
pub use self::os::{RADIXCHAR};
pub use self::os::{THOUSEP};
pub use self::os::{YESEXPR};
pub use self::os::{NOEXPR};
pub use self::os::{CRNCYSTR};

#[cfg(target_os = "linux")]
#[path = "linux/mod.rs"]
mod os;

extern "C" {
    pub fn nl_langinfo(item: ::nl_types::nl_item) -> *mut ::char_t;
    pub fn nl_langinfo_l(item: ::nl_types::nl_item,
                         l: ::locale::locale_t) -> *mut ::char_t;
}
<commit_msg>langinfo.h<commit_after>pub use self::os::{CODESET};
pub use self::os::{D_T_FMT};
pub use self::os::{D_FMT};
pub use self::os::{T_FMT};
pub use self::os::{T_FMT_AMPM};
pub use self::os::{AM_STR};
pub use self::os::{PM_STR};
pub use self::os::{DAY_1};
pub use self::os::{DAY_2};
pub use self::os::{DAY_3};
pub use self::os::{DAY_4};
pub use self::os::{DAY_5};
pub use self::os::{DAY_6};
pub use self::os::{DAY_7};
pub use self::os::{ABDAY_1};
pub use self::os::{ABDAY_2};
pub use self::os::{ABDAY_3};
pub use self::os::{ABDAY_4};
pub use self::os::{ABDAY_5};
pub use self::os::{ABDAY_6};
pub use self::os::{ABDAY_7};
pub use self::os::{MON_1};
pub use self::os::{MON_2};
pub use self::os::{MON_3};
pub use self::os::{MON_4};
pub use self::os::{MON_5};
pub use self::os::{MON_6};
pub use self::os::{MON_7};
pub use self::os::{MON_8};
pub use self::os::{MON_9};
pub use self::os::{MON_10};
pub use self::os::{MON_11};
pub use self::os::{MON_12};
pub use self::os::{ABMON_1};
pub use self::os::{ABMON_2};
pub use self::os::{ABMON_3};
pub use self::os::{ABMON_4};
pub use self::os::{ABMON_5};
pub use self::os::{ABMON_6};
pub use self::os::{ABMON_7};
pub use self::os::{ABMON_8};
pub use self::os::{ABMON_9};
pub use self::os::{ABMON_10};
pub use self::os::{ABMON_11};
pub use self::os::{ABMON_12};
pub use self::os::{ERA};
pub use self::os::{ERA_D_FMT};
pub use self::os::{ERA_D_T_FMT};
pub use self::os::{ERA_T_FMT};
pub use self::os::{ALT_DIGITS};
pub use self::os::{RADIXCHAR};
pub use self::os::{THOUSEP};
pub use self::os::{YESEXPR};
pub use self::os::{NOEXPR};
pub use self::os::{CRNCYSTR};

use {char_t};
use nl_types::{nl_item};
use locale::{locale_t};

#[cfg(target_os = "linux")]
#[path = "linux/mod.rs"]
mod os;

extern "C" {
    pub fn nl_langinfo(item: nl_item) -> *mut char_t;
    pub fn nl_langinfo_l(item: nl_item, l: locale_t) -> *mut char_t;
}
<|endoftext|>