<commit_before>use schema::*;
use diesel::*;

#[test]
fn one_to_many_returns_query_source_for_association() {
    let (connection, sean, tess, _) = conn_with_test_data();

    let seans_posts = posts::table.filter(posts::user_id.eq(sean.id))
        .load::<Post>(&connection)
        .unwrap();
    let tess_posts = posts::table.filter(posts::user_id.eq(tess.id))
        .load::<Post>(&connection)
        .unwrap();

    let found_posts: Vec<_> = Post::belonging_to(&sean).load(&connection).unwrap();
    assert_eq!(seans_posts, found_posts);

    let found_posts: Vec<_> = Post::belonging_to(&tess).load(&connection).unwrap();
    assert_eq!(tess_posts, found_posts);
}

#[test]
fn eager_loading_associations_for_multiple_records() {
    let (connection, sean, tess, _) = conn_with_test_data();

    let users = vec![sean.clone(), tess.clone()];
    let posts = Post::belonging_to(&users).load::<Post>(&connection).unwrap()
        .grouped_by(&users);
    let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

    let seans_posts = Post::belonging_to(&sean).load(&connection).unwrap();
    let tess_posts = Post::belonging_to(&tess).load(&connection).unwrap();
    let expected_data = vec![(sean, seans_posts), (tess, tess_posts)];
    assert_eq!(expected_data, users_and_posts);
}

mod eager_loading_with_string_keys {
    use diesel::*;
    use diesel::connection::SimpleConnection;
    use schema::{connection, drop_table_cascade};

    table! { users { id -> Text, } }
    table! { posts { id -> Text, user_id -> Text, } }

    #[derive(Queryable, Identifiable, Debug, PartialEq, Clone)]
    pub struct User {
        id: String,
    }

    #[derive(Queryable, Identifiable, Debug, PartialEq, Clone, Associations)]
    #[belongs_to(User)]
    pub struct Post {
        id: String,
        user_id: String,
    }

    #[test]
    #[cfg(not(feature="mysql"))] // FIXME: Figure out how to handle tests that modify schema
    fn eager_loading_associations_for_multiple_records() {
        let connection = connection();
        drop_table_cascade(&connection, "users");
        drop_table_cascade(&connection, "posts");
        drop_table_cascade(&connection, "fk_doesnt_reference_pk");
        connection.batch_execute(r#"
            CREATE TABLE users (id TEXT PRIMARY KEY NOT NULL);
            CREATE TABLE posts (id TEXT PRIMARY KEY NOT NULL, user_id TEXT NOT NULL);
            INSERT INTO users (id) VALUES ('Sean'), ('Tess');
            INSERT INTO posts (id, user_id) VALUES ('Hello', 'Sean'), ('World', 'Sean'), ('Hello 2', 'Tess');
        "#).unwrap();
        let sean = User { id: "Sean".into() };
        let tess = User { id: "Tess".into() };

        let users = vec![sean.clone(), tess.clone()];
        let posts = Post::belonging_to(&users).load::<Post>(&connection).unwrap()
            .grouped_by(&users);
        let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

        let seans_posts = Post::belonging_to(&sean).load(&connection).unwrap();
        let tess_posts = Post::belonging_to(&tess).load(&connection).unwrap();
        let expected_data = vec![(sean, seans_posts), (tess, tess_posts)];
        assert_eq!(expected_data, users_and_posts);
    }
}

#[test]
fn grouping_associations_maintains_ordering() {
    let (connection, sean, tess, _) = conn_with_test_data();

    let users = vec![sean.clone(), tess.clone()];
    let posts = Post::belonging_to(&users)
        .order(posts::title.desc())
        .load::<Post>(&connection).unwrap()
        .grouped_by(&users);
    let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

    let seans_posts = Post::belonging_to(&sean).order(posts::title.desc()).load(&connection).unwrap();
    let tess_posts = Post::belonging_to(&tess).order(posts::title.desc()).load(&connection).unwrap();
    let expected_data = vec![(sean.clone(), seans_posts), (tess.clone(), tess_posts)];
    assert_eq!(expected_data, users_and_posts);

    // Test when sorted manually
    let users = vec![sean.clone(), tess.clone()];
    let mut posts = Post::belonging_to(&users)
        .load::<Post>(&connection).unwrap();
    posts.sort_by(|a, b| b.title.cmp(&a.title));
    let posts = posts.grouped_by(&users);
    let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

    assert_eq!(expected_data, users_and_posts);
}

#[test]
fn associations_can_be_grouped_multiple_levels_deep() {
    // I'm manually defining the data rather than loding from the database here,
    // as it makes the tests *way* more readable if I omit setup here. This is
    // the equivalent to.
    //
    // ```rust
    // let users = users.load::<User>().unwrap();
    // let posts = Post::belonging_to(&users).load::<Post>().unwrap();
    // let comments = Comment::belonging_to(&users).load::<Comment>().unwrap();
    // ```
    let users = vec![User::new(1, "Sean"), User::new(2, "Tess")];
    let posts = vec![Post::new(1, 1, "Hello", None), Post::new(2, 2, "World", None), Post::new(3, 1, "Hello 2", None)];
    let comments = vec![Comment::new(1, 3, "LOL"), Comment::new(2, 1, "UR dumb"), Comment::new(3, 3, "Funny")]; // Never read the comments

    let expected_data = vec![
        (users[0].clone(), vec![(posts[0].clone(), vec![comments[1].clone()]), (posts[2].clone(), vec![comments[0].clone(), comments[2].clone()])]),
        (users[1].clone(), vec![(posts[1].clone(), vec![])]),
    ];

    let comments = comments.grouped_by(&posts);
    let posts_and_comments = posts.into_iter().zip(comments).grouped_by(&users);
    let data = users.into_iter().zip(posts_and_comments).collect::<Vec<_>>();

    assert_eq!(expected_data, data);
}

#[test]
fn self_referencing_associations() {
    #[derive(Insertable, Queryable, Associations, Identifiable)]
    #[derive(Debug, Clone, Copy, PartialEq)]
    #[table_name="trees"]
    #[belongs_to(Tree, foreign_key="parent_id")]
    struct Tree {
        id: i32,
        parent_id: Option<i32>,
    }

    let conn = connection();
    let test_data = vec![
        Tree { id: 1, parent_id: None },
        Tree { id: 2, parent_id: None },
        Tree { id: 3, parent_id: Some(1) },
        Tree { id: 4, parent_id: Some(2) },
        Tree { id: 5, parent_id: Some(1) },
    ];
    insert(&test_data).into(trees::table)
        .execute(&conn).unwrap();

    let parents = trees::table.filter(trees::parent_id.is_null())
        .load::<Tree>(&conn).unwrap();
    let children = Tree::belonging_to(&parents)
        .load::<Tree>(&conn).unwrap();
    let children = children.grouped_by(&parents);
    let data = parents.into_iter().zip(children).collect::<Vec<_>>();

    let expected_data = vec![
        (test_data[0], vec![test_data[2], test_data[4]]),
        (test_data[1], vec![test_data[3]]),
    ];
    assert_eq!(expected_data, data);
}

fn conn_with_test_data() -> (TestConnection, User, User, User) {
    let connection = connection_with_sean_and_tess_in_users_table();
    insert(&NewUser::new("Jim", None)).into(users::table).execute(&connection).unwrap();

    let sean = find_user_by_name("Sean", &connection);
    let tess = find_user_by_name("Tess", &connection);
    let jim = find_user_by_name("Jim", &connection);
    let new_posts = vec![sean.new_post("Hello", None), sean.new_post("World", None)];
    insert(&new_posts).into(posts::table).execute(&connection).unwrap();
    let new_posts = vec![tess.new_post("Hello 2", None), tess.new_post("World 2", None)];
    insert(&new_posts).into(posts::table).execute(&connection).unwrap();
    let new_posts = vec![jim.new_post("Hello 3", None), jim.new_post("World 3", None)];
    insert(&new_posts).into(posts::table).execute(&connection).unwrap();

    (connection, sean, tess, jim)
}
<commit_msg>Add a full test case for custom foreign keys<commit_after>use schema::*;
use diesel::*;

#[test]
fn one_to_many_returns_query_source_for_association() {
    let (connection, sean, tess, _) = conn_with_test_data();

    let seans_posts = posts::table.filter(posts::user_id.eq(sean.id))
        .load::<Post>(&connection)
        .unwrap();
    let tess_posts = posts::table.filter(posts::user_id.eq(tess.id))
        .load::<Post>(&connection)
        .unwrap();

    let found_posts: Vec<_> = Post::belonging_to(&sean).load(&connection).unwrap();
    assert_eq!(seans_posts, found_posts);

    let found_posts: Vec<_> = Post::belonging_to(&tess).load(&connection).unwrap();
    assert_eq!(tess_posts, found_posts);
}

#[test]
fn eager_loading_associations_for_multiple_records() {
    let (connection, sean, tess, _) = conn_with_test_data();

    let users = vec![sean.clone(), tess.clone()];
    let posts = Post::belonging_to(&users).load::<Post>(&connection).unwrap()
        .grouped_by(&users);
    let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

    let seans_posts = Post::belonging_to(&sean).load(&connection).unwrap();
    let tess_posts = Post::belonging_to(&tess).load(&connection).unwrap();
    let expected_data = vec![(sean, seans_posts), (tess, tess_posts)];
    assert_eq!(expected_data, users_and_posts);
}

mod eager_loading_with_string_keys {
    use diesel::*;
    use diesel::connection::SimpleConnection;
    use schema::{connection, drop_table_cascade};

