<commit_before>use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, LitStr};

use super::event_parse::{EventEnumEntry, EventEnumInput, EventKind};

pub fn expand_event_type_enum(
    input: EventEnumInput,
    ruma_common: TokenStream,
) -> syn::Result<TokenStream> {
    let mut room: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut state: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut message: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut ephemeral: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut room_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut global_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut to_device: Vec<&Vec<EventEnumEntry>> = vec![];
    for event in &input.enums {
        match event.kind {
            EventKind::GlobalAccountData => global_account.push(&event.events),
            EventKind::RoomAccountData => room_account.push(&event.events),
            EventKind::Ephemeral => ephemeral.push(&event.events),
            EventKind::MessageLike => {
                message.push(&event.events);
                room.push(&event.events);
            }
            EventKind::State => {
                state.push(&event.events);
                room.push(&event.events);
            }
            EventKind::ToDevice => to_device.push(&event.events),
            EventKind::RoomRedaction
            | EventKind::Presence
            | EventKind::Decrypted
            | EventKind::HierarchySpaceChild => {}
        }
    }
    let presence = vec![EventEnumEntry {
        attrs: vec![],
        ev_type: LitStr::new("m.presence", Span::call_site()),
    }];
    let mut all = input.enums.iter().map(|e| &e.events).collect::<Vec<_>>();
    all.push(&presence);

    let mut res = TokenStream::new();

    res.extend(
        generate_enum("EventType", &all, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("RoomEventType", &room, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("StateEventType", &state, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("MessageLikeEventType", &message, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("EphemeralRoomEventType", &ephemeral, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("RoomAccountDataEventType", &room_account, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("GlobalAccountDataEventType", &global_account, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("ToDeviceEventType", &to_device, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );

    Ok(res)
}

fn generate_enum(
    ident: &str,
    input: &[&Vec<EventEnumEntry>],
    ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
    let serde = quote! { #ruma_common::exports::serde };
    let enum_doc = format!("The type of `{}` this is.", ident.strip_suffix("Type").unwrap());

    let deprecated_attr = (ident == "EventType").then(|| {
        quote! {
            #[deprecated = "use a fine-grained event type enum like RoomEventType instead"]
        }
    });

    let ident = Ident::new(ident, Span::call_site());

    let mut deduped: Vec<&EventEnumEntry> = vec![];
    for item in input.iter().copied().flatten() {
        if let Some(idx) = deduped.iter().position(|e| e.ev_type == item.ev_type) {
            // If there is a variant without config attributes use that
            if deduped[idx].attrs != item.attrs && item.attrs.is_empty() {
                deduped[idx] = item;
            }
        } else {
            deduped.push(item);
        }
    }

    let event_types = deduped.iter().map(|e| &e.ev_type);

    let variants: Vec<_> = deduped
        .iter()
        .map(|e| {
            let start = e.to_variant()?.decl();
            let data = e.has_type_fragment().then(|| quote! { (::std::string::String) });

            Ok(quote! {
                #start #data
            })
        })
        .collect::<syn::Result<_>>()?;

    let to_cow_str_match_arms: Vec<_> = deduped
        .iter()
        .map(|e| {
            let v = e.to_variant()?;
            let start = v.match_arm(quote! { Self });
            let ev_type = &e.ev_type;

            Ok(if let Some(prefix) = ev_type.value().strip_suffix(".*") {
                let fstr = prefix.to_owned() + "{}";
                quote! { #start(_s) => ::std::borrow::Cow::Owned(::std::format!(#fstr, _s)) }
            } else {
                quote! { #start => ::std::borrow::Cow::Borrowed(#ev_type) }
            })
        })
        .collect::<syn::Result<_>>()?;

    let from_str_match_arms: Vec<_> = deduped
        .iter()
        .map(|e| {
            let v = e.to_variant()?;
            let ctor = v.ctor(quote! { Self });

            let match_arm = if let Some(prefix) = e.ev_type.value().strip_suffix('*') {
                quote! {
                    // Use if-let guard once available
                    _s if _s.starts_with(#prefix) => {
                        #ctor(::std::convert::From::from(_s.strip_prefix(#prefix).unwrap()))
                    }
                }
            } else {
                let t = &e.ev_type;
                quote! { #t => #ctor }
            };

            let attrs = &e.attrs;
            Ok(quote! { #(#attrs)* #match_arm })
        })
        .collect::<syn::Result<_>>()?;

    Ok(quote! {
        #[doc = #enum_doc]
        ///
        /// This type can hold an arbitrary string. To build events with a custom type, convert it
        /// from a string with `::from() / .into()`. To check for events that are not available as a
        /// documented variant here, use its string representation, obtained through `.to_string()`.
        #deprecated_attr
        #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
        #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
        pub enum #ident {
            #(
                #[doc = #event_types]
                #variants,
            )*
            #[doc(hidden)]
            _Custom(crate::PrivOwnedStr),
        }

        #[allow(deprecated)]
        impl #ident {
            fn to_cow_str(&self) -> ::std::borrow::Cow<'_, ::std::primitive::str> {
                match self {
                    #(#to_cow_str_match_arms,)*
                    Self::_Custom(crate::PrivOwnedStr(s)) => ::std::borrow::Cow::Borrowed(s),
                }
            }
        }

        #[allow(deprecated)]
        impl ::std::fmt::Display for #ident {
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                self.to_cow_str().fmt(f)
            }
        }

        #[allow(deprecated)]
        impl ::std::convert::From<&::std::primitive::str> for #ident {
            fn from(s: &::std::primitive::str) -> Self {
                match s {
                    #(#from_str_match_arms,)*
                    _ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))),
                }
            }
        }

        #[allow(deprecated)]
        impl ::std::convert::From<::std::string::String> for #ident {
            fn from(s: ::std::string::String) -> Self {
                ::std::convert::From::from(s.as_str())
            }
        }

        #[allow(deprecated)]
        impl<'de> #serde::Deserialize<'de> for #ident {
            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
            where
                D: #serde::Deserializer<'de>
            {
                let s = #ruma_common::serde::deserialize_cow_str(deserializer)?;
                Ok(::std::convert::From::from(&s[..]))
            }
        }

        #[allow(deprecated)]
        impl #serde::Serialize for #ident {
            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
            where
                S: #serde::Serializer,
            {
                self.to_cow_str().serialize(serializer)
            }
        }
    })
}
<commit_msg>events: Implement From<MessageLikeEventType | StateEventType> for RoomEventType<commit_after>use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, LitStr};

use super::event_parse::{EventEnumEntry, EventEnumInput, EventKind};

