<commit_before>//! FIXME: write short doc here

use ra_syntax::{ast::AstNode, TextRange, TextUnit};
use ra_text_edit::TextEdit;

use crate::{
    completion::{
        completion_context::CompletionContext,
        completion_item::{Builder, CompletionKind, Completions},
    },
    CompletionItem,
};

pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.db.feature_flags.get("completion.enable-postfix") {
        return;
    }

    let dot_receiver = match &ctx.dot_receiver {
        Some(it) => it,
        None => return,
    };

    let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
        let text = dot_receiver.syntax().text();
        let without_dot = ..text.len() - TextUnit::of_char('.');
        text.slice(without_dot).to_string()
    } else {
        dot_receiver.syntax().text().to_string()
    };

    let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
        Some(it) => it,
        None => return,
    };

    if receiver_ty.is_bool() || receiver_ty.is_unknown() {
        postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
            .add_to(acc);
        postfix_snippet(
            ctx,
            "while",
            "while expr {}",
            &format!("while {} {{\n$0\n}}", receiver_text),
        )
        .add_to(acc);
    }

    postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);

    postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
    postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);

    postfix_snippet(
        ctx,
        "match",
        "match expr {}",
        &format!("match {} {{\n    ${{1:_}} => {{$0\\}},\n}}", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);

    postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
        .add_to(acc);
}

fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
    let edit = {
        let receiver_range =
            ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
        let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
        TextEdit::replace(delete_range, snippet.to_string())
    };
    CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
        .detail(detail)
        .snippet_edit(edit)
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::completion::{do_completion, CompletionItem, CompletionKind};

    fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Postfix)
    }

    #[test]
    fn postfix_completion_works_for_trivial_path_expression() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar = true;
                    bar.<|>
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "Box::new(bar)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "dbg!(bar)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "if",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "if bar {$0}",
                detail: "if expr {}",
            },
            CompletionItem {
                label: "match",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "match bar {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "!bar",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "&bar",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "&mut bar",
                detail: "&mut expr",
            },
            CompletionItem {
                label: "while",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "while bar {\n$0\n}",
                detail: "while expr {}",
            },
        ]
        "###
        );
    }

    #[test]
    fn some_postfix_completions_ignored() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar: u8 = 12;
                    bar.<|>
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "Box::new(bar)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "dbg!(bar)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "match",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "match bar {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "!bar",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "&bar",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "&mut bar",
                detail: "&mut expr",
            },
        ]
        "###
        );
    }

    #[test]
    fn postfix_completion_works_for_ambiguous_float_literal() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    42.<|>
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "Box::new(42)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "dbg!(42)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "match",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "match 42 {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "!42",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "&42",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "&mut 42",
                detail: "&mut expr",
            },
        ]
        "###
        );
    }
}
<commit_msg>Fix range for postfix snippets<commit_after>//! FIXME: write short doc here

use ra_syntax::{ast::AstNode, TextRange, TextUnit};
use ra_text_edit::TextEdit;

use crate::{
    completion::{
        completion_context::CompletionContext,
        completion_item::{Builder, CompletionKind, Completions},
    },
    CompletionItem,
};

pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.db.feature_flags.get("completion.enable-postfix") {
        return;
    }

    let dot_receiver = match &ctx.dot_receiver {
        Some(it) => it,
        None => return,
    };

    let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
        let text = dot_receiver.syntax().text();
        let without_dot = ..text.len() - TextUnit::of_char('.');
        text.slice(without_dot).to_string()
    } else {
        dot_receiver.syntax().text().to_string()
    };

    let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
        Some(it) => it,
        None => return,
    };

    if receiver_ty.is_bool() || receiver_ty.is_unknown() {
        postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
            .add_to(acc);
        postfix_snippet(
            ctx,
            "while",
            "while expr {}",
            &format!("while {} {{\n$0\n}}", receiver_text),
        )
        .add_to(acc);
    }

    postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);

    postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
    postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);

    postfix_snippet(
        ctx,
        "match",
        "match expr {}",
        &format!("match {} {{\n    ${{1:_}} => {{$0\\}},\n}}", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);

    postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
        .add_to(acc);
}

fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
    let edit = {
        let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax();
        let receiver_range = ctx.sema.original_range(receiver_syntax).range;
        let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
        TextEdit::replace(delete_range, snippet.to_string())
    };
    CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
        .detail(detail)
        .snippet_edit(edit)
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::completion::{do_completion, CompletionItem, CompletionKind};

    fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Postfix)
    }

    #[test]
    fn postfix_completion_works_for_trivial_path_expression() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar = true;
                    bar.<|>
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "Box::new(bar)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "dbg!(bar)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "if",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "if bar {$0}",
                detail: "if expr {}",
            },
            CompletionItem {
                label: "match",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "match bar {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "!bar",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "&bar",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "&mut bar",
                detail: "&mut expr",
            },
            CompletionItem {
                label: "while",
                source_range: [89; 89),
                delete: [85; 89),
                insert: "while bar {\n$0\n}",
                detail: "while expr {}",
            },
        ]
        "###
        );
    }

    #[test]
    fn some_postfix_completions_ignored() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar: u8 = 12;
                    bar.<|>
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "Box::new(bar)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "dbg!(bar)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "match",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "match bar {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "!bar",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "&bar",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [91; 91),
                delete: [87; 91),
                insert: "&mut bar",
                detail: "&mut expr",
            },
        ]
        "###
        );
    }

    #[test]
    fn postfix_completion_works_for_ambiguous_float_literal() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    42.<|>
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "Box::new(42)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "dbg!(42)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "match",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "match 42 {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "!42",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "&42",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [52; 52),
                delete: [49; 52),
                insert: "&mut 42",
                detail: "&mut expr",
            },
        ]
        "###
        );
    }

    #[test]
    fn works_in_simple_macro() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                macro_rules! m { ($e:expr) => { $e } }
                fn main() {
                    let bar: u8 = 12;
                    m!(bar.b<|>)
                }
                "#,
            ),
            @r###"
        [
            CompletionItem {
                label: "box",
                source_range: [149; 150),
                delete: [145; 150),
                insert: "Box::new(bar)",
                detail: "Box::new(expr)",
            },
            CompletionItem {
                label: "dbg",
                source_range: [149; 150),
                delete: [145; 150),
                insert: "dbg!(bar)",
                detail: "dbg!(expr)",
            },
            CompletionItem {
                label: "match",
                source_range: [149; 150),
                delete: [145; 150),
                insert: "match bar {\n    ${1:_} => {$0\\},\n}",
                detail: "match expr {}",
            },
            CompletionItem {
                label: "not",
                source_range: [149; 150),
                delete: [145; 150),
                insert: "!bar",
                detail: "!expr",
            },
            CompletionItem {
                label: "ref",
                source_range: [149; 150),
                delete: [145; 150),
                insert: "&bar",
                detail: "&expr",
            },
            CompletionItem {
                label: "refm",
                source_range: [149; 150),
                delete: [145; 150),
                insert: "&mut bar",
                detail: "&mut expr",
            },
        ]
        "###
        );
    }
}
<|endoftext|>


<commit_before>use std::mem;

pub struct Any {
    ptr: *mut (),
    // Can't get a real TypeId because we don't have Reflect bounds on the
    // Serializer associates types, so settle for checking size_of and align_of.
    fingerprint: Fingerprint,
}

impl Any {
    pub fn new<T>(t: T) -> Self {
        let ptr: *mut T = Box::into_raw(Box::new(t));
        Any {
            ptr: ptr as *mut (),
            fingerprint: Fingerprint::of::<T>(),
        }
    }

    pub fn view<T>(&mut self) -> &mut T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        unsafe { &mut *ptr }
    }

    pub fn take<T>(self) -> T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        let box_t = unsafe { Box::from_raw(ptr) };
        *box_t
    }
}

#[derive(Eq, PartialEq)]
struct Fingerprint {
    size: usize,
    align: usize,
}

impl Fingerprint {
    fn of<T>() -> Fingerprint {
        Fingerprint {
            size: mem::size_of::<T>(),
            align: mem::align_of::<T>(),
        }
    }
}
<commit_msg>Update reason for not using TypeId<commit_after>use std::mem;

pub struct Any {
    ptr: *mut (),
    // Can't get a real TypeId because we don't have 'static bounds on the
    // Serializer associates types, so settle for checking size_of and align_of.
    fingerprint: Fingerprint,
}

impl Any {
    pub fn new<T>(t: T) -> Self {
        let ptr: *mut T = Box::into_raw(Box::new(t));
        Any {
            ptr: ptr as *mut (),
            fingerprint: Fingerprint::of::<T>(),
        }
    }

    pub fn view<T>(&mut self) -> &mut T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        unsafe { &mut *ptr }
    }

    pub fn take<T>(self) -> T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        let box_t = unsafe { Box::from_raw(ptr) };
        *box_t
    }
}