    table! { users { id -> Text, } }
    table! { posts { id -> Text, user_id -> Text, } }

    #[derive(Queryable, Identifiable, Debug, PartialEq, Clone)]
    pub struct User {
        id: String,
    }

    #[derive(Queryable, Identifiable, Debug, PartialEq, Clone, Associations)]
    #[belongs_to(User)]
    pub struct Post {
        id: String,
        user_id: String,
    }

    #[test]
    #[cfg(not(feature="mysql"))] // FIXME: Figure out how to handle tests that modify schema
    fn eager_loading_associations_for_multiple_records() {
        let connection = connection();
        drop_table_cascade(&connection, "users");
        drop_table_cascade(&connection, "posts");
        drop_table_cascade(&connection, "fk_doesnt_reference_pk");
        connection.batch_execute(r#"
            CREATE TABLE users (id TEXT PRIMARY KEY NOT NULL);
            CREATE TABLE posts (id TEXT PRIMARY KEY NOT NULL, user_id TEXT NOT NULL);
            INSERT INTO users (id) VALUES ('Sean'), ('Tess');
            INSERT INTO posts (id, user_id) VALUES ('Hello', 'Sean'), ('World', 'Sean'), ('Hello 2', 'Tess');
        "#).unwrap();
        let sean = User { id: "Sean".into() };
        let tess = User { id: "Tess".into() };

        let users = vec![sean.clone(), tess.clone()];
        let posts = Post::belonging_to(&users).load::<Post>(&connection).unwrap()
            .grouped_by(&users);
        let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

        let seans_posts = Post::belonging_to(&sean).load(&connection).unwrap();
        let tess_posts = Post::belonging_to(&tess).load(&connection).unwrap();
        let expected_data = vec![(sean, seans_posts), (tess, tess_posts)];
        assert_eq!(expected_data, users_and_posts);
    }
}

#[test]
fn grouping_associations_maintains_ordering() {
    let (connection, sean, tess, _) = conn_with_test_data();

    let users = vec![sean.clone(), tess.clone()];
    let posts = Post::belonging_to(&users)
        .order(posts::title.desc())
        .load::<Post>(&connection).unwrap()
        .grouped_by(&users);
    let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

    let seans_posts = Post::belonging_to(&sean).order(posts::title.desc()).load(&connection).unwrap();
    let tess_posts = Post::belonging_to(&tess).order(posts::title.desc()).load(&connection).unwrap();
    let expected_data = vec![(sean.clone(), seans_posts), (tess.clone(), tess_posts)];
    assert_eq!(expected_data, users_and_posts);

    // Test when sorted manually
    let users = vec![sean.clone(), tess.clone()];
    let mut posts = Post::belonging_to(&users)
        .load::<Post>(&connection).unwrap();
    posts.sort_by(|a, b| b.title.cmp(&a.title));
    let posts = posts.grouped_by(&users);
    let users_and_posts = users.into_iter().zip(posts).collect::<Vec<_>>();

    assert_eq!(expected_data, users_and_posts);
}

#[test]
fn associations_can_be_grouped_multiple_levels_deep() {
    // I'm manually defining the data rather than loding from the database here,
    // as it makes the tests *way* more readable if I omit setup here. This is
    // the equivalent to.
    //
    // ```rust
    // let users = users.load::<User>().unwrap();
    // let posts = Post::belonging_to(&users).load::<Post>().unwrap();
    // let comments = Comment::belonging_to(&users).load::<Comment>().unwrap();
    // ```
    let users = vec![User::new(1, "Sean"), User::new(2, "Tess")];
    let posts = vec![Post::new(1, 1, "Hello", None), Post::new(2, 2, "World", None), Post::new(3, 1, "Hello 2", None)];
    let comments = vec![Comment::new(1, 3, "LOL"), Comment::new(2, 1, "UR dumb"), Comment::new(3, 3, "Funny")]; // Never read the comments

    let expected_data = vec![
        (users[0].clone(), vec![(posts[0].clone(), vec![comments[1].clone()]), (posts[2].clone(), vec![comments[0].clone(), comments[2].clone()])]),
        (users[1].clone(), vec![(posts[1].clone(), vec![])]),
    ];

    let comments = comments.grouped_by(&posts);
    let posts_and_comments = posts.into_iter().zip(comments).grouped_by(&users);
    let data = users.into_iter().zip(posts_and_comments).collect::<Vec<_>>();

    assert_eq!(expected_data, data);
}

#[test]
fn self_referencing_associations() {
    #[derive(Insertable, Queryable, Associations, Identifiable)]
    #[derive(Debug, Clone, Copy, PartialEq)]
    #[table_name="trees"]
    #[belongs_to(Tree, foreign_key="parent_id")]
    struct Tree {
        id: i32,
        parent_id: Option<i32>,
    }

    let conn = connection();
    let test_data = vec![
        Tree { id: 1, parent_id: None },
        Tree { id: 2, parent_id: None },
        Tree { id: 3, parent_id: Some(1) },
        Tree { id: 4, parent_id: Some(2) },
        Tree { id: 5, parent_id: Some(1) },
    ];
    insert(&test_data).into(trees::table)
        .execute(&conn).unwrap();

    let parents = trees::table.filter(trees::parent_id.is_null())
        .load::<Tree>(&conn).unwrap();
    let children = Tree::belonging_to(&parents)
        .load::<Tree>(&conn).unwrap();
    let children = children.grouped_by(&parents);
    let data = parents.into_iter().zip(children).collect::<Vec<_>>();

    let expected_data = vec![
        (test_data[0], vec![test_data[2], test_data[4]]),
        (test_data[1], vec![test_data[3]]),
    ];
    assert_eq!(expected_data, data);
}

fn conn_with_test_data() -> (TestConnection, User, User, User) {
    let connection = connection_with_sean_and_tess_in_users_table();
    insert(&NewUser::new("Jim", None)).into(users::table).execute(&connection).unwrap();

    let sean = find_user_by_name("Sean", &connection);
    let tess = find_user_by_name("Tess", &connection);
    let jim = find_user_by_name("Jim", &connection);
    let new_posts = vec![sean.new_post("Hello", None), sean.new_post("World", None)];
    insert(&new_posts).into(posts::table).execute(&connection).unwrap();
    let new_posts = vec![tess.new_post("Hello 2", None), tess.new_post("World 2", None)];
    insert(&new_posts).into(posts::table).execute(&connection).unwrap();
    let new_posts = vec![jim.new_post("Hello 3", None), jim.new_post("World 3", None)];
    insert(&new_posts).into(posts::table).execute(&connection).unwrap();

    (connection, sean, tess, jim)
}

#[test]
#[cfg(not(feature="mysql"))] // FIXME: Figure out how to handle tests that modify schema
fn custom_foreign_key() {
    use diesel::*;
    use diesel::connection::SimpleConnection;
    let connection = connection();
    connection.batch_execute(r#"
            DROP TABLE posts;
            DROP TABLE users;
            CREATE TABLE users (id SERIAL PRIMARY KEY,
                                name TEXT NOT NULL);
            CREATE TABLE posts (id SERIAL PRIMARY KEY,
                                belongs_to_user INTEGER NOT NULL,
                                title TEXT NOT NULL);
            INSERT INTO users (id, name) VALUES (1, 'Sean'), (2, 'Tess');
            INSERT INTO posts (id, belongs_to_user, title) VALUES
                   (1, 1, 'Hello'),
                   (2, 2, 'World'),
                   (3, 1, 'Hello 2');
        "#).unwrap();
    table! {
        users {
            id -> Integer,
            name -> Text,
        }
    }

    table! {
        posts {
            id -> Integer,
            belongs_to_user -> Integer,
            title -> Text,
        }
    }

    #[derive(Debug, Queryable, Identifiable, PartialEq, Clone)]
    pub struct User {
        id: i32,
        name: String
    }
    #[derive(Debug, Queryable, Associations, Identifiable, Clone, PartialEq)]
    #[belongs_to(User, foreign_key="belongs_to_user")]
    pub struct Post {
        id: i32,
        belongs_to_user: i32,
        title: String,
    }
    let sean = User { id: 1, name: "Sean".into() };
    let tess = User { id: 2, name: "Tess".into() };
    let post1 = Post { id: 1, belongs_to_user: 1, title: "Hello".into() };
    let post2 = Post { id: 2, belongs_to_user: 2, title: "World".into() };
    let post3 = Post { id: 3, belongs_to_user: 1, title: "Hello 2".into() };

    assert_eq!(
        Post::belonging_to(&sean).load(&connection),
        Ok(vec![post1.clone(), post3.clone()])
    );

    assert_eq!(
        users::table.inner_join(posts::table).load(&connection),
        Ok(vec![(sean.clone(), post1), (tess, post2), (sean, post3)])
    );

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

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "LengthOrPercentageOrNumber",
    "Either::First(1.0)",
    "parse_non_negative",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                          "parse_at_least_one", products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type("stroke-dasharray",
                          "LengthOrPercentageOrNumber",
                          "Either::First(0.0)",
                          "parse_non_negative",
                          vector="True",
                          delegate_animate="True",
                          allow_empty="True",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          space_separated_allowed="True",
                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

${helpers.predefined_type(
    "stroke-dashoffset", "LengthOrPercentageOrNumber",
    "Either::First(0.0)",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_value_type="none"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(PartialEq, Clone, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {
                let result: Result<_, ParseError> = input.try(|i| {
                    try_match_ident_ignore_ascii_case! { i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(StyleParseError::UnspecifiedError.into())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(_) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(StyleParseError::UnspecifiedError.into())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>
<%helpers:vector_longhand name="-moz-context-properties"
                   animation_value_type="none"
                   products="gecko"
                   spec="Nonstandard (Internal-only)"
                   internal="True"
                   allow_empty="True">
    use values::CustomIdent;
    use values::computed::ComputedValueAsSpecified;

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }

    pub type SpecifiedValue = CustomIdent;

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let i = input.expect_ident()?;
        CustomIdent::from_ident(i, &["all", "none", "auto"])
    }
</%helpers:vector_longhand>
<commit_msg>Bug 1370797 - Expose -moz-context-properties on stylo<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "LengthOrPercentageOrNumber",
    "Either::First(1.0)",
    "parse_non_negative",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                          "parse_at_least_one", products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type("stroke-dasharray",
                          "LengthOrPercentageOrNumber",
                          "Either::First(0.0)",
                          "parse_non_negative",
                          vector="True",
                          delegate_animate="True",
                          allow_empty="True",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          space_separated_allowed="True",
                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

${helpers.predefined_type(
    "stroke-dashoffset", "LengthOrPercentageOrNumber",
    "Either::First(0.0)",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_value_type="none"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(PartialEq, Clone, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {
                let result: Result<_, ParseError> = input.try(|i| {
                    try_match_ident_ignore_ascii_case! { i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(StyleParseError::UnspecifiedError.into())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(_) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(StyleParseError::UnspecifiedError.into())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>
<%helpers:vector_longhand name="-moz-context-properties"
                   animation_value_type="none"
                   products="gecko"
                   spec="Nonstandard (Internal-only)"
                   allow_empty="True">
    use values::CustomIdent;
    use values::computed::ComputedValueAsSpecified;

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }

    pub type SpecifiedValue = CustomIdent;

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let i = input.expect_ident()?;
        CustomIdent::from_ident(i, &["all", "none", "auto"])
    }
</%helpers:vector_longhand>
<|endoftext|>


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

use crate::ir::*;
use crate::program::Program;
use common::WithLocation;
use std::sync::Arc;

pub trait Transformer {
    const NAME: &'static str;
    const VISIT_ARGUMENTS: bool;
    const VISIT_DIRECTIVES: bool;

    fn transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        self.default_transform_program(program)
    }

    fn default_transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        let mut next_program = Program::new(program.schema());
        let mut has_changes = false;
        for operation in program.operations() {
            match self.transform_operation(operation) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_operation(Arc::clone(operation)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_operation(Arc::new(replacement))
                }
            }
        }
        for fragment in program.fragments() {
            match self.transform_fragment(fragment) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_fragment(Arc::clone(fragment)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_fragment(Arc::new(replacement))
                }
            }
        }
        if has_changes {
            Some(next_program)
        } else {
            None
        }
    }

    // Fragment Definition
    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.default_transform_fragment(fragment)
    }

    fn default_transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(FragmentDefinition {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        })
    }

    // Operation Definition
    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.default_transform_operation(operation)
    }

    fn default_transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let selections = self.transform_selections(&operation.selections);
        // Special-case for empty selections
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&operation.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(OperationDefinition {
            directives: directives.unwrap_or_else(|| operation.directives.clone()),
            selections: selections.unwrap_or_else(|| operation.selections.clone()),
            ..operation.clone()
        })
    }

    // Selection
    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        self.transform_list(selections, Self::transform_selection)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        self.default_transform_selection(selection)
    }

    fn default_transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::FragmentSpread(selection) => self
                .transform_fragment_spread(selection)
                .map(Selection::FragmentSpread),
            Selection::InlineFragment(selection) => self
                .transform_inline_fragment(selection)
                .map(Selection::InlineFragment),
            Selection::LinkedField(selection) => self
                .transform_linked_field(selection)
                .map(Selection::LinkedField),
            Selection::ScalarField(selection) => self
                .transform_scalar_field(selection)
                .map(Selection::ScalarField),
            Selection::Condition(selection) => self
                .transform_condition(selection)
                .map(Selection::Condition),
        }
    }

    // Selection Kinds
    fn transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Arc<ScalarField>> {
        self.default_transform_scalar_field(field)
    }

    fn default_transform_scalar_field(
        &mut self,
        field: &ScalarField,
    ) -> Transformed<Arc<ScalarField>> {
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(ScalarField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            ..field.clone()
        }))
    }

    fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed<Arc<LinkedField>> {
        self.default_transform_linked_field(field)
    }

    fn default_transform_linked_field(
        &mut self,
        field: &LinkedField,
    ) -> Transformed<Arc<LinkedField>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&field.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if selections.is_none() && arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(LinkedField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            selections: selections.unwrap_or_else(|| field.selections.clone()),
            ..field.clone()
        }))
    }

