<commit_before>use cairo::{self, ImageSurface};
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use crate::parsers::ParseValue;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{FilterEffect, FilterError, Primitive};

/// The `feMerge` filter primitive.
pub struct FeMerge {
    base: Primitive,
}

/// The `<feMergeNode>` element.
#[derive(Default)]
pub struct FeMergeNode {
    in_: Option<Input>,
}

impl Default for FeMerge {
    /// Constructs a new `Merge` with empty properties.
    #[inline]
    fn default() -> FeMerge {
        FeMerge {
            base: Primitive::new::<Self>(),
        }
    }
}

impl NodeTrait for FeMerge {
    impl_node_as_filter_effect!();

    #[inline]
    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)
    }
}

impl NodeTrait for FeMergeNode {
    #[inline]
    fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!(svg "in") => self.in_ = Some(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}

impl FeMergeNode {
    fn render(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds: IRect,
        output_surface: Option<SharedImageSurface>,
    ) -> Result<SharedImageSurface, FilterError> {
        let input = ctx.get_input(draw_ctx, self.in_.as_ref())?;

        if output_surface.is_none() {
            return Ok(input.surface().clone());
        }
        let output_surface = output_surface.unwrap();

        // If we're combining two alpha-only surfaces, the result is alpha-only. Otherwise the
        // result is whatever the non-alpha-only type we're working on (which can be either sRGB or
        // linear sRGB depending on color-interpolation-filters).
        let surface_type = if input.surface().is_alpha_only() {
            output_surface.surface_type()
        } else {
            if !output_surface.is_alpha_only() {
                // All surface types should match (this is enforced by get_input()).
                assert_eq!(
                    output_surface.surface_type(),
                    input.surface().surface_type()
                );
            }

            input.surface().surface_type()
        };

        let output_surface = output_surface.into_image_surface()?;

        {
            let cr = cairo::Context::new(&output_surface);
            let r = cairo::Rectangle::from(bounds);
            cr.rectangle(r.x, r.y, r.width, r.height);
            cr.clip();

            input.surface().set_as_source_surface(&cr, 0f64, 0f64);
            cr.set_operator(cairo::Operator::Over);
            cr.paint();
        }

        Ok(SharedImageSurface::new(output_surface, surface_type)?)
    }
}

impl FilterEffect for FeMerge {
    fn render(
        &self,
        node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        // Compute the filter bounds, taking each child node's input into account.
        let mut bounds = self.base.get_bounds(ctx);
        for child in node
            .children()
            .filter(|c| c.borrow().get_type() == NodeType::FeMergeNode)
        {
            if child.borrow().is_in_error() {
                return Err(FilterError::ChildNodeInError);
            }

            let input = ctx.get_input(
                draw_ctx,
                child.borrow().get_impl::<FeMergeNode>().in_.as_ref(),
            )?;
            bounds = bounds.add_input(&input);
        }
        let bounds = bounds.into_irect(draw_ctx);

        // Now merge them all.
        let mut output_surface = None;
        for child in node
            .children()
            .filter(|c| c.borrow().get_type() == NodeType::FeMergeNode)
        {
            output_surface = Some(child.borrow().get_impl::<FeMergeNode>().render(
                ctx,
                draw_ctx,
                bounds,
                output_surface,
            )?);
        }

        let output_surface = match output_surface {
            Some(surface) => surface,
            None => SharedImageSurface::new(
                ImageSurface::create(
                    cairo::Format::ARgb32,
                    ctx.source_graphic().width(),
                    ctx.source_graphic().height(),
                )?,
                SurfaceType::AlphaOnly,
            )?,
        };

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}
<commit_msg>filters: use compose method also for FeMerge<commit_after>use cairo::{self, ImageSurface};
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use crate::parsers::ParseValue;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{FilterEffect, FilterError, Primitive};

/// The `feMerge` filter primitive.
pub struct FeMerge {
    base: Primitive,
}

/// The `<feMergeNode>` element.
#[derive(Default)]
pub struct FeMergeNode {
    in_: Option<Input>,
}

impl Default for FeMerge {
    /// Constructs a new `Merge` with empty properties.
    #[inline]
    fn default() -> FeMerge {
        FeMerge {
            base: Primitive::new::<Self>(),
        }
    }
}

impl NodeTrait for FeMerge {
    impl_node_as_filter_effect!();

    #[inline]
    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)
    }
}

impl NodeTrait for FeMergeNode {
    #[inline]
    fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!(svg "in") => self.in_ = Some(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}

impl FeMergeNode {
    fn render(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds: IRect,
        output_surface: Option<SharedImageSurface>,
    ) -> Result<SharedImageSurface, FilterError> {
        let input = ctx.get_input(draw_ctx, self.in_.as_ref())?;

        if output_surface.is_none() {
            return Ok(input.surface().clone());
        }

        input
            .surface()
            .compose(&output_surface.unwrap(), bounds, cairo::Operator::Over)
            .map_err(FilterError::CairoError)
    }
}

impl FilterEffect for FeMerge {
    fn render(
        &self,
        node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        // Compute the filter bounds, taking each child node's input into account.
        let mut bounds = self.base.get_bounds(ctx);
        for child in node
            .children()
            .filter(|c| c.borrow().get_type() == NodeType::FeMergeNode)
        {
            if child.borrow().is_in_error() {
                return Err(FilterError::ChildNodeInError);
            }

            let input = ctx.get_input(
                draw_ctx,
                child.borrow().get_impl::<FeMergeNode>().in_.as_ref(),
            )?;
            bounds = bounds.add_input(&input);
        }
        let bounds = bounds.into_irect(draw_ctx);

        // Now merge them all.
        let mut output_surface = None;
        for child in node
            .children()
            .filter(|c| c.borrow().get_type() == NodeType::FeMergeNode)
        {
            output_surface = Some(child.borrow().get_impl::<FeMergeNode>().render(
                ctx,
                draw_ctx,
                bounds,
                output_surface,
            )?);
        }

        let output_surface = match output_surface {
            Some(surface) => surface,
            None => SharedImageSurface::new(
                ImageSurface::create(
                    cairo::Format::ARgb32,
                    ctx.source_graphic().width(),
                    ctx.source_graphic().height(),
                )?,
                SurfaceType::AlphaOnly,
            )?,
        };

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}
<|endoftext|>


<commit_before>/* AES ECB cut and paste
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand::random;

use aes_lib::{AES_BLOCK_SIZE, decrypt_aes_ecb, encrypt_aes_ecb};

struct Profile {
    key: Vec<u8>,
}

impl Profile {
    fn new() -> Profile {
        let key = random_bytes(AES_BLOCK_SIZE);
        Profile{key: key}
    }

    fn profile_for(&self, email: &str) -> Vec<u8> {
        if email.contains("&") || email.contains("=") {
            fail!("Invalid character in email: {}", email);
        }
        let map = [("email".into_string(), email.to_string()),
                   ("uid".into_string(), "10".into_string()),
                   ("role".into_string(), "user".into_string())].to_vec();
        let encoded = encode_kv(map);
        encrypt_aes_ecb(encoded.as_slice(), self.key.as_slice())
    }

    fn decrypt(&self, encrypted: &[u8]) -> Vec<(String, String)> {
        let encoded = decrypt_aes_ecb(encrypted, self.key.as_slice());
        parse_kv(encoded.as_slice())
    }
}

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn parse_kv(string: &[u8]) -> Vec<(String, String)> {
    let kv = string.split(|&c| c == b'&');
    kv.map(|kv| {
        let pair: Vec<&[u8]> = kv.split(|&c| c == b'=').collect();
        (String::from_utf8(pair[0].to_vec()).unwrap(),
         String::from_utf8(pair[1].to_vec()).unwrap())
        }).collect()
}

fn encode_kv(map: Vec<(String, String)>) -> Vec<u8> {
    let mut bytes = Vec::new();
    for item in map.iter() {
        bytes.push_all(item.ref0().clone().into_bytes().as_slice());
        bytes.push(b'=');
        bytes.push_all(item.ref1().clone().into_bytes().as_slice());
        bytes.push(b'&');
    }
    bytes.pop();
    bytes
}

#[cfg(not(test))]
fn main() {
    let profile = Profile::new();
    let encrypted = profile.profile_for("bob@microsoft.com".as_slice());
    println!("Decrypted: {}", profile.decrypt(encrypted.as_slice()));
}

/*
 * Tests
 */
#[cfg(test)]
mod test {
    use super::{Profile, parse_kv, encode_kv};

