<commit_before>use orbtk::prelude::*;

// German localization file.
static SHOWCASE_DE_DE: &str = include_str!("../res/showcase/showcase_de_DE.ron");

fn main() {
    // use this only if you want to run it as web application.
    orbtk::initialize();

    // if no dictionary is set for the default language e.g. english the content of the text property will drawn.
    let localization = RonLocalization::create()
        .language("en_US")
        .dictionary("de_DE", SHOWCASE_DE_DE)
        .build();

    Application::new()
        .localization(localization)
        .window(|ctx| {
            Window::new()
                .title("OrbTk - showcase example")
                .position((100, 100))
                .size(640, 730)
                .resizeable(true)
                .child(MainView::new().build(ctx))
                .build(ctx)
        })
        .run();
}

// [START] views

widget!(MainView {});

impl Template for MainView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            TabWidget::new()
                .tab("Buttons / Text", ButtonView::new().build(ctx))
                .tab("Items", ItemsView::new().build(ctx))
                .tab("Layouts", LayoutView::new().build(ctx))
                .tab("Image", ImageView::new().build(ctx))
                .tab("Localization", LocalizationView::new().build(ctx))
                .tab("Navigation", NavigationView::new().build(ctx))
                .build(ctx),
        )
    }
}

widget!(ButtonView {});

impl Template for ButtonView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let slider = Slider::new().min(0.0).max(1.0).build(ctx);
        self.child(
            Grid::new()
                .margin(16)
                .columns(Columns::create().push(140).push(32).push(140))
                .child(
                    Stack::new()
                        .spacing(8)
                        .child(
                            Button::new()
                                .text("Button")
                                .icon(material_icons_font::MD_CHECK)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Primary")
                                .style("button_primary")
                                .icon(material_icons_font::MD_360)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Text only")
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .icon(material_icons_font::MD_CHECK)
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            ToggleButton::new()
                                .text("ToggleButton")
                                .icon(material_icons_font::MD_ALARM_ON)
                                .build(ctx),
                        )
                        .child(CheckBox::new().text("CheckBox").build(ctx))
                        .child(Switch::new().build(ctx))
                        .child(slider)
                        .child(ProgressBar::new().val(slider).build(ctx))
                        .build(ctx),
                )
                .child(
                    Stack::new()
                        .attach(Grid::column(2))
                        .spacing(8)
                        .child(TextBlock::new().text("Header").style("header").build(ctx))
                        .child(TextBlock::new().text("Text").style("body").build(ctx))
                        .child(TextBox::new().water_mark("Insert text...").build(ctx))
                        .child(
                            PasswordBox::new()
                                .water_mark("Insert password...")
                                .build(ctx),
                        )
                        .child(NumericBox::new().max(123).step(0.123).val(0.123).build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

type List = Vec<String>;

widget!(ItemsView { items: List });

impl Template for ItemsView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let items = vec![
            "Item 1".to_string(),
            "Item 2".to_string(),
            "Item 4".to_string(),
            "Item 5".to_string(),
        ];
        let count = items.len();
        self.items(items).child(
            Stack::new()
                .width(140)
                .margin(16)
                .spacing(8)
                .child(
                    TextBlock::new()
                        .text("ItemsWidget")
                        .style("header")
                        .build(ctx),
                )
                .child(
                    ItemsWidget::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ListView").style("header").build(ctx))
                .child(
                    ListView::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ComboBox").style("header").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .selected_index(0)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(LayoutView {});

impl Template for LayoutView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            Stack::new()
                .width(400)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Grid").style("header").build(ctx))
                .child(
                    Container::new()
                        .width(300)
                        .height(150)
                        .background("black")
                        .child(
                            Grid::new()
                                .columns(Columns::create().push("*").push("auto").push(50))
                                .rows(Rows::create().push("*").push("*"))
                                .child(
                                    Container::new()
                                        .background("lynch")
                                        .margin((10, 0, 0, 4))
                                        .attach(Grid::column(0))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,0)")
                                                .style("light_text")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .margin(10)
                                        .constraint(Constraint::create().width(150))
                                        .attach(Grid::column(1))
                                        .child(
                                            TextBlock::new()
                                                .text("(1,0)")
                                                .style("body")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .attach(Grid::column(2))
                                        .child(
                                            TextBlock::new()
                                                .text("(2,0)")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .attach(Grid::column(0))
                                        .attach(Grid::row(1))
                                        .attach(Grid::column_span(3))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,1) - ColumnSpan 3")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Stack").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .child(
                            Stack::new()
                                .spacing(4)
                                .child(Container::new().background("lynch").height(50).build(ctx))
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .height(50)
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Padding").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .height(150)
                        .padding((16, 8, 16, 8))
                        .child(Container::new().background("lynch").build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(ImageView {});

impl Template for ImageView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            ImageWidget::new()
                .margin(16)
                .image("res/showcase/orbtk_logo.png")
                .build(ctx),
        )
    }
}

widget!(LocalizationView<LocalizationState> { languages: List, selected_index: i32 });

impl Template for LocalizationView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let languages = vec!["English".to_string(), "German".to_string()];
        let count = languages.len();

        self.languages(languages).selected_index(0).child(
            Stack::new()
                .width(120)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Hello").build(ctx))
                .child(TextBlock::new().text("world").build(ctx))
                .child(TextBlock::new().text("I").build(ctx))
                .child(TextBlock::new().text("love").build(ctx))
                .child(TextBlock::new().text("localization").build(ctx))
                .child(TextBlock::new().text("!").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text =
                                bc.get_widget(id).get::<Vec<String>>("languages")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .on_changed("selected_index", move |states, _| {
                            states.get_mut::<LocalizationState>(id).change_language();
                        })
                        .selected_index(id)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(NavigationView {});

impl Template for NavigationView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let pager = Pager::new()
            .child(
                Container::new()
                    .padding(8)
                    .background("lynch")
                    .child(TextBlock::new().text("Page 1").build(ctx))
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("goldendream")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 2")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("linkwater")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 3")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        self.child(
            Grid::new()
                .margin(16)
                .rows(
                    Rows::create()
                        .push("*")
                        .push(8)
                        .push("auto")
                        .push(8)
                        .push("*")
                        .build(),
                )
                .child(pager)
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_LEFT)
                        .h_align("start")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).previous();
                            true
                        })
                        .build(ctx),
                )
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_RIGHT)
                        .h_align("end")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).next();
                            true
                        })
                        .build(ctx),
                )
                .child(
                    MasterDetail::new()
                        .responsive(true)
                        .break_point(64)
                        .attach(Grid::row(4))
                        .master(
                            Container::new()
                                .background("lynch")
                                .child(TextBlock::new().margin(8).text("master").build(ctx))
                                .build(ctx),
                        )
                        .detail(
                            Container::new()
                                .padding(8)
                                .background("goldendream")
                                .child(
                                    TextBlock::new()
                                        .foreground("black")
                                        .margin(8)
                                        .text("detail")
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

// [END] views

// [START] states

#[derive(Debug, Default, AsAny)]
struct LocalizationState {
    change_language: bool,
}

impl LocalizationState {
    fn change_language(&mut self) {
        self.change_language = true;
    }
}

impl State for LocalizationState {
    fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        if !self.change_language {
            return;
        }

        let index = *LocalizationView::selected_index_ref(&ctx.widget()) as usize;
        let selected_language = LocalizationView::languages_ref(&ctx.widget())[index].clone();

        match selected_language.as_str() {
            "English" => ctx.set_language("en_US"),
            "German" => ctx.set_language("de_DE"),
            _ => {}
        }

        self.change_language = false;
    }
}

// [END] states
<commit_msg>example update.<commit_after>use orbtk::prelude::*;

// German localization file.
static SHOWCASE_DE_DE: &str = include_str!("../res/showcase/showcase_de_DE.ron");

fn main() {
    // use this only if you want to run it as web application.
    orbtk::initialize();

    // if no dictionary is set for the default language e.g. english the content of the text property will drawn.
    let localization = RonLocalization::create()
        .language("en_US")
        .dictionary("de_DE", SHOWCASE_DE_DE)
        .build();

    Application::new()
        .localization(localization)
        .window(|ctx| {
            Window::new()
                .title("OrbTk - showcase example")
                .position((100, 100))
                .size(640, 730)
                .resizeable(true)
                .child(MainView::new().build(ctx))
                .build(ctx)
        })
        .run();
}

// [START] views

widget!(MainView {});

impl Template for MainView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            TabWidget::new()
                .tab("Buttons / Text", ButtonView::new().build(ctx))
                .tab("Items", ItemsView::new().build(ctx))
                .tab("Layouts", LayoutView::new().build(ctx))
                .tab("Image", ImageView::new().build(ctx))
                .tab("Localization", LocalizationView::new().build(ctx))
                .tab("Navigation", NavigationView::new().build(ctx))
                .build(ctx),
        )
    }
}