    fn transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        self.default_transform_inline_fragment(fragment)
    }

    fn default_transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(InlineFragment {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        }))
    }

    fn transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        self.default_transform_fragment_spread(spread)
    }
    fn default_transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        let arguments = self.transform_arguments(&spread.arguments);
        let directives = self.transform_directives(&spread.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(FragmentSpread {
            arguments: arguments.unwrap_or_else(|| spread.arguments.clone()),
            directives: directives.unwrap_or_else(|| spread.directives.clone()),
            ..spread.clone()
        }))
    }

    // Conditions
    fn transform_condition(&mut self, condition: &Condition) -> Transformed<Arc<Condition>> {
        self.default_transform_condition(condition)
    }

    fn default_transform_condition(
        &mut self,
        condition: &Condition,
    ) -> Transformed<Arc<Condition>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&condition.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let condition_value = self.transform_condition_value(&condition.value);
        if selections.is_none() && condition_value.is_none() {
            Transformed::Keep
        } else {
            Transformed::Replace(Arc::new(Condition {
                value: condition_value.unwrap_or_else(|| condition.value.clone()),
                selections: selections.unwrap_or_else(|| condition.selections.clone()),
                ..condition.clone()
            }))
        }
    }

    fn transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        if Self::VISIT_ARGUMENTS {
            self.default_transform_condition_value(condition_value)
        } else {
            None
        }
    }

    fn default_transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        match condition_value {
            ConditionValue::Variable(variable) => match self.transform_variable(variable) {
                Transformed::Keep => None,
                Transformed::Delete => unreachable!(
                    "default_transform_condition_value: Unexpected `Delete` from transfrom_variable."
                ),
                Transformed::Replace(next_variable) => {
                    Some(ConditionValue::Variable(next_variable))
                }
            },
            ConditionValue::Constant(_) => None,
        }
    }

    // Directives
    fn transform_directives(&mut self, directives: &[Directive]) -> Option<Vec<Directive>> {
        if Self::VISIT_DIRECTIVES {
            self.transform_list(directives, Self::transform_directive)
        } else {
            None
        }
    }

    fn transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        self.default_transform_directive(directive)
    }

    fn default_transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        let arguments = self.transform_arguments(&directive.arguments);
        match arguments {
            None => Transformed::Keep,
            Some(replacement) => Transformed::Replace(Directive {
                arguments: replacement,
                ..directive.clone()
            }),
        }
    }

    // Arguments
    fn transform_arguments(&mut self, arguments: &[Argument]) -> Option<Vec<Argument>> {
        if Self::VISIT_ARGUMENTS {
            self.transform_list(arguments, Self::transform_argument)
        } else {
            None
        }
    }

    fn transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        self.default_transform_argument(argument)
    }

    fn default_transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        match self.transform_value(&argument.value.item) {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(Argument {
                value: WithLocation::new(argument.value.location, replacement),
                ..argument.clone()
            }),
        }
    }

    // Values
    fn transform_value(&mut self, value: &Value) -> Transformed<Value> {
        self.default_transform_value(value)
    }

    fn default_transform_value(&mut self, value: &Value) -> Transformed<Value> {
        match value {
            Value::Variable(variable) => self.transform_variable(variable).map(Value::Variable),
            Value::Constant(_) => Transformed::Keep,
            Value::List(items) => match self.transform_list(items, Self::transform_value) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::List(replacement)),
            },
            Value::Object(arguments) => match self.transform_arguments(arguments) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::Object(replacement)),
            },
        }
    }

    fn transform_variable(&mut self, value: &Variable) -> Transformed<Variable> {
        let _ = value;
        Transformed::Keep
    }

    // Helpers
    fn transform_list<F, T>(&mut self, list: &[T], f: F) -> Option<Vec<T>>
    where
        F: Fn(&mut Self, &T) -> Transformed<T>,
        T: Clone,
    {
        if list.is_empty() {
            return None;
        }
        let mut result = Vec::new();
        let mut has_changes = false;
        for (index, prev_item) in list.iter().enumerate() {
            let next_item = f(self, prev_item);
            match next_item {
                Transformed::Keep => {
                    if has_changes {
                        result.push(prev_item.clone());
                    }
                }
                Transformed::Delete => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    has_changes = true;
                }
                Transformed::Replace(next_item) => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    result.push(next_item);
                    has_changes = true;
                }
            }
        }
        if has_changes {
            // Note that result can be empty if the input was empty and all items were skipped
            Some(result)
        } else {
            None
        }
    }
}