    #[test]
    fn test_parse_encode_kv() {
        let map = [("foo".into_string(), "bar".into_string()),
                   ("baz".into_string(), "qux".into_string()),
                   ("zap".into_string(), "zazzle".into_string())].to_vec();
        let encoded: Vec<u8> = b"foo=bar&baz=qux&zap=zazzle".to_vec();
        assert_eq!(map, parse_kv(encoded.as_slice()));
        assert_eq!(encode_kv(map), encoded);
    }

    #[test]
    fn test_profile() {
        let profile = Profile::new();
        let email = "bob@microsoft.com";
        let encrypted = profile.profile_for(email.as_slice());
        assert_eq!(profile.decrypt(encrypted.as_slice()),
                   [("email".into_string(), email.into_string()),
                    ("uid".into_string(), "10".into_string()),
                    ("role".into_string(), "user".into_string())].to_vec());
    }
}
<commit_msg>Replace Vec argument with slice<commit_after>/* AES ECB cut and paste
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand::random;

use aes_lib::{AES_BLOCK_SIZE, decrypt_aes_ecb, encrypt_aes_ecb};

struct Profile {
    key: Vec<u8>,
}

impl Profile {
    fn new() -> Profile {
        let key = random_bytes(AES_BLOCK_SIZE);
        Profile{key: key}
    }

    fn profile_for(&self, email: &str) -> Vec<u8> {
        if email.contains("&") || email.contains("=") {
            fail!("Invalid character in email: {}", email);
        }
        let map = [("email".into_string(), email.to_string()),
                   ("uid".into_string(), "10".into_string()),
                   ("role".into_string(), "user".into_string())];
        let encoded = encode_kv(map);
        encrypt_aes_ecb(encoded.as_slice(), self.key.as_slice())
    }

    fn decrypt(&self, encrypted: &[u8]) -> Vec<(String, String)> {
        let encoded = decrypt_aes_ecb(encrypted, self.key.as_slice());
        parse_kv(encoded.as_slice())
    }
}

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn parse_kv(string: &[u8]) -> Vec<(String, String)> {
    let kv = string.split(|&c| c == b'&');
    kv.map(|kv| {
        let pair: Vec<&[u8]> = kv.split(|&c| c == b'=').collect();
        (String::from_utf8(pair[0].to_vec()).unwrap(),
         String::from_utf8(pair[1].to_vec()).unwrap())
        }).collect()
}

fn encode_kv(map: &[(String, String)]) -> Vec<u8> {
    let mut bytes = Vec::new();
    for item in map.iter() {
        bytes.push_all(item.ref0().clone().into_bytes().as_slice());
        bytes.push(b'=');
        bytes.push_all(item.ref1().clone().into_bytes().as_slice());
        bytes.push(b'&');
    }
    bytes.pop();
    bytes
}

#[cfg(not(test))]
fn main() {
    let profile = Profile::new();
    let encrypted = profile.profile_for("bob@microsoft.com".as_slice());
    println!("Decrypted: {}", profile.decrypt(encrypted.as_slice()));
}

/*
 * Tests
 */
#[cfg(test)]
mod test {
    use super::{Profile, parse_kv, encode_kv};

    #[test]
    fn test_parse_encode_kv() {
        let map = [("foo".into_string(), "bar".into_string()),
                   ("baz".into_string(), "qux".into_string()),
                   ("zap".into_string(), "zazzle".into_string())];
        let encoded: Vec<u8> = b"foo=bar&baz=qux&zap=zazzle".to_vec();
        assert_eq!(map.to_vec(), parse_kv(encoded.as_slice()));
        assert_eq!(encode_kv(map), encoded);
    }

    #[test]
    fn test_profile() {
        let profile = Profile::new();
        let email = "bob@microsoft.com";
        let encrypted = profile.profile_for(email.as_slice());
        assert_eq!(profile.decrypt(encrypted.as_slice()),
                   [("email".into_string(), email.into_string()),
                    ("uid".into_string(), "10".into_string()),
                    ("role".into_string(), "user".into_string())].to_vec());
    }
}
<|endoftext|>


<commit_before>// Learn stuff about our users.

+ my name is *
- <set name=<formal>>Nice to meet you, <get name>.
- <set name=<formal>><get name>, nice to meet you.

+ my name is <bot master>
- <set name=<bot master>>That's my master's name too.

+ my name is <bot name>
- <set name=<bot name>>What a coincidence! That's my name too!
- <set name=<bot name>>That's my name too!

+ call me *
- <set name=<formal>><get name>, I will call you that from now on.

+ i am * years old
- <set age=<star>>A lot of people are <get age>, you're not alone.
- <set age=<star>>Cool, I'm <bot age> myself.{weight=49}

+ i am a (@malenoun)
- <set sex=male>Alright, you're a <star>.

+ i am a (@femalenoun)
- <set sex=female>Alright, you're female.

+ i (am from|live in) *
- <set location=<formal>>I've spoken to people from <get location> before.

+ my favorite * is *
- <set fav<star1>=<star2>>Why is it your favorite?

+ i am single
- <set status=single><set spouse=nobody>I am too.

+ i have a girlfriend
- <set status=girlfriend>What's her name?

+ i have a boyfriend
- <set status=boyfriend>What's his name?

+ *
% whats her name
- <set spouse=<formal>>That's a pretty name.

+ *
% whats his name
- <set spouse=<formal>>That's a cool name.

+ my (girlfriend|boyfriend)* name is *
- <set spouse=<formal>>That's a nice name.

+ (what is my name|who am i|do you know my name|do you know who i am){weight=10}
- Your name is <get name>.
- You told me your name is <get name>.
- Aren't you <get name>?

+ (how old am i|do you know how old i am|do you know my age){weight=10}
- You are <get age> years old.
- You're <get age>.

+ am i a (@malenoun) or a (@femalenoun){weight=10}
- You're a <get sex>.

+ am i (@malenoun) or (@femalenoun){weight=10}
- You're a <get sex>.

+ what is my favorite *{weight=10}
- Your favorite <star> is <get fav<star>>

+ who is my (boyfriend|girlfriend|spouse){weight=10}
- <get spouse>
<commit_msg>Bugfix in clients.rs from the demo brain<commit_after>// Learn stuff about our users.

+ my name is *
- <set name=<formal>>Nice to meet you, <get name>.
- <set name=<formal>><get name>, nice to meet you.

+ my name is <bot master>
- <set name=<bot master>>That's my master's name too.

+ my name is <bot name>
- <set name=<bot name>>What a coincidence! That's my name too!
- <set name=<bot name>>That's my name too!

+ call me *
- <set name=<formal>><get name>, I will call you that from now on.

+ i am * years old
- <set age=<star>>A lot of people are <get age>, you're not alone.
- <set age=<star>>Cool, I'm <bot age> myself.{weight=49}

+ i am a (@malenoun)
- <set sex=male>Alright, you're a <star>.

+ i am a (@femalenoun)
- <set sex=female>Alright, you're female.

+ i (am from|live in) *
- <set location={formal}<star2>{/formal}>I've spoken to people from <get location> before.

+ my favorite * is *
- <set fav<star1>=<star2>>Why is it your favorite?

+ i am single
- <set status=single><set spouse=nobody>I am too.

+ i have a girlfriend
- <set status=girlfriend>What's her name?

+ i have a boyfriend
- <set status=boyfriend>What's his name?

+ *
% whats her name
- <set spouse=<formal>>That's a pretty name.

+ *
% whats his name
- <set spouse=<formal>>That's a cool name.

+ my (girlfriend|boyfriend)* name is *
- <set spouse=<formal>>That's a nice name.

+ (what is my name|who am i|do you know my name|do you know who i am){weight=10}
- Your name is <get name>.
- You told me your name is <get name>.
- Aren't you <get name>?

+ (how old am i|do you know how old i am|do you know my age){weight=10}
- You are <get age> years old.
- You're <get age>.

+ am i a (@malenoun) or a (@femalenoun){weight=10}
- You're a <get sex>.

+ am i (@malenoun) or (@femalenoun){weight=10}
- You're a <get sex>.

+ what is my favorite *{weight=10}
- Your favorite <star> is <get fav<star>>

+ who is my (boyfriend|girlfriend|spouse){weight=10}
- <get spouse>
<|endoftext|>


<commit_before>//! Shader Toy.

use spectra::anim::spline::{Interpolate, Spline};
use spectra::render::shader::{ProgramError, Uniform, UniformBuilder, UniformInterface, UniformWarning, Uniformable};
use spectra::sys::res::Res;
use spectra::sys::time::Time;
use std::collections::HashMap;

/// Animated parameter.
///
/// Such a parameter is used to represent a shader uniform that gets animated by a spline.
pub struct AnimatedParam<'a> {
  update_: Box<Fn(Time) + 'a>,
}

impl<'a> AnimatedParam<'a> {
  pub fn new<T, Q>(
    uniform: Uniform<Q>,
    spline: Res<Spline<T>>
  ) -> Self
  where T: 'a + Interpolate, Q: 'a + From<T> + Uniformable {
    Self {
      update_: Box::new(move |t| {
        if let Some(value) = spline.borrow().sample(t.as_f32()) {
          uniform.update(value.into());
        }
      })
    }
  }
}

/// Set of animated params.
///
/// This is a shader uniform interface that will automatically update its parameters whenever they
/// change. In order
pub struct AnimatedParams<'a> {
  params: HashMap<String, AnimatedParam<'a>>
}