#[derive(Eq, PartialEq)]
struct Fingerprint {
    size: usize,
    align: usize,
}

impl Fingerprint {
    fn of<T>() -> Fingerprint {
        Fingerprint {
            size: mem::size_of::<T>(),
            align: mem::align_of::<T>(),
        }
    }
}
<|endoftext|>


<commit_before>macro_rules! endpoint {
    // No reexports besides `Request` and `Response`.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, []);
    };

    // No imports from super.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident, [$($import:ident),*]) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, [$($import),*], []);
    };

    // Explicit case.
    (
        $(#[$attr:meta])+
        [$($outer_mod:ident),*],
        $inner_mod:ident,
        [$($import:ident),*],
        [$($super_import:ident),*]
    ) => {
        #[$($attr)+]
        pub mod $inner_mod {
            use futures::Future;
            use hyper::client::connect::Connect;
            use ruma_client_api::$($outer_mod::)*$inner_mod::Endpoint;
            $(use super::$super_import;)*
            pub use ruma_client_api::$($outer_mod::)*$inner_mod::{
                Request,
                Response,
                $($import),*
            };

            use crate::{Client, Error};

            /// Make a request to this API endpoint.
            pub fn call<C>(
                client: Client<C>,
                request: Request,
            ) -> impl Future<Item = Response, Error = Error>
            where
                C: Connect + 'static,
            {
                client.request::<Endpoint>(request)
            }
        }
    };
}

/// Endpoints for the r0.x.x versions of the client API specification.
pub mod r0 {
    /// Account registration and management.
    pub mod account {
        endpoint!(
            /// Change the password for an account on this homeserver.
            [r0, account],
            change_password
        );

        endpoint!(
            /// Deactivate the user's account, removing all ability for the user to log in again.
            [r0, account],
            deactivate
        );

        endpoint!(
            /// Register for an account on this homeserver.
            [r0, account],
            register,
            [AuthenticationData, RegistrationKind]
        );

        endpoint!(
            /// Request a password change token by email.
            [r0, account],
            request_password_change_token
        );

        endpoint!(
            /// Request an account registration token by email.
            [r0, account],
            request_register_token
        );
    }

    /// Room aliases.
    pub mod alias {
        endpoint!(
            /// Create a new mapping from a room alias to a room ID.
            [r0, alias],
            create_alias
        );

        endpoint!(
            /// Remove a mapping from a room alias to a room ID.
            [r0, alias],
            delete_alias
        );

        endpoint!(
            /// Resolve a room alias to the corresponding room ID.
            [r0, alias],
            get_alias
        );
    }

    /// Client configuration.
    pub mod config {
        endpoint!(
            /// Set account data for the user.
            [r0, config],
            set_global_account_data
        );

        endpoint!(
            /// Set account data scoped to a room for the user.
            [r0, config],
            set_room_account_data
        );
    }

    /// Account contact information.
    pub mod contact {
        endpoint!(
            /// Add contact information to the user's account.
            [r0, contact],
            create_contact,
            [ThreePidCredentials]
        );

        endpoint!(
            /// Get a list of the third party identifiers that the homeserver has associated with the user's account.
            [r0, contact],
            get_contacts,
            [Medium, ThirdPartyIdentifier]
        );

        endpoint!(
            /// Request an email address verification token by email.
            [r0, contact],
            request_contact_verification_token
        );
    }

    /// Event context.
    pub mod context {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, context],
            get_context
        );
    }

    /// The public room directory.
    pub mod directory {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, directory],
            get_public_rooms,
            [PublicRoomsChunk]
        );
    }

    /// Event filters.
    pub mod filter {
        pub use ruma_client_api::r0::filter::{
            EventFormat, Filter, FilterDefinition, RoomEventFilter, RoomFilter,
        };

        endpoint!(
            /// Create a new filter.
            [r0, filter],
            create_filter
        );

        endpoint!(
            /// Get a filter.
            [r0, filter],
            get_filter
        );
    }

    /// Media repository.
    pub mod media {
        endpoint!(
            /// Upload media to the media repository.
            [r0, media],
            create_content
        );

        endpoint!(
            /// Download media from the media repository.
            [r0, media],
            get_content
        );

        endpoint!(
            /// Download a thumbnail image  for the media in the media repository.
            [r0, media],
            get_content_thumbnail,
            [Method]
        );
    }

    /// Room membership.
    pub mod membership {
        pub use ruma_client_api::r0::membership::ThirdPartySigned;

        endpoint!(
            /// Ban a user from a room.
            [r0, membership],
            ban_user
        );

        endpoint!(
            /// Permanently forget a room.
            [r0, membership],
            forget_room
        );

        endpoint!(
            /// Invite a user to a room.
            [r0, membership],
            invite_user
        );

        endpoint!(
            /// Join a room using its ID.
            [r0, membership],
            join_room_by_id
        );

        endpoint!(
            /// Join a room using its ID or an alias.
            [r0, membership],
            join_room_by_id_or_alias
        );

        endpoint!(
            /// Kick a user from a room.
            [r0, membership],
            kick_user
        );

        endpoint!(
            /// Leave a room.
            [r0, membership],
            leave_room
        );

        endpoint!(
            /// Unban a user from a room.
            [r0, membership],
            unban_user
        );
    }

    /// User presence.
    pub mod presence {
        endpoint!(
            /// Get a user's presence state.
            [r0, presence],
            get_presence
        );

        endpoint!(
            /// Get a list of presence events for users on the presence subscription list.
            [r0, presence],
            get_subscribed_presences
        );

        endpoint!(
            /// Set a user's presence state.
            [r0, presence],
            set_presence
        );

        endpoint!(
            /// Add or remove users from the presence subscription list.
            [r0, presence],
            update_presence_subscriptions
        );
    }

    /// User profiles.
    pub mod profile {
        endpoint!(
            /// Get the URL for a user's avatar.
            [r0, profile],
            get_avatar_url
        );

        endpoint!(
            /// Get a user's display name.
            [r0, profile],
            get_display_name
        );

        endpoint!(
            /// Get a user's full profile.
            [r0, profile],
            get_profile
        );

        endpoint!(
            /// Set the URL to the user's avatar.
            [r0, profile],
            set_avatar_url
        );

        endpoint!(
            /// Set the user's display name.
            [r0, profile],
            set_display_name
        );
    }

    /// Push notifications.
    pub mod push {}

    /// Event receipts.
    pub mod receipt {
        endpoint!(
            /// Update a receipt marker to point to a given event.
            [r0, receipt],
            create_receipt,
            [ReceiptType]
        );
    }

    /// Event redaction.
    pub mod redact {
        endpoint!(
            /// Redact an event from a room.
            [r0, redact],
            redact_event
        );
    }

    /// Room creation.
    pub mod room {
        endpoint!(
            /// Create a room.
            [r0, room],
            create_room,
            [CreationContent, RoomPreset, Visibility]
        );
    }

    /// Event searches.
    pub mod search {
        endpoint!(
            /// Search for events.
            [r0, search],
            search_events,
            [
                Categories,
                Criteria,
                EventContext,
                EventContextResult,
                Grouping,
                Groupings,
                ResultCategories,
                ResultGroup,
                RoomEventResults,
                SearchResult,
                UserProfile,
                GroupingKey,
                OrderBy,
                SearchKeys
            ]
        );
    }

    /// Sending events.
    pub mod send {
        endpoint!(
            /// Send a message to a room.
            [r0, send],
            send_message_event
        );

        endpoint!(
            /// Send a state event with an empty state key.
            [r0, send],
            send_state_event_for_empty_key
        );

        endpoint!(
            /// Send a state event with a particular state key.
            [r0, send],
            send_state_event_for_key
        );
    }

    /// Server administration.
    pub mod server {
        endpoint!(
            /// Get administrative information about a user.
            [r0, server],
            get_user_info,
            [ConnectionInfo, DeviceInfo, SessionInfo]
        );
    }

    /// User session management.
    pub mod session {
        endpoint!(
            /// Log in to an account, creating an access token.
            [r0, session],
            login,
            [LoginType, Medium]
        );

        endpoint!(
            /// Log out of an account by invalidating the access token.
            [r0, session],
            logout
        );
    }

    /// Getting and synchronizing events.
    pub mod sync {
        endpoint!(
            /// Get the list of members for a room.
            [r0, sync],
            get_member_events
        );

        endpoint!(
            /// Get message and state events for a room.
            [r0, sync],
            get_message_events,
            [Direction]
        );

        endpoint!(
            /// Get the state events for the current state of a room.
            [r0, sync],
            get_state_events
        );

        endpoint!(
            /// Get a particular state event with an empty state key for a room.
            [r0, sync],
            get_state_events_for_empty_key
        );

        endpoint!(
            /// Get a particular state event with a particular state key for a room.
            [r0, sync],
            get_state_events_for_key
        );

        endpoint!(
            /// Synchronize the client's state with the latest state on the homeserver.
            [r0, sync],
            sync_events,
            [
                AccountData,
                Ephemeral,
                Filter,
                InviteState,
                InvitedRoom,
                JoinedRoom,
                LeftRoom,
                Presence,
                Rooms,
                SetPresence,
                State,
                Timeline,
                UnreadNotificationsCount
            ]
        );
    }

    /// Tagging rooms.
    pub mod tag {
        endpoint!(
            /// Create a tag on a room.
            [r0, tag],
            create_tag
        );

        endpoint!(
            /// Delete a tag on a room.
            [r0, tag],
            delete_tag
        );

        endpoint!(
            /// Get the user's tags for a room.
            [r0, tag],
            get_tags
        );
    }

    /// Typing notifications.
    pub mod typing {
        endpoint!(
            /// Indicate that the user is currently typing.
            [r0, typing],
            create_typing_event
        );
    }

    /// Voice over IP.
    pub mod voip {
        endpoint!(
            /// Get credentials for initiating voice over IP calls via a TURN server.
            [r0, voip],
            get_turn_server_info
        );
    }
}