#[derive(Clone)]
pub enum Transformed<T> {
    Delete,
    Keep,
    Replace(T),
}

impl<T> Transformed<T> {
    pub fn map<F, U>(self, f: F) -> Transformed<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(f(replacement)),
        }
    }
}
<commit_msg>Make `transform_variable` return Option<Variable><commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::ir::*;
use crate::program::Program;
use common::WithLocation;
use std::sync::Arc;

pub trait Transformer {
    const NAME: &'static str;
    const VISIT_ARGUMENTS: bool;
    const VISIT_DIRECTIVES: bool;

    fn transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        self.default_transform_program(program)
    }

    fn default_transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        let mut next_program = Program::new(program.schema());
        let mut has_changes = false;
        for operation in program.operations() {
            match self.transform_operation(operation) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_operation(Arc::clone(operation)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_operation(Arc::new(replacement))
                }
            }
        }
        for fragment in program.fragments() {
            match self.transform_fragment(fragment) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_fragment(Arc::clone(fragment)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_fragment(Arc::new(replacement))
                }
            }
        }
        if has_changes {
            Some(next_program)
        } else {
            None
        }
    }

    // Fragment Definition
    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.default_transform_fragment(fragment)
    }

    fn default_transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(FragmentDefinition {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        })
    }

    // Operation Definition
    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.default_transform_operation(operation)
    }

    fn default_transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let selections = self.transform_selections(&operation.selections);
        // Special-case for empty selections
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&operation.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(OperationDefinition {
            directives: directives.unwrap_or_else(|| operation.directives.clone()),
            selections: selections.unwrap_or_else(|| operation.selections.clone()),
            ..operation.clone()
        })
    }

    // Selection
    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        self.transform_list(selections, Self::transform_selection)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        self.default_transform_selection(selection)
    }

    fn default_transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::FragmentSpread(selection) => self
                .transform_fragment_spread(selection)
                .map(Selection::FragmentSpread),
            Selection::InlineFragment(selection) => self
                .transform_inline_fragment(selection)
                .map(Selection::InlineFragment),
            Selection::LinkedField(selection) => self
                .transform_linked_field(selection)
                .map(Selection::LinkedField),
            Selection::ScalarField(selection) => self
                .transform_scalar_field(selection)
                .map(Selection::ScalarField),
            Selection::Condition(selection) => self
                .transform_condition(selection)
                .map(Selection::Condition),
        }
    }

    // Selection Kinds
    fn transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Arc<ScalarField>> {
        self.default_transform_scalar_field(field)
    }

    fn default_transform_scalar_field(
        &mut self,
        field: &ScalarField,
    ) -> Transformed<Arc<ScalarField>> {
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(ScalarField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            ..field.clone()
        }))
    }

    fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed<Arc<LinkedField>> {
        self.default_transform_linked_field(field)
    }

    fn default_transform_linked_field(
        &mut self,
        field: &LinkedField,
    ) -> Transformed<Arc<LinkedField>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&field.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if selections.is_none() && arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(LinkedField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            selections: selections.unwrap_or_else(|| field.selections.clone()),
            ..field.clone()
        }))
    }

    fn transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        self.default_transform_inline_fragment(fragment)
    }

    fn default_transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(InlineFragment {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        }))
    }

    fn transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        self.default_transform_fragment_spread(spread)
    }
    fn default_transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        let arguments = self.transform_arguments(&spread.arguments);
        let directives = self.transform_directives(&spread.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(FragmentSpread {
            arguments: arguments.unwrap_or_else(|| spread.arguments.clone()),
            directives: directives.unwrap_or_else(|| spread.directives.clone()),
            ..spread.clone()
        }))
    }

    // Conditions
    fn transform_condition(&mut self, condition: &Condition) -> Transformed<Arc<Condition>> {
        self.default_transform_condition(condition)
    }

    fn default_transform_condition(
        &mut self,
        condition: &Condition,
    ) -> Transformed<Arc<Condition>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&condition.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let condition_value = self.transform_condition_value(&condition.value);
        if selections.is_none() && condition_value.is_none() {
            Transformed::Keep
        } else {
            Transformed::Replace(Arc::new(Condition {
                value: condition_value.unwrap_or_else(|| condition.value.clone()),
                selections: selections.unwrap_or_else(|| condition.selections.clone()),
                ..condition.clone()
            }))
        }
    }

    fn transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        if Self::VISIT_ARGUMENTS {
            self.default_transform_condition_value(condition_value)
        } else {
            None
        }
    }

    fn default_transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        match condition_value {
            ConditionValue::Variable(variable) => match self.transform_variable(variable) {
                None => None,
                Some(next_variable) => Some(ConditionValue::Variable(next_variable)),
            },
            ConditionValue::Constant(_) => None,
        }
    }

    // Directives
    fn transform_directives(&mut self, directives: &[Directive]) -> Option<Vec<Directive>> {
        if Self::VISIT_DIRECTIVES {
            self.transform_list(directives, Self::transform_directive)
        } else {
            None
        }
    }

    fn transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        self.default_transform_directive(directive)
    }

    fn default_transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        let arguments = self.transform_arguments(&directive.arguments);
        match arguments {
            None => Transformed::Keep,
            Some(replacement) => Transformed::Replace(Directive {
                arguments: replacement,
                ..directive.clone()
            }),
        }
    }

    // Arguments
    fn transform_arguments(&mut self, arguments: &[Argument]) -> Option<Vec<Argument>> {
        if Self::VISIT_ARGUMENTS {
            self.transform_list(arguments, Self::transform_argument)
        } else {
            None
        }
    }

    fn transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        self.default_transform_argument(argument)
    }

    fn default_transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        match self.transform_value(&argument.value.item) {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(Argument {
                value: WithLocation::new(argument.value.location, replacement),
                ..argument.clone()
            }),
        }
    }

    // Values
    fn transform_value(&mut self, value: &Value) -> Transformed<Value> {
        self.default_transform_value(value)
    }

    fn default_transform_value(&mut self, value: &Value) -> Transformed<Value> {
        match value {
            Value::Variable(variable) => match self.transform_variable(variable) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::Variable(replacement)),
            },
            Value::Constant(_) => Transformed::Keep,
            Value::List(items) => match self.transform_list(items, Self::transform_value) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::List(replacement)),
            },
            Value::Object(arguments) => match self.transform_arguments(arguments) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::Object(replacement)),
            },
        }
    }

    fn transform_variable(&mut self, _variable: &Variable) -> Option<Variable> {
        None
    }

    // Helpers
    fn transform_list<F, T>(&mut self, list: &[T], f: F) -> Option<Vec<T>>
    where
        F: Fn(&mut Self, &T) -> Transformed<T>,
        T: Clone,
    {
        if list.is_empty() {
            return None;
        }
        let mut result = Vec::new();
        let mut has_changes = false;
        for (index, prev_item) in list.iter().enumerate() {
            let next_item = f(self, prev_item);
            match next_item {
                Transformed::Keep => {
                    if has_changes {
                        result.push(prev_item.clone());
                    }
                }
                Transformed::Delete => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    has_changes = true;
                }
                Transformed::Replace(next_item) => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    result.push(next_item);
                    has_changes = true;
                }
            }
        }
        if has_changes {
            // Note that result can be empty if the input was empty and all items were skipped
            Some(result)
        } else {
            None
        }
    }
}