pub fn expand_event_type_enum(
    input: EventEnumInput,
    ruma_common: TokenStream,
) -> syn::Result<TokenStream> {
    let mut room: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut state: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut message: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut ephemeral: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut room_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut global_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut to_device: Vec<&Vec<EventEnumEntry>> = vec![];
    for event in &input.enums {
        match event.kind {
            EventKind::GlobalAccountData => global_account.push(&event.events),
            EventKind::RoomAccountData => room_account.push(&event.events),
            EventKind::Ephemeral => ephemeral.push(&event.events),
            EventKind::MessageLike => {
                message.push(&event.events);
                room.push(&event.events);
            }
            EventKind::State => {
                state.push(&event.events);
                room.push(&event.events);
            }
            EventKind::ToDevice => to_device.push(&event.events),
            EventKind::RoomRedaction
            | EventKind::Presence
            | EventKind::Decrypted
            | EventKind::HierarchySpaceChild => {}
        }
    }
    let presence = vec![EventEnumEntry {
        attrs: vec![],
        ev_type: LitStr::new("m.presence", Span::call_site()),
    }];
    let mut all = input.enums.iter().map(|e| &e.events).collect::<Vec<_>>();
    all.push(&presence);

    let mut res = TokenStream::new();

    res.extend(
        generate_enum("EventType", &all, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("RoomEventType", &room, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("StateEventType", &state, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("MessageLikeEventType", &message, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("EphemeralRoomEventType", &ephemeral, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("RoomAccountDataEventType", &room_account, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("GlobalAccountDataEventType", &global_account, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );
    res.extend(
        generate_enum("ToDeviceEventType", &to_device, &ruma_common)
            .unwrap_or_else(syn::Error::into_compile_error),
    );

    Ok(res)
}

fn generate_enum(
    ident: &str,
    input: &[&Vec<EventEnumEntry>],
    ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
    let serde = quote! { #ruma_common::exports::serde };
    let enum_doc = format!("The type of `{}` this is.", ident.strip_suffix("Type").unwrap());

    let deprecated_attr = (ident == "EventType").then(|| {
        quote! {
            #[deprecated = "use a fine-grained event type enum like RoomEventType instead"]
        }
    });

    let ident = Ident::new(ident, Span::call_site());

    let mut deduped: Vec<&EventEnumEntry> = vec![];
    for item in input.iter().copied().flatten() {
        if let Some(idx) = deduped.iter().position(|e| e.ev_type == item.ev_type) {
            // If there is a variant without config attributes use that
            if deduped[idx].attrs != item.attrs && item.attrs.is_empty() {
                deduped[idx] = item;
            }
        } else {
            deduped.push(item);
        }
    }

    let event_types = deduped.iter().map(|e| &e.ev_type);

    let variants: Vec<_> = deduped
        .iter()
        .map(|e| {
            let start = e.to_variant()?.decl();
            let data = e.has_type_fragment().then(|| quote! { (::std::string::String) });

            Ok(quote! {
                #start #data
            })
        })
        .collect::<syn::Result<_>>()?;

    let to_cow_str_match_arms: Vec<_> = deduped
        .iter()
        .map(|e| {
            let v = e.to_variant()?;
            let start = v.match_arm(quote! { Self });
            let ev_type = &e.ev_type;

            Ok(if let Some(prefix) = ev_type.value().strip_suffix(".*") {
                let fstr = prefix.to_owned() + "{}";
                quote! { #start(_s) => ::std::borrow::Cow::Owned(::std::format!(#fstr, _s)) }
            } else {
                quote! { #start => ::std::borrow::Cow::Borrowed(#ev_type) }
            })
        })
        .collect::<syn::Result<_>>()?;

    let from_str_match_arms: Vec<_> = deduped
        .iter()
        .map(|e| {
            let v = e.to_variant()?;
            let ctor = v.ctor(quote! { Self });

            let match_arm = if let Some(prefix) = e.ev_type.value().strip_suffix('*') {
                quote! {
                    // Use if-let guard once available
                    _s if _s.starts_with(#prefix) => {
                        #ctor(::std::convert::From::from(_s.strip_prefix(#prefix).unwrap()))
                    }
                }
            } else {
                let t = &e.ev_type;
                quote! { #t => #ctor }
            };

            let attrs = &e.attrs;
            Ok(quote! { #(#attrs)* #match_arm })
        })
        .collect::<syn::Result<_>>()?;

    let from_ident_for_room = if ident == "StateEventType" || ident == "MessageLikeEventType" {
        let match_arms: Vec<_> = deduped
            .iter()
            .map(|e| {
                let v = e.to_variant()?;
                let ident_var = v.match_arm(quote! { #ident });
                let room_var = v.ctor(quote! { Self });

                Ok(if e.has_type_fragment() {
                    quote! { #ident_var (_s) => #room_var (_s) }
                } else {
                    quote! { #ident_var => #room_var }
                })
            })
            .collect::<syn::Result<_>>()?;

        Some(quote! {
            #[allow(deprecated)]
            impl ::std::convert::From<#ident> for RoomEventType {
                fn from(s: #ident) -> Self {
                    match s {
                        #(#match_arms,)*
                        #ident ::_Custom(_s) => Self::_Custom(_s),
                    }
                }
            }
        })
    } else {
        None
    };

    Ok(quote! {
        #[doc = #enum_doc]
        ///
        /// This type can hold an arbitrary string. To build events with a custom type, convert it
        /// from a string with `::from() / .into()`. To check for events that are not available as a
        /// documented variant here, use its string representation, obtained through `.to_string()`.
        #deprecated_attr
        #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
        #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
        pub enum #ident {
            #(
                #[doc = #event_types]
                #variants,
            )*
            #[doc(hidden)]
            _Custom(crate::PrivOwnedStr),
        }

        #[allow(deprecated)]
        impl #ident {
            fn to_cow_str(&self) -> ::std::borrow::Cow<'_, ::std::primitive::str> {
                match self {
                    #(#to_cow_str_match_arms,)*
                    Self::_Custom(crate::PrivOwnedStr(s)) => ::std::borrow::Cow::Borrowed(s),
                }
            }
        }

        #[allow(deprecated)]
        impl ::std::fmt::Display for #ident {
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                self.to_cow_str().fmt(f)
            }
        }

        #[allow(deprecated)]
        impl ::std::convert::From<&::std::primitive::str> for #ident {
            fn from(s: &::std::primitive::str) -> Self {
                match s {
                    #(#from_str_match_arms,)*
                    _ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))),
                }
            }
        }

        #[allow(deprecated)]
        impl ::std::convert::From<::std::string::String> for #ident {
            fn from(s: ::std::string::String) -> Self {
                ::std::convert::From::from(s.as_str())
            }
        }

        #[allow(deprecated)]
        impl<'de> #serde::Deserialize<'de> for #ident {
            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
            where
                D: #serde::Deserializer<'de>
            {
                let s = #ruma_common::serde::deserialize_cow_str(deserializer)?;
                Ok(::std::convert::From::from(&s[..]))
            }
        }

        #[allow(deprecated)]
        impl #serde::Serialize for #ident {
            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
            where
                S: #serde::Serializer,
            {
                self.to_cow_str().serialize(serializer)
            }
        }

        #from_ident_for_room
    })
}
<|endoftext|>


<commit_before>use super::*;
use test;

#[test]
fn get_engine_type_no_runner() {
    let mut task = Task::new();
    task.script = Some(vec!["test".to_string()]);

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Unsupported);
}

#[test]
fn get_engine_type_no_script() {
    let mut task = Task::new();
    task.script_runner = Some("@rust".to_string());

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Unsupported);
}

#[test]
fn get_engine_type_unsupported_runner() {
    let mut task = Task::new();
    task.script_runner = Some("@bad".to_string());
    task.script = Some(vec!["test".to_string()]);

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Unsupported);
}

#[test]
fn get_engine_type_rust() {
    let mut task = Task::new();
    task.script_runner = Some("@rust".to_string());
    task.script = Some(vec!["test".to_string()]);

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Rust);
}

#[test]
fn invoke_no_runner() {
    let mut task = Task::new();
    task.script = Some(vec!["test".to_string()]);

    let output = invoke(&task);

    assert!(!output);
}

#[test]
fn invoke_no_script() {
    let mut task = Task::new();
    task.script_runner = Some("@rust".to_string());

    let output = invoke(&task);

    assert!(!output);
}

#[test]
fn invoke_unsupported_runner() {
    let mut task = Task::new();
    task.script_runner = Some("@bad".to_string());
    task.script = Some(vec!["test".to_string()]);

    let output = invoke(&task);

    assert!(!output);
}

#[test]
fn invoke_rust_runner() {
    if test::should_test(false) {
        let mut task = Task::new();
        task.script_runner = Some("@rust".to_string());
        task.script = Some(vec!["fn main() {println!(\"test\");}".to_string()]);

        let output = invoke(&task);

        assert!(output);
    }
}

#[test]
#[should_panic]
fn invoke_rust_runner_error() {
    if test::should_test(false) {
        let mut task = Task::new();
        task.script_runner = Some("@rust".to_string());
        task.script = Some(vec!["fn main() {bad!(\"test\");}".to_string()]);

        let output = invoke(&task);

        assert!(output);
    }
}
<commit_msg>fix build<commit_after>use super::*;
use test;

#[test]
fn get_engine_type_no_runner() {
    let mut task = Task::new();
    task.script = Some(vec!["test".to_string()]);

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Unsupported);
}

#[test]
fn get_engine_type_no_script() {
    let mut task = Task::new();
    task.script_runner = Some("@rust".to_string());

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Unsupported);
}

#[test]
fn get_engine_type_unsupported_runner() {
    let mut task = Task::new();
    task.script_runner = Some("@bad".to_string());
    task.script = Some(vec!["test".to_string()]);

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Unsupported);
}

#[test]
fn get_engine_type_rust() {
    let mut task = Task::new();
    task.script_runner = Some("@rust".to_string());
    task.script = Some(vec!["test".to_string()]);

    let output = get_engine_type(&task);

    assert_eq!(output, EngineType::Rust);
}

#[test]
fn invoke_no_runner() {
    let mut task = Task::new();
    task.script = Some(vec!["test".to_string()]);

    let output = invoke(&task);

    assert!(!output);
}

#[test]
fn invoke_no_script() {
    let mut task = Task::new();
    task.script_runner = Some("@rust".to_string());

    let output = invoke(&task);

    assert!(!output);
}

#[test]
fn invoke_unsupported_runner() {
    let mut task = Task::new();
    task.script_runner = Some("@bad".to_string());
    task.script = Some(vec!["test".to_string()]);

    let output = invoke(&task);

    assert!(!output);
}

#[test]
fn invoke_rust_runner() {
    if test::should_test(false) {
        let mut task = Task::new();
        task.script_runner = Some("@rust".to_string());
        task.script = Some(vec!["fn main() {println!(\"test\");}".to_string()]);

        let output = invoke(&task);

        assert!(output);
    }
}

#[test]
#[should_panic]
fn invoke_rust_runner_error() {
    if test::should_test(true) {
        let mut task = Task::new();
        task.script_runner = Some("@rust".to_string());
        task.script = Some(vec!["fn main() {bad!(\"test\");}".to_string()]);

        let output = invoke(&task);

        assert!(output);
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use serde::de;
use serde::ser::{Serialize, Serializer};
use serde::de::{Deserialize, Deserializer, Visitor, Unexpected};

pub type Integer = i64;
pub type Float = f32;

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct True; // TODO(knsd): Option<True> => bool?

impl Deserialize for True {
    fn deserialize<D>(deserializer: D) -> Result<True, D::Error> where D: Deserializer {
        struct TrueVisitor;

        impl Visitor for TrueVisitor {
            type Value = True;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("true")
            }

            fn visit_bool<E>(self, value: bool) -> Result<True, E> where E: de::Error {
                match value {
                    true => Ok(True),
                    false => Err(E::invalid_value(Unexpected::Bool(value), &self))
                }
            }
        }

        deserializer.deserialize_bool(TrueVisitor)
    }
}

impl Serialize for True {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        serializer.serialize_bool(true)
    }
}
<commit_msg>docs: document primitive types<commit_after>use std::fmt;