//impl<'a> UniformInterface for AnimatedParams<'a> {
//  fn uniform_interface(builder: UniformBuilder, env: ) -> Result<(Self, UniformWarning), ProgramError> {
//  }
//}
<commit_msg>Ok I need a bit more work in luminance to make uniform interfaces easier.<commit_after>//! Shader Toy.

use spectra::anim::spline::{Interpolate, Spline, SplineDeserializerAdapter};
use spectra::render::shader::{ProgramError, Uniform, UniformBuilder, UniformInterface, UniformWarning, Uniformable};
use spectra::sys::res::{FSKey, Res, Store};
use spectra::sys::time::Time;
use std::collections::{HashMap, HashSet};

/// Animated parameter.
///
/// Such a parameter is used to represent a shader uniform that gets animated by a spline.
pub struct AnimatedParam<'a> {
  update_: Box<Fn(Time) + 'a>,
}

impl<'a> AnimatedParam<'a> {
  pub fn new<T, Q, C>(
    builder: &UniformBuilder,
    store: &mut Store<C>,
    ctx: &mut C,
    name: &str
  ) -> Result<Self, ()>
  where T: 'static + SplineDeserializerAdapter + Interpolate, Q: 'a + From<T> + Uniformable {
    // first, get the spline
    let spline: Res<Spline<T>> =
      store.get(&FSKey::new(&format!("anim/parameters/{}.json", name)), ctx).map_err(|_| ())?; // FIXME

    // then, ask for the uniform
    let uniform = builder.ask::<Q>(name).map_err(|_| ())?; // FIXME

    let animated_param = Self {
      update_: Box::new(move |t| {
        if let Some(value) = spline.borrow().sample(t.as_f32()) {
          uniform.update(value.into());
        }
      })
    };

    Ok(animated_param)
  }
}

/// Set of animated params.
///
/// This is a shader uniform interface that will automatically update its parameters whenever they
/// change. In order
pub struct AnimatedParams<'a> {
  params: HashMap<String, AnimatedParam<'a>>
}

struct AnimatedParamsContext<'a, C> where C: 'a {
  keys: HashSet<String>,
  store: &'a mut Store<C>,
  ctx: &'a mut C
}

// FIXME: for now, we just load 1D floating spline parameters
impl<'a, 'b, C> UniformInterface<AnimatedParamsContext<'b, C>> for AnimatedParams<'a> where C: 'b {
  fn uniform_interface(builder: UniformBuilder, env: AnimatedParamsContext<C>) -> Result<(Self, Vec<UniformWarning>), ProgramError> {
    let AnimatedParamsContext { keys, mut store, ctx } = env;

    // build the key-value iterator
    let kv = keys.into_iter().filter_map(move |k| {
      // try to create the animated parameter
      let ap = AnimatedParam::new::<f32, f32, C>(&builder, &mut store, ctx, &k);
      ap.map(|v| (k, v)).ok()
    });

    let params = AnimatedParams {
      params: kv.collect()
    };

    Ok((params, Vec::new()))
  }
}
<|endoftext|>


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

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{FcConfigGetCurrent, FcConfigGetFonts, FcConfigSubstitute};
use fontconfig::fontconfig::{FcDefaultSubstitute, FcFontMatch, FcNameParse, FcPatternGetString};
use fontconfig::fontconfig::{FcFontSetDestroy, FcMatchPattern, FcPatternCreate, FcPatternDestroy};
use fontconfig::fontconfig::{FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy, FcPatternAddString};
use fontconfig::fontconfig::{FcObjectSetAdd, FcPatternGetInteger};
use libc;
use libc::{c_char, c_int};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::ptr;
use super::c_str_to_string;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";
static FC_FONTFORMAT: &'static [u8] = b"fontformat\0";

pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
    unsafe {
        let config = FcConfigGetCurrent();
        let font_set = FcConfigGetFonts(config, FcSetSystem);
        for i in 0..((*font_set).nfont as isize) {
            let font = (*font_set).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::null_mut();
            let mut format: *mut FcChar8 = ptr::null_mut();
            let mut v: c_int = 0;
            if FcPatternGetString(*font, FC_FONTFORMAT.as_ptr() as *mut c_char, v, &mut format) != FcResultMatch {
                continue;
            }

            // Skip bitmap fonts. They aren't supported by FreeType.
            let fontformat = c_str_to_string(format as *const c_char);
            if fontformat != "TrueType" &&
               fontformat != "CFF" &&
               fontformat != "Type 1" {
                continue;
            }

            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
                let family_name = c_str_to_string(family as *const c_char);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn for_each_variation<F>(family_name: &str, mut callback: F)
    where F: FnMut(String)
{
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(!pattern.is_null());
        let family_name_c = CString::new(family_name).unwrap();
        let family_name = family_name_c.as_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(!object_set.is_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut c_char);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut c_char);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in 0..((*matches).nfont as isize) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::null_mut();
            let result = FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file);
            let file = if result == FcResultMatch {
                c_str_to_string(file as *const c_char)
            } else {
                panic!();
            };
            let mut index: libc::c_int = 0;
            let result = FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index);
            let index = if result == FcResultMatch {
                index
            } else {
                panic!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn system_default_family(generic_name: &str) -> Option<String> {
    let generic_name_c = CString::new(generic_name).unwrap();
    let generic_name_ptr = generic_name_c.as_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::null_mut(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::null_mut();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
            let result = c_str_to_string(match_string as *const c_char);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os = "linux")]
pub fn last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_owned(),
        "DejaVu Sans".to_owned(),
        "Arial".to_owned()
    )
}

#[cfg(target_os = "windows")]
pub fn last_resort_font_families() -> Vec<String> {
    vec!(
        "Arial".to_owned()
    )
}

#[cfg(target_os = "linux")]
pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans";

#[cfg(target_os = "windows")]
pub static SANS_SERIF_FONT_FAMILY: &'static str = "Arial";

<commit_msg>Auto merge of #18923 - tigercosmos:master, r=jdm<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 fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{FcConfigGetCurrent, FcConfigGetFonts, FcConfigSubstitute};
use fontconfig::fontconfig::{FcDefaultSubstitute, FcFontMatch, FcNameParse, FcPatternGetString};
use fontconfig::fontconfig::{FcFontSetDestroy, FcMatchPattern, FcPatternCreate, FcPatternDestroy};
use fontconfig::fontconfig::{FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy, FcPatternAddString};
use fontconfig::fontconfig::{FcObjectSetAdd, FcPatternGetInteger};
use libc;
use libc::{c_char, c_int};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::ptr;
use super::c_str_to_string;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";
static FC_FONTFORMAT: &'static [u8] = b"fontformat\0";

pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
    unsafe {
        let config = FcConfigGetCurrent();
        let font_set = FcConfigGetFonts(config, FcSetSystem);
        for i in 0..((*font_set).nfont as isize) {
            let font = (*font_set).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::null_mut();
            let mut format: *mut FcChar8 = ptr::null_mut();
            let mut v: c_int = 0;
            if FcPatternGetString(*font, FC_FONTFORMAT.as_ptr() as *mut c_char, v, &mut format) != FcResultMatch {
                continue;
            }

            // Skip bitmap fonts. They aren't supported by FreeType.
            let fontformat = c_str_to_string(format as *const c_char);
            if fontformat != "TrueType" &&
               fontformat != "CFF" &&
               fontformat != "Type 1" {
                continue;
            }

            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
                let family_name = c_str_to_string(family as *const c_char);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn for_each_variation<F>(family_name: &str, mut callback: F)
    where F: FnMut(String)
{
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(!pattern.is_null());
        let family_name_c = CString::new(family_name).unwrap();
        let family_name = family_name_c.as_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(!object_set.is_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut c_char);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut c_char);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in 0..((*matches).nfont as isize) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::null_mut();
            let result = FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file);
            let file = if result == FcResultMatch {
                c_str_to_string(file as *const c_char)
            } else {
                panic!();
            };
            let mut index: libc::c_int = 0;
            let result = FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index);
            let index = if result == FcResultMatch {
                index
            } else {
                panic!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn system_default_family(generic_name: &str) -> Option<String> {
    let generic_name_c = CString::new(generic_name).unwrap();
    let generic_name_ptr = generic_name_c.as_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::null_mut(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::null_mut();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
            let result = c_str_to_string(match_string as *const c_char);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

pub fn last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_owned(),
        "DejaVu Sans".to_owned(),
        "Arial".to_owned()
    )
}

pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans";
<|endoftext|>


<commit_before>extern mod png;

use std::num::{sqrt, min, max, cos, sin};

static WIDTH: f64 = 800.;
static HEIGHT: f64 = 800.;

struct Color {
  r: u8,
  g: u8,
  b: u8
}

static Red: Color = Color { r: 255, g: 0, b: 0 };
static Green: Color = Color { r: 0, g: 255, b: 0 };
static Blue: Color = Color { r: 0, g: 0, b: 255 };
static White: Color = Color { r: 255, g: 255, b: 255 };
static Black: Color = Color { r: 0, g: 0, b: 0 };

struct Point {
  x: f64,
  y: f64,
  z: f64
}

struct Spot {
  pos: Point,
  color: Color,
}

struct Sphere {
  pos: Point,
  radius: f64,
  shininess: f64,
  color: Color
}

struct Scene {
  eye: Point,
  spot: Spot,
  sphere: Sphere
}

fn solve_poly(a: f64, b: f64, c: f64) -> f64 {
  let delta = b.pow(&2.0) - 4. * a * c;

  if delta < 0. {
    return 0.;
  }

  let k1 = (-b - sqrt(delta)) / (2. * a);
  let k2 = (-b + sqrt(delta)) / (2. * a);

  return match (k1, k2) {
    (k1, k2) if k1 < 0. && k2 < 0. => 0.,
    (k1, k2) if k2 <= 0. => k1,
    (k1, k2) if k1 <= 0. => k2,
    _ => min(k1, k2)
  }
}

fn hit_sphere(sphere: &Sphere, eye: &Point, vector: &Point) -> f64 {
  let a = vector.x.pow(&2.) + vector.y.pow(&2.) + vector.z.pow(&2.);
  let b = 2. * (eye.x * vector.x + eye.y * vector.y + eye.z * vector.z);
  let c = eye.x.pow(&2.) + eye.y.pow(&2.) + eye.z.pow(&2.) - sphere.radius.pow(&2.);
  return solve_poly(a, b, c);
}

fn get_closest(obj: &Sphere, eye: &Point, vector: &Point) -> f64 {
  let e = ~Point { x: eye.x - obj.pos.x, y: eye.y - obj.pos.y, z: eye.z - obj.pos.z };
  let v = ~Point { x: vector.x, y: vector.y, z: vector.z };
  return hit_sphere(obj, e, v);
}

fn get_light(obj: &Sphere, spot: &Spot, inter: &Point, light: &Point) -> Color {
  let perp = Point { x: inter.x, y: inter.y, z: inter.z };
  let norme_l = sqrt(light.x.pow(&2.) + light.y.pow(&2.) + light.z.pow(&2.));
  let norme_n = sqrt(perp.x.pow(&2.) + perp.y.pow(&2.) + perp.z.pow(&2.));
  let cos_a = (light.x * perp.x + light.y * perp.y + light.z * perp.z) / (norme_l * norme_n);

  if cos_a <= 0. {
    return Black;
  }

  Color {
    r: ((obj.color.r as f64) * cos_a * (1. - obj.shininess) + (spot.color.r as f64) * cos_a * obj.shininess) as u8,
    g: ((obj.color.g as f64) * cos_a * (1. - obj.shininess) + (spot.color.g as f64) * cos_a * obj.shininess) as u8,
    b: ((obj.color.b as f64) * cos_a * (1. - obj.shininess) + (spot.color.b as f64) * cos_a * obj.shininess) as u8
  }
}

fn main() {
  let mut pixels = ~[Black, ..((WIDTH * HEIGHT) as uint)];

  let eye = ~Point { x: -300., y: 0., z: 200. };
  let spot = ~Spot {
    pos: Point { x: -300., y: 100., z: 200. },
    color: White
  };
  let sphere = ~Sphere {
    pos: Point { x: 0., y: 0., z: 0. },
    radius: 160.,
    shininess: 0.2,
    color: Red
  };

  for x in range(0., WIDTH) {
    for y in range(0., HEIGHT) {
      let vector = ~Point {
        x: 100.,
        y: (WIDTH / 2. - x),
        z: (HEIGHT / 2. - y)
      };

      // Always 0 :'(
      let closest = get_closest(sphere, eye, vector);

      // // Shadow
      // let inter = Point {
      //   x: eye.x + closest * vector.x,
      //   y: eye.y + closest * vector.y,
      //   z: eye.z + closest * vector.z
      // };
      // let light = Point {
      //   x: spot.pos.x - inter.x,
      //   y: spot.pos.y - inter.y,
      //   z: spot.pos.z - inter.z
      // };

      let inter = ~Point {
        x: (eye.x - sphere.pos.x) + closest * vector.x,
        y: (eye.y - sphere.pos.y) + closest * vector.y,
        z: (eye.z - sphere.pos.z) + closest * vector.z
      };
      let light = ~Point {
        x: spot.pos.x - inter.x,
        y: spot.pos.y - inter.y,
        z: spot.pos.z - inter.z
      };

      pixels[(y * WIDTH + x) as u32] = get_light(sphere, spot, inter, light);
    }
  }

  let img = png::Image {
    width: WIDTH as u32,
    height: HEIGHT as u32,
    color_type: png::RGB8,
    pixels: pixels.flat_map(|&Color { r: r, g: g, b: b }| { ~[r, g, b] })
  };

  png::store_png(&img, &Path::new("out.png"));
}
<commit_msg>Add Shape trait<commit_after>extern mod png;

use std::num::{sqrt, min, max, cos, sin};

static WIDTH: f64 = 800.;
static HEIGHT: f64 = 800.;

struct Color {
  r: u8,
  g: u8,
  b: u8
}

static Red: Color = Color { r: 255, g: 0, b: 0 };
static Green: Color = Color { r: 0, g: 255, b: 0 };
static Blue: Color = Color { r: 0, g: 0, b: 255 };
static White: Color = Color { r: 255, g: 255, b: 255 };
static Black: Color = Color { r: 0, g: 0, b: 0 };

struct Point {
  x: f64,
  y: f64,
  z: f64
}

struct Spot {
  pos: Point,
  color: Color,
}

struct Scene {
  eye: Point,
  spot: Spot,
  sphere: Sphere
}

fn solve_poly(a: f64, b: f64, c: f64) -> f64 {
  let delta = b.pow(&2.0) - 4. * a * c;

  if delta < 0. {
    return 0.;
  }

  let k1 = (-b - sqrt(delta)) / (2. * a);
  let k2 = (-b + sqrt(delta)) / (2. * a);

  return match (k1, k2) {
    (k1, k2) if k1 < 0. && k2 < 0. => 0.,
    (k1, k2) if k2 <= 0. => k1,
    (k1, k2) if k1 <= 0. => k2,
    _ => min(k1, k2)
  }
}

trait Shape {
  fn hit(&self, eye: &Point, vector: &Point) -> f64;
  fn get_perp(&self, inter: &Point) -> ~Point;
}

struct Sphere {
  pos: Point,
  radius: f64,
  shininess: f64,
  color: Color
}

impl Shape for Sphere {
  fn hit(&self, eye: &Point, vector: &Point) -> f64 {
    let a = vector.x.pow(&2.) + vector.y.pow(&2.) + vector.z.pow(&2.);
    let b = 2. * (eye.x * vector.x + eye.y * vector.y + eye.z * vector.z);
    let c = eye.x.pow(&2.) + eye.y.pow(&2.) + eye.z.pow(&2.) - self.radius.pow(&2.);
    return solve_poly(a, b, c);
  }

  fn get_perp(&self, inter: &Point) -> ~Point {
    ~Point { x: inter.x, y: inter.y, z: inter.z }
  }
}

fn get_closest(obj: &Sphere, eye: &Point, vector: &Point) -> f64 {
  let e = ~Point { x: eye.x - obj.pos.x, y: eye.y - obj.pos.y, z: eye.z - obj.pos.z };
  let v = ~Point { x: vector.x, y: vector.y, z: vector.z };
  return obj.hit(e, v);
}

fn get_light(obj: &Sphere, spot: &Spot, inter: &Point, light: &Point) -> Color {
  let perp = obj.get_perp(inter);
  let norme_l = sqrt(light.x.pow(&2.) + light.y.pow(&2.) + light.z.pow(&2.));
  let norme_n = sqrt(perp.x.pow(&2.) + perp.y.pow(&2.) + perp.z.pow(&2.));
  let cos_a = (light.x * perp.x + light.y * perp.y + light.z * perp.z) / (norme_l * norme_n);

  if cos_a <= 0. {
    return Black;
  }

  Color {
    r: ((obj.color.r as f64) * cos_a * (1. - obj.shininess) + (spot.color.r as f64) * cos_a * obj.shininess) as u8,
    g: ((obj.color.g as f64) * cos_a * (1. - obj.shininess) + (spot.color.g as f64) * cos_a * obj.shininess) as u8,
    b: ((obj.color.b as f64) * cos_a * (1. - obj.shininess) + (spot.color.b as f64) * cos_a * obj.shininess) as u8
  }
}

fn main() {
  let mut pixels = ~[Black, ..((WIDTH * HEIGHT) as uint)];

  let eye = ~Point { x: -300., y: 0., z: 200. };
  let spot = ~Spot {
    pos: Point { x: -300., y: 100., z: 200. },
    color: White
  };
  let sphere = ~Sphere {
    pos: Point { x: 0., y: 0., z: 0. },
    radius: 160.,
    shininess: 0.2,
    color: Red
  };

  for x in range(0., WIDTH) {
    for y in range(0., HEIGHT) {
      let vector = ~Point {
        x: 100.,
        y: WIDTH / 2. - x,
        z: HEIGHT / 2. - y
      };

      let closest = get_closest(sphere, eye, vector);

      // // Shadow
      // let inter = Point {
      //   x: eye.x + closest * vector.x,
      //   y: eye.y + closest * vector.y,
      //   z: eye.z + closest * vector.z
      // };
      // let light = Point {
      //   x: spot.pos.x - inter.x,
      //   y: spot.pos.y - inter.y,
      //   z: spot.pos.z - inter.z
      // };

      let inter = ~Point {
        x: (eye.x - sphere.pos.x) + closest * vector.x,
        y: (eye.y - sphere.pos.y) + closest * vector.y,
        z: (eye.z - sphere.pos.z) + closest * vector.z
      };
      let light = ~Point {
        x: spot.pos.x - inter.x,
        y: spot.pos.y - inter.y,
        z: spot.pos.z - inter.z
      };

      pixels[(y * WIDTH + x) as u32] = get_light(sphere, spot, inter, light);
    }
  }

  let img = png::Image {
    width: WIDTH as u32,
    height: HEIGHT as u32,
    color_type: png::RGB8,
    pixels: pixels.flat_map(|&Color { r, g, b }| { ~[r, g, b] })
  };

  png::store_png(&img, &Path::new("out.png"));
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//    https://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 crate::ast_discoverer::Discoveries;
use crate::{
    cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine,
    RebuildDependencyRecorder,
};
use autocxx_parser::{Subclass, SubclassAttrs};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use std::{collections::HashSet, fmt::Display, io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
use syn::{Item, LitStr};

/// Errors which may occur when parsing a Rust source file to discover
/// and interpret include_cxx macros.
#[derive(Debug)]
pub enum ParseError {
    /// Unable to open the source file
    FileOpen(std::io::Error),
    /// The .rs file couldn't be read.
    FileRead(std::io::Error),
    /// The .rs file couldn't be parsed.
    Syntax(syn::Error),
    /// The include CPP macro could not be expanded into
    /// Rust bindings to C++, because of some problem during the conversion
    /// process. This could be anything from a C++ parsing error to some
    /// C++ feature that autocxx can't yet handle and isn't able to skip
    /// over. It could also cover errors in your syntax of the `include_cpp`
    /// macro or the directives inside.
    AutocxxCodegenError(EngineError),
    /// There are two or more `include_cpp` macros with the same
    /// mod name.
    ConflictingModNames,
    ZeroModsForDynamicDiscovery,
    MultipleModsForDynamicDiscovery,
    DiscoveredRustItemsWhenNotInAutoDiscover,
}

impl Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
            ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
            ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
            ParseError::AutocxxCodegenError(err) =>
                write!(f, "Unable to parse include_cpp! macro: {}", err)?,
            ParseError::ConflictingModNames =>
                write!(f, "There are two or more include_cpp! macros with the same output mod name. Use name!")?,
            ParseError::ZeroModsForDynamicDiscovery =>
                write!(f, "This file contains extra information to append to an include_cpp! but no such include_cpp! was found in this file.")?,
            ParseError::MultipleModsForDynamicDiscovery =>
                write!(f, "This file contains extra information to append to an include_cpp! but multiple such include_cpp! declarations were found in this file.")?,
            ParseError::DiscoveredRustItemsWhenNotInAutoDiscover =>
                write!(f, "This file contains extra information to append to an \"extern Rust\" but auto-discover was switched off.")?,
        }
        Ok(())
    }
}