/// Endpoints that cannot change with new versions of the Matrix specification.
pub mod unversioned {
    endpoint!(
        /// Get the versions of the specification supported by this homeserver.
        [unversioned],
        get_supported_versions
    );
}
<commit_msg>Add joined rooms membership endpoint.<commit_after>macro_rules! endpoint {
    // No reexports besides `Request` and `Response`.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, []);
    };

    // No imports from super.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident, [$($import:ident),*]) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, [$($import),*], []);
    };

    // Explicit case.
    (
        $(#[$attr:meta])+
        [$($outer_mod:ident),*],
        $inner_mod:ident,
        [$($import:ident),*],
        [$($super_import:ident),*]
    ) => {
        #[$($attr)+]
        pub mod $inner_mod {
            use futures::Future;
            use hyper::client::connect::Connect;
            use ruma_client_api::$($outer_mod::)*$inner_mod::Endpoint;
            $(use super::$super_import;)*
            pub use ruma_client_api::$($outer_mod::)*$inner_mod::{
                Request,
                Response,
                $($import),*
            };

            use crate::{Client, Error};

            /// Make a request to this API endpoint.
            pub fn call<C>(
                client: Client<C>,
                request: Request,
            ) -> impl Future<Item = Response, Error = Error>
            where
                C: Connect + 'static,
            {
                client.request::<Endpoint>(request)
            }
        }
    };
}

/// Endpoints for the r0.x.x versions of the client API specification.
pub mod r0 {
    /// Account registration and management.
    pub mod account {
        endpoint!(
            /// Change the password for an account on this homeserver.
            [r0, account],
            change_password
        );

        endpoint!(
            /// Deactivate the user's account, removing all ability for the user to log in again.
            [r0, account],
            deactivate
        );

        endpoint!(
            /// Register for an account on this homeserver.
            [r0, account],
            register,
            [AuthenticationData, RegistrationKind]
        );

        endpoint!(
            /// Request a password change token by email.
            [r0, account],
            request_password_change_token
        );

        endpoint!(
            /// Request an account registration token by email.
            [r0, account],
            request_register_token
        );
    }

    /// Room aliases.
    pub mod alias {
        endpoint!(
            /// Create a new mapping from a room alias to a room ID.
            [r0, alias],
            create_alias
        );

        endpoint!(
            /// Remove a mapping from a room alias to a room ID.
            [r0, alias],
            delete_alias
        );

        endpoint!(
            /// Resolve a room alias to the corresponding room ID.
            [r0, alias],
            get_alias
        );
    }

    /// Client configuration.
    pub mod config {
        endpoint!(
            /// Set account data for the user.
            [r0, config],
            set_global_account_data
        );

        endpoint!(
            /// Set account data scoped to a room for the user.
            [r0, config],
            set_room_account_data
        );
    }

    /// Account contact information.
    pub mod contact {
        endpoint!(
            /// Add contact information to the user's account.
            [r0, contact],
            create_contact,
            [ThreePidCredentials]
        );

        endpoint!(
            /// Get a list of the third party identifiers that the homeserver has associated with the user's account.
            [r0, contact],
            get_contacts,
            [Medium, ThirdPartyIdentifier]
        );

        endpoint!(
            /// Request an email address verification token by email.
            [r0, contact],
            request_contact_verification_token
        );
    }

    /// Event context.
    pub mod context {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, context],
            get_context
        );
    }

    /// The public room directory.
    pub mod directory {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, directory],
            get_public_rooms,
            [PublicRoomsChunk]
        );
    }

    /// Event filters.
    pub mod filter {
        pub use ruma_client_api::r0::filter::{
            EventFormat, Filter, FilterDefinition, RoomEventFilter, RoomFilter,
        };

        endpoint!(
            /// Create a new filter.
            [r0, filter],
            create_filter
        );

        endpoint!(
            /// Get a filter.
            [r0, filter],
            get_filter
        );
    }

    /// Media repository.
    pub mod media {
        endpoint!(
            /// Upload media to the media repository.
            [r0, media],
            create_content
        );

        endpoint!(
            /// Download media from the media repository.
            [r0, media],
            get_content
        );

        endpoint!(
            /// Download a thumbnail image  for the media in the media repository.
            [r0, media],
            get_content_thumbnail,
            [Method]
        );
    }

    /// Room membership.
    pub mod membership {
        pub use ruma_client_api::r0::membership::ThirdPartySigned;

        endpoint!(
            /// Ban a user from a room.
            [r0, membership],
            ban_user
        );

        endpoint!(
            /// Permanently forget a room.
            [r0, membership],
            forget_room
        );

        endpoint!(
            /// Invite a user to a room.
            [r0, membership],
            invite_user
        );

        endpoint!(
            /// Join a room using its ID.
            [r0, membership],
            join_room_by_id
        );

        endpoint!(
            /// Join a room using its ID or an alias.
            [r0, membership],
            join_room_by_id_or_alias
        );

        endpoint!(
            /// Get a list of the user's current rooms.
            [r0, membership],
            joined_rooms
        );

        endpoint!(
            /// Kick a user from a room.
            [r0, membership],
            kick_user
        );

        endpoint!(
            /// Leave a room.
            [r0, membership],
            leave_room
        );

        endpoint!(
            /// Unban a user from a room.
            [r0, membership],
            unban_user
        );
    }

    /// User presence.
    pub mod presence {
        endpoint!(
            /// Get a user's presence state.
            [r0, presence],
            get_presence
        );

        endpoint!(
            /// Get a list of presence events for users on the presence subscription list.
            [r0, presence],
            get_subscribed_presences
        );

        endpoint!(
            /// Set a user's presence state.
            [r0, presence],
            set_presence
        );

        endpoint!(
            /// Add or remove users from the presence subscription list.
            [r0, presence],
            update_presence_subscriptions
        );
    }

    /// User profiles.
    pub mod profile {
        endpoint!(
            /// Get the URL for a user's avatar.
            [r0, profile],
            get_avatar_url
        );

        endpoint!(
            /// Get a user's display name.
            [r0, profile],
            get_display_name
        );

        endpoint!(
            /// Get a user's full profile.
            [r0, profile],
            get_profile
        );

        endpoint!(
            /// Set the URL to the user's avatar.
            [r0, profile],
            set_avatar_url
        );

        endpoint!(
            /// Set the user's display name.
            [r0, profile],
            set_display_name
        );
    }

    /// Push notifications.
    pub mod push {}

    /// Event receipts.
    pub mod receipt {
        endpoint!(
            /// Update a receipt marker to point to a given event.
            [r0, receipt],
            create_receipt,
            [ReceiptType]
        );
    }

    /// Event redaction.
    pub mod redact {
        endpoint!(
            /// Redact an event from a room.
            [r0, redact],
            redact_event
        );
    }

    /// Room creation.
    pub mod room {
        endpoint!(
            /// Create a room.
            [r0, room],
            create_room,
            [CreationContent, RoomPreset, Visibility]
        );
    }

    /// Event searches.
    pub mod search {
        endpoint!(
            /// Search for events.
            [r0, search],
            search_events,
            [
                Categories,
                Criteria,
                EventContext,
                EventContextResult,
                Grouping,
                Groupings,
                ResultCategories,
                ResultGroup,
                RoomEventResults,
                SearchResult,
                UserProfile,
                GroupingKey,
                OrderBy,
                SearchKeys
            ]
        );
    }

    /// Sending events.
    pub mod send {
        endpoint!(
            /// Send a message to a room.
            [r0, send],
            send_message_event
        );

        endpoint!(
            /// Send a state event with an empty state key.
            [r0, send],
            send_state_event_for_empty_key
        );

        endpoint!(
            /// Send a state event with a particular state key.
            [r0, send],
            send_state_event_for_key
        );
    }

    /// Server administration.
    pub mod server {
        endpoint!(
            /// Get administrative information about a user.
            [r0, server],
            get_user_info,
            [ConnectionInfo, DeviceInfo, SessionInfo]
        );
    }

    /// User session management.
    pub mod session {
        endpoint!(
            /// Log in to an account, creating an access token.
            [r0, session],
            login,
            [LoginType, Medium]
        );

        endpoint!(
            /// Log out of an account by invalidating the access token.
            [r0, session],
            logout
        );
    }

    /// Getting and synchronizing events.
    pub mod sync {
        endpoint!(
            /// Get the list of members for a room.
            [r0, sync],
            get_member_events
        );

        endpoint!(
            /// Get message and state events for a room.
            [r0, sync],
            get_message_events,
            [Direction]
        );

        endpoint!(
            /// Get the state events for the current state of a room.
            [r0, sync],
            get_state_events
        );

        endpoint!(
            /// Get a particular state event with an empty state key for a room.
            [r0, sync],
            get_state_events_for_empty_key
        );

        endpoint!(
            /// Get a particular state event with a particular state key for a room.
            [r0, sync],
            get_state_events_for_key
        );

        endpoint!(
            /// Synchronize the client's state with the latest state on the homeserver.
            [r0, sync],
            sync_events,
            [
                AccountData,
                Ephemeral,
                Filter,
                InviteState,
                InvitedRoom,
                JoinedRoom,
                LeftRoom,
                Presence,
                Rooms,
                SetPresence,
                State,
                Timeline,
                UnreadNotificationsCount
            ]
        );
    }

    /// Tagging rooms.
    pub mod tag {
        endpoint!(
            /// Create a tag on a room.
            [r0, tag],
            create_tag
        );

        endpoint!(
            /// Delete a tag on a room.
            [r0, tag],
            delete_tag
        );

        endpoint!(
            /// Get the user's tags for a room.
            [r0, tag],
            get_tags
        );
    }

    /// Typing notifications.
    pub mod typing {
        endpoint!(
            /// Indicate that the user is currently typing.
            [r0, typing],
            create_typing_event
        );
    }

    /// Voice over IP.
    pub mod voip {
        endpoint!(
            /// Get credentials for initiating voice over IP calls via a TURN server.
            [r0, voip],
            get_turn_server_info
        );
    }
}