use serde::de;
use serde::ser::{Serialize, Serializer};
use serde::de::{Deserialize, Deserializer, Visitor, Unexpected};

/// The Telegram `Integer`, currently i64.
pub type Integer = i64;

/// The Telegram `Float`, currently f32.
pub type Float = f32;

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct True; // TODO(knsd): Option<True> => bool?

impl Deserialize for True {
    fn deserialize<D>(deserializer: D) -> Result<True, D::Error> where D: Deserializer {
        struct TrueVisitor;

        impl Visitor for TrueVisitor {
            type Value = True;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("true")
            }

            fn visit_bool<E>(self, value: bool) -> Result<True, E> where E: de::Error {
                match value {
                    true => Ok(True),
                    false => Err(E::invalid_value(Unexpected::Bool(value), &self))
                }
            }
        }

        deserializer.deserialize_bool(TrueVisitor)
    }
}

impl Serialize for True {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        serializer.serialize_bool(true)
    }
}
<|endoftext|>


<commit_before>use latch::{LatchProbe, SpinLatch};
use log::Event::*;
use job::StackJob;
use registry::{self, WorkerThread};
use std::any::Any;
use unwind;

use FnContext;

#[cfg(test)]
mod test;

/// The `join` function takes two closures and *potentially* runs them
/// in parallel. It returns a pair of the results from those closures.
///
/// Conceptually, calling `join()` is similar to spawning two threads,
/// one executing each of the two closures. However, the
/// implementation is quite different and incurs very low
/// overhead. The underlying technique is called "work stealing": the
/// Rayon runtime uses a fixed pool of worker threads and attempts to
/// only execute code in parallel when there are idle CPUs to handle
/// it.
///
/// When `join` is called from outside the thread pool, the calling
/// thread will block while the closures execute in the pool.  When
/// `join` is called within the pool, the calling thread still actively
/// participates in the thread pool. It will begin by executing closure
/// A (on the current thread). While it is doing that, it will advertise
/// closure B as being available for other threads to execute. Once closure A
/// has completed, the current thread will try to execute closure B;
/// if however closure B has been stolen, then it will look for other work
/// while waiting for the thief to fully execute closure B. (This is the
/// typical work-stealing strategy).
///
/// ### Warning about blocking I/O
///
/// The assumption is that the closures given to `join()` are
/// CPU-bound tasks that do not perform I/O or other blocking
/// operations. If you do perform I/O, and that I/O should block
/// (e.g., waiting for a network request), the overall performance may
/// be poor.  Moreover, if you cause one closure to be blocked waiting
/// on another (for example, using a channel), that could lead to a
/// deadlock.
///
/// ### Panics
///
/// No matter what happens, both closures will always be executed.  If
/// a single closure panics, whether it be the first or second
/// closure, that panic will be propagated and hence `join()` will
/// panic with the same panic value. If both closures panic, `join()`
/// will panic with the panic value from the first closure.
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce() -> RA + Send,
          B: FnOnce() -> RB + Send,
          RA: Send,
          RB: Send
{
    join_context(|_| oper_a(), |_| oper_b())
}

/// The `join_context` function is identical to `join`, except the closures
/// have a parameter that provides context for the way the closure has been
/// called, especially indicating whether they're executing on a different
/// thread than where `join_context` was called.  This will occur if the second
/// job is stolen by a different thread, or if `join_context` was called from
/// outside the thread pool to begin with.
pub fn join_context<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce(FnContext) -> RA + Send,
          B: FnOnce(FnContext) -> RB + Send,
          RA: Send,
          RB: Send
{
    registry::in_worker(|worker_thread, injected| unsafe {
        log!(Join { worker: worker_thread.index() });

        // Create virtual wrapper for task b; this all has to be
        // done here so that the stack frame can keep it all live
        // long enough.
        let job_b = StackJob::new(|migrated| oper_b(FnContext::new(migrated)),
                                  SpinLatch::new());
        let job_b_ref = job_b.as_job_ref();
        worker_thread.push(job_b_ref);

        // Execute task a; hopefully b gets stolen in the meantime.
        let status_a = unwind::halt_unwinding(move || oper_a(FnContext::new(injected)));
        let result_a = match status_a {
            Ok(v) => v,
            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err),
        };

        // Now that task A has finished, try to pop job B from the
        // local stack.  It may already have been popped by job A; it
        // may also have been stolen. There may also be some tasks
        // pushed on top of it in the stack, and we will have to pop
        // those off to get to it.
        while !job_b.latch.probe() {
            if let Some(job) = worker_thread.take_local_job() {
                if job == job_b_ref {
                    // Found it! Let's run it.
                    //
                    // Note that this could panic, but it's ok if we unwind here.
                    log!(PoppedRhs { worker: worker_thread.index() });
                    let result_b = job_b.run_inline(injected);
                    return (result_a, result_b);
                } else {
                    log!(PoppedJob { worker: worker_thread.index() });
                    worker_thread.execute(job);
                }
            } else {
                // Local deque is empty. Time to steal from other
                // threads.
                log!(LostJob { worker: worker_thread.index() });
                worker_thread.wait_until(&job_b.latch);
                debug_assert!(job_b.latch.probe());
                break;
            }
        }

        return (result_a, job_b.into_result());
    })
}

/// If job A panics, we still cannot return until we are sure that job
/// B is complete. This is because it may contain references into the
/// enclosing stack frame(s).
#[cold] // cold path
unsafe fn join_recover_from_panic(worker_thread: &WorkerThread,
                                  job_b_latch: &SpinLatch,
                                  err: Box<Any + Send>)
                                  -> !
{
    worker_thread.wait_until(job_b_latch);
    unwind::resume_unwinding(err)
}
<commit_msg>cleanup the opening sentence in a few functions<commit_after>use latch::{LatchProbe, SpinLatch};
use log::Event::*;
use job::StackJob;
use registry::{self, WorkerThread};
use std::any::Any;
use unwind;

use FnContext;

#[cfg(test)]
mod test;

/// Takes two closures and *potentially* runs them in parallel. It
/// returns a pair of the results from those closures.
///
/// Conceptually, calling `join()` is similar to spawning two threads,
/// one executing each of the two closures. However, the
/// implementation is quite different and incurs very low
/// overhead. The underlying technique is called "work stealing": the
/// Rayon runtime uses a fixed pool of worker threads and attempts to
/// only execute code in parallel when there are idle CPUs to handle
/// it.
///
/// When `join` is called from outside the thread pool, the calling
/// thread will block while the closures execute in the pool.  When
/// `join` is called within the pool, the calling thread still actively
/// participates in the thread pool. It will begin by executing closure
/// A (on the current thread). While it is doing that, it will advertise
/// closure B as being available for other threads to execute. Once closure A
/// has completed, the current thread will try to execute closure B;
/// if however closure B has been stolen, then it will look for other work
/// while waiting for the thief to fully execute closure B. (This is the
/// typical work-stealing strategy).
///
/// ### Warning about blocking I/O
///
/// The assumption is that the closures given to `join()` are
/// CPU-bound tasks that do not perform I/O or other blocking
/// operations. If you do perform I/O, and that I/O should block
/// (e.g., waiting for a network request), the overall performance may
/// be poor.  Moreover, if you cause one closure to be blocked waiting
/// on another (for example, using a channel), that could lead to a
/// deadlock.
///
/// ### Panics
///
/// No matter what happens, both closures will always be executed.  If
/// a single closure panics, whether it be the first or second
/// closure, that panic will be propagated and hence `join()` will
/// panic with the same panic value. If both closures panic, `join()`
/// will panic with the panic value from the first closure.
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce() -> RA + Send,
          B: FnOnce() -> RB + Send,
          RA: Send,
          RB: Send
{
    join_context(|_| oper_a(), |_| oper_b())
}

/// Indentical to `join`, except that the closures have a parameter
/// that provides context for the way the closure has been called,
/// especially indicating whether they're executing on a different
/// thread than where `join_context` was called.  This will occur if
/// the second job is stolen by a different thread, or if
/// `join_context` was called from outside the thread pool to begin
/// with.
pub fn join_context<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce(FnContext) -> RA + Send,
          B: FnOnce(FnContext) -> RB + Send,
          RA: Send,
          RB: Send
{
    registry::in_worker(|worker_thread, injected| unsafe {
        log!(Join { worker: worker_thread.index() });

        // Create virtual wrapper for task b; this all has to be
        // done here so that the stack frame can keep it all live
        // long enough.
        let job_b = StackJob::new(|migrated| oper_b(FnContext::new(migrated)),
                                  SpinLatch::new());
        let job_b_ref = job_b.as_job_ref();
        worker_thread.push(job_b_ref);

        // Execute task a; hopefully b gets stolen in the meantime.
        let status_a = unwind::halt_unwinding(move || oper_a(FnContext::new(injected)));
        let result_a = match status_a {
            Ok(v) => v,
            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err),
        };

        // Now that task A has finished, try to pop job B from the
        // local stack.  It may already have been popped by job A; it
        // may also have been stolen. There may also be some tasks
        // pushed on top of it in the stack, and we will have to pop
        // those off to get to it.
        while !job_b.latch.probe() {
            if let Some(job) = worker_thread.take_local_job() {
                if job == job_b_ref {
                    // Found it! Let's run it.
                    //
                    // Note that this could panic, but it's ok if we unwind here.
                    log!(PoppedRhs { worker: worker_thread.index() });
                    let result_b = job_b.run_inline(injected);
                    return (result_a, result_b);
                } else {
                    log!(PoppedJob { worker: worker_thread.index() });
                    worker_thread.execute(job);
                }
            } else {
                // Local deque is empty. Time to steal from other
                // threads.
                log!(LostJob { worker: worker_thread.index() });
                worker_thread.wait_until(&job_b.latch);
                debug_assert!(job_b.latch.probe());
                break;
            }
        }

        return (result_a, job_b.into_result());
    })
}