/// Parse a Rust file, and spot any include_cpp macros within it.
pub fn parse_file<P1: AsRef<Path>>(
    rs_file: P1,
    auto_allowlist: bool,
) -> Result<ParsedFile, ParseError> {
    let mut source = String::new();
    let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
    file.read_to_string(&mut source)
        .map_err(ParseError::FileRead)?;
    proc_macro2::fallback::force();
    let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
    parse_file_contents(source, auto_allowlist)
}

fn parse_file_contents(source: syn::File, auto_allowlist: bool) -> Result<ParsedFile, ParseError> {
    let mut results = Vec::new();
    let mut extra_superclasses = Vec::new();
    let mut discoveries = Discoveries::default();
    for item in source.items {
        results.push(match item {
            Item::Macro(mac)
                if mac
                    .mac
                    .path
                    .segments
                    .last()
                    .map(|s| s.ident == "include_cpp")
                    .unwrap_or(false) =>
            {
                Segment::Autocxx(
                    crate::IncludeCppEngine::new_from_syn(mac.mac.clone())
                        .map_err(ParseError::AutocxxCodegenError)?,
                )
            }
            Item::Mod(itm)
                if itm
                    .attrs
                    .iter()
                    .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") =>
            {
                Segment::Cxx(CxxBridge::from(itm))
            }
            Item::Struct(ref its) if auto_allowlist => {
                let attrs = &its.attrs;
                let is_superclass_attr = attrs.iter().find(|attr| {
                    attr.path
                        .segments
                        .last()
                        .map(|seg| seg.ident == "is_subclass")
                        .unwrap_or(false)
                });
                if let Some(is_superclass_attr) = is_superclass_attr {
                    if !is_superclass_attr.tokens.is_empty() {
                        let subclass = its.ident.clone();
                        let args: SubclassAttrs = is_superclass_attr
                            .parse_args()
                            .map_err(ParseError::Syntax)?;
                        if let Some(superclass) = args.superclass {
                            extra_superclasses.push(Subclass {
                                superclass,
                                subclass,
                            })
                        }
                    }
                }
                discoveries.search_item(&item);
                Segment::Other(item)
            }
            _ => {
                discoveries.search_item(&item);
                Segment::Other(item)
            }
        });
    }
    if !auto_allowlist {
        if !discoveries.extern_rust_types.is_empty() || !discoveries.extern_rust_funs.is_empty() {
            return Err(ParseError::DiscoveredRustItemsWhenNotInAutoDiscover);
        }
    }
    if !extra_superclasses.is_empty() || !discoveries.is_empty() {
        let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
            Segment::Autocxx(engine) => Some(engine),
            _ => None,
        });
        let our_seg = autocxx_seg_iterator.next();
        match our_seg {
            None => return Err(ParseError::ZeroModsForDynamicDiscovery),
            Some(engine) => {
                engine
                    .config_mut()
                    .subclasses
                    .append(&mut extra_superclasses);
                if auto_allowlist {
                    for cpp in discoveries.cpp_list {
                        engine
                            .config_mut()
                            .allowlist
                            .push(LitStr::new(&cpp, Span::call_site()))
                            .map_err(ParseError::Syntax)?;
                    }
                }
                engine
                    .config_mut()
                    .extern_rust_funs
                    .append(&mut discoveries.extern_rust_funs);
                engine
                    .config_mut()
                    .rust_types
                    .append(&mut discoveries.extern_rust_types);
            }
        }
        if autocxx_seg_iterator.next().is_some() {
            return Err(ParseError::MultipleModsForDynamicDiscovery);
        }
    }
    let autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
        Segment::Autocxx(engine) => Some(engine),
        _ => None,
    });
    for seg in autocxx_seg_iterator {
        seg.config.confirm_complete().map_err(ParseError::Syntax)?;
    }
    Ok(ParsedFile(results))
}