#[derive(Clone)]
pub enum Transformed<T> {
    Delete,
    Keep,
    Replace(T),
}

impl<T> Transformed<T> {
    pub fn map<F, U>(self, f: F) -> Transformed<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(f(replacement)),
        }
    }
}
<|endoftext|>


<commit_before>use super::managed_display::ManagedDisplay;
use entities::{ EntitiesState, EntitiesHelper, EntityID, ComponentID, NativeComponentType };
use nalgebra::na;
use nalgebra::na::{ Vec3, Eye };
use std::collections::{ HashSet, HashMap };
use std::sync::Arc;
use super::sprite_displayer::SpriteDisplayer;
use super::Drawable;

mod custom_display_system;

pub struct DisplaySystem {
	display: Arc<ManagedDisplay>,
	customDisplay: custom_display_system::CustomDisplaySystem,
	sprites: HashMap<ComponentID, (Option<SpriteDisplayer>, String)>,
	logger: Box<::log::Logger>
}

impl DisplaySystem {
	pub fn new<L: ::log::Logger + 'static>(display: Arc<ManagedDisplay>, state: &EntitiesState, mut logger: L)
		-> DisplaySystem
	{
		declmagic_info!(logger, "created display system");

		DisplaySystem {
			display: display.clone(),
			customDisplay: custom_display_system::CustomDisplaySystem::new(display.clone(), state, logger.clone()),
			sprites: HashMap::new(),
			logger: box logger
		}
	}