/// Endpoints that cannot change with new versions of the Matrix specification.
pub mod unversioned {
    endpoint!(
        /// Get the versions of the specification supported by this homeserver.
        [unversioned],
        get_supported_versions
    );
}
<|endoftext|>


<commit_before>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
pub enum WatchEvent {
    /// A new value is available.
    Value{from: ServiceId, value: Value},

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was connected or
    /// disconnected. Value `SetChanged(n)` means that the set now
    /// holds `n` input services.
    SetChanged(usize),
}

/// The public API.
pub trait API {
    /// Get a list of nodes matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/node/list`
    fn get_nodes(&NodeRequest) -> Vec<Node>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/node/tag/$NodeId`
    fn put_node_tag(&NodeId, String) -> Result<(), Error>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/node/tag/$NodeId`
    fn delete_node_tag(&NodeId, String) -> Result<(), Error>;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/service/list`
    fn get_input_services(&InputRequest) -> Vec<Service<Input>>;
    fn get_output_services(&OutputRequest) -> Vec<Service<Output>>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate service.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/tag/$ServiceId`
    fn put_service_tag(&ServiceId, String) -> Result<(), Error>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate services.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/service/tag/$ServiceId`
    fn delete_service_tag(&ServiceId, String) -> Result<(), Error>;

    /// Read one value from an input enpoint
    ///
    /// # REST API
    ///
    /// GET /api/v1/service/value/$ServiceId
    fn get_service_value(&Service<Input>) -> Result<Value, Error>;

    /// Send one value to an output enpoint
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/value/$ServiceId`
    fn put_service_value(&Service<Output>, Value) -> Result<(), Error>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/service/watch/$ServiceId`
    fn register_service_watch<F>(WatchOptions, cb: F)
                                 -> Result<Self::WatchGuard, Error>
        where F: Fn(WatchEvent) + Send;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    source: InputRequest,
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: InputRequest::new(),
        }
    }

    /// Restrict to input services in a given set. Note that if all
    /// the inputs do not have the same `ServiceKind`, the call to
    /// `register_service_watch` will fail.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  as it changes.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }
}
<commit_msg>Refined WatchEvent on inputs being added/removed<commit_after>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The service that sent the value.
        from: ServiceId,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    InputRemoved(ServiceId),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    InputAdded(ServiceId),
}

/// The public API.
pub trait API {
    /// Get a list of nodes matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/node/list`
    fn get_nodes(&NodeRequest) -> Vec<Node>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/node/tag/$NodeId`
    fn put_node_tag(&NodeId, String) -> Result<(), Error>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/node/tag/$NodeId`
    fn delete_node_tag(&NodeId, String) -> Result<(), Error>;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/service/list`
    fn get_input_services(&InputRequest) -> Vec<Service<Input>>;
    fn get_output_services(&OutputRequest) -> Vec<Service<Output>>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate service.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/tag/$ServiceId`
    fn put_service_tag(&ServiceId, String) -> Result<(), Error>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate services.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/service/tag/$ServiceId`
    fn delete_service_tag(&ServiceId, String) -> Result<(), Error>;

    /// Read one value from an input enpoint
    ///
    /// # REST API
    ///
    /// GET /api/v1/service/value/$ServiceId
    fn get_service_value(&Service<Input>) -> Result<Value, Error>;

    /// Send one value to an output enpoint
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/value/$ServiceId`
    fn put_service_value(&Service<Output>, Value) -> Result<(), Error>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/service/watch/$ServiceId`
    fn register_service_watch<F>(WatchOptions, cb: F)
                                 -> Result<Self::WatchGuard, Error>
        where F: Fn(WatchEvent) + Send;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    source: InputRequest,
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: InputRequest::new(),
        }
    }

    /// Restrict to input services in a given set. Note that if all
    /// the inputs do not have the same `ServiceKind`, the call to
    /// `register_service_watch` will fail.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  as it changes.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Error, Formatter};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BinOp {
    And,
    Ge,
    Gt,
    Eql,       // ==
    EqlStrict, // ===
    Le,
    Lt,
    Minus,
    Neq,       // !=
    NeqStrict, // !==
    Or,
    Plus,
    Slash,
    Star,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 110,
    Sign = 100,
    Inc = 90,
    Mult = 70,
    Add = 60,
    Equality = 50,
    And = 40,
    Or = 30,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            BinOp::And => Precedence::And,
            BinOp::Ge | BinOp::Gt | BinOp::Eql | BinOp::Le | BinOp::Lt |
                BinOp::Neq | BinOp::EqlStrict | BinOp::NeqStrict
                => Precedence::Equality,
            BinOp::Or => Precedence::Or,
            BinOp::Minus | BinOp::Plus => Precedence::Add,
            BinOp::Slash | BinOp::Star => Precedence::Mult,
        }
    }

    pub fn is_commutative(&self) -> bool {
        match *self {
            BinOp::Minus | BinOp::Slash => false,
            _ => true
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            BinOp::And => write!(fmt, "&&"),
            BinOp::Ge => write!(fmt, ">="),
            BinOp::Gt => write!(fmt, ">"),
            BinOp::Eql => write!(fmt, "=="),
            BinOp::EqlStrict => write!(fmt, "==="),
            BinOp::Le => write!(fmt, "<="),
            BinOp::Lt => write!(fmt, "<"),
            BinOp::Minus => write!(fmt, "-"),
            BinOp::Neq => write!(fmt, "!="),
            BinOp::NeqStrict => write!(fmt, "!=="),
            BinOp::Or => write!(fmt, "||"),
            BinOp::Plus => write!(fmt, "+"),
            BinOp::Slash => write!(fmt, "/"),
            BinOp::Star => write!(fmt, "*"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum Exp {
    BinExp(Box<Exp>, BinOp, Box<Exp>),
    Bool(bool),
    Call(Box<Exp>, Vec<Box<Exp>>),
    Defun(Option<String>, Vec<String>, Box<Stmt>),
    Float(f64),
    InstanceVar(Box<Exp>, String),
    Neg(Box<Exp>),
    Null,
    NewObject(Box<Exp>, Vec<Box<Exp>>),
    Object(Vec<(String, Box<Exp>)>),
    Pos(Box<Exp>),
    PostDec(Box<Exp>),
    PostInc(Box<Exp>),
    PreDec(Box<Exp>),
    PreInc(Box<Exp>),
    TypeOf(Box<Exp>),
    Str(String),
    Undefined,
    Var(String),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            Exp::BinExp(_, ref o, _) => o.precedence(),
            Exp::Neg(_) | Exp::Pos(_) => Precedence::Sign,
            Exp::PostDec(_) | Exp::PostInc(_) | Exp::PreDec(_) | Exp::PreInc(_) => Precedence::Inc,
            _ => Precedence::Const
        }
    }
}

macro_rules! group {
    ($e:expr, $p:expr) => {
        if $e.precedence() < $p {
            format!("({})", $e)
        } else {
            format!("{}", $e)
        }
    }
}

impl Exp {
    fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        match *self {
            Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                // Put grouping parentheses if the left subexpression has a lower-precedence
                // operator, e.g. (1 + 2) * 3
                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right_prec = e2.precedence();

                // Put grouping parentheses around the right subexpression if it has a
                // lower-precedence operator,  __OR__ if `o` is not commutative and the precedence
                // is the same, e.g. (1 + 2) * 3 __OR__ 1 - (2 + 3)
                let right = if prec > right_prec || (!o.is_commutative() && prec == right_prec) {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
            Exp::Bool(b) => write!(fmt, "{}", b),
            Exp::Call(ref func, ref args) => {
                try!(write!(fmt, "{}(", func));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg));
                }

                write!(fmt, ")")
            }
            Exp::Defun(ref opt, ref params, ref body) => {
                try!(write!(fmt, "function"));

                if let &Some(ref func) = opt {
                    try!(write!(fmt, " {}", func));
                }

                try!(write!(fmt, "("));

                for (i, param) in params.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", param));
                }

                try!(write!(fmt, ") {{\n"));
                try!(body.fmt_helper(&mut fmt, indent_level + 2));

                let indent : String = (0..indent_level).map(|_| " ").collect();

                write!(fmt, "{}}}", indent)
            }
            Exp::Float(f) => write!(fmt, "{}", f),
            Exp::InstanceVar(ref obj, ref name) => write!(fmt, "{}.{}", obj, name),
            Exp::Neg(ref e) => write!(fmt, "-{}", group!(e, Precedence::Sign)),
            Exp::NewObject(ref name, ref args) => {
                try!(write!(fmt, "new {}(", name));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg))
                }

                write!(fmt, ")")
            }
            Exp::Null => write!(fmt, "null"),
            Exp::Object(ref properties) => {
                if properties.is_empty() {
                    return write!(fmt, "{{}}");
                }

                try!(writeln!(fmt, "{{"));

                let indent : String = (0..indent_level + 2).map(|_| " ").collect();

                for (i, &(ref name, ref prop)) in properties.iter().enumerate() {
                    if i != 0 {
                        try!(writeln!(fmt, ","));
                    }

                    try!(write!(fmt, "{}{}: ", indent, name));
                    try!(prop.fmt_helper(&mut fmt, indent_level + 2));
                }

                write!(fmt, "\n}}")
            }
            Exp::Pos(ref e) => write!(fmt, "+{}", group!(e, Precedence::Sign)),
            Exp::PostDec(ref e) => write!(fmt, "{}--", group!(e, Precedence::Inc)),
            Exp::PostInc(ref e) => write!(fmt, "{}++", group!(e, Precedence::Inc)),
            Exp::PreDec(ref e) => write!(fmt, "--{}", group!(e, Precedence::Inc)),
            Exp::PreInc(ref e) => write!(fmt, "++{}", group!(e, Precedence::Inc)),
            Exp::TypeOf(ref e) => write!(fmt, "typeof {}", e),
            Exp::Str(ref s) => write!(fmt, "\"{}\"", s),
            Exp::Undefined => write!(fmt, "undefined"),
            Exp::Var(ref v) => write!(fmt, "{}", v),
        }
    }
}

impl Display for Exp {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}

#[derive(Clone, Debug)]
pub enum Stmt {
    Assign(String, Exp),
    BareExp(Exp),
    Decl(String, Exp),
    Empty,
    If(Exp, Box<Stmt>, Option<Box<Stmt>>),
    Ret(Exp),
    Seq(Box<Stmt>, Box<Stmt>),
    Try(Box<Stmt>, Option<Box<Stmt>>, Option<Box<Stmt>>),
    Throw(Box<Exp>),
    While(Exp, Box<Stmt>),
}

impl Stmt {
    pub fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! indented_stmt {
            ($stmt:expr) => {
                try!($stmt.fmt_helper(&mut fmt, indent_level + 2))
            }
        }

        macro_rules! exp_semi {
            ($exp:expr) => {{
                try!($exp.fmt_helper(&mut fmt, indent_level));
                writeln!(fmt, ";")
            }}
        }

        let indent : String = (0..indent_level).map(|_| " ").collect();

        match *self {
            Stmt::Assign(ref v, ref exp) => {
                try!(write!(fmt, "{}{} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::BareExp(ref exp) => {
                try!(write!(fmt, "{}", indent));
                exp_semi!(exp)
            }
            Stmt::Decl(ref v, ref exp) => {
                try!(write!(fmt, "{}var {} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::Empty => Ok(()),
            Stmt::If(ref e, ref s, ref els) => {
                try!(write!(fmt, "{}if (", indent));
                try!(e.fmt_helper(&mut fmt, indent_level + 2));
                try!(writeln!(fmt, ") {{\n"));
                indented_stmt!(s);

                if let &Some(ref stmt) = els {
                    try!(write!(fmt, "{}else {{\n", indent));
                    indented_stmt!(stmt);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                Ok(())
            }
            Stmt::Ret(ref e) => {
                try!(write!(fmt, "{}return ", indent));
                exp_semi!(e)
            }
            Stmt::Seq(ref s1, ref s2) => {
                try!(s1.fmt_helper(&mut fmt, indent_level));
                s2.fmt_helper(&mut fmt, indent_level)
            }
            Stmt::Throw(ref e) => {
                write!(fmt, "{}throw {}", indent, e)
            }
            Stmt::Try(ref stmt, ref o1, ref o2) => {
                try!(write!(fmt, "{}try {{\n", indent));
                try!(stmt.fmt_helper(&mut fmt, indent_level + 2));
                try!(write!(fmt, "{}}}\n", indent));

                if let &Some(ref catch_stmt) = o1 {
                    try!(write!(fmt, "{}catch {{\n", indent));
                    try!(catch_stmt.fmt_helper(&mut fmt, indent_level + 2));
                    try!(write!(fmt, "{}}}\n", indent));
                }

                if let &Some(ref finally_stmt) = o2 {
                    try!(write!(fmt, "{}finally {{\n", indent));
                    try!(finally_stmt.fmt_helper(&mut fmt, indent_level + 2));
                    try!(write!(fmt, "{}}}\n", indent));
                }

                Ok(())
            }
            Stmt::While(ref exp, ref stmt) => {
                try!(write!(fmt, "{}while ({}) {{\n", indent, exp));
                try!(stmt.fmt_helper(&mut fmt, indent_level + 2));
                write!(fmt, "{}}}\n", indent)
            }
        }
    }
}

impl Display for Stmt {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}
<commit_msg>use Vec<Stmt> instead of Seq(Stmt, Stmt)<commit_after>use std::fmt::{Display, Error, Formatter};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BinOp {
    And,
    Ge,
    Gt,
    Eql,       // ==
    EqlStrict, // ===
    Le,
    Lt,
    Minus,
    Neq,       // !=
    NeqStrict, // !==
    Or,
    Plus,
    Slash,
    Star,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 110,
    Sign = 100,
    Inc = 90,
    Mult = 70,
    Add = 60,
    Equality = 50,
    And = 40,
    Or = 30,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            BinOp::And => Precedence::And,
            BinOp::Ge | BinOp::Gt | BinOp::Eql | BinOp::Le | BinOp::Lt |
                BinOp::Neq | BinOp::EqlStrict | BinOp::NeqStrict
                => Precedence::Equality,
            BinOp::Or => Precedence::Or,
            BinOp::Minus | BinOp::Plus => Precedence::Add,
            BinOp::Slash | BinOp::Star => Precedence::Mult,
        }
    }

    pub fn is_commutative(&self) -> bool {
        match *self {
            BinOp::Minus | BinOp::Slash => false,
            _ => true
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            BinOp::And => write!(fmt, "&&"),
            BinOp::Ge => write!(fmt, ">="),
            BinOp::Gt => write!(fmt, ">"),
            BinOp::Eql => write!(fmt, "=="),
            BinOp::EqlStrict => write!(fmt, "==="),
            BinOp::Le => write!(fmt, "<="),
            BinOp::Lt => write!(fmt, "<"),
            BinOp::Minus => write!(fmt, "-"),
            BinOp::Neq => write!(fmt, "!="),
            BinOp::NeqStrict => write!(fmt, "!=="),
            BinOp::Or => write!(fmt, "||"),
            BinOp::Plus => write!(fmt, "+"),
            BinOp::Slash => write!(fmt, "/"),
            BinOp::Star => write!(fmt, "*"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum Exp {
    BinExp(Box<Exp>, BinOp, Box<Exp>),
    Bool(bool),
    Call(Box<Exp>, Vec<Box<Exp>>),
    Defun(Option<String>, Vec<String>, Vec<Stmt>),
    Float(f64),
    InstanceVar(Box<Exp>, String),
    Neg(Box<Exp>),
    Null,
    NewObject(Box<Exp>, Vec<Box<Exp>>),
    Object(Vec<(String, Box<Exp>)>),
    Pos(Box<Exp>),
    PostDec(Box<Exp>),
    PostInc(Box<Exp>),
    PreDec(Box<Exp>),
    PreInc(Box<Exp>),
    TypeOf(Box<Exp>),
    Str(String),
    Undefined,
    Var(String),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            Exp::BinExp(_, ref o, _) => o.precedence(),
            Exp::Neg(_) | Exp::Pos(_) => Precedence::Sign,
            Exp::PostDec(_) | Exp::PostInc(_) | Exp::PreDec(_) | Exp::PreInc(_) => Precedence::Inc,
            _ => Precedence::Const
        }
    }
}

macro_rules! group {
    ($e:expr, $p:expr) => {
        if $e.precedence() < $p {
            format!("({})", $e)
        } else {
            format!("{}", $e)
        }
    }
}

impl Exp {
    fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! stmt_block {
            ($stmt:expr) => {
                for s in $stmt {
                    try!(s.fmt_helper(&mut fmt, indent_level + 2))
                }
            }
        }

        match *self {
            Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                // Put grouping parentheses if the left subexpression has a lower-precedence
                // operator, e.g. (1 + 2) * 3
                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right_prec = e2.precedence();

                // Put grouping parentheses around the right subexpression if it has a
                // lower-precedence operator,  __OR__ if `o` is not commutative and the precedence
                // is the same, e.g. (1 + 2) * 3 __OR__ 1 - (2 + 3)
                let right = if prec > right_prec || (!o.is_commutative() && prec == right_prec) {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
            Exp::Bool(b) => write!(fmt, "{}", b),
            Exp::Call(ref func, ref args) => {
                try!(write!(fmt, "{}(", func));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg));
                }

                write!(fmt, ")")
            }
            Exp::Defun(ref opt, ref params, ref body) => {
                try!(write!(fmt, "function"));

                if let &Some(ref func) = opt {
                    try!(write!(fmt, " {}", func));
                }

                try!(write!(fmt, "("));

                for (i, param) in params.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", param));
                }

                try!(write!(fmt, ") {{\n"));
                stmt_block!(body);

                let indent : String = (0..indent_level).map(|_| " ").collect();

                write!(fmt, "{}}}", indent)
            }
            Exp::Float(f) => write!(fmt, "{}", f),
            Exp::InstanceVar(ref obj, ref name) => write!(fmt, "{}.{}", obj, name),
            Exp::Neg(ref e) => write!(fmt, "-{}", group!(e, Precedence::Sign)),
            Exp::NewObject(ref name, ref args) => {
                try!(write!(fmt, "new {}(", name));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg))
                }

                write!(fmt, ")")
            }
            Exp::Null => write!(fmt, "null"),
            Exp::Object(ref properties) => {
                if properties.is_empty() {
                    return write!(fmt, "{{}}");
                }

                try!(writeln!(fmt, "{{"));

                let indent : String = (0..indent_level + 2).map(|_| " ").collect();

                for (i, &(ref name, ref prop)) in properties.iter().enumerate() {
                    if i != 0 {
                        try!(writeln!(fmt, ","));
                    }

                    try!(write!(fmt, "{}{}: ", indent, name));
                    try!(prop.fmt_helper(&mut fmt, indent_level + 2));
                }

                write!(fmt, "\n}}")
            }
            Exp::Pos(ref e) => write!(fmt, "+{}", group!(e, Precedence::Sign)),
            Exp::PostDec(ref e) => write!(fmt, "{}--", group!(e, Precedence::Inc)),
            Exp::PostInc(ref e) => write!(fmt, "{}++", group!(e, Precedence::Inc)),
            Exp::PreDec(ref e) => write!(fmt, "--{}", group!(e, Precedence::Inc)),
            Exp::PreInc(ref e) => write!(fmt, "++{}", group!(e, Precedence::Inc)),
            Exp::TypeOf(ref e) => write!(fmt, "typeof {}", e),
            Exp::Str(ref s) => write!(fmt, "\"{}\"", s),
            Exp::Undefined => write!(fmt, "undefined"),
            Exp::Var(ref v) => write!(fmt, "{}", v),
        }
    }
}

impl Display for Exp {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}

#[derive(Clone, Debug)]
pub enum Stmt {
    Assign(String, Exp),
    BareExp(Exp),
    Decl(String, Exp),
    Empty,
    If(Exp, Vec<Stmt>, Vec<Stmt>),
    Ret(Exp),
    Seq(Box<Stmt>, Box<Stmt>),
    Try(Vec<Stmt>, Vec<Stmt>, Vec<Stmt>),
    Throw(Box<Exp>),
    While(Exp, Vec<Stmt>),
}

impl Stmt {
    pub fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! stmt_block {
            ($stmt:expr) => {
                for s in $stmt {
                    try!(s.fmt_helper(&mut fmt, indent_level + 2))
                }
            }
        }

        macro_rules! indented_stmt {
            ($s:expr) => {
                try!($s.fmt_helper(&mut fmt, indent_level + 2))
            }
        }

        macro_rules! exp_semi {
            ($exp:expr) => {{
                try!($exp.fmt_helper(&mut fmt, indent_level));
                writeln!(fmt, ";")
            }}
        }

        let indent : String = (0..indent_level).map(|_| " ").collect();

        match *self {
            Stmt::Assign(ref v, ref exp) => {
                try!(write!(fmt, "{}{} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::BareExp(ref exp) => {
                try!(write!(fmt, "{}", indent));
                exp_semi!(exp)
            }
            Stmt::Decl(ref v, ref exp) => {
                try!(write!(fmt, "{}var {} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::Empty => Ok(()),
            Stmt::If(ref e, ref s, ref els) => {
                try!(write!(fmt, "{}if (", indent));
                try!(e.fmt_helper(&mut fmt, indent_level + 2));
                try!(writeln!(fmt, ") {{\n"));
                stmt_block!(s);

                if els.len() > 0 {
                    stmt_block!(s);
                }

                //if let &Some(ref stmt) = els {
                //    try!(write!(fmt, "{}else {{\n", indent));
                //    indented_stmt!(stmt);
                //    try!(write!(fmt, "{}}}\n", indent));
                //}

                Ok(())
            }
            Stmt::Ret(ref e) => {
                try!(write!(fmt, "{}return ", indent));
                exp_semi!(e)
            }
            Stmt::Seq(ref s1, ref s2) => {
                try!(s1.fmt_helper(&mut fmt, indent_level));
                s2.fmt_helper(&mut fmt, indent_level)
            }
            Stmt::Throw(ref e) => {
                write!(fmt, "{}throw {}", indent, e)
            }
            Stmt::Try(ref stmt, ref catch_block, ref finally_block) => {
                try!(write!(fmt, "{}try {{\n", indent));
                stmt_block!(stmt);
                try!(write!(fmt, "{}}}\n", indent));

                if catch_block.len() > 0 {
                    try!(write!(fmt, "{}catch {{\n", indent));
                    stmt_block!(catch_block);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                if finally_block.len() > 0 {
                    try!(write!(fmt, "{}finally {{\n", indent));
                    stmt_block!(finally_block);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                Ok(())
            }
            Stmt::While(ref exp, ref stmt) => {
                try!(write!(fmt, "{}while ({}) {{\n", indent, exp));
                stmt_block!(stmt);
                write!(fmt, "{}}}\n", indent)
            }
        }
    }
}

impl Display for Stmt {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}
<|endoftext|>


<commit_before>use cgmath::Point3;

use crate::aabb::AABB;
use crate::pt_renderer::{Intersect, Ray};
use crate::triangle::RTTriangle;

pub struct BVHNode {
    aabb: AABB,
    pub start_i: usize,
    pub end_i: usize,
    left_child_i: Option<usize>,
    right_child_i: Option<usize>,
}

impl BVHNode {
    fn new(indices: &[usize], aabbs: &[AABB], start_i: usize, end_i: usize) -> BVHNode {
        let mut node = BVHNode {
            aabb: aabbs[indices[start_i]].clone(),
            start_i, end_i,
            left_child_i: None, right_child_i: None
        };
        for &i in indices[(start_i + 1)..end_i].iter() {
            node.aabb.add_aabb(&aabbs[i]);
        }
        node
    }

    fn size(&self) -> usize {
        self.end_i - self.start_i
    }

    pub fn is_leaf(&self) -> bool {
        self.left_child_i.is_none()
    }
}

impl Intersect<'a, f32> for BVHNode {
    fn intersect(&self, ray: &Ray) -> Option<f32> {
        self.aabb.intersect(ray)
    }
}

const MAX_LEAF_SIZE: usize = 8;

pub struct BVH {
    nodes: Vec<BVHNode>,
}

impl BVH {
    pub fn empty() -> BVH {
        BVH { nodes: Vec::new() }
    }

    pub fn build_object_median(triangles: &mut Vec<RTTriangle>) -> BVH {
        let mut indices: Vec<usize> = (0..triangles.len()).collect();
        let aabbs: Vec<AABB> = triangles.iter().map(|t| t.aabb()).collect();
        let centers: Vec<Point3<f32>> = triangles.into_iter().map(|t| t.center()).collect();
        let mut nodes = Vec::with_capacity(f32::log2(triangles.len() as f32) as usize);
        nodes.push(BVHNode::new(&indices, &aabbs, 0, triangles.len()));
        let mut split_stack = vec![0usize];

        while let Some(node_i) = split_stack.pop() {
            let start_i = nodes[node_i].start_i;
            let end_i = nodes[node_i].end_i;
            let axis_i = nodes[node_i].aabb.longest_edge_i();
            indices[start_i..end_i]
                .sort_unstable_by(|&i1, &i2| {
                    let c1 = centers[i1][axis_i];
                    let c2 = centers[i2][axis_i];
                    c1.partial_cmp(&c2).unwrap()
                });
            let mid_i = (start_i + end_i) / 2;

            let left_child = BVHNode::new(&indices, &aabbs, start_i, mid_i);
            nodes[node_i].left_child_i = Some(nodes.len());
            if left_child.size() > MAX_LEAF_SIZE {
                split_stack.push(nodes.len());
            }
            nodes.push(left_child);

            let right_child = BVHNode::new(&indices, &aabbs, mid_i, end_i);
            nodes[node_i].right_child_i = Some(nodes.len());
            if right_child.size() > MAX_LEAF_SIZE {
                split_stack.push(nodes.len());
            }
            nodes.push(right_child);
        }

        // Make triangles ordering match indices ordering
        // TODO: This could be done better
        *triangles = indices.iter().map(|&i| triangles[i].clone()).collect();
        nodes.shrink_to_fit();
        BVH { nodes }
    }

    pub fn get_children(&self, node: &BVHNode) -> Option<(&BVHNode, &BVHNode)> {
        let left = if let Some(left_i) = node.left_child_i {
            &self.nodes[left_i]
        } else {
            return None;
        };
        let right = if let Some(right_i) = node.right_child_i {
            &self.nodes[right_i]
        } else {
            return None;
        };
        Some((left, right))
    }

    pub fn root(&self) -> &BVHNode {
        &self.nodes[0]
    }
}
<commit_msg>Simplify bvh get_children<commit_after>use cgmath::Point3;

use crate::aabb::AABB;
use crate::pt_renderer::{Intersect, Ray};
use crate::triangle::RTTriangle;

pub struct BVHNode {
    aabb: AABB,
    pub start_i: usize,
    pub end_i: usize,
    left_child_i: Option<usize>,
    right_child_i: Option<usize>,
}

impl BVHNode {
    fn new(indices: &[usize], aabbs: &[AABB], start_i: usize, end_i: usize) -> BVHNode {
        let mut node = BVHNode {
            aabb: aabbs[indices[start_i]].clone(),
            start_i, end_i,
            left_child_i: None, right_child_i: None
        };
        for &i in indices[(start_i + 1)..end_i].iter() {
            node.aabb.add_aabb(&aabbs[i]);
        }
        node
    }

    fn size(&self) -> usize {
        self.end_i - self.start_i
    }

    pub fn is_leaf(&self) -> bool {
        self.left_child_i.is_none()
    }
}

impl Intersect<'a, f32> for BVHNode {
    fn intersect(&self, ray: &Ray) -> Option<f32> {
        self.aabb.intersect(ray)
    }
}

const MAX_LEAF_SIZE: usize = 8;

pub struct BVH {
    nodes: Vec<BVHNode>,
}

impl BVH {
    pub fn empty() -> BVH {
        BVH { nodes: Vec::new() }
    }

    pub fn build_object_median(triangles: &mut Vec<RTTriangle>) -> BVH {
        let mut indices: Vec<usize> = (0..triangles.len()).collect();
        let aabbs: Vec<AABB> = triangles.iter().map(|t| t.aabb()).collect();
        let centers: Vec<Point3<f32>> = triangles.into_iter().map(|t| t.center()).collect();
        let mut nodes = Vec::with_capacity(f32::log2(triangles.len() as f32) as usize);
        nodes.push(BVHNode::new(&indices, &aabbs, 0, triangles.len()));
        let mut split_stack = vec![0usize];

        while let Some(node_i) = split_stack.pop() {
            let start_i = nodes[node_i].start_i;
            let end_i = nodes[node_i].end_i;
            let axis_i = nodes[node_i].aabb.longest_edge_i();
            indices[start_i..end_i]
                .sort_unstable_by(|&i1, &i2| {
                    let c1 = centers[i1][axis_i];
                    let c2 = centers[i2][axis_i];
                    c1.partial_cmp(&c2).unwrap()
                });
            let mid_i = (start_i + end_i) / 2;

            let left_child = BVHNode::new(&indices, &aabbs, start_i, mid_i);
            nodes[node_i].left_child_i = Some(nodes.len());
            if left_child.size() > MAX_LEAF_SIZE {
                split_stack.push(nodes.len());
            }
            nodes.push(left_child);

            let right_child = BVHNode::new(&indices, &aabbs, mid_i, end_i);
            nodes[node_i].right_child_i = Some(nodes.len());
            if right_child.size() > MAX_LEAF_SIZE {
                split_stack.push(nodes.len());
            }
            nodes.push(right_child);
        }

        // Make triangles ordering match indices ordering
        // TODO: This could be done better
        *triangles = indices.iter().map(|&i| triangles[i].clone()).collect();
        nodes.shrink_to_fit();
        BVH { nodes }
    }

    pub fn get_children(&self, node: &BVHNode) -> Option<(&BVHNode, &BVHNode)> {
        if let Some(left_i) = node.left_child_i {
            Some((&self.nodes[left_i], &self.nodes[node.right_child_i.unwrap()]))
        } else {
            None
        }
    }

    pub fn root(&self) -> &BVHNode {
        &self.nodes[0]
    }
}
<|endoftext|>


<commit_before>extern crate url;
extern crate hyper;
extern crate xml;

use self::url::{Url, ParseError};
use self::hyper::server::response::Response;
use self::hyper::status::StatusCode;
use self::hyper::header::Location;
use self::hyper::Client;
use self::hyper::error::Error as HyperError;
use self::xml::reader::{EventReader, XmlEvent};
use self::xml::reader::Error as XmlError;

/// The username returned by `verify_ticket` on success
pub type Name = String;
/// The error returned by `verify_ticket` on failure
pub type TicketError = String;

/// The details of a CAS server.  All URLs are the full urls
#[derive(Debug)]
pub struct CasClient {
    /// Login url (such as https://login.case.edu/cas/login)
    login_url: Url,
    /// Logout url (such as https://login.case.edu/cas/logout)
    logout_url: Url,
    /// Verify url, accessed by the server
    /// (such as https://login.case.edu/cas/serviceValidate)
    verify_url: Url,
    /// The URL of your service, which is used in the login sequence and
    /// so the login server knows where to redirect you back to
    service_url: Url,
}

/// The response from the server from `verify_ticket`
#[derive(Debug)]
pub enum ServiceResponse {
    /// Returned on successful login
    Success(Name),
    /// Returned on unsuccessful login
    Failure(TicketError),
}

#[derive(Debug)]
enum XmlMatchStatus {
    None,
    ExpectSuccess,
}

/// Errors that can happen when verifying.  Xml is unlikely.
#[derive(Debug)]
pub enum VerifyError {
    Hyper(HyperError),
    Xml(XmlError),
}

impl From<HyperError> for VerifyError {
    fn from(err: HyperError) -> VerifyError {
        VerifyError::Hyper(err)
    }
}
impl From<XmlError> for VerifyError {
    fn from(err: XmlError) -> VerifyError {
        VerifyError::Xml(err)
    }
}

impl CasClient {
    /// Construct a new CasClient. The for each url except service_url, the
    /// \<thing\>_path is concatinated onto base_url to form the Url
    pub fn new(
        base_url: &str, login_path: &str, logout_path: &str, verify_path: &str,
        service_url: &str) -> Result<CasClient, ParseError> {
        Ok(CasClient {
            login_url: try!(
                Url::parse(&format!("{}{}", base_url, login_path))),
            logout_url: try!(
                Url::parse(&format!("{}{}", base_url, logout_path))),
            verify_url: try!(
                Url::parse(&format!("{}{}", base_url, verify_path))),
            service_url: try!(Url::parse(service_url)),
        })
    }

    /// Get the URL to redirect to for login.  Use this if you are not using
    /// Hyper as your web server
    pub fn get_login_url(&self) -> String {
        let mut url = self.login_url.clone();
        let param = vec![("service", self.service_url.serialize())];
        url.set_query_from_pairs(param);
        url.serialize()
    }

    /// Consumes a hyper::server::response::Response to return a 302 redirect
    /// to the CAS login url.  Use this if you're using Hyper as you web
    /// server
    pub fn login_redirect(&self, mut res: Response) {
        {
            let mut s = res.status_mut();
            *s = StatusCode::Found;
        }
        {
            let mut h = res.headers_mut();
            h.set::<Location>(Location(self.get_login_url()));
        }
        res.send(b"").unwrap();
    }

    /// Get the URL to redirect to for logout.  Use this if you are not using
    /// Hyper as your web server
    pub fn get_logout_url(&self) -> String {
        self.logout_url.serialize()
    }

    /// Consumes a hyper::server::response::Response to return a 302 redirect
    /// to the CAS logout url.  use this if you're using Hyper as your web
    /// server
    pub fn logout_redirect(&self, mut res: Response) {
        {
            let mut s = res.status_mut();
            *s = StatusCode::Found;
        }
        {
            let mut h = res.headers_mut();
            h.set::<Location>(Location(self.get_logout_url()));
        }
        res.send(b"").unwrap();
    }

    /// When login completes, the CAS server will redirec to your service_url
    /// with the added parameter ticket=\<ticket\>.  You pass \<ticket\> here,
    /// and it checks with the CAS server whether or not the login was
    /// successful.  On success, this will return
    /// `Ok(ServiceResponse::Success(username))`, where username is the username
    /// from the CAS server.  On failure it returns
    /// `Ok(ServiceResponse::Failure(reason))`, where reason is the reason for
    /// the failure.  In the event of an http error or an xml error, this
    /// returns Err(VerifyError)
    pub fn verify_ticket(&self, ticket: &str)
        -> Result<ServiceResponse, VerifyError> {
        let mut url: Url = self.verify_url.clone();
        let param = vec![
            ("service", self.service_url.serialize()),
            ("ticket", ticket.to_string()),
        ];
        url.set_query_from_pairs(param);

        let res = try!(Client::new().get(&url.serialize()).send());

        let parser = EventReader::new(res);
        let mut status = XmlMatchStatus::None;
        for e in parser {
            match try!(e) {
                XmlEvent::StartElement { name, attributes, .. } => {
                    if name.local_name == "authenticationSuccess".to_string() {
                        status = XmlMatchStatus::ExpectSuccess;
                    } else if name.local_name == "authenticationFailure"
                        .to_string() {
                            let reason = attributes[0].value.clone();
                            return Ok(ServiceResponse::Failure(reason));
                    }
                }
                XmlEvent::Characters(s) => {
                    match status {
                        XmlMatchStatus::None => {}
                        XmlMatchStatus::ExpectSuccess => {
                            return Ok(ServiceResponse::Success(s));
                        }
                    }
                }
                _ => {}
            }
        }

        Ok(ServiceResponse::Success("mtb89".to_string()))
    }
}
<commit_msg>lack of authentication response should probably be a failure<commit_after>extern crate url;
extern crate hyper;
extern crate xml;

use self::url::{Url, ParseError};
use self::hyper::server::response::Response;
use self::hyper::status::StatusCode;
use self::hyper::header::Location;
use self::hyper::Client;
use self::hyper::error::Error as HyperError;
use self::xml::reader::{EventReader, XmlEvent};
use self::xml::reader::Error as XmlError;

/// The username returned by `verify_ticket` on success
pub type Name = String;
/// The error returned by `verify_ticket` on failure
pub type TicketError = String;

/// The details of a CAS server.  All URLs are the full urls
#[derive(Debug)]
pub struct CasClient {
    /// Login url (such as https://login.case.edu/cas/login)
    login_url: Url,
    /// Logout url (such as https://login.case.edu/cas/logout)
    logout_url: Url,
    /// Verify url, accessed by the server
    /// (such as https://login.case.edu/cas/serviceValidate)
    verify_url: Url,
    /// The URL of your service, which is used in the login sequence and
    /// so the login server knows where to redirect you back to
    service_url: Url,
}

/// The response from the server from `verify_ticket`
#[derive(Debug)]
pub enum ServiceResponse {
    /// Returned on successful login
    Success(Name),
    /// Returned on unsuccessful login
    Failure(TicketError),
}

#[derive(Debug)]
enum XmlMatchStatus {
    None,
    ExpectSuccess,
}

/// Errors that can happen when verifying.  Xml is unlikely.
#[derive(Debug)]
pub enum VerifyError {
    Hyper(HyperError),
    Xml(XmlError),
}

impl From<HyperError> for VerifyError {
    fn from(err: HyperError) -> VerifyError {
        VerifyError::Hyper(err)
    }
}
impl From<XmlError> for VerifyError {
    fn from(err: XmlError) -> VerifyError {
        VerifyError::Xml(err)
    }
}

impl CasClient {
    /// Construct a new CasClient. The for each url except service_url, the
    /// \<thing\>_path is concatinated onto base_url to form the Url
    pub fn new(
        base_url: &str, login_path: &str, logout_path: &str, verify_path: &str,
        service_url: &str) -> Result<CasClient, ParseError> {
        Ok(CasClient {
            login_url: try!(
                Url::parse(&format!("{}{}", base_url, login_path))),
            logout_url: try!(
                Url::parse(&format!("{}{}", base_url, logout_path))),
            verify_url: try!(
                Url::parse(&format!("{}{}", base_url, verify_path))),
            service_url: try!(Url::parse(service_url)),
        })
    }

    /// Get the URL to redirect to for login.  Use this if you are not using
    /// Hyper as your web server
    pub fn get_login_url(&self) -> String {
        let mut url = self.login_url.clone();
        let param = vec![("service", self.service_url.serialize())];
        url.set_query_from_pairs(param);
        url.serialize()
    }

    /// Consumes a hyper::server::response::Response to return a 302 redirect
    /// to the CAS login url.  Use this if you're using Hyper as you web
    /// server
    pub fn login_redirect(&self, mut res: Response) {
        {
            let mut s = res.status_mut();
            *s = StatusCode::Found;
        }
        {
            let mut h = res.headers_mut();
            h.set::<Location>(Location(self.get_login_url()));
        }
        res.send(b"").unwrap();
    }

    /// Get the URL to redirect to for logout.  Use this if you are not using
    /// Hyper as your web server
    pub fn get_logout_url(&self) -> String {
        self.logout_url.serialize()
    }

    /// Consumes a hyper::server::response::Response to return a 302 redirect
    /// to the CAS logout url.  use this if you're using Hyper as your web
    /// server
    pub fn logout_redirect(&self, mut res: Response) {
        {
            let mut s = res.status_mut();
            *s = StatusCode::Found;
        }
        {
            let mut h = res.headers_mut();
            h.set::<Location>(Location(self.get_logout_url()));
        }
        res.send(b"").unwrap();
    }

    /// When login completes, the CAS server will redirec to your service_url
    /// with the added parameter ticket=\<ticket\>.  You pass \<ticket\> here,
    /// and it checks with the CAS server whether or not the login was
    /// successful.  On success, this will return
    /// `Ok(ServiceResponse::Success(username))`, where username is the username
    /// from the CAS server.  On failure it returns
    /// `Ok(ServiceResponse::Failure(reason))`, where reason is the reason for
    /// the failure.  In the event of an http error or an xml error, this
    /// returns Err(VerifyError)
    pub fn verify_ticket(&self, ticket: &str)
        -> Result<ServiceResponse, VerifyError> {
        let mut url: Url = self.verify_url.clone();
        let param = vec![
            ("service", self.service_url.serialize()),
            ("ticket", ticket.to_string()),
        ];
        url.set_query_from_pairs(param);

        let res = try!(Client::new().get(&url.serialize()).send());

        let parser = EventReader::new(res);
        let mut status = XmlMatchStatus::None;
        for e in parser {
            match try!(e) {
                XmlEvent::StartElement { name, attributes, .. } => {
                    if name.local_name == "authenticationSuccess".to_string() {
                        status = XmlMatchStatus::ExpectSuccess;
                    } else if name.local_name == "authenticationFailure"
                        .to_string() {
                            let reason = attributes[0].value.clone();
                            return Ok(ServiceResponse::Failure(reason));
                    }
                }
                XmlEvent::Characters(s) => {
                    match status {
                        XmlMatchStatus::None => {}
                        XmlMatchStatus::ExpectSuccess => {
                            return Ok(ServiceResponse::Success(s));
                        }
                    }
                }
                _ => {}
            }
        }

        let error = "did not detect authentication reply from CAS server"
            .to_string();
        Ok(ServiceResponse::Failure(error))
    }
}
<|endoftext|>