/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines',
/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp
/// macros within this file. Also contains `syn::Item` structures for all
/// the rest of the Rust code, such that it can be reconstituted if necessary.
pub struct ParsedFile(Vec<Segment>);

#[allow(clippy::large_enum_variant)]
enum Segment {
    Autocxx(IncludeCppEngine),
    Cxx(CxxBridge),
    Other(Item),
}

pub trait CppBuildable {
    fn generate_h_and_cxx(
        &self,
        suppress_system_headers: bool,
    ) -> Result<GeneratedCpp, cxx_gen::Error>;
}

impl ParsedFile {
    /// Get all the autocxxes in this parsed file.
    pub fn get_rs_buildables(&self) -> impl Iterator<Item = &IncludeCppEngine> {
        self.0.iter().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            _ => None,
        })
    }

    /// Get all items which can result in C++ code
    pub fn get_cpp_buildables(&self) -> impl Iterator<Item = &dyn CppBuildable> {
        self.0.iter().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp as &dyn CppBuildable),
            Segment::Cxx(cxxbridge) => Some(cxxbridge as &dyn CppBuildable),
            _ => None,
        })
    }

    fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> {
        self.0.iter_mut().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            _ => None,
        })
    }

    pub fn include_dirs(&self) -> impl Iterator<Item = &PathBuf> {
        self.0
            .iter()
            .filter_map(|s| match s {
                Segment::Autocxx(includecpp) => Some(includecpp.include_dirs()),
                _ => None,
            })
            .flatten()
    }

    pub fn resolve_all(
        &mut self,
        autocxx_inc: Vec<PathBuf>,
        extra_clang_args: &[&str],
        dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
        suppress_system_headers: bool,
    ) -> Result<(), ParseError> {
        let mut mods_found = HashSet::new();
        let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
            dep_recorder.map(Rc::from);
        for include_cpp in self.get_autocxxes_mut() {
            #[allow(clippy::manual_map)] // because of dyn shenanigans
            let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
            {
                None => None,
                Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
                    inner_dep_recorder.clone(),
                ))),
            };
            if !mods_found.insert(include_cpp.get_mod_name()) {
                return Err(ParseError::ConflictingModNames);
            }
            include_cpp
                .generate(
                    autocxx_inc.clone(),
                    extra_clang_args,
                    dep_recorder,
                    suppress_system_headers,
                )
                .map_err(ParseError::AutocxxCodegenError)?
        }
        Ok(())
    }
}

impl ToTokens for ParsedFile {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for seg in &self.0 {
            match seg {
                Segment::Other(item) => item.to_tokens(tokens),
                Segment::Autocxx(autocxx) => {
                    let these_tokens = autocxx.generate_rs();
                    tokens.extend(these_tokens);
                }
                Segment::Cxx(itemmod) => itemmod.to_tokens(tokens),
            }
        }
    }
}

/// Shenanigans required to share the same RebuildDependencyRecorder
/// with all of the include_cpp instances in this one file.
#[derive(Debug, Clone)]
struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);

impl CompositeDepRecorder {
    fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
        CompositeDepRecorder(inner)
    }
}

impl UnwindSafe for CompositeDepRecorder {}

impl RebuildDependencyRecorder for CompositeDepRecorder {
    fn record_header_file_dependency(&self, filename: &str) {
        self.0.record_header_file_dependency(filename);
    }
}
<commit_msg>Fix bug with multiple mods.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//    https://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 crate::ast_discoverer::Discoveries;
use crate::{
    cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine,
    RebuildDependencyRecorder,
};
use autocxx_parser::{Subclass, SubclassAttrs};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use std::{collections::HashSet, fmt::Display, io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
use syn::{Item, LitStr};

/// Errors which may occur when parsing a Rust source file to discover
/// and interpret include_cxx macros.
#[derive(Debug)]
pub enum ParseError {
    /// Unable to open the source file
    FileOpen(std::io::Error),
    /// The .rs file couldn't be read.
    FileRead(std::io::Error),
    /// The .rs file couldn't be parsed.
    Syntax(syn::Error),
    /// The include CPP macro could not be expanded into
    /// Rust bindings to C++, because of some problem during the conversion
    /// process. This could be anything from a C++ parsing error to some
    /// C++ feature that autocxx can't yet handle and isn't able to skip
    /// over. It could also cover errors in your syntax of the `include_cpp`
    /// macro or the directives inside.
    AutocxxCodegenError(EngineError),
    /// There are two or more `include_cpp` macros with the same
    /// mod name.
    ConflictingModNames,
    ZeroModsForDynamicDiscovery,
    MultipleModsForDynamicDiscovery,
    DiscoveredRustItemsWhenNotInAutoDiscover,
}

impl Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
            ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
            ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
            ParseError::AutocxxCodegenError(err) =>
                write!(f, "Unable to parse include_cpp! macro: {}", err)?,
            ParseError::ConflictingModNames =>
                write!(f, "There are two or more include_cpp! macros with the same output mod name. Use name!")?,
            ParseError::ZeroModsForDynamicDiscovery =>
                write!(f, "This file contains extra information to append to an include_cpp! but no such include_cpp! was found in this file.")?,
            ParseError::MultipleModsForDynamicDiscovery =>
                write!(f, "This file contains extra information to append to an include_cpp! but multiple such include_cpp! declarations were found in this file.")?,
            ParseError::DiscoveredRustItemsWhenNotInAutoDiscover =>
                write!(f, "This file contains extra information to append to an \"extern Rust\" but auto-discover was switched off.")?,
        }
        Ok(())
    }
}

/// Parse a Rust file, and spot any include_cpp macros within it.
pub fn parse_file<P1: AsRef<Path>>(
    rs_file: P1,
    auto_allowlist: bool,
) -> Result<ParsedFile, ParseError> {
    let mut source = String::new();
    let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
    file.read_to_string(&mut source)
        .map_err(ParseError::FileRead)?;
    proc_macro2::fallback::force();
    let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
    parse_file_contents(source, auto_allowlist)
}

fn parse_file_contents(source: syn::File, auto_allowlist: bool) -> Result<ParsedFile, ParseError> {
    let mut results = Vec::new();
    let mut extra_superclasses = Vec::new();
    let mut discoveries = Discoveries::default();
    for item in source.items {
        results.push(match item {
            Item::Macro(mac)
                if mac
                    .mac
                    .path
                    .segments
                    .last()
                    .map(|s| s.ident == "include_cpp")
                    .unwrap_or(false) =>
            {
                Segment::Autocxx(
                    crate::IncludeCppEngine::new_from_syn(mac.mac.clone())
                        .map_err(ParseError::AutocxxCodegenError)?,
                )
            }
            Item::Mod(itm)
                if itm
                    .attrs
                    .iter()
                    .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") =>
            {
                Segment::Cxx(CxxBridge::from(itm))
            }
            Item::Struct(ref its) if auto_allowlist => {
                let attrs = &its.attrs;
                let is_superclass_attr = attrs.iter().find(|attr| {
                    attr.path
                        .segments
                        .last()
                        .map(|seg| seg.ident == "is_subclass")
                        .unwrap_or(false)
                });
                if let Some(is_superclass_attr) = is_superclass_attr {
                    if !is_superclass_attr.tokens.is_empty() {
                        let subclass = its.ident.clone();
                        let args: SubclassAttrs = is_superclass_attr
                            .parse_args()
                            .map_err(ParseError::Syntax)?;
                        if let Some(superclass) = args.superclass {
                            extra_superclasses.push(Subclass {
                                superclass,
                                subclass,
                            })
                        }
                    }
                }
                discoveries.search_item(&item);
                Segment::Other(item)
            }
            _ => {
                discoveries.search_item(&item);
                Segment::Other(item)
            }
        });
    }
    if !auto_allowlist {
        if !discoveries.extern_rust_types.is_empty() || !discoveries.extern_rust_funs.is_empty() {
            return Err(ParseError::DiscoveredRustItemsWhenNotInAutoDiscover);
        }
    }
    if !extra_superclasses.is_empty() || (auto_allowlist && !discoveries.is_empty()) {
        let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
            Segment::Autocxx(engine) => Some(engine),
            _ => None,
        });
        let our_seg = autocxx_seg_iterator.next();
        match our_seg {
            None => return Err(ParseError::ZeroModsForDynamicDiscovery),
            Some(engine) => {
                engine
                    .config_mut()
                    .subclasses
                    .append(&mut extra_superclasses);
                if auto_allowlist {
                    for cpp in discoveries.cpp_list {
                        engine
                            .config_mut()
                            .allowlist
                            .push(LitStr::new(&cpp, Span::call_site()))
                            .map_err(ParseError::Syntax)?;
                    }
                }
                engine
                    .config_mut()
                    .extern_rust_funs
                    .append(&mut discoveries.extern_rust_funs);
                engine
                    .config_mut()
                    .rust_types
                    .append(&mut discoveries.extern_rust_types);
            }
        }
        if autocxx_seg_iterator.next().is_some() {
            return Err(ParseError::MultipleModsForDynamicDiscovery);
        }
    }
    let autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
        Segment::Autocxx(engine) => Some(engine),
        _ => None,
    });
    for seg in autocxx_seg_iterator {
        seg.config.confirm_complete().map_err(ParseError::Syntax)?;
    }
    Ok(ParsedFile(results))
}