	pub fn draw(&mut self, state: &EntitiesState, _: &u64)
	{
		self.update_sprite_displayers(state);

		let camera = DisplaySystem::get_camera(state).unwrap_or_else(|| {
			declmagic_warn!(self.logger, "no active camera on the scene");
			Eye::new_identity(4)
		});

	 	for (cmp, &(ref sprite, _)) in self.sprites.iter() {
	 		let pos = ::physics::PhysicsSystem::get_entity_position(state, &state.get_owner(cmp).unwrap());
	 		let translationMatrix = na::Mat4::new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, pos.x, pos.y, pos.z, 1.0);
			sprite.as_ref().map(|e| e.draw(&(translationMatrix * camera)));
		}

		self.customDisplay.draw(state);
	}

	fn update_sprite_displayers(&mut self, state: &EntitiesState)
	{
		// getting the list of all sprite displayer components
		let listOfComponents = state.get_components_iter()
            .filter(|c| state.is_component_visible(*c).unwrap())
			.filter(|c| match state.get_type(*c) { Ok(NativeComponentType(t)) => t.as_slice() == "spriteDisplay", _ => false })
			.map(|c| c.clone())
			.collect::<HashSet<ComponentID>>();

		// removing from the list the elements that have disappeared
		{	let toRemove = self.sprites.keys().filter(|e| !listOfComponents.contains(e.clone())).map(|e| e.clone()).collect::<Vec<ComponentID>>();
			for c in toRemove.move_iter() {
				self.sprites.remove(&c);
			}
		}

		// adding elements that are not yet created
		{	let toCreate = listOfComponents.iter().filter(|e| !self.sprites.contains_key(e.clone())).map(|e| e.clone()).collect::<Vec<ComponentID>>();
			for spriteDisplayComponent in toCreate.move_iter() {
				// inserting in sprites list
				self.sprites.insert(spriteDisplayComponent.clone(), (None, format!("")));
			}
		}

		// updating everything
		for (component, &(ref mut sprite, ref mut currTexName)) in self.sprites.mut_iter() {
			// getting the name of the texture
			let textureName = match state.get_as_string(component, "texture") {
				Some(s) => s,
				_ => {
					// TODO:
					//declmagic_error!(self.logger, "component {} has no valid \"texture\" element", component)
					continue
				}
			};

			if sprite.is_none() || currTexName.as_slice() != textureName.as_slice() {
				(*sprite) = Some(SpriteDisplayer::new(self.display.clone(), textureName.as_slice()).unwrap());
			}

			// getting coordinates
			match sprite {
				&Some(ref mut s) =>
					s.set_rectangle_coords(
						match state.get(component, "leftX") { Ok(&::entities::Number(ref nb)) => Some(*nb as f32), _ => None },
						match state.get(component, "topY") { Ok(&::entities::Number(ref nb)) => Some(*nb as f32), _ => None },
						match state.get(component, "rightX") { Ok(&::entities::Number(ref nb)) => Some(*nb as f32), _ => None },
						match state.get(component, "bottomY") { Ok(&::entities::Number(ref nb)) => Some(*nb as f32), _ => None }
					),
				_ => fail!()
			}
		}
	}

	/// Returns the camera matrix of the scene.
	pub fn get_camera(state: &EntitiesState)
		-> Option<na::Mat4<f32>>
	{
		let cameraInfos = state
			.get_components_iter()
			.filter(|c| state.is_component_visible(*c).unwrap())
			.filter(|c| match state.get_type(*c) { Ok(NativeComponentType(t)) => t.as_slice() == "camera", _ => false })
			.max_by(|c| match state.get(*c, "priority") { Ok(&::entities::Number(ref n)) => (((*n) * 1000f64) as int), _ => 1000 })
			.and_then(|c| match state.get(c, "matrix") { Ok(&::entities::List(ref data)) => Some((c, data)), _ => None })
			.map(|(c, data)| (c, data.iter().filter_map(|elem| match elem { &::entities::Number(ref n) => Some(n.clone() as f32), _ => None }).collect::<Vec<f32>>()) );

		if cameraInfos.is_none() {
			return None;
		}

		let (cameraComponent, matrixData) = cameraInfos.unwrap();
		let matrix = na::Mat4::new(*matrixData.get(0), *matrixData.get(1), *matrixData.get(2), *matrixData.get(3), *matrixData.get(4), *matrixData.get(5), *matrixData.get(6), *matrixData.get(7), *matrixData.get(8), *matrixData.get(9), *matrixData.get(10), *matrixData.get(11), *matrixData.get(12), *matrixData.get(13), *matrixData.get(14), *matrixData.get(15));

		let position = ::physics::PhysicsSystem::get_entity_position(state, &state.get_owner(cameraComponent).unwrap());
		let positionMatrix = na::Mat4::new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -position.x, -position.y, -position.z, 1.0);

		Some(positionMatrix * matrix)
	}
}
<commit_msg>Sprites now support properties<commit_after>use super::managed_display::ManagedDisplay;
use entities::{ EntitiesState, EntitiesHelper, EntityID, ComponentID, NativeComponentType };
use nalgebra::na;
use nalgebra::na::{ Vec3, Eye };
use std::collections::{ HashSet, HashMap };
use std::sync::Arc;
use super::sprite_displayer::SpriteDisplayer;
use super::Drawable;