/// If job A panics, we still cannot return until we are sure that job
/// B is complete. This is because it may contain references into the
/// enclosing stack frame(s).
#[cold] // cold path
unsafe fn join_recover_from_panic(worker_thread: &WorkerThread,
                                  job_b_latch: &SpinLatch,
                                  err: Box<Any + Send>)
                                  -> !
{
    worker_thread.wait_until(job_b_latch);
    unwind::resume_unwinding(err)
}
<|endoftext|>


<commit_before>use super::geo_class_and_kind;
use crate::StrError;
use russell_lab::{Matrix, Vector};

/// Holds the state of a shape entity
#[derive(Clone, Debug)]
pub struct StateOfShape {
    // -- steady ----------------------------------------------------------------------------------------------------
    //
    /// Maps local node index to global point id (aka "connectivity"); corresponds to coords_transp (nnode)
    pub node_to_point: Vec<usize>,

    /// Matrix Xᵀ: (space_ndim,nnode) transposed coordinates matrix (real space)
    ///
    /// ```text
    /// The (non-transposed) matrix X is such that:
    ///
    ///     ┌               ┐
    ///     | x⁰₀  x⁰₁  x⁰₂ |
    ///     | x¹₀  x¹₁  x¹₂ |
    /// X = | x²₀  x²₁  x²₂ |
    ///     |      ...      |
    ///     | xᴹ₀  xᴹ₁  xᴹ₂ |
    ///     └               ┘_(nnode,space_ndim)
    ///
    /// where `M = nnode - 1`
    /// ```
    pub coords_transp: Matrix,

    /// Minimum (space_ndim) coordinates from the X matrix
    pub coords_min: Vec<f64>,

    /// Maximum (space_ndim) coordinates from the X matrix
    pub coords_max: Vec<f64>,

    // -- transient -------------------------------------------------------------------------------------------------
    //
    /// Array N: (nnode) interpolation functions at reference coordinate ksi
    pub interp: Vector,

    /// Matrix L: (nnode,geo_ndim) derivatives of interpolation functions w.r.t reference coordinate ksi
    pub deriv: Matrix,

    /// Matrix J: (space_ndim,geo_ndim) Jacobian matrix
    pub jacobian: Matrix,

    /// Matrix inv(J): (space_ndim,space_ndim) Inverse Jacobian matrix (only if geo_ndim == space_ndim) at ksi
    pub inv_jacobian: Matrix,

    /// Matrix G: (nnode,space_ndim) Gradient of shape functions (only if geo_ndim == space_ndim) at ksi
    pub gradient: Matrix,

    // -- internal --------------------------------------------------------------------------------------------------
    //
    /// Tells whether the last entry of the coordinates matrix has been given or not
    pub(super) last_coord_given: bool,
}

impl StateOfShape {
    /// Allocates a new instance
    ///
    /// # Input
    ///
    /// * `space_ndim` -- the space dimension (1, 2, or 3)
    /// * `geo_ndim` -- the dimension of the shape; e.g. a 1D line in a 3D space
    /// * `nnode` -- the number of points defining the shape (number of nodes)
    pub fn new(space_ndim: usize, geo_ndim: usize, nnode: usize) -> Result<Self, StrError> {
        geo_class_and_kind(geo_ndim, nnode)?; // just to check combination
        Ok(StateOfShape {
            node_to_point: vec![0; nnode],
            coords_transp: Matrix::new(space_ndim, nnode),
            coords_min: vec![f64::MAX; space_ndim],
            coords_max: vec![f64::MIN; space_ndim],
            interp: Vector::new(nnode),
            deriv: Matrix::new(nnode, geo_ndim),
            jacobian: Matrix::new(space_ndim, geo_ndim),
            inv_jacobian: if geo_ndim == space_ndim {
                Matrix::new(space_ndim, space_ndim)
            } else {
                Matrix::new(0, 0)
            },
            gradient: if geo_ndim == space_ndim {
                Matrix::new(nnode, space_ndim)
            } else {
                Matrix::new(0, 0)
            },
            last_coord_given: false,
        })
    }

    /// Sets a component of the coordinates matrix
    ///
    /// ```text
    /// The (non-transposed) matrix X is such that:
    ///
    ///     ┌               ┐
    ///     | x⁰₀  x⁰₁  x⁰₂ |
    ///     | x¹₀  x¹₁  x¹₂ |
    /// X = | x²₀  x²₁  x²₂ |
    ///     |      ...      |
    ///     | xᴹ₀  xᴹ₁  xᴹ₂ |
    ///     └               ┘_(nnode,space_ndim)
    ///
    /// where `M = nnode - 1`
    /// ```
    ///
    /// # Input
    ///
    /// * `point_id` -- the global point id to configure the `node_to_point` map; aka "connectivity"
    /// * `m` -- node index in `0 ≤ m ≤ nnode - 1`
    /// * `j` -- dimension index in `0 ≤ j ≤ space_ndim - 1`
    /// * `value` -- the X(m,j) component
    ///
    /// # Updated variables
    ///
    /// * `node_to_point` -- (nnode) array of point ids that works as a map from "local node index" to "global point id"
    /// * `coords_transp` -- Matrix Xᵀ: (space_ndim,nnode) transposed coordinates matrix (real space)
    /// * `coords_min` -- minimum (space_ndim) coordinates from the X matrix
    /// * `coords_max` -- maximum (space_ndim) coordinates from the X matrix
    pub fn set_node(&mut self, point_id: usize, m: usize, j: usize, value: f64) -> Result<(), StrError> {
        let (space_ndim, nnode) = self.coords_transp.dims();
        if m >= nnode {
            return Err("index of node must be in 0 ≤ m ≤ nnode - 1");
        }
        if j >= space_ndim {
            return Err("index of space dimension must be in 0 ≤ j ≤ space_ndim - 1");
        }
        self.node_to_point[m] = point_id;
        self.coords_transp[j][m] = value;
        if value < self.coords_min[j] {
            self.coords_min[j] = value;
        }
        if value > self.coords_max[j] {
            self.coords_max[j] = value;
        }
        if m == nnode - 1 && j == space_ndim - 1 {
            self.last_coord_given = true;
        } else {
            self.last_coord_given = false;
        }
        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use crate::shapes::StateOfShape;
    use crate::StrError;

    #[test]
    fn set_node_captures_wrong_input() -> Result<(), StrError> {
        let mut state = StateOfShape::new(1, 1, 2)?;
        let point_id = 123;
        assert_eq!(
            state.set_node(point_id, 3, 0, 0.0).err(),
            Some("index of node is invalid")
        );
        assert_eq!(
            state.set_node(point_id, 0, 2, 0.0).err(),
            Some("index of space dimension is invalid")
        );
        Ok(())
    }

    #[test]
    fn set_node_resets_ok_last_coord() -> Result<(), StrError> {
        let point_id = 123;
        let mut state = StateOfShape::new(1, 1, 2)?;
        assert_eq!(state.last_coord_given, false);
        state.set_node(point_id, 0, 0, 0.0)?;
        assert_eq!(state.last_coord_given, false);
        state.set_node(point_id, 1, 0, 0.0)?;
        assert_eq!(state.last_coord_given, true);
        state.set_node(point_id, 0, 0, 0.0)?; // must reset
        assert_eq!(state.last_coord_given, false);
        Ok(())
    }

    #[test]
    fn set_node_works() -> Result<(), StrError> {
        let mut state = StateOfShape::new(2, 2, 3)?;
        state.set_node(123, 0, 0, -1.23)?;
        state.set_node(123, 0, 1, 1.23)?;
        state.set_node(456, 1, 0, -4.56)?;
        state.set_node(456, 1, 1, 4.56)?;
        state.set_node(789, 2, 0, -7.89)?;
        state.set_node(789, 2, 1, 7.89)?;
        assert_eq!(state.node_to_point, &[123, 456, 789]);
        assert_eq!(
            format!("{}", state.coords_transp),
            "┌                   ┐\n\
             │ -1.23 -4.56 -7.89 │\n\
             │  1.23  4.56  7.89 │\n\
             └                   ┘"
        );
        assert_eq!(state.last_coord_given, true);
        Ok(())
    }
}
<commit_msg>Fix tests of StateOfShape<commit_after>use super::geo_class_and_kind;
use crate::StrError;
use russell_lab::{Matrix, Vector};

/// Holds the state of a shape entity
#[derive(Clone, Debug)]
pub struct StateOfShape {
    // -- steady ----------------------------------------------------------------------------------------------------
    //
    /// Maps local node index to global point id (aka "connectivity"); corresponds to coords_transp (nnode)
    pub node_to_point: Vec<usize>,