/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines',
/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp
/// macros within this file. Also contains `syn::Item` structures for all
/// the rest of the Rust code, such that it can be reconstituted if necessary.
pub struct ParsedFile(Vec<Segment>);

#[allow(clippy::large_enum_variant)]
enum Segment {
    Autocxx(IncludeCppEngine),
    Cxx(CxxBridge),
    Other(Item),
}

pub trait CppBuildable {
    fn generate_h_and_cxx(
        &self,
        suppress_system_headers: bool,
    ) -> Result<GeneratedCpp, cxx_gen::Error>;
}

impl ParsedFile {
    /// Get all the autocxxes in this parsed file.
    pub fn get_rs_buildables(&self) -> impl Iterator<Item = &IncludeCppEngine> {
        self.0.iter().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            _ => None,
        })
    }

    /// Get all items which can result in C++ code
    pub fn get_cpp_buildables(&self) -> impl Iterator<Item = &dyn CppBuildable> {
        self.0.iter().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp as &dyn CppBuildable),
            Segment::Cxx(cxxbridge) => Some(cxxbridge as &dyn CppBuildable),
            _ => None,
        })
    }

    fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> {
        self.0.iter_mut().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            _ => None,
        })
    }

    pub fn include_dirs(&self) -> impl Iterator<Item = &PathBuf> {
        self.0
            .iter()
            .filter_map(|s| match s {
                Segment::Autocxx(includecpp) => Some(includecpp.include_dirs()),
                _ => None,
            })
            .flatten()
    }

    pub fn resolve_all(
        &mut self,
        autocxx_inc: Vec<PathBuf>,
        extra_clang_args: &[&str],
        dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
        suppress_system_headers: bool,
    ) -> Result<(), ParseError> {
        let mut mods_found = HashSet::new();
        let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
            dep_recorder.map(Rc::from);
        for include_cpp in self.get_autocxxes_mut() {
            #[allow(clippy::manual_map)] // because of dyn shenanigans
            let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
            {
                None => None,
                Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
                    inner_dep_recorder.clone(),
                ))),
            };
            if !mods_found.insert(include_cpp.get_mod_name()) {
                return Err(ParseError::ConflictingModNames);
            }
            include_cpp
                .generate(
                    autocxx_inc.clone(),
                    extra_clang_args,
                    dep_recorder,
                    suppress_system_headers,
                )
                .map_err(ParseError::AutocxxCodegenError)?
        }
        Ok(())
    }
}

impl ToTokens for ParsedFile {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for seg in &self.0 {
            match seg {
                Segment::Other(item) => item.to_tokens(tokens),
                Segment::Autocxx(autocxx) => {
                    let these_tokens = autocxx.generate_rs();
                    tokens.extend(these_tokens);
                }
                Segment::Cxx(itemmod) => itemmod.to_tokens(tokens),
            }
        }
    }
}

/// Shenanigans required to share the same RebuildDependencyRecorder
/// with all of the include_cpp instances in this one file.
#[derive(Debug, Clone)]
struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);

impl CompositeDepRecorder {
    fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
        CompositeDepRecorder(inner)
    }
}

impl UnwindSafe for CompositeDepRecorder {}

impl RebuildDependencyRecorder for CompositeDepRecorder {
    fn record_header_file_dependency(&self, filename: &str) {
        self.0.record_header_file_dependency(filename);
    }
}
<|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/. */

//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
//! We implement the prefixed `@-moz-document`.

use cssparser::{Parser, Token, SourceLocation};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use media_queries::Device;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use stylesheets::CssRules;
use values::CssUrl;

#[derive(Debug)]
/// A @-moz-document rule
pub struct DocumentRule {
    /// The parsed condition
    pub condition: DocumentCondition,
    /// Child rules
    pub rules: Arc<Locked<CssRules>>,
    /// The line and column of the rule's source code.
    pub source_location: SourceLocation,
}

impl DocumentRule {
    /// Measure heap usage.
    #[cfg(feature = "gecko")]
    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
        // Measurement of other fields may be added later.
        self.rules.unconditional_shallow_size_of(ops) +
            self.rules.read_with(guard).size_of(guard, ops)
    }
}

impl ToCssWithGuard for DocumentRule {
    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
        dest.write_str("@-moz-document ")?;
        self.condition.to_css(&mut CssWriter::new(dest))?;
        dest.write_str(" {")?;
        for rule in self.rules.read_with(guard).0.iter() {
            dest.write_str(" ")?;
            rule.to_css(guard, dest)?;
        }
        dest.write_str(" }")
    }
}

impl DeepCloneWithLock for DocumentRule {
    /// Deep clones this DocumentRule.
    fn deep_clone_with_lock(
        &self,
        lock: &SharedRwLock,
        guard: &SharedRwLockReadGuard,
        params: &DeepCloneParams,
    ) -> Self {
        let rules = self.rules.read_with(guard);
        DocumentRule {
            condition: self.condition.clone(),
            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
            source_location: self.source_location.clone(),
        }
    }
}

/// A URL matching function for a `@document` rule's condition.
#[derive(Clone, Debug, ToCss)]
pub enum UrlMatchingFunction {
    /// Exact URL matching function. It evaluates to true whenever the
    /// URL of the document being styled is exactly the URL given.
    Url(CssUrl),
    /// URL prefix matching function. It evaluates to true whenever the
    /// URL of the document being styled has the argument to the
    /// function as an initial substring (which is true when the two
    /// strings are equal). When the argument is the empty string,
    /// it evaluates to true for all documents.
    #[css(function)]
    UrlPrefix(String),
    /// Domain matching function. It evaluates to true whenever the URL
    /// of the document being styled has a host subcomponent and that
    /// host subcomponent is exactly the argument to the ‘domain()’
    /// function or a final substring of the host component is a
    /// period (U+002E) immediately followed by the argument to the
    /// ‘domain()’ function.
    #[css(function)]
    Domain(String),
    /// Regular expression matching function. It evaluates to true
    /// whenever the regular expression matches the entirety of the URL
    /// of the document being styled.
    #[css(function)]
    Regexp(String),
}

macro_rules! parse_quoted_or_unquoted_string {
    ($input:ident, $url_matching_function:expr) => {
        $input.parse_nested_block(|input| {
            let start = input.position();
            input.parse_entirely(|input| {
                let location = input.current_source_location();
                match *input.next()? {
                    Token::QuotedString(ref value) => {
                        Ok($url_matching_function(value.as_ref().to_owned()))
                    },
                    ref t => Err(location.new_unexpected_token_error(t.clone())),
                }
            }).or_else(|_: ParseError| {
                while let Ok(_) = input.next() {}
                Ok($url_matching_function(input.slice_from(start).to_string()))
            })
        })
    }
}