widget!(ButtonView {});

impl Template for ButtonView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let slider = Slider::new().min(0.0).max(1.0).build(ctx);
        self.child(
            Grid::new()
                .margin(16)
                .columns(Columns::create().push(140).push(32).push(140))
                .child(
                    Stack::new()
                        .spacing(8)
                        .child(
                            Button::new()
                                .text("Button")
                                .icon(material_icons_font::MD_CHECK)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Primary")
                                .style("button_primary")
                                .icon(material_icons_font::MD_360)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Text only")
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .icon(material_icons_font::MD_CHECK)
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            ToggleButton::new()
                                .text("ToggleButton")
                                .icon(material_icons_font::MD_ALARM_ON)
                                .build(ctx),
                        )
                        .child(CheckBox::new().text("CheckBox").build(ctx))
                        .child(Switch::new().build(ctx))
                        .child(slider)
                        .child(ProgressBar::new().val(slider).build(ctx))
                        .build(ctx),
                )
                .child(
                    Stack::new()
                        .attach(Grid::column(2))
                        .spacing(8)
                        .child(TextBlock::new().text("Header").style("header").build(ctx))
                        .child(TextBlock::new().text("Text").style("body").build(ctx))
                        .child(TextBox::new().water_mark("Insert text...").build(ctx))
                        .child(
                            PasswordBox::new()
                                .water_mark("Insert password...")
                                .build(ctx),
                        )
                        .child(NumericBox::new().max(123).step(0.123).val(0.123).build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

type List = Vec<String>;

widget!(ItemsView { items: List });

impl Template for ItemsView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let items = vec![
            "Item 1".to_string(),
            "Item 2".to_string(),
            "Item 4".to_string(),
            "Item 5".to_string(),
        ];
        let count = items.len();
        self.items(items).child(
            Stack::new()
                .width(140)
                .margin(16)
                .spacing(8)
                .child(
                    TextBlock::new()
                        .text("ItemsWidget")
                        .style("header")
                        .build(ctx),
                )
                .child(
                    ItemsWidget::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ListView").style("header").build(ctx))
                .child(
                    ListView::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ComboBox").style("header").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .selected_index(0)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(LayoutView {});

impl Template for LayoutView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            Stack::new()
                .width(400)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Grid").style("header").build(ctx))
                .child(
                    Container::new()
                        .width(300)
                        .height(150)
                        .background("black")
                        .child(
                            Grid::new()
                                .columns(Columns::create().push("*").push("auto").push(50))
                                .rows(Rows::create().push("*").push("*"))
                                .child(
                                    Container::new()
                                        .background("lynch")
                                        .margin((10, 0, 0, 4))
                                        .attach(Grid::column(0))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,0)")
                                                .style("light_text")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .margin(10)
                                        .constraint(Constraint::create().width(150))
                                        .attach(Grid::column(1))
                                        .child(
                                            TextBlock::new()
                                                .text("(1,0)")
                                                .style("body")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .attach(Grid::column(2))
                                        .child(
                                            TextBlock::new()
                                                .text("(2,0)")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .attach(Grid::column(0))
                                        .attach(Grid::row(1))
                                        .attach(Grid::column_span(3))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,1) - ColumnSpan 3")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Stack").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .child(
                            Stack::new()
                                .spacing(4)
                                .child(Container::new().background("lynch").height(50).build(ctx))
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .height(50)
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Padding").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .height(150)
                        .padding((16, 8, 16, 8))
                        .child(Container::new().background("lynch").build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(ImageView {});

impl Template for ImageView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            ImageWidget::new()
                .margin(16)
                .image("res/showcase/orbtk_logo.png")
                .build(ctx),
        )
    }
}

widget!(LocalizationView<LocalizationState> { languages: List, selected_index: i32 });

impl Template for LocalizationView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let languages = vec!["English".to_string(), "German".to_string()];
        let count = languages.len();

        self.languages(languages).selected_index(0).child(
            Stack::new()
                .width(120)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Hello").build(ctx))
                .child(TextBlock::new().text("world").build(ctx))
                .child(TextBlock::new().text("I").build(ctx))
                .child(TextBlock::new().text("love").build(ctx))
                .child(TextBlock::new().text("localization").build(ctx))
                .child(TextBlock::new().text("!").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text =
                                bc.get_widget(id).get::<Vec<String>>("languages")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .on_changed("selected_index", move |states, _| {
                            states.get_mut::<LocalizationState>(id).change_language();
                        })
                        .selected_index(id)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(NavigationView {});

impl Template for NavigationView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let pager = Pager::new()
            .child(
                Container::new()
                    .padding(8)
                    .background("lynch")
                    .child(TextBlock::new().text("Page 1").build(ctx))
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("goldendream")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 2")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("linkwater")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 3")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        self.child(
            Grid::new()
                .margin(16)
                .rows(
                    Rows::create()
                        .push("*")
                        .push(8)
                        .push("auto")
                        .push(8)
                        .push("*")
                        .build(),
                )
                .child(pager)
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_LEFT)
                        .h_align("start")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).previous();
                            true
                        })
                        .build(ctx),
                )
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_RIGHT)
                        .h_align("end")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).next();
                            true
                        })
                        .build(ctx),
                )
                .child(
                    MasterDetail::new()
                        .responsive(true)
                        .break_point(540)
                        .attach(Grid::row(4))
                        .master(
                            Container::new()
                                .background("lynch")
                                .child(TextBlock::new().margin(8).text("master").build(ctx))
                                .build(ctx),
                        )
                        .detail(
                            Container::new()
                                .padding(8)
                                .background("goldendream")
                                .child(
                                    TextBlock::new()
                                        .foreground("black")
                                        .margin(8)
                                        .text("detail")
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

// [END] views

// [START] states

#[derive(Debug, Default, AsAny)]
struct LocalizationState {
    change_language: bool,
}

impl LocalizationState {
    fn change_language(&mut self) {
        self.change_language = true;
    }
}

impl State for LocalizationState {
    fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        if !self.change_language {
            return;
        }

        let index = *LocalizationView::selected_index_ref(&ctx.widget()) as usize;
        let selected_language = LocalizationView::languages_ref(&ctx.widget())[index].clone();

        match selected_language.as_str() {
            "English" => ctx.set_language("en_US"),
            "German" => ctx.set_language("de_DE"),
            _ => {}
        }

        self.change_language = false;
    }
}

// [END] states
<|endoftext|>


<commit_before>#![allow(missing_docs)] // For downcast.

use na::{DVectorSliceMut, RealField};

use crate::joint::{Joint, JointMotor};
use crate::object::{Body, BodyPartHandle, Multibody, MultibodyLink};
use crate::solver::{
    BilateralGroundConstraint, ConstraintSet, GenericNonlinearConstraint, IntegrationParameters,
    UnilateralGroundConstraint,
};

/// Trait implemented by joints using the reduced-coordinates approach and allowing only one degree of freedom.
pub trait UnitJoint<N: RealField>: Joint<N> {
    /// The generalized coordinate of the unit joint.
    fn position(&self) -> N;
    /// The motor applied to the degree of freedom of the unit joitn.
    fn motor(&self) -> &JointMotor<N, N>;
    /// The lower limit, if any, set to the generalized coordinate of this unit joint.
    fn min_position(&self) -> Option<N>;
    /// The upper limit, if any, set to the generalized coordinate of this unit joint.
    fn max_position(&self) -> Option<N>;
}

impl_downcast!(UnitJoint<N> where N: RealField);

/// Computes the maximum number of velocity constraints to be applied by the given unit joint.
pub fn unit_joint_num_velocity_constraints<N: RealField, J: UnitJoint<N>>(joint: &J) -> usize {
    // FIXME: don't always keep the constraints active.
    let mut nconstraints = 0;

    if joint.motor().enabled {
        nconstraints += 1;
    }
    if joint.min_position().is_some() {
        nconstraints += 1;
    }
    if joint.max_position().is_some() {
        nconstraints += 1;
    }

    nconstraints
}

/// Initializes and generate the velocity constraints applicable to the multibody links attached
/// to this joint.
pub fn unit_joint_velocity_constraints<N: RealField, J: UnitJoint<N>>(
    joint: &J,
    parameters: &IntegrationParameters<N>,
    multibody: &Multibody<N>,
    link: &MultibodyLink<N>,
    assembly_id: usize,
    dof_id: usize,
    ext_vels: &[N],
    ground_j_id: &mut usize,
    jacobians: &mut [N],
    constraints: &mut ConstraintSet<N, (), (), usize>,
) {
    let ndofs = multibody.ndofs();
    let impulses = multibody.impulses();
    let mut is_min_constraint_active = false;
    let joint_velocity = multibody.joint_velocity(link);

    let motor = joint.motor();
    if motor.enabled {
        let dvel = joint_velocity[dof_id] + ext_vels[link.assembly_id];

        DVectorSliceMut::from_slice(&mut jacobians[*ground_j_id..], ndofs).fill(N::zero());
        jacobians[*ground_j_id + link.assembly_id + dof_id] = N::one();

        let wj_id = *ground_j_id + ndofs;
        multibody.inv_mass_mul_unit_joint_force(link, dof_id, N::one(), &mut jacobians[wj_id..]);

        let inv_r = jacobians[wj_id + link.assembly_id + dof_id]; // = J^t * M^-1 J
        let rhs = dvel
            - motor
                .desired_velocity
                .clamp(-motor.max_velocity, motor.max_velocity);
        let limits = motor.impulse_limits();
        let impulse_id = link.impulse_id + dof_id * 3;

        let constraint = BilateralGroundConstraint {
            impulse: impulses[impulse_id] * parameters.warmstart_coeff,
            r: N::one() / inv_r,
            rhs,
            limits,
            impulse_id,
            assembly_id,
            j_id: *ground_j_id,
            wj_id: *ground_j_id + ndofs,
            ndofs,
        };

        constraints.velocity.bilateral_ground.push(constraint);
        *ground_j_id += 2 * ndofs;
    }

    if let Some(min_position) = joint.min_position() {
        let err = min_position - joint.position();
        let dvel = joint_velocity[dof_id] + ext_vels[link.assembly_id + dof_id];

        if err >= N::zero() {
            is_min_constraint_active = true;
            DVectorSliceMut::from_slice(&mut jacobians[*ground_j_id..], ndofs).fill(N::zero());
            jacobians[*ground_j_id + link.assembly_id + dof_id] = N::one();

            let wj_id = *ground_j_id + ndofs;
            multibody.inv_mass_mul_unit_joint_force(
                link,
                dof_id,
                N::one(),
                &mut jacobians[wj_id..],
            );

            let inv_r = jacobians[wj_id + link.assembly_id + dof_id]; // = J^t * M^-1 J

            let impulse_id = link.impulse_id + dof_id * 3 + 1;
            let constraint = UnilateralGroundConstraint {
                impulse: impulses[impulse_id] * parameters.warmstart_coeff,
                r: N::one() / inv_r,
                rhs: dvel,
                impulse_id,
                assembly_id,
                j_id: *ground_j_id,
                wj_id: *ground_j_id + ndofs,
                ndofs,
            };

            constraints.velocity.unilateral_ground.push(constraint);
            *ground_j_id += 2 * ndofs;
        }
    }

    if let Some(max_position) = joint.max_position() {
        let err = -(max_position - joint.position());
        let dvel = -joint_velocity[dof_id] - ext_vels[link.assembly_id + dof_id];

        if err >= N::zero() {
            DVectorSliceMut::from_slice(&mut jacobians[*ground_j_id..], ndofs).fill(N::zero());
            jacobians[*ground_j_id + link.assembly_id + dof_id] = -N::one();
            let wj_id = *ground_j_id + ndofs;

            if is_min_constraint_active {
                // This jacobian is simply the negation of the first one.
                for i in 0..ndofs {
                    jacobians[wj_id + i] = -jacobians[*ground_j_id - ndofs + i];
                }
            } else {
                multibody.inv_mass_mul_unit_joint_force(
                    link,
                    dof_id,
                    -N::one(),
                    &mut jacobians[wj_id..],
                );
            }

            let inv_r = -jacobians[wj_id + link.assembly_id + dof_id]; // = J^t * M^-1 J

            let impulse_id = link.impulse_id + dof_id * 3 + 2;
            let constraint = UnilateralGroundConstraint {
                impulse: impulses[impulse_id] * parameters.warmstart_coeff,
                r: N::one() / inv_r,
                rhs: dvel,
                impulse_id,
                assembly_id,
                j_id: *ground_j_id,
                wj_id: *ground_j_id + ndofs,
                ndofs,
            };

            constraints.velocity.unilateral_ground.push(constraint);
            *ground_j_id += 2 * ndofs;
        }
    }
}

/// Initializes and generate the position constraints applicable to the multibody links attached
/// to this joint.
pub fn unit_joint_position_constraint<N: RealField, J: UnitJoint<N>>(
    joint: &J,
    multibody: &Multibody<N>,
    link: &MultibodyLink<N>,
    handle: BodyPartHandle<()>,
    dof_id: usize,
    is_angular: bool,
    jacobians: &mut [N],
) -> Option<GenericNonlinearConstraint<N, ()>> {
    let mut sign = N::one();
    let mut rhs = None;

    if let Some(min_position) = joint.min_position() {
        let err = min_position - joint.position();
        if err > N::zero() {
            rhs = Some(-err);
        }
    }

    if rhs.is_none() {
        if let Some(max_position) = joint.max_position() {
            let err = -(max_position - joint.position());
            if err > N::zero() {
                rhs = Some(-err);
                sign = -N::one();
            }
        }
    }

    if let Some(rhs) = rhs {
        let ndofs = multibody.ndofs();

        multibody.inv_mass_mul_unit_joint_force(link, dof_id, sign, jacobians);

        let inv_r = sign * jacobians[link.assembly_id + dof_id]; // = J^t * M^-1 J

        return Some(GenericNonlinearConstraint::new(
            handle,
            None,
            is_angular,
            ndofs,
            0,
            0,
            0,
            rhs,
            N::one() / inv_r,
        ));
    }

    None
}
<commit_msg>Make clamped max motor velocity code cleaner<commit_after>#![allow(missing_docs)] // For downcast.

use na::{DVectorSliceMut, RealField};

use crate::joint::{Joint, JointMotor};
use crate::object::{Body, BodyPartHandle, Multibody, MultibodyLink};
use crate::solver::{
    BilateralGroundConstraint, ConstraintSet, GenericNonlinearConstraint, IntegrationParameters,
    UnilateralGroundConstraint,
};

/// Trait implemented by joints using the reduced-coordinates approach and allowing only one degree of freedom.
pub trait UnitJoint<N: RealField>: Joint<N> {
    /// The generalized coordinate of the unit joint.
    fn position(&self) -> N;
    /// The motor applied to the degree of freedom of the unit joitn.
    fn motor(&self) -> &JointMotor<N, N>;
    /// The lower limit, if any, set to the generalized coordinate of this unit joint.
    fn min_position(&self) -> Option<N>;
    /// The upper limit, if any, set to the generalized coordinate of this unit joint.
    fn max_position(&self) -> Option<N>;
}

impl_downcast!(UnitJoint<N> where N: RealField);

/// Computes the maximum number of velocity constraints to be applied by the given unit joint.
pub fn unit_joint_num_velocity_constraints<N: RealField, J: UnitJoint<N>>(joint: &J) -> usize {
    // FIXME: don't always keep the constraints active.
    let mut nconstraints = 0;

    if joint.motor().enabled {
        nconstraints += 1;
    }
    if joint.min_position().is_some() {
        nconstraints += 1;
    }
    if joint.max_position().is_some() {
        nconstraints += 1;
    }

    nconstraints
}

/// Initializes and generate the velocity constraints applicable to the multibody links attached
/// to this joint.
pub fn unit_joint_velocity_constraints<N: RealField, J: UnitJoint<N>>(
    joint: &J,
    parameters: &IntegrationParameters<N>,
    multibody: &Multibody<N>,
    link: &MultibodyLink<N>,
    assembly_id: usize,
    dof_id: usize,
    ext_vels: &[N],
    ground_j_id: &mut usize,
    jacobians: &mut [N],
    constraints: &mut ConstraintSet<N, (), (), usize>,
) {
    let ndofs = multibody.ndofs();
    let impulses = multibody.impulses();
    let mut is_min_constraint_active = false;
    let joint_velocity = multibody.joint_velocity(link);

    let motor = joint.motor();
    if motor.enabled {
        let dvel = joint_velocity[dof_id] + ext_vels[link.assembly_id];

        DVectorSliceMut::from_slice(&mut jacobians[*ground_j_id..], ndofs).fill(N::zero());
        jacobians[*ground_j_id + link.assembly_id + dof_id] = N::one();

        let wj_id = *ground_j_id + ndofs;
        multibody.inv_mass_mul_unit_joint_force(link, dof_id, N::one(), &mut jacobians[wj_id..]);

        let inv_r = jacobians[wj_id + link.assembly_id + dof_id]; // = J^t * M^-1 J
        let velocity = motor
            .desired_velocity
            .clamp(-motor.max_velocity, motor.max_velocity);
        let rhs = dvel - velocity;
        let limits = motor.impulse_limits();
        let impulse_id = link.impulse_id + dof_id * 3;

        let constraint = BilateralGroundConstraint {
            impulse: impulses[impulse_id] * parameters.warmstart_coeff,
            r: N::one() / inv_r,
            rhs,
            limits,
            impulse_id,
            assembly_id,
            j_id: *ground_j_id,
            wj_id: *ground_j_id + ndofs,
            ndofs,
        };

        constraints.velocity.bilateral_ground.push(constraint);
        *ground_j_id += 2 * ndofs;
    }

    if let Some(min_position) = joint.min_position() {
        let err = min_position - joint.position();
        let dvel = joint_velocity[dof_id] + ext_vels[link.assembly_id + dof_id];

        if err >= N::zero() {
            is_min_constraint_active = true;
            DVectorSliceMut::from_slice(&mut jacobians[*ground_j_id..], ndofs).fill(N::zero());
            jacobians[*ground_j_id + link.assembly_id + dof_id] = N::one();

            let wj_id = *ground_j_id + ndofs;
            multibody.inv_mass_mul_unit_joint_force(
                link,
                dof_id,
                N::one(),
                &mut jacobians[wj_id..],
            );

            let inv_r = jacobians[wj_id + link.assembly_id + dof_id]; // = J^t * M^-1 J

            let impulse_id = link.impulse_id + dof_id * 3 + 1;
            let constraint = UnilateralGroundConstraint {
                impulse: impulses[impulse_id] * parameters.warmstart_coeff,
                r: N::one() / inv_r,
                rhs: dvel,
                impulse_id,
                assembly_id,
                j_id: *ground_j_id,
                wj_id: *ground_j_id + ndofs,
                ndofs,
            };

            constraints.velocity.unilateral_ground.push(constraint);
            *ground_j_id += 2 * ndofs;
        }
    }

    if let Some(max_position) = joint.max_position() {
        let err = -(max_position - joint.position());
        let dvel = -joint_velocity[dof_id] - ext_vels[link.assembly_id + dof_id];

        if err >= N::zero() {
            DVectorSliceMut::from_slice(&mut jacobians[*ground_j_id..], ndofs).fill(N::zero());
            jacobians[*ground_j_id + link.assembly_id + dof_id] = -N::one();
            let wj_id = *ground_j_id + ndofs;

            if is_min_constraint_active {
                // This jacobian is simply the negation of the first one.
                for i in 0..ndofs {
                    jacobians[wj_id + i] = -jacobians[*ground_j_id - ndofs + i];
                }
            } else {
                multibody.inv_mass_mul_unit_joint_force(
                    link,
                    dof_id,
                    -N::one(),
                    &mut jacobians[wj_id..],
                );
            }

            let inv_r = -jacobians[wj_id + link.assembly_id + dof_id]; // = J^t * M^-1 J

            let impulse_id = link.impulse_id + dof_id * 3 + 2;
            let constraint = UnilateralGroundConstraint {
                impulse: impulses[impulse_id] * parameters.warmstart_coeff,
                r: N::one() / inv_r,
                rhs: dvel,
                impulse_id,
                assembly_id,
                j_id: *ground_j_id,
                wj_id: *ground_j_id + ndofs,
                ndofs,
            };

            constraints.velocity.unilateral_ground.push(constraint);
            *ground_j_id += 2 * ndofs;
        }
    }
}

/// Initializes and generate the position constraints applicable to the multibody links attached
/// to this joint.
pub fn unit_joint_position_constraint<N: RealField, J: UnitJoint<N>>(
    joint: &J,
    multibody: &Multibody<N>,
    link: &MultibodyLink<N>,
    handle: BodyPartHandle<()>,
    dof_id: usize,
    is_angular: bool,
    jacobians: &mut [N],
) -> Option<GenericNonlinearConstraint<N, ()>> {
    let mut sign = N::one();
    let mut rhs = None;

    if let Some(min_position) = joint.min_position() {
        let err = min_position - joint.position();
        if err > N::zero() {
            rhs = Some(-err);
        }
    }

    if rhs.is_none() {
        if let Some(max_position) = joint.max_position() {
            let err = -(max_position - joint.position());
            if err > N::zero() {
                rhs = Some(-err);
                sign = -N::one();
            }
        }
    }

    if let Some(rhs) = rhs {
        let ndofs = multibody.ndofs();

        multibody.inv_mass_mul_unit_joint_force(link, dof_id, sign, jacobians);

        let inv_r = sign * jacobians[link.assembly_id + dof_id]; // = J^t * M^-1 J

        return Some(GenericNonlinearConstraint::new(
            handle,
            None,
            is_angular,
            ndofs,
            0,
            0,
            0,
            rhs,
            N::one() / inv_r,
        ));
    }

    None
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::rc::Rc;

use gfx_device_gl::Factory;
use piston_window::*;

use game::asset_factory::AssetFactory;
use game::config::Config;
use game::factory::TetrominoFactory;
use game::grid::Grid;
use game::render_options::RenderOptions;
use game::scenes::{Scene, SceneResult};
use game::tetromino::{Direction, MoveResult, Rotation, RotationResult, Tetromino};
use game::timer::Timer;
use game::window::GameWindow;

use game::leveling::{Level, LevelingSystem, DefaultLevelingSystem, LevelMetaData};
use game::scoring::{Score, ScoringSystem, ScoreMetaData, DefaultScoringSystem};

static DROP_TIME: f64 = 0.5;
static DROP_FACTOR: f64 = 0.1;

pub struct Game {
    time: Rc<Cell<f64>>,
    config: Config,
    asset_factory: AssetFactory,
    quit: bool,
    grid: Grid,
    lockstep_timer: Timer,
    drop_timer: Timer,
    scoring_system: Box<ScoringSystem>,
    score: Score,
    leveling_system: Box<LevelingSystem>,
    level: Level,
    total_lines_cleared: u32,
    tetromino: Tetromino,
    tetromino_factory: TetrominoFactory,
}

impl Scene for Game {
    fn process(&mut self, e: &Event) -> SceneResult {
        match *e {
            Event::Update(update) => {
                // Update the global game time
                self.update_time(update.dt);

                // Drop the current block if it needs dropping
                if let SceneResult::GameOver = self.move_down(None) {
                    return SceneResult::GameOver;
                }

                // Apply the currently active tetromino into the grid
                self.grid.apply_tetromino(&self.tetromino, &self.config);
            }
            Event::Input(ref input_event) => {
                self.handle_input(input_event);
            }
            _ => (),
        }

        if self.quit {
            SceneResult::MainMenu
        } else {
            SceneResult::None
        }
    }

    fn render(&mut self, window: &mut GameWindow, e: &Event) {
        window.draw_2d(e, |mut c, g| {
            clear([0.0, 0.0, 0.0, 1.0], g);

            let mut options = RenderOptions {
                config: &self.config,
                context: &mut c,
                graphics: g,
                character_cache: self.asset_factory.font.as_mut().unwrap(),
            };

            self.grid.render(&mut options, e);
            self.score.render(&mut options);
            self.level.render(&mut options);
        });
    }
}

impl Game {
    pub fn new(config: Config, gfx_factory: Factory) -> Game {
        let time = RcCell!(0.0);
        let factory = TetrominoFactory::new();

        let tetromino = factory.create(&config);
        let score = Score::new(&config);
        let level = Level::new(&config);

        Game {
            time: time.clone(),
            config: config,
            asset_factory: AssetFactory::new(gfx_factory),
            quit: false,
            grid: Grid::new(),
            lockstep_timer: Timer::new(0.5, time.clone()),
            drop_timer: Timer::new(DROP_TIME, time.clone()),
            score: score,
            level: level,
            scoring_system: Box::new(DefaultScoringSystem),
            leveling_system: Box::new(DefaultLevelingSystem),
            total_lines_cleared: 0,
            tetromino: tetromino,
            tetromino_factory: factory,
        }
    }

    /// Increments the global time
    fn update_time(&mut self, delta: f64) {
        self.time.set(self.time.get() + delta);
    }

    fn move_down<O>(&mut self, force_drop: O) -> SceneResult
        where O: Into<Option<bool>>
    {
        // If we can drop, check if we are ready and drop the active tetromino
        match self.tetromino.can_move(Direction::South, &self.grid.boxes) {
            MoveResult::Allow => {
                if self.drop_timer.elapsed() || force_drop.into().is_some() {
                    self.tetromino.drop_down();
                    self.drop_timer.reset(None);
                    self.lockstep_timer.reset(None);
                }
            }
            MoveResult::Blocked => {
                if self.tetromino.y <= 0 {
                    // We've hit the top
                    return SceneResult::GameOver;
                } else {
                    self.handle_blocked(None);
                }
            }
            _ => (),
        }

        SceneResult::None
    }

    fn handle_blocked<O>(&mut self, force: O)
        where O: Into<Option<bool>>
    {
        // If its blocked.. first check if our LockStep has elapsed
        if self.lockstep_timer.elapsed() || force.into().is_some() {
            // Store the tetromino in the grid and create a new tetromino
            self.new_tetromino();
            self.lockstep_timer.stop();
            let lines_cleared = self.grid.remove_complete_lines(&self.config);
            self.total_lines_cleared += lines_cleared;

            {
                let mut level_metadata = LevelMetaData {
                    level: &mut self.level,
                    lines_cleared: lines_cleared,
                    total_lines_cleared: self.total_lines_cleared,
                };

                self.scoring_system.update_score(&mut self.score,
                                                 ScoreMetaData { lines_cleared: lines_cleared });

                self.leveling_system.process(&mut level_metadata);
            }
            self.drop_timer.reset(DROP_TIME - (self.level.level as f64 * DROP_FACTOR));
        }
    }

    /// Drops a Tetromino straight down until it hits the lowest
    /// possible point.
    fn drop_tetromino(&mut self) {
        while let MoveResult::Allow = self.tetromino.can_move(Direction::South, &self.grid.boxes) {
            self.tetromino.drop_down();
        }
    }

    fn new_tetromino(&mut self) {
        self.grid.store_tetromino(&self.tetromino);
        self.tetromino = self.tetromino_factory.create(&self.config);
    }

    fn handle_input(&mut self, input: &Input) {
        if let Input::Press(ref button) = *input {
            match *button {
                Button::Keyboard(Key::Escape) => self.quit = true,
                Button::Keyboard(Key::Z) => {
                    if let RotationResult::Allow = self.tetromino
                        .can_rotate(Rotation::CounterClockwise, &self.grid.boxes) {
                        self.tetromino.rotate(Rotation::CounterClockwise);
                    }
                }
                Button::Keyboard(Key::X) => {
                    if let RotationResult::Allow = self.tetromino
                        .can_rotate(Rotation::Clockwise, &self.grid.boxes) {
                        self.tetromino.rotate(Rotation::Clockwise);
                    }
                }
                Button::Keyboard(Key::Left) => {
                    match self.tetromino.can_move(Direction::West, &self.grid.boxes) {
                        MoveResult::Allow => self.tetromino.move_left(),
                        MoveResult::Blocked => self.new_tetromino(),
                        _ => (),
                    }
                }
                Button::Keyboard(Key::Right) => {
                    match self.tetromino.can_move(Direction::East, &self.grid.boxes) {
                        MoveResult::Allow => self.tetromino.move_right(),
                        MoveResult::Blocked => self.new_tetromino(),
                        _ => (),
                    }
                }
                Button::Keyboard(Key::Down) => {
                    self.move_down(true);
                }
                Button::Keyboard(Key::Space) => {
                    self.drop_tetromino();
                    // Stop the lockstep timer straight away
                    self.handle_blocked(true);
                }
                _ => (),
            }
        }
    }
}
<commit_msg>Converted lockstep time to constant<commit_after>use std::cell::Cell;
use std::rc::Rc;

use gfx_device_gl::Factory;
use piston_window::*;

use game::asset_factory::AssetFactory;
use game::config::Config;
use game::factory::TetrominoFactory;
use game::grid::Grid;
use game::render_options::RenderOptions;
use game::scenes::{Scene, SceneResult};
use game::tetromino::{Direction, MoveResult, Rotation, RotationResult, Tetromino};
use game::timer::Timer;
use game::window::GameWindow;

use game::leveling::{Level, LevelingSystem, DefaultLevelingSystem, LevelMetaData};
use game::scoring::{Score, ScoringSystem, ScoreMetaData, DefaultScoringSystem};

static LOCK_STEP_TIME: f64 = 0.5;

static DROP_TIME: f64 = 0.5;
static DROP_FACTOR: f64 = 0.1;

pub struct Game {
    time: Rc<Cell<f64>>,
    config: Config,
    asset_factory: AssetFactory,
    quit: bool,
    grid: Grid,
    lockstep_timer: Timer,
    drop_timer: Timer,
    scoring_system: Box<ScoringSystem>,
    score: Score,
    leveling_system: Box<LevelingSystem>,
    level: Level,
    total_lines_cleared: u32,
    tetromino: Tetromino,
    tetromino_factory: TetrominoFactory,
}

impl Scene for Game {
    fn process(&mut self, e: &Event) -> SceneResult {
        match *e {
            Event::Update(update) => {
                // Update the global game time
                self.update_time(update.dt);

                // Drop the current block if it needs dropping
                if let SceneResult::GameOver = self.move_down(None) {
                    return SceneResult::GameOver;
                }

                // Apply the currently active tetromino into the grid
                self.grid.apply_tetromino(&self.tetromino, &self.config);
            }
            Event::Input(ref input_event) => {
                self.handle_input(input_event);
            }
            _ => (),
        }

        if self.quit {
            SceneResult::MainMenu
        } else {
            SceneResult::None
        }
    }

    fn render(&mut self, window: &mut GameWindow, e: &Event) {
        window.draw_2d(e, |mut c, g| {
            clear([0.0, 0.0, 0.0, 1.0], g);

            let mut options = RenderOptions {
                config: &self.config,
                context: &mut c,
                graphics: g,
                character_cache: self.asset_factory.font.as_mut().unwrap(),
            };

            self.grid.render(&mut options, e);
            self.score.render(&mut options);
            self.level.render(&mut options);
        });
    }
}

impl Game {
    pub fn new(config: Config, gfx_factory: Factory) -> Game {
        let time = RcCell!(0.0);
        let factory = TetrominoFactory::new();

        let tetromino = factory.create(&config);
        let score = Score::new(&config);
        let level = Level::new(&config);

        Game {
            time: time.clone(),
            config: config,
            asset_factory: AssetFactory::new(gfx_factory),
            quit: false,
            grid: Grid::new(),
            lockstep_timer: Timer::new(LOCK_STEP_TIME, time.clone()),
            drop_timer: Timer::new(DROP_TIME, time.clone()),
            score: score,
            level: level,
            scoring_system: Box::new(DefaultScoringSystem),
            leveling_system: Box::new(DefaultLevelingSystem),
            total_lines_cleared: 0,
            tetromino: tetromino,
            tetromino_factory: factory,
        }
    }

    /// Increments the global time
    fn update_time(&mut self, delta: f64) {
        self.time.set(self.time.get() + delta);
    }

    fn move_down<O>(&mut self, force_drop: O) -> SceneResult
        where O: Into<Option<bool>>
    {
        // If we can drop, check if we are ready and drop the active tetromino
        match self.tetromino.can_move(Direction::South, &self.grid.boxes) {
            MoveResult::Allow => {
                if self.drop_timer.elapsed() || force_drop.into().is_some() {
                    self.tetromino.drop_down();
                    self.drop_timer.reset(None);
                    self.lockstep_timer.reset(None);
                }
            }
            MoveResult::Blocked => {
                if self.tetromino.y <= 0 {
                    // We've hit the top
                    return SceneResult::GameOver;
                } else {
                    self.handle_blocked(None);
                }
            }
            _ => (),
        }

        SceneResult::None
    }

    fn handle_blocked<O>(&mut self, force: O)
        where O: Into<Option<bool>>
    {
        // If its blocked.. first check if our LockStep has elapsed
        if self.lockstep_timer.elapsed() || force.into().is_some() {
            // Store the tetromino in the grid and create a new tetromino
            self.new_tetromino();
            self.lockstep_timer.stop();
            let lines_cleared = self.grid.remove_complete_lines(&self.config);
            self.total_lines_cleared += lines_cleared;

            {
                let mut level_metadata = LevelMetaData {
                    level: &mut self.level,
                    lines_cleared: lines_cleared,
                    total_lines_cleared: self.total_lines_cleared,
                };

                self.scoring_system.update_score(&mut self.score,
                                                 ScoreMetaData { lines_cleared: lines_cleared });

                self.leveling_system.process(&mut level_metadata);
            }
            self.drop_timer.reset(DROP_TIME - (self.level.level as f64 * DROP_FACTOR));
        }
    }

    /// Drops a Tetromino straight down until it hits the lowest
    /// possible point.
    fn drop_tetromino(&mut self) {
        while let MoveResult::Allow = self.tetromino.can_move(Direction::South, &self.grid.boxes) {
            self.tetromino.drop_down();
        }
    }

    fn new_tetromino(&mut self) {
        self.grid.store_tetromino(&self.tetromino);
        self.tetromino = self.tetromino_factory.create(&self.config);
    }

    fn handle_input(&mut self, input: &Input) {
        if let Input::Press(ref button) = *input {
            match *button {
                Button::Keyboard(Key::Escape) => self.quit = true,
                Button::Keyboard(Key::Z) => {
                    if let RotationResult::Allow = self.tetromino
                        .can_rotate(Rotation::CounterClockwise, &self.grid.boxes) {
                        self.tetromino.rotate(Rotation::CounterClockwise);
                    }
                }
                Button::Keyboard(Key::X) => {
                    if let RotationResult::Allow = self.tetromino
                        .can_rotate(Rotation::Clockwise, &self.grid.boxes) {
                        self.tetromino.rotate(Rotation::Clockwise);
                    }
                }
                Button::Keyboard(Key::Left) => {
                    match self.tetromino.can_move(Direction::West, &self.grid.boxes) {
                        MoveResult::Allow => self.tetromino.move_left(),
                        MoveResult::Blocked => self.new_tetromino(),
                        _ => (),
                    }
                }
                Button::Keyboard(Key::Right) => {
                    match self.tetromino.can_move(Direction::East, &self.grid.boxes) {
                        MoveResult::Allow => self.tetromino.move_right(),
                        MoveResult::Blocked => self.new_tetromino(),
                        _ => (),
                    }
                }
                Button::Keyboard(Key::Down) => {
                    self.move_down(true);
                }
                Button::Keyboard(Key::Space) => {
                    self.drop_tetromino();
                    // Stop the lockstep timer straight away
                    self.handle_blocked(true);
                }
                _ => (),
            }
        }
    }
}
<|endoftext|>


<commit_before>use ws;
use ws::{CloseCode, Handler, Message , Sender, listen};
use std::thread;

#[derive(Debug)]
enum Action {
    Next,
    Step(u64),
    AddBreakPtn(u64),
    DelBreakPtn(u64),
    Err
}

pub struct Debugger {
    pub step: u64,
}

struct Websocket(Sender);

impl Debugger {
    pub fn new() -> Debugger {
        let debugger = Debugger { step: 0 };
        debugger.listen();
        debugger
    }

    fn listen(&self) {
        thread::spawn(move || { listen("127.0.0.1:3012", |out| Websocket(out)).unwrap(); });
    }
}

impl Handler for Websocket {
    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
        println!("{:?}", handle(&msg));
        Ok(())
    }

    fn on_close(&mut self, code: CloseCode, reason: &str) {
        println!("WebSocket closing for ({:?}) {}", code, reason);
    }
}

//TODO: Better error handling
fn handle(s: &Message) -> Result<Action, ()> {
    let msg = s.as_text().unwrap();
    let mut split = msg.split(",");

    match split.next().unwrap() {
        "Next" => Ok(Action::Next),
        "Step" => Ok(Action::Step(split.next().unwrap().parse::<u64>().unwrap())),
        "AddBreakPtn" => Ok(Action::AddBreakPtn(split.next().unwrap().parse::<u64>().unwrap())),
        "DelBreakPtn" => Ok(Action::DelBreakPtn(split.next().unwrap().parse::<u64>().unwrap())),
        _ => Err(())
    }
}
<commit_msg>handle tests<commit_after>use ws;
use ws::{CloseCode, Handler, Message , Sender, listen};
use std::thread;

#[derive(Debug, PartialEq)]
enum Action {
    Next,
    Step(u64),
    AddBreakPtn(u64),
    DelBreakPtn(u64),
    Err
}

pub struct Debugger {
    pub step: u64,
}

struct Websocket(Sender);

impl Debugger {
    pub fn new() -> Debugger {
        let debugger = Debugger { step: 0 };
        debugger.listen();
        debugger
    }

    fn listen(&self) {
        thread::spawn(move || { listen("127.0.0.1:3012", |out| Websocket(out)).unwrap(); });
    }
}

impl Handler for Websocket {
    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
        println!("{:?}", handle(msg.as_text().unwrap()));
        Ok(())
    }

    fn on_close(&mut self, code: CloseCode, reason: &str) {
        println!("WebSocket closing for ({:?}) {}", code, reason);
    }
}

//TODO: Better error handling
fn handle(s: &str) -> Result<Action, ()> {
    let mut split = s.split(",");

    match split.next().unwrap() {
        "Next" => Ok(Action::Next),
        "Step" => Ok(Action::Step(split.next().unwrap().parse::<u64>().unwrap())),
        "AddBreakPtn" => Ok(Action::AddBreakPtn(split.next().unwrap().parse::<u64>().unwrap())),
        "DelBreakPtn" => Ok(Action::DelBreakPtn(split.next().unwrap().parse::<u64>().unwrap())),
        _ => Err(())
    }
}

#[test]
fn handle_cmd() {
    assert_eq!(handle("Next"), Ok(Action::Next));
    assert_eq!(handle("Step,45"), Ok(Action::Step(45)));
    assert_eq!(handle("AddBreakPtn,678"), Ok(Action::AddBreakPtn(678)));
    assert_eq!(handle("DelBreakPtn,654"), Ok(Action::DelBreakPtn(654)));

    assert_eq!(handle("Foo"), Err(()));
    assert_eq!(handle("Step,-45"), Err(()));
    assert_eq!(handle("AddBreakPtn"), Err(()));
    assert_eq!(handle("DelBreakPtn,0"), Err(()));

}
<|endoftext|>


<commit_before>// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Structs for creating and using a Resolver
use std::io;
use std::net::IpAddr;
use std::sync::Mutex;

use proto::rr::RecordType;
use tokio::runtime::{self, Runtime};

use crate::config::{ResolverConfig, ResolverOpts};
use crate::error::*;
use crate::lookup;
use crate::lookup::Lookup;
use crate::lookup_ip::LookupIp;
use crate::name_server::{TokioConnection, TokioConnectionProvider};
use crate::AsyncResolver;

/// The Resolver is used for performing DNS queries.
///
/// For forward (A) lookups, hostname -> IP address, see: `Resolver::lookup_ip`
///
/// Special note about resource consumption. The Resolver and all Trust-DNS software is built around the Tokio async-io library. This synchronous Resolver is intended to be a simpler wrapper for of the [`AsyncResolver`]. To allow the `Resolver` to be [`Send`] + [`Sync`], the construction of the `AsyncResolver` is lazy, this means some of the features of the `AsyncResolver`, like performance based resolution via the most efficient `NameServer` will be lost (the lookup cache is shared across invocations of the `Resolver`). If these other features of the Trust-DNS Resolver are desired, please use the tokio based [`AsyncResolver`].
///
/// *Note: Threaded/Sync usage*: In multithreaded scenarios, the internal Tokio Runtime will block on an internal Mutex for the tokio Runtime in use. For higher performance, it's recommended to use the [`AsyncResolver`].
pub struct Resolver {
    // TODO: Mutex allows this to be Sync, another option would be to instantiate a thread_local, but that has other
    //   drawbacks. One major issues, is if this Resolver is shared across threads, it will cause all to block on any
    //   query. A TLS on the other hand would not, at the cost of only allowing a Resolver to be configured once per Thread
    runtime: Mutex<Runtime>,
    async_resolver: AsyncResolver<TokioConnection, TokioConnectionProvider>,
}

macro_rules! lookup_fn {
    ($p:ident, $l:ty) => {
        /// Performs a lookup for the associated type.
        ///
        /// *hint* queries that end with a '.' are fully qualified names and are cheaper lookups
        ///
        /// # Arguments
        ///
        /// * `query` - a `&str` which parses to a domain name, failure to parse will return an error
        pub fn $p(&self, query: &str) -> ResolveResult<$l> {
            let lookup = self.async_resolver.$p(query);
            self.runtime.lock()?.block_on(lookup)
        }
    };
    ($p:ident, $l:ty, $t:ty) => {
        /// Performs a lookup for the associated type.
        ///
        /// # Arguments
        ///
        /// * `query` - a type which can be converted to `Name` via `From`.
        pub fn $p(&self, query: $t) -> ResolveResult<$l> {
            let lookup = self.async_resolver.$p(query);
            self.runtime.lock()?.block_on(lookup)
        }
    };
}

impl Resolver {
    /// Constructs a new Resolver with the specified configuration.
    ///
    /// # Arguments
    /// * `config` - configuration for the resolver
    /// * `options` - resolver options for performing lookups
    ///
    /// # Returns
    ///
    /// A new `Resolver` or an error if there was an error with the configuration.
    pub fn new(config: ResolverConfig, options: ResolverOpts) -> io::Result<Self> {
        let mut builder = runtime::Builder::new();
        builder.basic_scheduler();
        builder.enable_all();

        let runtime = builder.build()?;
        let async_resolver = AsyncResolver::new(config, options, runtime.handle().clone())
            .expect("failed to create resolver");

        Ok(Resolver {
            runtime: Mutex::new(runtime),
            async_resolver,
        })
    }

    /// Constructs a new Resolver with default config and default options.
    ///
    /// See [`ResolverConfig::default`] and [`ResolverOpts::default`] for more information.
    ///
    /// # Returns
    ///
    /// A new `Resolver` or an error if there was an error with the configuration.
    pub fn default() -> io::Result<Self> {
        Self::new(ResolverConfig::default(), ResolverOpts::default())
    }

    /// Constructs a new Resolver with the system configuration.
    ///
    /// This will use `/etc/resolv.conf` on Unix OSes and the registry on Windows.
    #[cfg(any(unix, target_os = "windows"))]
    #[cfg(feature = "system-config")]
    pub fn from_system_conf() -> io::Result<Self> {
        let (config, options) = super::system_conf::read_system_conf()?;
        Self::new(config, options)
    }

    /// Generic lookup for any RecordType
    ///
    /// *WARNING* This interface may change in the future, please use [`Self::lookup_ip`] or another variant for more stable interfaces.
    ///
    /// # Arguments
    ///
    /// * `name` - name of the record to lookup, if name is not a valid domain name, an error will be returned
    /// * `record_type` - type of record to lookup
    pub fn lookup(&self, name: &str, record_type: RecordType) -> ResolveResult<Lookup> {
        let lookup = self
            .async_resolver
            .lookup(name, record_type, Default::default());
        self.runtime.lock()?.block_on(lookup)
    }

    /// Performs a dual-stack DNS lookup for the IP for the given hostname.
    ///
    /// See the configuration and options parameters for controlling the way in which A(Ipv4) and AAAA(Ipv6) lookups will be performed. For the least expensive query a fully-qualified-domain-name, FQDN, which ends in a final `.`, e.g. `www.example.com.`, will only issue one query. Anything else will always incur the cost of querying the `ResolverConfig::domain` and `ResolverConfig::search`.
    ///
    /// # Arguments
    ///
    /// * `host` - string hostname, if this is an invalid hostname, an error will be returned.
    pub fn lookup_ip(&self, host: &str) -> ResolveResult<LookupIp> {
        let lookup = self.async_resolver.lookup_ip(host);
        self.runtime.lock()?.block_on(lookup)
    }

    lookup_fn!(reverse_lookup, lookup::ReverseLookup, IpAddr);
    lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup);
    lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup);
    lookup_fn!(mx_lookup, lookup::MxLookup);
    lookup_fn!(ns_lookup, lookup::NsLookup);
    lookup_fn!(soa_lookup, lookup::SoaLookup);
    lookup_fn!(srv_lookup, lookup::SrvLookup);
    lookup_fn!(txt_lookup, lookup::TxtLookup);
}