mod custom_display_system;

pub struct DisplaySystem {
	display: Arc<ManagedDisplay>,
	customDisplay: custom_display_system::CustomDisplaySystem,
	sprites: HashMap<ComponentID, (Option<SpriteDisplayer>, String)>,
	logger: Box<::log::Logger>
}

impl DisplaySystem {
	pub fn new<L: ::log::Logger + 'static>(display: Arc<ManagedDisplay>, state: &EntitiesState, mut logger: L)
		-> DisplaySystem
	{
		declmagic_info!(logger, "created display system");

		DisplaySystem {
			display: display.clone(),
			customDisplay: custom_display_system::CustomDisplaySystem::new(display.clone(), state, logger.clone()),
			sprites: HashMap::new(),
			logger: box logger
		}
	}

	pub fn draw(&mut self, state: &EntitiesState, _: &u64)
	{
		self.update_sprite_displayers(state);

		let camera = DisplaySystem::get_camera(state).unwrap_or_else(|| {
			declmagic_warn!(self.logger, "no active camera on the scene");
			Eye::new_identity(4)
		});

	 	for (cmp, &(ref sprite, _)) in self.sprites.iter() {
	 		let pos = ::physics::PhysicsSystem::get_entity_position(state, &state.get_owner(cmp).unwrap());
	 		let translationMatrix = na::Mat4::new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, pos.x, pos.y, pos.z, 1.0);
			sprite.as_ref().map(|e| e.draw(&(translationMatrix * camera)));
		}

		self.customDisplay.draw(state);
	}

	fn update_sprite_displayers(&mut self, state: &EntitiesState)
	{
		// getting the list of all sprite displayer components
		let listOfComponents = state.get_components_iter()
            .filter(|c| state.is_component_visible(*c).unwrap())
			.filter(|c| match state.get_type(*c) { Ok(NativeComponentType(t)) => t.as_slice() == "spriteDisplay", _ => false })
			.map(|c| c.clone())
			.collect::<HashSet<ComponentID>>();

		// removing from the list the elements that have disappeared
		{	let toRemove = self.sprites.keys().filter(|e| !listOfComponents.contains(e.clone())).map(|e| e.clone()).collect::<Vec<ComponentID>>();
			for c in toRemove.move_iter() {
				self.sprites.remove(&c);
			}
		}

		// adding elements that are not yet created
		{	let toCreate = listOfComponents.iter().filter(|e| !self.sprites.contains_key(e.clone())).map(|e| e.clone()).collect::<Vec<ComponentID>>();
			for spriteDisplayComponent in toCreate.move_iter() {
				// inserting in sprites list
				self.sprites.insert(spriteDisplayComponent.clone(), (None, format!("")));
			}
		}

		// updating everything
		for (component, &(ref mut sprite, ref mut currTexName)) in self.sprites.mut_iter() {
			// getting the name of the texture
			let textureName = match state.get_as_string(component, "texture") {
				Some(s) => s,
				_ => {
					// TODO:
					//declmagic_error!(self.logger, "component {} has no valid \"texture\" element", component)
					continue
				}
			};

			if sprite.is_none() || currTexName.as_slice() != textureName.as_slice() {
				(*sprite) = Some(SpriteDisplayer::new(self.display.clone(), textureName.as_slice()).unwrap());
			}

			// getting coordinates
			match sprite {
				&Some(ref mut s) =>
					s.set_rectangle_coords(
						state.get_as_number(component, "leftX").map(|n| n as f32),
						state.get_as_number(component, "topY").map(|n| n as f32),
						state.get_as_number(component, "rightX").map(|n| n as f32),
						state.get_as_number(component, "bottomY").map(|n| n as f32)
					),
				_ => fail!()
			}
		}
	}

	/// Returns the camera matrix of the scene.
	pub fn get_camera(state: &EntitiesState)
		-> Option<na::Mat4<f32>>
	{
		let cameraInfos = state
			.get_components_iter()
			.filter(|c| state.is_component_visible(*c).unwrap())
			.filter(|c| match state.get_type(*c) { Ok(NativeComponentType(t)) => t.as_slice() == "camera", _ => false })
			.max_by(|c| match state.get(*c, "priority") { Ok(&::entities::Number(ref n)) => (((*n) * 1000f64) as int), _ => 1000 })
			.and_then(|c| match state.get(c, "matrix") { Ok(&::entities::List(ref data)) => Some((c, data)), _ => None })
			.map(|(c, data)| (c, data.iter().filter_map(|elem| match elem { &::entities::Number(ref n) => Some(n.clone() as f32), _ => None }).collect::<Vec<f32>>()) );

		if cameraInfos.is_none() {
			return None;
		}

		let (cameraComponent, matrixData) = cameraInfos.unwrap();
		let matrix = na::Mat4::new(*matrixData.get(0), *matrixData.get(1), *matrixData.get(2), *matrixData.get(3), *matrixData.get(4), *matrixData.get(5), *matrixData.get(6), *matrixData.get(7), *matrixData.get(8), *matrixData.get(9), *matrixData.get(10), *matrixData.get(11), *matrixData.get(12), *matrixData.get(13), *matrixData.get(14), *matrixData.get(15));

		let position = ::physics::PhysicsSystem::get_entity_position(state, &state.get_owner(cameraComponent).unwrap());
		let positionMatrix = na::Mat4::new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -position.x, -position.y, -position.z, 1.0);

		Some(positionMatrix * matrix)
	}
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::borrow::Cow;
use std::cell::Cell;

#[derive(Debug, PartialEq, Clone)]
pub enum HintType {
    OpaqueType(String),
    MapType(String),
    TypeName(String),
}

#[derive(Debug, PartialEq, Clone)]
pub struct Hint {
    pub hint_type: HintType,
    pub used: Cell<bool>,
}

impl Hint {
    pub fn new(hint_type: HintType) -> Self {
        Hint {
            hint_type,
            used: Cell::new(false),
        }
    }

    pub fn default_map() -> Self {
        Hint::new(HintType::MapType("::std::collections::HashMap".into()))
    }

    pub fn opaque_type(name: impl Into<String>) -> Self {
        Hint::new(HintType::OpaqueType(name.into()))
    }

    pub fn type_name(name: impl Into<String>) -> Self {
        Hint::new(HintType::TypeName(name.into()))
    }
}

pub struct Hints<'a> {
    pub hints: Vec<(Cow<'a, [&'a str]>, &'a Hint)>,
    pub applicable: Vec<&'a Hint>,
}

fn is_index(s: &str) -> bool {
    s == "-" || s.bytes().all(|b| b >= b'0' && b <= b'9')
}

impl<'a> Hints<'a> {
    pub fn new() -> Self {
        Hints {
            hints: Vec::new(),
            applicable: Vec::new(),
        }
    }