    /// Matrix Xᵀ: (space_ndim,nnode) transposed coordinates matrix (real space)
    ///
    /// ```text
    /// The (non-transposed) matrix X is such that:
    ///
    ///     ┌               ┐
    ///     | x⁰₀  x⁰₁  x⁰₂ |
    ///     | x¹₀  x¹₁  x¹₂ |
    /// X = | x²₀  x²₁  x²₂ |
    ///     |      ...      |
    ///     | xᴹ₀  xᴹ₁  xᴹ₂ |
    ///     └               ┘_(nnode,space_ndim)
    ///
    /// where `M = nnode - 1`
    /// ```
    pub coords_transp: Matrix,

    /// Minimum (space_ndim) coordinates from the X matrix
    pub coords_min: Vec<f64>,

    /// Maximum (space_ndim) coordinates from the X matrix
    pub coords_max: Vec<f64>,

    // -- transient -------------------------------------------------------------------------------------------------
    //
    /// Array N: (nnode) interpolation functions at reference coordinate ksi
    pub interp: Vector,

    /// Matrix L: (nnode,geo_ndim) derivatives of interpolation functions w.r.t reference coordinate ksi
    pub deriv: Matrix,

    /// Matrix J: (space_ndim,geo_ndim) Jacobian matrix
    pub jacobian: Matrix,

    /// Matrix inv(J): (space_ndim,space_ndim) Inverse Jacobian matrix (only if geo_ndim == space_ndim) at ksi
    pub inv_jacobian: Matrix,

    /// Matrix G: (nnode,space_ndim) Gradient of shape functions (only if geo_ndim == space_ndim) at ksi
    pub gradient: Matrix,

    // -- internal --------------------------------------------------------------------------------------------------
    //
    /// Tells whether the last entry of the coordinates matrix has been given or not
    pub(super) last_coord_given: bool,
}

impl StateOfShape {
    /// Allocates a new instance
    ///
    /// # Input
    ///
    /// * `space_ndim` -- the space dimension (1, 2, or 3)
    /// * `geo_ndim` -- the dimension of the shape; e.g. a 1D line in a 3D space
    /// * `nnode` -- the number of points defining the shape (number of nodes)
    pub fn new(space_ndim: usize, geo_ndim: usize, nnode: usize) -> Result<Self, StrError> {
        geo_class_and_kind(geo_ndim, nnode)?; // just to check combination
        Ok(StateOfShape {
            node_to_point: vec![0; nnode],
            coords_transp: Matrix::new(space_ndim, nnode),
            coords_min: vec![f64::MAX; space_ndim],
            coords_max: vec![f64::MIN; space_ndim],
            interp: Vector::new(nnode),
            deriv: Matrix::new(nnode, geo_ndim),
            jacobian: Matrix::new(space_ndim, geo_ndim),
            inv_jacobian: if geo_ndim == space_ndim {
                Matrix::new(space_ndim, space_ndim)
            } else {
                Matrix::new(0, 0)
            },
            gradient: if geo_ndim == space_ndim {
                Matrix::new(nnode, space_ndim)
            } else {
                Matrix::new(0, 0)
            },
            last_coord_given: false,
        })
    }