#[cfg(test)]
mod tests {
    #![allow(clippy::dbg_macro, clippy::print_stdout)]

    use std::net::*;

    use super::*;

    fn require_send_sync<S: Send + Sync>() {}

    #[test]
    fn test_resolver_sendable() {
        require_send_sync::<Resolver>();
    }

    #[test]
    fn test_lookup() {
        let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }

    #[test]
    #[ignore]
    #[cfg(any(unix, target_os = "windows"))]
    fn test_system_lookup() {
        let resolver = Resolver::from_system_conf().unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }
}
<commit_msg>resolver: use IntoName trait on synchronous resolver interface<commit_after>// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Structs for creating and using a Resolver
use std::io;
use std::net::IpAddr;
use std::sync::Mutex;

use proto::rr::domain::TryParseIp;
use proto::rr::IntoName;
use proto::rr::RecordType;
use tokio::runtime::{self, Runtime};

use crate::config::{ResolverConfig, ResolverOpts};
use crate::error::*;
use crate::lookup;
use crate::lookup::Lookup;
use crate::lookup_ip::LookupIp;
use crate::name_server::{TokioConnection, TokioConnectionProvider};
use crate::AsyncResolver;

/// The Resolver is used for performing DNS queries.
///
/// For forward (A) lookups, hostname -> IP address, see: `Resolver::lookup_ip`
///
/// Special note about resource consumption. The Resolver and all Trust-DNS software is built around the Tokio async-io library. This synchronous Resolver is intended to be a simpler wrapper for of the [`AsyncResolver`]. To allow the `Resolver` to be [`Send`] + [`Sync`], the construction of the `AsyncResolver` is lazy, this means some of the features of the `AsyncResolver`, like performance based resolution via the most efficient `NameServer` will be lost (the lookup cache is shared across invocations of the `Resolver`). If these other features of the Trust-DNS Resolver are desired, please use the tokio based [`AsyncResolver`].
///
/// *Note: Threaded/Sync usage*: In multithreaded scenarios, the internal Tokio Runtime will block on an internal Mutex for the tokio Runtime in use. For higher performance, it's recommended to use the [`AsyncResolver`].
pub struct Resolver {
    // TODO: Mutex allows this to be Sync, another option would be to instantiate a thread_local, but that has other
    //   drawbacks. One major issues, is if this Resolver is shared across threads, it will cause all to block on any
    //   query. A TLS on the other hand would not, at the cost of only allowing a Resolver to be configured once per Thread
    runtime: Mutex<Runtime>,
    async_resolver: AsyncResolver<TokioConnection, TokioConnectionProvider>,
}