impl UrlMatchingFunction {
    /// Parse a URL matching function for a`@document` rule's condition.
    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
        -> Result<UrlMatchingFunction, ParseError<'i>> {
        if input.try(|input| input.expect_function_matching("url-prefix")).is_ok() {
            parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::UrlPrefix)
        } else if input.try(|input| input.expect_function_matching("domain")).is_ok() {
            parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::Domain)
        } else if input.try(|input| input.expect_function_matching("regexp")).is_ok() {
            input.parse_nested_block(|input| {
                Ok(UrlMatchingFunction::Regexp(input.expect_string()?.as_ref().to_owned()))
            })
        } else if let Ok(url) = input.try(|input| CssUrl::parse(context, input)) {
            Ok(UrlMatchingFunction::Url(url))
        } else {
            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }
    }

    #[cfg(feature = "gecko")]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, device: &Device) -> bool {
        use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
        use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
        use nsstring::nsCStr;

        let func = match *self {
            UrlMatchingFunction::Url(_) => GeckoUrlMatchingFunction::eURL,
            UrlMatchingFunction::UrlPrefix(_) => GeckoUrlMatchingFunction::eURLPrefix,
            UrlMatchingFunction::Domain(_) => GeckoUrlMatchingFunction::eDomain,
            UrlMatchingFunction::Regexp(_) => GeckoUrlMatchingFunction::eRegExp,
        };

        let pattern = nsCStr::from(match *self {
            UrlMatchingFunction::Url(ref url) => url.as_str(),
            UrlMatchingFunction::UrlPrefix(ref pat) |
            UrlMatchingFunction::Domain(ref pat) |
            UrlMatchingFunction::Regexp(ref pat) => pat,
        });
        unsafe {
            Gecko_DocumentRule_UseForPresentation(device.pres_context(), &*pattern, func)
        }
    }

    #[cfg(not(feature = "gecko"))]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, _: &Device) -> bool {
        false
    }
}

/// A `@document` rule's condition.
///
/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>
///
/// The `@document` rule's condition is written as a comma-separated list of
/// URL matching functions, and the condition evaluates to true whenever any
/// one of those functions evaluates to true.
#[css(comma)]
#[derive(Clone, Debug, ToCss)]
pub struct DocumentCondition(#[css(iterable)] Vec<UrlMatchingFunction>);

impl DocumentCondition {
    /// Parse a document condition.
    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
        -> Result<Self, ParseError<'i>> {
        input.parse_comma_separated(|input| UrlMatchingFunction::parse(context, input))
             .map(DocumentCondition)
    }

    /// Evaluate a document condition.
    pub fn evaluate(&self, device: &Device) -> bool {
        self.0.iter().any(|ref url_matching_function|
            url_matching_function.evaluate(device)
        )
    }
}
<commit_msg>style: Cleanup @-moz-document parsing a bit.<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/. */

//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
//! We implement the prefixed `@-moz-document`.

use cssparser::{Parser, SourceLocation};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use media_queries::Device;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{CssWriter, ParseError, ToCss};
use stylesheets::CssRules;
use values::CssUrl;

#[derive(Debug)]
/// A @-moz-document rule
pub struct DocumentRule {
    /// The parsed condition
    pub condition: DocumentCondition,
    /// Child rules
    pub rules: Arc<Locked<CssRules>>,
    /// The line and column of the rule's source code.
    pub source_location: SourceLocation,
}

impl DocumentRule {
    /// Measure heap usage.
    #[cfg(feature = "gecko")]
    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
        // Measurement of other fields may be added later.
        self.rules.unconditional_shallow_size_of(ops) +
            self.rules.read_with(guard).size_of(guard, ops)
    }
}

impl ToCssWithGuard for DocumentRule {
    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
        dest.write_str("@-moz-document ")?;
        self.condition.to_css(&mut CssWriter::new(dest))?;
        dest.write_str(" {")?;
        for rule in self.rules.read_with(guard).0.iter() {
            dest.write_str(" ")?;
            rule.to_css(guard, dest)?;
        }
        dest.write_str(" }")
    }
}

impl DeepCloneWithLock for DocumentRule {
    /// Deep clones this DocumentRule.
    fn deep_clone_with_lock(
        &self,
        lock: &SharedRwLock,
        guard: &SharedRwLockReadGuard,
        params: &DeepCloneParams,
    ) -> Self {
        let rules = self.rules.read_with(guard);
        DocumentRule {
            condition: self.condition.clone(),
            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
            source_location: self.source_location.clone(),
        }
    }
}

/// A URL matching function for a `@document` rule's condition.
#[derive(Clone, Debug, ToCss)]
pub enum UrlMatchingFunction {
    /// Exact URL matching function. It evaluates to true whenever the
    /// URL of the document being styled is exactly the URL given.
    Url(CssUrl),
    /// URL prefix matching function. It evaluates to true whenever the
    /// URL of the document being styled has the argument to the
    /// function as an initial substring (which is true when the two
    /// strings are equal). When the argument is the empty string,
    /// it evaluates to true for all documents.
    #[css(function)]
    UrlPrefix(String),
    /// Domain matching function. It evaluates to true whenever the URL
    /// of the document being styled has a host subcomponent and that
    /// host subcomponent is exactly the argument to the ‘domain()’
    /// function or a final substring of the host component is a
    /// period (U+002E) immediately followed by the argument to the
    /// ‘domain()’ function.
    #[css(function)]
    Domain(String),
    /// Regular expression matching function. It evaluates to true
    /// whenever the regular expression matches the entirety of the URL
    /// of the document being styled.
    #[css(function)]
    Regexp(String),
}

macro_rules! parse_quoted_or_unquoted_string {
    ($input:ident, $url_matching_function:expr) => {
        $input.parse_nested_block(|input| {
            let start = input.position();
            input.parse_entirely(|input| {
                let string = input.expect_string()?;
                Ok($url_matching_function(string.as_ref().to_owned()))
            }).or_else(|_: ParseError| {
                while let Ok(_) = input.next() {}
                Ok($url_matching_function(input.slice_from(start).to_string()))
            })
        })
    }
}

impl UrlMatchingFunction {
    /// Parse a URL matching function for a`@document` rule's condition.
    pub fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try(|input| input.expect_function_matching("url-prefix")).is_ok() {
            return parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::UrlPrefix)
        }

        if input.try(|input| input.expect_function_matching("domain")).is_ok() {
            return parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::Domain)
        }

        if input.try(|input| input.expect_function_matching("regexp")).is_ok() {
            return input.parse_nested_block(|input| {
                Ok(UrlMatchingFunction::Regexp(input.expect_string()?.as_ref().to_owned()))
            });
        }

        let url = CssUrl::parse(context, input)?;
        Ok(UrlMatchingFunction::Url(url))
    }

    #[cfg(feature = "gecko")]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, device: &Device) -> bool {
        use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
        use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
        use nsstring::nsCStr;

        let func = match *self {
            UrlMatchingFunction::Url(_) => GeckoUrlMatchingFunction::eURL,
            UrlMatchingFunction::UrlPrefix(_) => GeckoUrlMatchingFunction::eURLPrefix,
            UrlMatchingFunction::Domain(_) => GeckoUrlMatchingFunction::eDomain,
            UrlMatchingFunction::Regexp(_) => GeckoUrlMatchingFunction::eRegExp,
        };

        let pattern = nsCStr::from(match *self {
            UrlMatchingFunction::Url(ref url) => url.as_str(),
            UrlMatchingFunction::UrlPrefix(ref pat) |
            UrlMatchingFunction::Domain(ref pat) |
            UrlMatchingFunction::Regexp(ref pat) => pat,
        });
        unsafe {
            Gecko_DocumentRule_UseForPresentation(device.pres_context(), &*pattern, func)
        }
    }

    #[cfg(not(feature = "gecko"))]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, _: &Device) -> bool {
        false
    }
}

/// A `@document` rule's condition.
///
/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>
///
/// The `@document` rule's condition is written as a comma-separated list of
/// URL matching functions, and the condition evaluates to true whenever any
/// one of those functions evaluates to true.
#[css(comma)]
#[derive(Clone, Debug, ToCss)]
pub struct DocumentCondition(#[css(iterable)] Vec<UrlMatchingFunction>);

impl DocumentCondition {
    /// Parse a document condition.
    pub fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        input.parse_comma_separated(|input| UrlMatchingFunction::parse(context, input))
             .map(DocumentCondition)
    }

    /// Evaluate a document condition.
    pub fn evaluate(&self, device: &Device) -> bool {
        self.0.iter().any(|url_matching_function| {
            url_matching_function.evaluate(device)
        })
    }
}
<|endoftext|>