    pub fn add(&mut self, pointer: &'a str, hint: &'a Hint) {
        if pointer.is_empty() {
            self.applicable.push(hint);
        } else {
            if !pointer.starts_with('/') {
                panic!(
                    "Invalid JSON pointer: {:?}\n{}",
                    pointer, "A pointer not referring to the root has to start with '/'",
                );
            }
            let tokens: Vec<_> = pointer.split('/').skip(1).collect();
            let pair: (Cow<[&str]>, &Hint) = (tokens.into(), hint);
            self.hints.push(pair);
        }
    }

    /// ([/a/b, /a/c, /d/e], "a") -> [/b, /c]
    pub fn step_field(&self, name: &str) -> Hints {
        self.step(|first| first == name)
    }

    /// [/1/b, /a/c, /-/e] -> [/b, /c, /e]
    pub fn step_any(&self) -> Hints {
        self.step(|_first| true)
    }

    /// [/1/b, /a/c, /-/e] -> [/b, /e]
    pub fn step_array(&self) -> Hints {
        self.step(is_index)
    }

    /// ([/2/b, /a/c, /-/e, /3/d], 3) -> [/e, /d]
    pub fn step_index(&self, index: usize) -> Hints {
        let i_str = &index.to_string();
        self.step(|first| first == i_str)
    }

    fn step<F: Fn(&str) -> bool>(&self, pred: F) -> Hints {
        let mut filtered = Vec::new();
        let mut applicable = Vec::new();

        for &(ref path, hint) in self.hints.iter() {
            if pred(path[0]) {
                let stepped: &[&str] = &path[1..];
                if stepped.is_empty() {
                    applicable.push(hint);
                } else {
                    filtered.push((stepped.into(), hint));
                }
            }
        }

        Hints {
            hints: filtered,
            applicable,
        }
    }
}

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

    #[test]
    fn basic_pointers() {
        let hint = Hint::default_map();
        let mut hints = Hints::new();
        hints.add("/a/b", &hint);
        hints.add("/foo", &hint);
        hints.add("/foo", &hint);
        hints.add("", &hint);

        assert_eq!(hints.hints.len(), 3);
        assert_eq!(hints.applicable.len(), 1);
    }

    #[test]
    #[should_panic]
    fn invalid_pointer() {
        let hint = Hint::default_map();
        let mut hints = Hints::new();
        hints.add("foo", &hint);
    }

    #[test]
    fn step_field() {
        let hint = Hint::default_map();
        let mut hints = Hints::new();
        hints.add("/a/b", &hint);
        hints.add("/b/c", &hint);
        hints.add("/b/", &hint);
        hints.add("/b", &hint);

        let hints = hints.step_field("b");

        assert_eq!(hints.hints.len(), 2);
        assert_eq!(hints.applicable.len(), 1);
    }
}
<commit_msg>bugfix: Wildcards in hints were not used correctly<commit_after>#![allow(dead_code)]

use std::borrow::Cow;
use std::cell::Cell;

#[derive(Debug, PartialEq, Clone)]
pub enum HintType {
    OpaqueType(String),
    MapType(String),
    TypeName(String),
}

#[derive(Debug, PartialEq, Clone)]
pub struct Hint {
    pub hint_type: HintType,
    pub used: Cell<bool>,
}

impl Hint {
    pub fn new(hint_type: HintType) -> Self {
        Hint {
            hint_type,
            used: Cell::new(false),
        }
    }

    pub fn default_map() -> Self {
        Hint::new(HintType::MapType("::std::collections::HashMap".into()))
    }

    pub fn opaque_type(name: impl Into<String>) -> Self {
        Hint::new(HintType::OpaqueType(name.into()))
    }

    pub fn type_name(name: impl Into<String>) -> Self {
        Hint::new(HintType::TypeName(name.into()))
    }
}

pub struct Hints<'a> {
    pub hints: Vec<(Cow<'a, [&'a str]>, &'a Hint)>,
    pub applicable: Vec<&'a Hint>,
}

fn is_index(s: &str) -> bool {
    s == "-" || s.bytes().all(|b| b >= b'0' && b <= b'9')
}

impl<'a> Hints<'a> {
    pub fn new() -> Self {
        Hints {
            hints: Vec::new(),
            applicable: Vec::new(),
        }
    }

    pub fn add(&mut self, pointer: &'a str, hint: &'a Hint) {
        if pointer.is_empty() {
            self.applicable.push(hint);
        } else {
            if !pointer.starts_with('/') {
                panic!(
                    "Invalid JSON pointer: {:?}\n{}",
                    pointer, "A pointer not referring to the root has to start with '/'",
                );
            }
            let tokens: Vec<_> = pointer.split('/').skip(1).collect();
            let pair: (Cow<[&str]>, &Hint) = (tokens.into(), hint);
            self.hints.push(pair);
        }
    }

    /// ([/a/b, /a/c, /d/e], "a") -> [/b, /c]
    pub fn step_field(&self, name: &str) -> Hints {
        self.step(|first| first == name)
    }

    /// [/1/b, /a/c, /-/e] -> [/b, /c, /e]
    pub fn step_any(&self) -> Hints {
        self.step(|_first| true)
    }

    /// [/1/b, /a/c, /-/e] -> [/b, /e]
    pub fn step_array(&self) -> Hints {
        self.step(is_index)
    }

    /// ([/2/b, /a/c, /-/e, /3/d], 3) -> [/e, /d]
    pub fn step_index(&self, index: usize) -> Hints {
        let i_str = &index.to_string();
        self.step(|first| first == "-" || first == i_str)
    }

    fn step<F: Fn(&str) -> bool>(&self, pred: F) -> Hints {
        let mut filtered = Vec::new();
        let mut applicable = Vec::new();

        for &(ref path, hint) in self.hints.iter() {
            if pred(path[0]) {
                let stepped: &[&str] = &path[1..];
                if stepped.is_empty() {
                    applicable.push(hint);
                } else {
                    filtered.push((stepped.into(), hint));
                }
            }
        }

        Hints {
            hints: filtered,
            applicable,
        }
    }
}

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

    #[test]
    fn basic_pointers() {
        let hint = Hint::default_map();
        let mut hints = Hints::new();
        hints.add("/a/b", &hint);
        hints.add("/foo", &hint);
        hints.add("/foo", &hint);
        hints.add("", &hint);

        assert_eq!(hints.hints.len(), 3);
        assert_eq!(hints.applicable.len(), 1);
    }

    #[test]
    #[should_panic]
    fn invalid_pointer() {
        let hint = Hint::default_map();
        let mut hints = Hints::new();
        hints.add("foo", &hint);
    }

    #[test]
    fn step_field() {
        let hint = Hint::default_map();
        let mut hints = Hints::new();
        hints.add("/a/b", &hint);
        hints.add("/b/c", &hint);
        hints.add("/b/", &hint);
        hints.add("/b", &hint);

        let hints = hints.step_field("b");

        assert_eq!(hints.hints.len(), 2);
        assert_eq!(hints.applicable.len(), 1);
    }
}
<|endoftext|>