macro_rules! lookup_fn {
    ($p:ident, $l:ty) => {
        /// Performs a lookup for the associated type.
        ///
        /// *hint* queries that end with a '.' are fully qualified names and are cheaper lookups
        ///
        /// # Arguments
        ///
        /// * `query` - a `&str` which parses to a domain name, failure to parse will return an error
        pub fn $p<N: IntoName>(&self, query: N) -> ResolveResult<$l> {
            let lookup = self.async_resolver.$p(query);
            self.runtime.lock()?.block_on(lookup)
        }
    };
    ($p:ident, $l:ty, $t:ty) => {
        /// Performs a lookup for the associated type.
        ///
        /// # Arguments
        ///
        /// * `query` - a type which can be converted to `Name` via `From`.
        pub fn $p(&self, query: $t) -> ResolveResult<$l> {
            let lookup = self.async_resolver.$p(query);
            self.runtime.lock()?.block_on(lookup)
        }
    };
}

impl Resolver {
    /// Constructs a new Resolver with the specified configuration.
    ///
    /// # Arguments
    /// * `config` - configuration for the resolver
    /// * `options` - resolver options for performing lookups
    ///
    /// # Returns
    ///
    /// A new `Resolver` or an error if there was an error with the configuration.
    pub fn new(config: ResolverConfig, options: ResolverOpts) -> io::Result<Self> {
        let mut builder = runtime::Builder::new();
        builder.basic_scheduler();
        builder.enable_all();

        let runtime = builder.build()?;
        let async_resolver = AsyncResolver::new(config, options, runtime.handle().clone())
            .expect("failed to create resolver");

        Ok(Resolver {
            runtime: Mutex::new(runtime),
            async_resolver,
        })
    }