    /// Sets a component of the coordinates matrix
    ///
    /// ```text
    /// The (non-transposed) matrix X is such that:
    ///
    ///     ┌               ┐
    ///     | x⁰₀  x⁰₁  x⁰₂ |
    ///     | x¹₀  x¹₁  x¹₂ |
    /// X = | x²₀  x²₁  x²₂ |
    ///     |      ...      |
    ///     | xᴹ₀  xᴹ₁  xᴹ₂ |
    ///     └               ┘_(nnode,space_ndim)
    ///
    /// where `M = nnode - 1`
    /// ```
    ///
    /// # Input
    ///
    /// * `point_id` -- the global point id to configure the `node_to_point` map; aka "connectivity"
    /// * `m` -- node index in `0 ≤ m ≤ nnode - 1`
    /// * `j` -- dimension index in `0 ≤ j ≤ space_ndim - 1`
    /// * `value` -- the X(m,j) component
    ///
    /// # Updated variables
    ///
    /// * `node_to_point` -- (nnode) array of point ids that works as a map from "local node index" to "global point id"
    /// * `coords_transp` -- Matrix Xᵀ: (space_ndim,nnode) transposed coordinates matrix (real space)
    /// * `coords_min` -- minimum (space_ndim) coordinates from the X matrix
    /// * `coords_max` -- maximum (space_ndim) coordinates from the X matrix
    pub fn set_node(&mut self, point_id: usize, m: usize, j: usize, value: f64) -> Result<(), StrError> {
        let (space_ndim, nnode) = self.coords_transp.dims();
        if m >= nnode {
            return Err("index of node must be in 0 ≤ m ≤ nnode - 1");
        }
        if j >= space_ndim {
            return Err("index of space dimension must be in 0 ≤ j ≤ space_ndim - 1");
        }
        self.node_to_point[m] = point_id;
        self.coords_transp[j][m] = value;
        if value < self.coords_min[j] {
            self.coords_min[j] = value;
        }
        if value > self.coords_max[j] {
            self.coords_max[j] = value;
        }
        if m == nnode - 1 && j == space_ndim - 1 {
            self.last_coord_given = true;
        } else {
            self.last_coord_given = false;
        }
        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use crate::shapes::StateOfShape;
    use crate::StrError;

    #[test]
    fn set_node_captures_wrong_input() -> Result<(), StrError> {
        let mut state = StateOfShape::new(1, 1, 2)?;
        let point_id = 123;
        assert_eq!(
            state.set_node(point_id, 3, 0, 0.0).err(),
            Some("index of node must be in 0 ≤ m ≤ nnode - 1")
        );
        assert_eq!(
            state.set_node(point_id, 0, 2, 0.0).err(),
            Some("index of space dimension must be in 0 ≤ j ≤ space_ndim - 1")
        );
        Ok(())
    }

    #[test]
    fn set_node_resets_last_coord_given() -> Result<(), StrError> {
        let point_id = 123;
        let mut state = StateOfShape::new(1, 1, 2)?;
        assert_eq!(state.last_coord_given, false);
        state.set_node(point_id, 0, 0, 0.0)?;
        assert_eq!(state.last_coord_given, false);
        state.set_node(point_id, 1, 0, 0.0)?;
        assert_eq!(state.last_coord_given, true);
        state.set_node(point_id, 0, 0, 0.0)?; // must reset
        assert_eq!(state.last_coord_given, false);
        Ok(())
    }

    #[test]
    fn set_node_works() -> Result<(), StrError> {
        let mut state = StateOfShape::new(2, 2, 3)?;
        state.set_node(123, 0, 0, -1.23)?;
        state.set_node(123, 0, 1, 1.23)?;
        state.set_node(456, 1, 0, -4.56)?;
        state.set_node(456, 1, 1, 4.56)?;
        state.set_node(789, 2, 0, -7.89)?;
        state.set_node(789, 2, 1, 7.89)?;
        assert_eq!(state.node_to_point, &[123, 456, 789]);
        assert_eq!(
            format!("{}", state.coords_transp),
            "┌                   ┐\n\
             │ -1.23 -4.56 -7.89 │\n\
             │  1.23  4.56  7.89 │\n\
             └                   ┘"
        );
        assert_eq!(state.last_coord_given, true);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use core::convert::Infallible;

/// Enum mapping type
///
/// This trait is internally used by `#[derive(Enum)]`.
///
/// This trait is also implemented by `bool` and `u8`. While `u8` is
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum: Sized {
    /// Length of the enum.
    const LENGTH: usize;

    /// Takes an usize, and returns an element matching `into_usize` function.
    fn from_usize(value: usize) -> Self;
    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
    fn into_usize(self) -> usize;
}

/// Trait associating
pub trait EnumArray<V>: Enum {
    /// Representation of an enum map for type `V`.
    type Array: Array<V>;
}

/// Array for enum-map storage.
///
/// This trait is inteded for primitive array types (with fixed length).
///
/// # Safety
///
/// The array length needs to match actual storage.
pub unsafe trait Array<V> {
    // This is necessary duplication because the length in Enum trait can be
    // provided by user and may not be trustworthy for unsafe code.
    const LENGTH: usize;

    /// Coerces a reference to the array into a reference to a slice.
    fn slice(&self) -> &[V];

    /// Coerces a mutable reference to the array into a mutable reference to a slice.
    fn slice_mut(&mut self) -> &mut [V];
}

unsafe impl<V, const N: usize> Array<V> for [V; N] {
    const LENGTH: usize = N;
    fn slice(&self) -> &[V] {
        self
    }
    fn slice_mut(&mut self) -> &mut [V] {
        self
    }
}

impl Enum for bool {
    const LENGTH: usize = 2;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for bool {
    type Array = [T; Self::LENGTH];
}

impl Enum for u8 {
    const LENGTH: usize = 256;

    #[inline]
    fn from_usize(value: usize) -> Self {
        value.try_into().unwrap()
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for u8 {
    type Array = [T; Self::LENGTH];
}

impl Enum for Infallible {
    const LENGTH: usize = 0;

    #[inline]
    fn from_usize(_: usize) -> Self {
        unreachable!();
    }
    #[inline]
    fn into_usize(self) -> usize {
        match self {}
    }
}

impl<T> EnumArray<T> for Infallible {
    type Array = [T; Self::LENGTH];
}
<commit_msg>Improve documentation for internal traits<commit_after>use core::convert::Infallible;

/// Enum mapping type.
///
/// This trait is implemented by `#[derive(Enum)]`.
///
/// This trait is also implemented by `bool` and `u8`. While `u8` is
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum: Sized {
    /// Length of the enum.
    const LENGTH: usize;

    /// Takes an usize, and returns an element matching `into_usize` function.
    fn from_usize(value: usize) -> Self;
    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
    fn into_usize(self) -> usize;
}

/// Trait associating enum with an array.
///
/// This exists due to limitations of Rust compiler that prevent arrays from using
/// associated constants in structures. The array length must match `LENGTH` of an
/// `Enum`.
pub trait EnumArray<V>: Enum {
    /// Representation of an enum map for type `V`.
    type Array: Array<V>;
}

/// Array for enum-map storage.
///
/// This trait is inteded for primitive array types (with fixed length).
///
/// # Safety
///
/// The array length needs to match actual storage.
pub unsafe trait Array<V> {
    // This is necessary duplication because the length in Enum trait can be
    // provided by user and may not be trustworthy for unsafe code.
    const LENGTH: usize;

    /// Coerces a reference to the array into a reference to a slice.
    fn slice(&self) -> &[V];

    /// Coerces a mutable reference to the array into a mutable reference to a slice.
    fn slice_mut(&mut self) -> &mut [V];
}

unsafe impl<V, const N: usize> Array<V> for [V; N] {
    const LENGTH: usize = N;
    fn slice(&self) -> &[V] {
        self
    }
    fn slice_mut(&mut self) -> &mut [V] {
        self
    }
}

impl Enum for bool {
    const LENGTH: usize = 2;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for bool {
    type Array = [T; Self::LENGTH];
}

impl Enum for u8 {
    const LENGTH: usize = 256;

    #[inline]
    fn from_usize(value: usize) -> Self {
        value.try_into().unwrap()
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for u8 {
    type Array = [T; Self::LENGTH];
}

impl Enum for Infallible {
    const LENGTH: usize = 0;

    #[inline]
    fn from_usize(_: usize) -> Self {
        unreachable!();
    }
    #[inline]
    fn into_usize(self) -> usize {
        match self {}
    }
}

impl<T> EnumArray<T> for Infallible {
    type Array = [T; Self::LENGTH];
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Module contains RPC method handlers for Full Node JSON-RPC interface
use crate::{
    errors::JsonRpcError,
    views::{
        AccountStateWithProofView, AccountView, BlockMetadata, CurrencyInfoView, EventView,
        StateProofView, TransactionView,
    },
};
use anyhow::{ensure, format_err, Error, Result};
use core::future::Future;
use debug_interface::prelude::*;
use futures::{channel::oneshot, SinkExt};
use hex;
use libra_mempool::MempoolClientSender;
use libra_types::{
    account_address::AccountAddress,
    account_config::CurrencyInfoResource,
    account_state::AccountState,
    event::EventKey,
    ledger_info::LedgerInfoWithSignatures,
    mempool_status::MempoolStatusCode,
    move_resource::MoveStorage,
    on_chain_config::{OnChainConfig, RegisteredCurrencies},
    transaction::SignedTransaction,
};
use serde_json::Value;
use std::{collections::HashMap, convert::TryFrom, ops::Deref, pin::Pin, str::FromStr, sync::Arc};
use storage_interface::DbReader;

#[derive(Clone)]
pub(crate) struct JsonRpcService {
    db: Arc<dyn DbReader>,
    mempool_sender: MempoolClientSender,
}

impl JsonRpcService {
    pub fn new(db: Arc<dyn DbReader>, mempool_sender: MempoolClientSender) -> Self {
        Self { db, mempool_sender }
    }

    pub fn get_latest_ledger_info(&self) -> Result<LedgerInfoWithSignatures> {
        self.db.get_latest_ledger_info()
    }
}

type RpcHandler =
    Box<fn(JsonRpcService, JsonRpcRequest) -> Pin<Box<dyn Future<Output = Result<Value>> + Send>>>;

pub(crate) type RpcRegistry = HashMap<String, RpcHandler>;

pub(crate) struct JsonRpcRequest {
    pub params: Vec<Value>,
    pub ledger_info: LedgerInfoWithSignatures,
}

impl JsonRpcRequest {
    /// Returns the request parameter at the given index. Note: this method should not panic with
    /// an out of bounds error, as the number of request parameters has already been checked.
    fn get_param(&self, index: usize) -> Value {
        self.params[index].clone()
    }

    fn version(&self) -> u64 {
        self.ledger_info.ledger_info().version()
    }
}

/// Submits transaction to full node
async fn submit(mut service: JsonRpcService, request: JsonRpcRequest) -> Result<()> {
    let txn_payload: String = serde_json::from_value(request.get_param(0))?;
    let transaction: SignedTransaction = lcs::from_bytes(&hex::decode(txn_payload)?)?;
    trace_code_block!("json-rpc::submit", {"txn", transaction.sender(), transaction.sequence_number()});

    let (req_sender, callback) = oneshot::channel();
    service
        .mempool_sender
        .send((transaction, req_sender))
        .await?;
    let (mempool_status, vm_status) = callback.await??;

    if let Some(vm_error) = vm_status {
        Err(Error::new(JsonRpcError::vm_error(vm_error)))
    } else if mempool_status.code == MempoolStatusCode::Accepted {
        Ok(())
    } else {
        Err(Error::new(JsonRpcError::mempool_error(mempool_status)?))
    }
}

/// Returns account state (AccountView) by given address
async fn get_account_state(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Option<AccountView>> {
    let address: String = serde_json::from_value(request.get_param(0))?;
    let account_address = AccountAddress::from_str(&address)?;
    let response = service
        .db
        .get_account_state_with_proof_by_version(account_address, request.version())?
        .0;
    if let Some(blob) = response {
        let account_state = AccountState::try_from(&blob)?;
        if let Some(account) = account_state.get_account_resource()? {
            if let Some(balance) = account_state.get_balance_resource()? {
                return Ok(Some(AccountView::new(&account, &balance)));
            }
        }
    }
    Ok(None)
}

/// Returns the current blockchain metadata
/// Can be used to verify that target Full Node is up-to-date
async fn get_metadata(_service: JsonRpcService, request: JsonRpcRequest) -> Result<BlockMetadata> {
    Ok(BlockMetadata {
        version: request.version(),
        timestamp: request.ledger_info.ledger_info().timestamp_usecs(),
    })
}

/// Returns transactions by range
async fn get_transactions(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Vec<TransactionView>> {
    let start_version: u64 = serde_json::from_value(request.get_param(0))?;
    let limit: u64 = serde_json::from_value(request.get_param(1))?;
    let include_events: bool = serde_json::from_value(request.get_param(2))?;

    ensure!(
        limit > 0 && limit <= 1000,
        "limit must be smaller than 1000"
    );

    let txs =
        service
            .db
            .get_transactions(start_version, limit, request.version(), include_events)?;

    let mut result = vec![];

    let all_events = if include_events {
        txs.events
            .ok_or_else(|| format_err!("Storage layer didn't return events when requested!"))?
    } else {
        vec![]
    };

    let txs_with_info = txs
        .transactions
        .into_iter()
        .zip(txs.proof.transaction_infos().iter());

    for (v, (tx, info)) in txs_with_info.enumerate() {
        let events = if include_events {
            all_events
                .get(v)
                .ok_or_else(|| format_err!("Missing events for version: {}", v))?
                .iter()
                .cloned()
                .map(|x| (start_version + v as u64, x).into())
                .collect()
        } else {
            vec![]
        };

        result.push(TransactionView {
            version: start_version + v as u64,
            transaction: tx.into(),
            events,
            vm_status: info.major_status(),
            gas_used: info.gas_used(),
        });
    }
    Ok(result)
}

/// Returns account transaction by account and sequence_number
async fn get_account_transaction(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Option<TransactionView>> {
    let p_account: String = serde_json::from_value(request.get_param(0))?;
    let sequence: u64 = serde_json::from_value(request.get_param(1))?;
    let include_events: bool = serde_json::from_value(request.get_param(2))?;

    let account = AccountAddress::try_from(p_account)?;

    let tx = service
        .db
        .get_txn_by_account(account, sequence, request.version(), include_events)?;

    if let Some(tx) = tx {
        if include_events {
            ensure!(
                tx.events.is_some(),
                "Storage layer didn't return events when requested!"
            );
        }
        let tx_version = tx.version;

        let events = tx
            .events
            .unwrap_or_default()
            .into_iter()
            .map(|x| ((tx_version, x).into()))
            .collect();

        Ok(Some(TransactionView {
            version: tx_version,
            transaction: tx.transaction.into(),
            events,
            vm_status: tx.proof.transaction_info().major_status(),
            gas_used: tx.proof.transaction_info().gas_used(),
        }))
    } else {
        Ok(None)
    }
}

/// Returns events by given access path
async fn get_events(service: JsonRpcService, request: JsonRpcRequest) -> Result<Vec<EventView>> {
    let raw_event_key: String = serde_json::from_value(request.get_param(0))?;
    let start: u64 = serde_json::from_value(request.get_param(1))?;
    let limit: u64 = serde_json::from_value(request.get_param(2))?;

    let event_key = EventKey::try_from(&hex::decode(raw_event_key)?[..])?;
    let events_with_proof = service.db.get_events(&event_key, start, true, limit)?;

    let req_version = request.version();
    let events = events_with_proof
        .into_iter()
        .filter(|(version, _event)| version <= &req_version)
        .map(|event| event.into())
        .collect();
    Ok(events)
}

/// Returns meta information about supported currencies
async fn currencies_info(
    service: JsonRpcService,
    _request: JsonRpcRequest,
) -> Result<Vec<CurrencyInfoView>> {
    let raw_data = service
        .db
        .deref()
        .batch_fetch_resources(vec![RegisteredCurrencies::CONFIG_ID.access_path()])?;
    ensure!(raw_data.len() == 1, "invalid storage result");
    let currencies = RegisteredCurrencies::from_bytes(&raw_data[0])?;
    let access_paths: Vec<_> = currencies
        .currency_codes()
        .iter()
        .map(|code| CurrencyInfoResource::resource_path_for(code.clone()))
        .collect();

    let mut currencies = vec![];
    for raw_data in service.db.deref().batch_fetch_resources(access_paths)? {
        let currency_info = CurrencyInfoResource::try_from_bytes(&raw_data)?;
        currencies.push(CurrencyInfoView::from(currency_info));
    }
    Ok(currencies)
}

/// Returns proof of new state relative to version known to client
async fn get_state_proof(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<StateProofView> {
    let known_version: u64 = serde_json::from_value(request.get_param(0))?;
    let proofs = service
        .db
        .get_state_proof_with_ledger_info(known_version, request.ledger_info.clone())?;
    StateProofView::try_from((request.ledger_info, proofs.0, proofs.1))
}

/// Returns the account state to the client, alongside a proof relative to the version and
/// ledger_version specified by the client. If version or ledger_version are not specified,
/// the latest known versions will be used.
async fn get_account_state_with_proof(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<AccountStateWithProofView> {
    let address: String = serde_json::from_value(request.get_param(0))?;
    let account_address = AccountAddress::from_str(&address)?;

    // If versions are specified by the request parameters, use them, otherwise use the defaults
    let version =
        serde_json::from_value::<u64>(request.get_param(1)).unwrap_or_else(|_| request.version());
    let ledger_version =
        serde_json::from_value::<u64>(request.get_param(2)).unwrap_or_else(|_| request.version());

    let account_state_with_proof =
        service
            .db
            .get_account_state_with_proof(account_address, version, ledger_version)?;
    Ok(AccountStateWithProofView::try_from(
        account_state_with_proof,
    )?)
}

/// Builds registry of all available RPC methods
/// To register new RPC method, add it via `register_rpc_method!` macros call
/// Note that RPC method name will equal to name of function
pub(crate) fn build_registry() -> RpcRegistry {
    let mut registry = RpcRegistry::new();
    register_rpc_method!(registry, submit, 1);
    register_rpc_method!(registry, get_metadata, 0);
    register_rpc_method!(registry, get_account_state, 1);
    register_rpc_method!(registry, get_transactions, 3);
    register_rpc_method!(registry, get_account_transaction, 3);
    register_rpc_method!(registry, get_events, 3);
    register_rpc_method!(registry, currencies_info, 0);

    register_rpc_method!(registry, get_state_proof, 1);
    register_rpc_method!(registry, get_account_state_with_proof, 3);

    registry
}
<commit_msg>[json-rpc] use versioned db in currencies_info endpoint<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Module contains RPC method handlers for Full Node JSON-RPC interface
use crate::{
    errors::JsonRpcError,
    views::{
        AccountStateWithProofView, AccountView, BlockMetadata, CurrencyInfoView, EventView,
        StateProofView, TransactionView,
    },
};
use anyhow::{ensure, format_err, Error, Result};
use core::future::Future;
use debug_interface::prelude::*;
use futures::{channel::oneshot, SinkExt};
use hex;
use libra_mempool::MempoolClientSender;
use libra_types::{
    account_address::AccountAddress,
    account_config::CurrencyInfoResource,
    account_state::AccountState,
    event::EventKey,
    ledger_info::LedgerInfoWithSignatures,
    mempool_status::MempoolStatusCode,
    move_resource::MoveStorage,
    on_chain_config::{OnChainConfig, RegisteredCurrencies},
    transaction::SignedTransaction,
};
use serde_json::Value;
use std::{collections::HashMap, convert::TryFrom, ops::Deref, pin::Pin, str::FromStr, sync::Arc};
use storage_interface::DbReader;

#[derive(Clone)]
pub(crate) struct JsonRpcService {
    db: Arc<dyn DbReader>,
    mempool_sender: MempoolClientSender,
}

impl JsonRpcService {
    pub fn new(db: Arc<dyn DbReader>, mempool_sender: MempoolClientSender) -> Self {
        Self { db, mempool_sender }
    }

    pub fn get_latest_ledger_info(&self) -> Result<LedgerInfoWithSignatures> {
        self.db.get_latest_ledger_info()
    }
}

type RpcHandler =
    Box<fn(JsonRpcService, JsonRpcRequest) -> Pin<Box<dyn Future<Output = Result<Value>> + Send>>>;

pub(crate) type RpcRegistry = HashMap<String, RpcHandler>;

pub(crate) struct JsonRpcRequest {
    pub params: Vec<Value>,
    pub ledger_info: LedgerInfoWithSignatures,
}

impl JsonRpcRequest {
    /// Returns the request parameter at the given index. Note: this method should not panic with
    /// an out of bounds error, as the number of request parameters has already been checked.
    fn get_param(&self, index: usize) -> Value {
        self.params[index].clone()
    }

    fn version(&self) -> u64 {
        self.ledger_info.ledger_info().version()
    }
}

/// Submits transaction to full node
async fn submit(mut service: JsonRpcService, request: JsonRpcRequest) -> Result<()> {
    let txn_payload: String = serde_json::from_value(request.get_param(0))?;
    let transaction: SignedTransaction = lcs::from_bytes(&hex::decode(txn_payload)?)?;
    trace_code_block!("json-rpc::submit", {"txn", transaction.sender(), transaction.sequence_number()});

    let (req_sender, callback) = oneshot::channel();
    service
        .mempool_sender
        .send((transaction, req_sender))
        .await?;
    let (mempool_status, vm_status) = callback.await??;

    if let Some(vm_error) = vm_status {
        Err(Error::new(JsonRpcError::vm_error(vm_error)))
    } else if mempool_status.code == MempoolStatusCode::Accepted {
        Ok(())
    } else {
        Err(Error::new(JsonRpcError::mempool_error(mempool_status)?))
    }
}

/// Returns account state (AccountView) by given address
async fn get_account_state(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Option<AccountView>> {
    let address: String = serde_json::from_value(request.get_param(0))?;
    let account_address = AccountAddress::from_str(&address)?;
    let response = service
        .db
        .get_account_state_with_proof_by_version(account_address, request.version())?
        .0;
    if let Some(blob) = response {
        let account_state = AccountState::try_from(&blob)?;
        if let Some(account) = account_state.get_account_resource()? {
            if let Some(balance) = account_state.get_balance_resource()? {
                return Ok(Some(AccountView::new(&account, &balance)));
            }
        }
    }
    Ok(None)
}

/// Returns the current blockchain metadata
/// Can be used to verify that target Full Node is up-to-date
async fn get_metadata(_service: JsonRpcService, request: JsonRpcRequest) -> Result<BlockMetadata> {
    Ok(BlockMetadata {
        version: request.version(),
        timestamp: request.ledger_info.ledger_info().timestamp_usecs(),
    })
}

/// Returns transactions by range
async fn get_transactions(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Vec<TransactionView>> {
    let start_version: u64 = serde_json::from_value(request.get_param(0))?;
    let limit: u64 = serde_json::from_value(request.get_param(1))?;
    let include_events: bool = serde_json::from_value(request.get_param(2))?;

    ensure!(
        limit > 0 && limit <= 1000,
        "limit must be smaller than 1000"
    );

    let txs =
        service
            .db
            .get_transactions(start_version, limit, request.version(), include_events)?;

    let mut result = vec![];

    let all_events = if include_events {
        txs.events
            .ok_or_else(|| format_err!("Storage layer didn't return events when requested!"))?
    } else {
        vec![]
    };

    let txs_with_info = txs
        .transactions
        .into_iter()
        .zip(txs.proof.transaction_infos().iter());

    for (v, (tx, info)) in txs_with_info.enumerate() {
        let events = if include_events {
            all_events
                .get(v)
                .ok_or_else(|| format_err!("Missing events for version: {}", v))?
                .iter()
                .cloned()
                .map(|x| (start_version + v as u64, x).into())
                .collect()
        } else {
            vec![]
        };

        result.push(TransactionView {
            version: start_version + v as u64,
            transaction: tx.into(),
            events,
            vm_status: info.major_status(),
            gas_used: info.gas_used(),
        });
    }
    Ok(result)
}

/// Returns account transaction by account and sequence_number
async fn get_account_transaction(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Option<TransactionView>> {
    let p_account: String = serde_json::from_value(request.get_param(0))?;
    let sequence: u64 = serde_json::from_value(request.get_param(1))?;
    let include_events: bool = serde_json::from_value(request.get_param(2))?;

    let account = AccountAddress::try_from(p_account)?;

    let tx = service
        .db
        .get_txn_by_account(account, sequence, request.version(), include_events)?;

    if let Some(tx) = tx {
        if include_events {
            ensure!(
                tx.events.is_some(),
                "Storage layer didn't return events when requested!"
            );
        }
        let tx_version = tx.version;

        let events = tx
            .events
            .unwrap_or_default()
            .into_iter()
            .map(|x| ((tx_version, x).into()))
            .collect();

        Ok(Some(TransactionView {
            version: tx_version,
            transaction: tx.transaction.into(),
            events,
            vm_status: tx.proof.transaction_info().major_status(),
            gas_used: tx.proof.transaction_info().gas_used(),
        }))
    } else {
        Ok(None)
    }
}

/// Returns events by given access path
async fn get_events(service: JsonRpcService, request: JsonRpcRequest) -> Result<Vec<EventView>> {
    let raw_event_key: String = serde_json::from_value(request.get_param(0))?;
    let start: u64 = serde_json::from_value(request.get_param(1))?;
    let limit: u64 = serde_json::from_value(request.get_param(2))?;

    let event_key = EventKey::try_from(&hex::decode(raw_event_key)?[..])?;
    let events_with_proof = service.db.get_events(&event_key, start, true, limit)?;

    let req_version = request.version();
    let events = events_with_proof
        .into_iter()
        .filter(|(version, _event)| version <= &req_version)
        .map(|event| event.into())
        .collect();
    Ok(events)
}

/// Returns meta information about supported currencies
async fn currencies_info(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<Vec<CurrencyInfoView>> {
    let raw_data = service.db.deref().batch_fetch_resources_by_version(
        vec![RegisteredCurrencies::CONFIG_ID.access_path()],
        request.version(),
    )?;
    ensure!(raw_data.len() == 1, "invalid storage result");
    let currencies = RegisteredCurrencies::from_bytes(&raw_data[0])?;
    let access_paths: Vec<_> = currencies
        .currency_codes()
        .iter()
        .map(|code| CurrencyInfoResource::resource_path_for(code.clone()))
        .collect();

    let mut currencies = vec![];
    for raw_data in service
        .db
        .deref()
        .batch_fetch_resources_by_version(access_paths, request.version())?
    {
        let currency_info = CurrencyInfoResource::try_from_bytes(&raw_data)?;
        currencies.push(CurrencyInfoView::from(currency_info));
    }
    Ok(currencies)
}

/// Returns proof of new state relative to version known to client
async fn get_state_proof(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<StateProofView> {
    let known_version: u64 = serde_json::from_value(request.get_param(0))?;
    let proofs = service
        .db
        .get_state_proof_with_ledger_info(known_version, request.ledger_info.clone())?;
    StateProofView::try_from((request.ledger_info, proofs.0, proofs.1))
}

/// Returns the account state to the client, alongside a proof relative to the version and
/// ledger_version specified by the client. If version or ledger_version are not specified,
/// the latest known versions will be used.
async fn get_account_state_with_proof(
    service: JsonRpcService,
    request: JsonRpcRequest,
) -> Result<AccountStateWithProofView> {
    let address: String = serde_json::from_value(request.get_param(0))?;
    let account_address = AccountAddress::from_str(&address)?;

    // If versions are specified by the request parameters, use them, otherwise use the defaults
    let version =
        serde_json::from_value::<u64>(request.get_param(1)).unwrap_or_else(|_| request.version());
    let ledger_version =
        serde_json::from_value::<u64>(request.get_param(2)).unwrap_or_else(|_| request.version());

    let account_state_with_proof =
        service
            .db
            .get_account_state_with_proof(account_address, version, ledger_version)?;
    Ok(AccountStateWithProofView::try_from(
        account_state_with_proof,
    )?)
}

/// Builds registry of all available RPC methods
/// To register new RPC method, add it via `register_rpc_method!` macros call
/// Note that RPC method name will equal to name of function
pub(crate) fn build_registry() -> RpcRegistry {
    let mut registry = RpcRegistry::new();
    register_rpc_method!(registry, submit, 1);
    register_rpc_method!(registry, get_metadata, 0);
    register_rpc_method!(registry, get_account_state, 1);
    register_rpc_method!(registry, get_transactions, 3);
    register_rpc_method!(registry, get_account_transaction, 3);
    register_rpc_method!(registry, get_events, 3);
    register_rpc_method!(registry, currencies_info, 0);

    register_rpc_method!(registry, get_state_proof, 1);
    register_rpc_method!(registry, get_account_state_with_proof, 3);

    registry
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{Matrix, Mat4, Mat3, ToMat4};
use cgmath::projection;
use cgmath::angle;
use cgmath::vector::{Vec3, Vec2};

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

/// usage: let s = mvp_matrix; println(type_of(&s));
fn type_of<T>(_: &T) -> &'static str {
  unsafe {
    (*std::unstable::intrinsics::get_tydesc::<T>()).name
  }
}

fn print_mat4(name: &str, mat: Mat4<f32>) {
  println!("{}:", name);
  for i in range(0u, 4) {
    for j in range(0u, 4) {
      print!("{} ", *mat.cr(i, j));
    }
    println("");
  }
  println("");
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
  let shader = gl::CreateShader(ty);
  unsafe {
    // Attempt to compile the shader
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    // Get the compile status
    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    // Get the link status
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint,
  window: glfw::Window
}

impl Win {
  fn new() -> Win {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    let mut win =  Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };

    win.window.make_context_current();
    win.window.set_cursor_pos_callback(~CursorPosContext);
    win.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    // Create GLSL shaders
    win.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    win.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    win.program = link_program(win.vertex_shader, win.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut win.vertex_array_obj);
      gl::BindVertexArray(win.vertex_array_obj);

      // Create a Vertex Buffer Object and copy the vertex data to it
      gl::GenBuffers(1, &mut win.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, win.vertex_buffer_obj);
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(win.program);
      gl::BindFragDataLocation(win.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        win.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);
      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      // Get a handle for our "model_view_proj_matrix" uniform.
      // Only at initialisation time.
      win.matrix_id = gl::GetUniformLocation(
        win.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }

    win
  }

  fn cleanup(&self) {
    // Cleanup
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let fov = angle::deg(45.0f32);
    let ratio = 4.0 / 3.0;
    let display_range_min = 0.1;
    let display_range_max = 100.0;
    let projection_matrix = projection::perspective(
      fov, ratio, display_range_min, display_range_max
    );

    let mut mvp_matrix = projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);
    }

    unsafe {
      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform.
      // For each model you render, since the model_view_proj_matrix
      // will be different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();

    // Clear the screen to black
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);

    // Draw a triangle from the 3 vertices
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);

    self.window.swap_buffers();
  }

  fn is_running(&self) -> bool {
    return !self.window.should_close()
  }

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      // println!("Cursor position: ({}, {})", xpos, ypos);
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    let distance = 1.0;
    if action != glfw::Press {
      return;
    }
    match key {
      glfw::KeyEscape | glfw::KeyQ
                     => window.set_should_close(true),
      glfw::KeySpace => println!("space"),
      glfw::KeyUp    => unsafe { CAMERA_POS.y -= distance },
      glfw::KeyDown  => unsafe { CAMERA_POS.y += distance },
      glfw::KeyRight => unsafe { CAMERA_POS.x -= distance },
      glfw::KeyLeft  => unsafe { CAMERA_POS.x += distance },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>Comments and formatting<commit_after>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{Matrix, Mat4, Mat3, ToMat4};
use cgmath::projection;
use cgmath::angle;
use cgmath::vector::{Vec3, Vec2};

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

/// usage: let s = mvp_matrix; println(type_of(&s));
fn type_of<T>(_: &T) -> &'static str {
  unsafe {
    (*std::unstable::intrinsics::get_tydesc::<T>()).name
  }
}

fn print_mat4(name: &str, mat: Mat4<f32>) {
  println!("{}:", name);
  for i in range(0u, 4) {
    for j in range(0u, 4) {
      print!("{} ", *mat.cr(i, j));
    }
    println("");
  }
  println("");
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
  let shader = gl::CreateShader(ty);
  unsafe {
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint,
  window: glfw::Window
}

impl Win {
  fn new() -> Win {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    let mut win =  Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };

    win.window.make_context_current();
    win.window.set_cursor_pos_callback(~CursorPosContext);
    win.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    win.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    win.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    win.program = link_program(win.vertex_shader, win.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut win.vertex_array_obj);
      gl::BindVertexArray(win.vertex_array_obj);

      // Create a Vertex Buffer Object and copy the vertex data to it
      gl::GenBuffers(1, &mut win.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, win.vertex_buffer_obj);
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(win.program);
      gl::BindFragDataLocation(
        win.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        win.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);
      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      win.matrix_id = gl::GetUniformLocation(
        win.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }

    win
  }

  fn cleanup(&self) {
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let fov = angle::deg(45.0f32);
    let ratio = 4.0 / 3.0;
    let display_range_min = 0.1;
    let display_range_max = 100.0;
    let projection_matrix = projection::perspective(
      fov, ratio, display_range_min, display_range_max
    );

    let mut mvp_matrix = projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);
    }

    unsafe {
      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform for each model
      // you render, since the model_view_proj_matrix will be
      // different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);
    self.window.swap_buffers();
  }

  fn is_running(&self) -> bool {
    return !self.window.should_close()
  }

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    let distance = 1.0;
    if action != glfw::Press {
      return;
    }
    match key {
      glfw::KeyEscape | glfw::KeyQ
                     => window.set_should_close(true),
      glfw::KeySpace => println!("space"),
      glfw::KeyUp    => unsafe { CAMERA_POS.y -= distance },
      glfw::KeyDown  => unsafe { CAMERA_POS.y += distance },
      glfw::KeyRight => unsafe { CAMERA_POS.x -= distance },
      glfw::KeyLeft  => unsafe { CAMERA_POS.x += distance },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<|endoftext|>