    /// Constructs a new Resolver with default config and default options.
    ///
    /// See [`ResolverConfig::default`] and [`ResolverOpts::default`] for more information.
    ///
    /// # Returns
    ///
    /// A new `Resolver` or an error if there was an error with the configuration.
    pub fn default() -> io::Result<Self> {
        Self::new(ResolverConfig::default(), ResolverOpts::default())
    }

    /// Constructs a new Resolver with the system configuration.
    ///
    /// This will use `/etc/resolv.conf` on Unix OSes and the registry on Windows.
    #[cfg(any(unix, target_os = "windows"))]
    #[cfg(feature = "system-config")]
    pub fn from_system_conf() -> io::Result<Self> {
        let (config, options) = super::system_conf::read_system_conf()?;
        Self::new(config, options)
    }

    /// Generic lookup for any RecordType
    ///
    /// *WARNING* This interface may change in the future, please use [`Self::lookup_ip`] or another variant for more stable interfaces.
    ///
    /// # Arguments
    ///
    /// * `name` - name of the record to lookup, if name is not a valid domain name, an error will be returned
    /// * `record_type` - type of record to lookup
    pub fn lookup<N: IntoName>(&self, name: N, record_type: RecordType) -> ResolveResult<Lookup> {
        let lookup = self
            .async_resolver
            .lookup(name, record_type, Default::default());
        self.runtime.lock()?.block_on(lookup)
    }

    /// Performs a dual-stack DNS lookup for the IP for the given hostname.
    ///
    /// See the configuration and options parameters for controlling the way in which A(Ipv4) and AAAA(Ipv6) lookups will be performed. For the least expensive query a fully-qualified-domain-name, FQDN, which ends in a final `.`, e.g. `www.example.com.`, will only issue one query. Anything else will always incur the cost of querying the `ResolverConfig::domain` and `ResolverConfig::search`.
    ///
    /// # Arguments
    ///
    /// * `host` - string hostname, if this is an invalid hostname, an error will be returned.
    pub fn lookup_ip<N: IntoName + TryParseIp>(&self, host: N) -> ResolveResult<LookupIp> {
        let lookup = self.async_resolver.lookup_ip(host);
        self.runtime.lock()?.block_on(lookup)
    }

    lookup_fn!(reverse_lookup, lookup::ReverseLookup, IpAddr);
    lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup);
    lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup);
    lookup_fn!(mx_lookup, lookup::MxLookup);
    lookup_fn!(ns_lookup, lookup::NsLookup);
    lookup_fn!(soa_lookup, lookup::SoaLookup);
    lookup_fn!(srv_lookup, lookup::SrvLookup);
    lookup_fn!(txt_lookup, lookup::TxtLookup);
}

#[cfg(test)]
mod tests {
    #![allow(clippy::dbg_macro, clippy::print_stdout)]

    use std::net::*;

    use super::*;

    fn require_send_sync<S: Send + Sync>() {}

    #[test]
    fn test_resolver_sendable() {
        require_send_sync::<Resolver>();
    }

    #[test]
    fn test_lookup() {
        let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }

    #[test]
    #[ignore]
    #[cfg(any(unix, target_os = "windows"))]
    fn test_system_lookup() {
        let resolver = Resolver::from_system_conf().unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }
}
<|endoftext|>