<commit_before>#![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))]

extern crate gtk;
extern crate gdk;

use gtk::traits::*;
use gtk::signal::Inhibit;
use gdk::enums::modifier_type;

/// Expands to its argument if GTK+ 3.10 support is configured and to `()` otherwise
#[cfg(not(feature = "gtk_3_10"))]
macro_rules! with_gtk_3_10 {
    ($ex:expr) => (
        ()
    );
    ($bl:block) => {
        ()
    }
}

/// Expands to its argument if GTK+ 3.10 support is configured and to `()` otherwise
#[cfg(feature = "gtk_3_10")]
macro_rules! with_gtk_3_10 {
    ($ex:expr) => (
        $ex
    );
    ($bl:block) => {
        $bl
    }
}

fn about_clicked(_: gtk::Button) {
    let dialog = gtk::AboutDialog::new().unwrap();

    let crew = [
        "James T. Kirk",
        "Spock",
        "Leonard McCoy",
    ];

    dialog.set_authors(&crew);
    dialog.set_artists(&crew[1..]);

    println!("Authors: {:?}", dialog.get_authors());
    println!("Artists: {:?}", dialog.get_artists());
    println!("Documenters: {:?}", dialog.get_documenters());

    dialog.run();
    dialog.destroy();
}

fn main() {
    gtk::init();
    println!("Major: {}, Minor: {}", gtk::get_major_version(), gtk::get_minor_version());
    let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let frame = gtk::Frame::new(Some("Yep a frame")).unwrap();
    let _box = gtk::Box::new(gtk::Orientation::Horizontal, 10).unwrap();
    let v_box = gtk::Box::new(gtk::Orientation::Horizontal, 10).unwrap();
    let button_box = gtk::ButtonBox::new(gtk::Orientation::Horizontal).unwrap();
    let label = gtk::Label::new("Yeah a wonderful label too !").unwrap();
    let button = gtk::Button::new_with_label("Whattttt a button !").unwrap();
    let button_about = gtk::Button::new_with_label("About?").unwrap();
    let button_recent = gtk::Button::new_with_label("Choose a recent one !").unwrap();
    let button_font = gtk::Button::new_with_label("Choose a font !").unwrap();
    let app_button = gtk::Button::new_with_label("App ?").unwrap();
    let file_button = gtk::Button::new_with_label("file ?").unwrap();
    let font_button = gtk::FontButton::new().unwrap();
    let toggle_button = gtk::ToggleButton::new_with_label("Toggle Me !").unwrap();
    let check_button = gtk::CheckButton::new_with_label("Labeled check button").unwrap();
    let color_button = gtk::ColorButton::new().unwrap();
    let menu_button = with_gtk_3_10!(
        gtk::MenuButton::new().unwrap()
    );
    let link_button = gtk::LinkButton::new("www.rust-lang.org").unwrap();
    let volume_button = gtk::VolumeButton::new().unwrap();
    let entry = gtk::Entry::new().unwrap();
    let search_entry = with_gtk_3_10!(
        gtk::SearchEntry::new().unwrap()
    );
    let separator = gtk::Separator::new(gtk::Orientation::Horizontal).unwrap();
    let separator2 = gtk::Separator::new(gtk::Orientation::Horizontal).unwrap();
    let switch = gtk::Switch::new().unwrap();
    let switch2 = gtk::Switch::new().unwrap();
    let scale = gtk::Scale::new_with_range(gtk::Orientation::Horizontal, 0., 100., 1.).unwrap();
    let level_bar = with_gtk_3_10!(
        gtk::LevelBar::new_for_interval(0., 100.).unwrap()
    );
    let spin_button = gtk::SpinButton::new_with_range(0., 100., 1.).unwrap();
    let spinner = gtk::Spinner::new().unwrap();
    let image = gtk::Image::new_from_file("./test/resources/gtk.jpg").unwrap();
    let progress_bar = gtk::ProgressBar::new().unwrap();
    let arrow = gtk::Arrow::new(gtk::ArrowType::Right, gtk::ShadowType::EtchedOut).unwrap();
    let calendar = gtk::Calendar::new().unwrap();
    let info_bar = gtk::InfoBar::new().unwrap();
    let tmp_button = with_gtk_3_10!(
        gtk::Button::new_from_icon_name("edit-clear", gtk::IconSize::Button).unwrap()
    );

    println!("test");

    with_gtk_3_10! {{
        info_bar.show_close_button(true);
    }}

    /*info_bar.connect(signals::Response::new(|response_id| {
        info_bar.hide()
    }));*/ //TODO: Why does this not work?

    progress_bar.set_fraction(0.7);
    spinner.start();
    with_gtk_3_10! {{
        level_bar.set_value(37.);
    }}
    switch2.set_active(true);
    frame.set_border_width(10);
    _box.set_border_width(5);
    entry.set_placeholder("An Entry with a placeholder !");
    volume_button.set_orientation(gtk::Orientation::Horizontal);
    label.set_justify(gtk::Justification::Left);
    window.set_title("Yeah a beautiful window with rgtk !");
    window.set_window_position(gtk::WindowPosition::Center);
    window.add(&frame);

    let entry_clone = entry.clone();
    button.connect_clicked(move |_| {
        let dialog = gtk::Dialog::with_buttons(
            "Hello!", None, gtk::DialogFlags::Modal,
            [("No", 0), ("Yes", 1), ("Yes!", 2)]);

        let ret = dialog.run();

        dialog.destroy();

        entry_clone.set_text(&format!("Clicked {}", ret));
    });

    // use a plain function instead of a closure
    button_about.connect_clicked(about_clicked);

    button_font.connect_clicked(|_| {
        let dialog = gtk::FontChooserDialog::new("Font chooser test", None).unwrap();

        dialog.run();
        dialog.destroy();
    });

    button_recent.connect_clicked(|_| {
        let dialog = gtk::RecentChooserDialog::new(
            "Recent chooser test", None,
            [("Ok", gtk::ResponseType::Ok), ("Cancel", gtk::ResponseType::Cancel)]);

        dialog.run();
        dialog.destroy();
    });

    file_button.connect_clicked(|_| {
        //entry.set_text("Clicked!");
        let dialog = gtk::FileChooserDialog::new(
            "Choose a file", None, gtk::FileChooserAction::Open,
            [("Open", gtk::ResponseType::Ok), ("Cancel", gtk::ResponseType::Cancel)]);

        dialog.set_select_multiple(true);
        dialog.run();
        let files = dialog.get_filenames();
        dialog.destroy();

        println!("Files: {:?}", files);
    });

    app_button.connect_clicked(|_| {
        //entry.set_text("Clicked!");
        let dialog = gtk::AppChooserDialog::new_for_content_type(None, gtk::DialogFlags::Modal, "sh").unwrap();

        dialog.run();
        dialog.destroy();
    });

    let entry_clone = entry.clone();
    window.connect_key_press_event(move |_, key| {
        let keyval = key.keyval;
        let keystate = key.state;

        println!("key pressed: {} / {:?}", keyval, keystate);
        println!("text: {}", entry_clone.get_text().unwrap());

        if keystate.intersects(modifier_type::ControlMask) {
            println!("You pressed Ctrl!");
        }

        Inhibit(false)
    });

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    frame.add(&_box);
    with_gtk_3_10! {{
        button_box.add(&tmp_button)
    }};
    button_box.add(&button);
    button_box.add(&button_about);
    button_box.add(&button_font);
    button_box.add(&button_recent);
    button_box.add(&file_button);
    button_box.add(&app_button);
    button_box.add(&font_button);
    button_box.add(&toggle_button);
    button_box.add(&color_button);
    button_box.add(&volume_button);
    v_box.add(&switch);
    with_gtk_3_10! {{
        v_box.add(&menu_button);
    }}
    v_box.add(&switch2);
    v_box.add(&check_button);
    v_box.add(&link_button);
    v_box.add(&spin_button);
    _box.add(&info_bar);
    _box.add(&v_box);
    _box.add(&scale);
    with_gtk_3_10! {{
        _box.add(&level_bar);
    }}
    _box.add(&button_box);
    _box.add(&progress_bar);
    _box.add(&separator);
    _box.add(&label);
    _box.add(&entry);
    _box.add(&separator2);
    with_gtk_3_10! {{
        _box.add(&search_entry);
    }}
    _box.add(&spinner);
    _box.add(&image);
    _box.add(&arrow);
    _box.add(&calendar);
    _box.set_orientation(gtk::Orientation::Vertical);
    // window.set_decorated(false);
    window.set_decorated(true);
    window.show_all();
    gtk::main();
}
<commit_msg>Change rgtk to gtk text<commit_after>#![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))]

extern crate gtk;
extern crate gdk;

use gtk::traits::*;
use gtk::signal::Inhibit;
use gdk::enums::modifier_type;

/// Expands to its argument if GTK+ 3.10 support is configured and to `()` otherwise
#[cfg(not(feature = "gtk_3_10"))]
macro_rules! with_gtk_3_10 {
    ($ex:expr) => (
        ()
    );
    ($bl:block) => {
        ()
    }
}

/// Expands to its argument if GTK+ 3.10 support is configured and to `()` otherwise
#[cfg(feature = "gtk_3_10")]
macro_rules! with_gtk_3_10 {
    ($ex:expr) => (
        $ex
    );
    ($bl:block) => {
        $bl
    }
}

fn about_clicked(_: gtk::Button) {
    let dialog = gtk::AboutDialog::new().unwrap();

    let crew = [
        "James T. Kirk",
        "Spock",
        "Leonard McCoy",
    ];

    dialog.set_authors(&crew);
    dialog.set_artists(&crew[1..]);

    println!("Authors: {:?}", dialog.get_authors());
    println!("Artists: {:?}", dialog.get_artists());
    println!("Documenters: {:?}", dialog.get_documenters());

    dialog.run();
    dialog.destroy();
}

fn main() {
    gtk::init();
    println!("Major: {}, Minor: {}", gtk::get_major_version(), gtk::get_minor_version());
    let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let frame = gtk::Frame::new(Some("Yep a frame")).unwrap();
    let _box = gtk::Box::new(gtk::Orientation::Horizontal, 10).unwrap();
    let v_box = gtk::Box::new(gtk::Orientation::Horizontal, 10).unwrap();
    let button_box = gtk::ButtonBox::new(gtk::Orientation::Horizontal).unwrap();
    let label = gtk::Label::new("Yeah a wonderful label too !").unwrap();
    let button = gtk::Button::new_with_label("Whattttt a button !").unwrap();
    let button_about = gtk::Button::new_with_label("About?").unwrap();
    let button_recent = gtk::Button::new_with_label("Choose a recent one !").unwrap();
    let button_font = gtk::Button::new_with_label("Choose a font !").unwrap();
    let app_button = gtk::Button::new_with_label("App ?").unwrap();
    let file_button = gtk::Button::new_with_label("file ?").unwrap();
    let font_button = gtk::FontButton::new().unwrap();
    let toggle_button = gtk::ToggleButton::new_with_label("Toggle Me !").unwrap();
    let check_button = gtk::CheckButton::new_with_label("Labeled check button").unwrap();
    let color_button = gtk::ColorButton::new().unwrap();
    let menu_button = with_gtk_3_10!(
        gtk::MenuButton::new().unwrap()
    );
    let link_button = gtk::LinkButton::new("www.rust-lang.org").unwrap();
    let volume_button = gtk::VolumeButton::new().unwrap();
    let entry = gtk::Entry::new().unwrap();
    let search_entry = with_gtk_3_10!(
        gtk::SearchEntry::new().unwrap()
    );
    let separator = gtk::Separator::new(gtk::Orientation::Horizontal).unwrap();
    let separator2 = gtk::Separator::new(gtk::Orientation::Horizontal).unwrap();
    let switch = gtk::Switch::new().unwrap();
    let switch2 = gtk::Switch::new().unwrap();
    let scale = gtk::Scale::new_with_range(gtk::Orientation::Horizontal, 0., 100., 1.).unwrap();
    let level_bar = with_gtk_3_10!(
        gtk::LevelBar::new_for_interval(0., 100.).unwrap()
    );
    let spin_button = gtk::SpinButton::new_with_range(0., 100., 1.).unwrap();
    let spinner = gtk::Spinner::new().unwrap();
    let image = gtk::Image::new_from_file("./test/resources/gtk.jpg").unwrap();
    let progress_bar = gtk::ProgressBar::new().unwrap();
    let arrow = gtk::Arrow::new(gtk::ArrowType::Right, gtk::ShadowType::EtchedOut).unwrap();
    let calendar = gtk::Calendar::new().unwrap();
    let info_bar = gtk::InfoBar::new().unwrap();
    let tmp_button = with_gtk_3_10!(
        gtk::Button::new_from_icon_name("edit-clear", gtk::IconSize::Button).unwrap()
    );

    println!("test");

    with_gtk_3_10! {{
        info_bar.show_close_button(true);
    }}

    /*info_bar.connect(signals::Response::new(|response_id| {
        info_bar.hide()
    }));*/ //TODO: Why does this not work?

    progress_bar.set_fraction(0.7);
    spinner.start();
    with_gtk_3_10! {{
        level_bar.set_value(37.);
    }}
    switch2.set_active(true);
    frame.set_border_width(10);
    _box.set_border_width(5);
    entry.set_placeholder("An Entry with a placeholder !");
    volume_button.set_orientation(gtk::Orientation::Horizontal);
    label.set_justify(gtk::Justification::Left);
    window.set_title("Yeah a beautiful window with gtk !");
    window.set_window_position(gtk::WindowPosition::Center);
    window.add(&frame);

    let entry_clone = entry.clone();
    button.connect_clicked(move |_| {
        let dialog = gtk::Dialog::with_buttons(
            "Hello!", None, gtk::DialogFlags::Modal,
            [("No", 0), ("Yes", 1), ("Yes!", 2)]);

        let ret = dialog.run();

        dialog.destroy();

        entry_clone.set_text(&format!("Clicked {}", ret));
    });

    // use a plain function instead of a closure
    button_about.connect_clicked(about_clicked);

    button_font.connect_clicked(|_| {
        let dialog = gtk::FontChooserDialog::new("Font chooser test", None).unwrap();

        dialog.run();
        dialog.destroy();
    });

    button_recent.connect_clicked(|_| {
        let dialog = gtk::RecentChooserDialog::new(
            "Recent chooser test", None,
            [("Ok", gtk::ResponseType::Ok), ("Cancel", gtk::ResponseType::Cancel)]);

        dialog.run();
        dialog.destroy();
    });

    file_button.connect_clicked(|_| {
        //entry.set_text("Clicked!");
        let dialog = gtk::FileChooserDialog::new(
            "Choose a file", None, gtk::FileChooserAction::Open,
            [("Open", gtk::ResponseType::Ok), ("Cancel", gtk::ResponseType::Cancel)]);

        dialog.set_select_multiple(true);
        dialog.run();
        let files = dialog.get_filenames();
        dialog.destroy();

        println!("Files: {:?}", files);
    });

    app_button.connect_clicked(|_| {
        //entry.set_text("Clicked!");
        let dialog = gtk::AppChooserDialog::new_for_content_type(None, gtk::DialogFlags::Modal, "sh").unwrap();

        dialog.run();
        dialog.destroy();
    });

    let entry_clone = entry.clone();
    window.connect_key_press_event(move |_, key| {
        let keyval = key.keyval;
        let keystate = key.state;

        println!("key pressed: {} / {:?}", keyval, keystate);
        println!("text: {}", entry_clone.get_text().unwrap());

        if keystate.intersects(modifier_type::ControlMask) {
            println!("You pressed Ctrl!");
        }

        Inhibit(false)
    });

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    frame.add(&_box);
    with_gtk_3_10! {{
        button_box.add(&tmp_button)
    }};
    button_box.add(&button);
    button_box.add(&button_about);
    button_box.add(&button_font);
    button_box.add(&button_recent);
    button_box.add(&file_button);
    button_box.add(&app_button);
    button_box.add(&font_button);
    button_box.add(&toggle_button);
    button_box.add(&color_button);
    button_box.add(&volume_button);
    v_box.add(&switch);
    with_gtk_3_10! {{
        v_box.add(&menu_button);
    }}
    v_box.add(&switch2);
    v_box.add(&check_button);
    v_box.add(&link_button);
    v_box.add(&spin_button);
    _box.add(&info_bar);
    _box.add(&v_box);
    _box.add(&scale);
    with_gtk_3_10! {{
        _box.add(&level_bar);
    }}
    _box.add(&button_box);
    _box.add(&progress_bar);
    _box.add(&separator);
    _box.add(&label);
    _box.add(&entry);
    _box.add(&separator2);
    with_gtk_3_10! {{
        _box.add(&search_entry);
    }}
    _box.add(&spinner);
    _box.add(&image);
    _box.add(&arrow);
    _box.add(&calendar);
    _box.set_orientation(gtk::Orientation::Vertical);
    // window.set_decorated(false);
    window.set_decorated(true);
    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before>use super::*;

use dpi::LogicalPosition;
use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, StartCause, WindowEvent};
use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed};
use window::{WindowId as RootWI};
use stdweb::{
    traits::*,
    web::{
        document,
        event::*,
        html_element::CanvasElement,
    },
};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::collections::vec_deque::IntoIter as VecDequeIter;
use std::marker::PhantomData;
use std::rc::Rc;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32);

impl DeviceId {
    pub unsafe fn dummy() -> Self {
        DeviceId(0)
    }
}

pub struct EventLoop<T: 'static> {
    elw: RootELW<T>,
}

pub struct EventLoopWindowTarget<T: 'static> {
    pub(crate) runner: EventLoopRunnerShared<T>,
}

impl<T> EventLoopWindowTarget<T> {
    fn new() -> Self {
        EventLoopWindowTarget {
            runner: Rc::new(ELRShared {
                runner: RefCell::new(None),
                events: RefCell::new(VecDeque::new())
            })
        }
    }
}

#[derive(Clone)]
pub struct EventLoopProxy<T> {
    runner: EventLoopRunnerShared<T>
}

impl<T> EventLoopProxy<T> {
    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
        self.runner.send_event(Event::UserEvent(event));
        Ok(())
    }
}

pub type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;

pub struct ELRShared<T> {
    runner: RefCell<Option<EventLoopRunner<T>>>,
    events: RefCell<VecDeque<Event<T>>>, // TODO: this may not be necessary?
}

struct EventLoopRunner<T> {
    control: ControlFlow,
    handling: bool,
    event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
}

impl<T> EventLoop<T> {
    pub fn new() -> Self {
        EventLoop {
            elw: RootELW {
                p: EventLoopWindowTarget::new(),
                _marker: PhantomData
            },
        }
    }

    pub fn get_available_monitors(&self) -> VecDequeIter<MonitorHandle> {
        VecDeque::new().into_iter()
    }

    pub fn get_primary_monitor(&self) -> MonitorHandle {
        MonitorHandle
    }

    pub fn run<F>(self, mut event_handler: F) -> !
        where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) {
        let runner = self.elw.p.runner;

        let relw = RootELW {
            p: EventLoopWindowTarget::new(),
            _marker: PhantomData
        };
        runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl)));

        let document = &document();
        add_event(&runner, document, |elrs, _: BlurEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(false)
            });
        });
        add_event(&runner, document, |elrs, _: FocusEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(true)
            });

        });
        add_event(&runner, document, |elrs, event: KeyDownEvent| {
            let key = event.key();
            let mut characters = key.chars();
            let first = characters.next();
            let second = characters.next();
            if let (Some(key), None) = (first, second) {
                elrs.send_event(Event::WindowEvent {
                    window_id: RootWI(WindowId),
                    event: WindowEvent::ReceivedCharacter(key)
                });
            }
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Pressed,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });
        add_event(&runner, document, |elrs, event: KeyUpEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Released,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });
        stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here?

        // Throw an exception to break out of Rust exceution and use unreachable to tell the
        // compiler this function won't return, giving it a return type of '!'
        js! {
            throw "Using exceptions for control flow, don't mind me";
        }
        unreachable!();
    }

    pub fn create_proxy(&self) -> EventLoopProxy<T> {
        EventLoopProxy {
            runner: self.elw.p.runner.clone()
        }
    }

    pub fn window_target(&self) -> &RootELW<T> {
        &self.elw
    }
}

pub fn register<T: 'static>(elrs: &EventLoopRunnerShared<T>, canvas: &CanvasElement) {
    add_event(elrs, canvas, |elrs, event: PointerOutEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorLeft {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerOverEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorEntered {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerMoveEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorMoved {
                device_id: RootDI(DeviceId(event.pointer_id())),
                position: LogicalPosition {
                    x: event.offset_x(),
                    y: event.offset_y()
                },
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerUpEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Pressed,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerDownEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Released,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
}

fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IEventTarget, mut handler: F)
        where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared<T>, E) + 'static {
    let elrs = elrs.clone();

    target.add_event_listener(move |event: E| {
        event.prevent_default();
        event.stop_propagation();
        event.cancel_bubble();

        handler(&elrs, event);
    });
}

impl<T> ELRShared<T> {
    fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
        *self.runner.borrow_mut() = Some(EventLoopRunner {
            control: ControlFlow::Poll,
            handling: false
            event_handler
        });
    }

    // TODO: handle event loop closures
    // TODO: handle event buffer
    pub fn send_event(&self, event: Event<T>) {
        match *self.runner.borrow_mut() {
            Some(ref mut runner) if !runner.handling => {
                runner.handling = true;
                let closed = runner.control == ControlFlow::Exit;
                // TODO: bracket this in control flow events?
                (runner.event_handler)(event, &mut runner.control);
                if closed {
                    runner.control = ControlFlow::Exit;
                }
                runner.handling = false;
            }
            _ => self.events.borrow_mut().push_back(event)
        }
    }

}

<commit_msg>Handle ControlFlow::Exit and dealing with events-in-events<commit_after>use super::*;

use dpi::LogicalPosition;
use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, StartCause, WindowEvent};
use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed};
use window::{WindowId as RootWI};
use stdweb::{
    traits::*,
    web::{
        document,
        event::*,
        html_element::CanvasElement,
    },
};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::collections::vec_deque::IntoIter as VecDequeIter;
use std::marker::PhantomData;
use std::rc::Rc;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32);

impl DeviceId {
    pub unsafe fn dummy() -> Self {
        DeviceId(0)
    }
}

pub struct EventLoop<T: 'static> {
    elw: RootELW<T>,
}

pub struct EventLoopWindowTarget<T: 'static> {
    pub(crate) runner: EventLoopRunnerShared<T>,
}

impl<T> EventLoopWindowTarget<T> {
    fn new() -> Self {
        EventLoopWindowTarget {
            runner: Rc::new(ELRShared {
                runner: RefCell::new(None),
                events: RefCell::new(VecDeque::new())
            })
        }
    }
}

#[derive(Clone)]
pub struct EventLoopProxy<T> {
    runner: EventLoopRunnerShared<T>
}

impl<T> EventLoopProxy<T> {
    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
        self.runner.send_event(Event::UserEvent(event));
        Ok(())
    }
}

pub type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;

pub struct ELRShared<T> {
    runner: RefCell<Option<EventLoopRunner<T>>>,
    events: RefCell<VecDeque<Event<T>>>, // TODO: this may not be necessary?
}

struct EventLoopRunner<T> {
    control: ControlFlow,
    handling: bool,
    event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
}

impl<T> EventLoop<T> {
    pub fn new() -> Self {
        EventLoop {
            elw: RootELW {
                p: EventLoopWindowTarget::new(),
                _marker: PhantomData
            },
        }
    }

    pub fn get_available_monitors(&self) -> VecDequeIter<MonitorHandle> {
        VecDeque::new().into_iter()
    }

    pub fn get_primary_monitor(&self) -> MonitorHandle {
        MonitorHandle
    }

    pub fn run<F>(self, mut event_handler: F) -> !
        where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) {
        let runner = self.elw.p.runner;

        let relw = RootELW {
            p: EventLoopWindowTarget::new(),
            _marker: PhantomData
        };
        runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl)));

        let document = &document();
        add_event(&runner, document, |elrs, _: BlurEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(false)
            });
        });
        add_event(&runner, document, |elrs, _: FocusEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(true)
            });

        });
        add_event(&runner, document, |elrs, event: KeyDownEvent| {
            let key = event.key();
            let mut characters = key.chars();
            let first = characters.next();
            let second = characters.next();
            if let (Some(key), None) = (first, second) {
                elrs.send_event(Event::WindowEvent {
                    window_id: RootWI(WindowId),
                    event: WindowEvent::ReceivedCharacter(key)
                });
            }
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Pressed,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });
        add_event(&runner, document, |elrs, event: KeyUpEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Released,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });
        stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here?

        // Throw an exception to break out of Rust exceution and use unreachable to tell the
        // compiler this function won't return, giving it a return type of '!'
        js! {
            throw "Using exceptions for control flow, don't mind me";
        }
        unreachable!();
    }

    pub fn create_proxy(&self) -> EventLoopProxy<T> {
        EventLoopProxy {
            runner: self.elw.p.runner.clone()
        }
    }

    pub fn window_target(&self) -> &RootELW<T> {
        &self.elw
    }
}

pub fn register<T: 'static>(elrs: &EventLoopRunnerShared<T>, canvas: &CanvasElement) {
    add_event(elrs, canvas, |elrs, event: PointerOutEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorLeft {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerOverEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorEntered {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerMoveEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorMoved {
                device_id: RootDI(DeviceId(event.pointer_id())),
                position: LogicalPosition {
                    x: event.offset_x(),
                    y: event.offset_y()
                },
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerUpEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Pressed,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerDownEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Released,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
}

fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IEventTarget, mut handler: F)
        where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared<T>, E) + 'static {
    let elrs = elrs.clone();

    target.add_event_listener(move |event: E| {
        // Don't capture the event if the events loop has been destroyed
        if elrs.runner.borrow().control == ControlFlow::Exit {
            return;
        }

        event.prevent_default();
        event.stop_propagation();
        event.cancel_bubble();

        handler(&elrs, event);
    });
}

impl<T> ELRShared<T> {
    fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
        *self.runner.borrow_mut() = Some(EventLoopRunner {
            control: ControlFlow::Poll,
            handling: false
            event_handler
        });
    }

    // TODO: handle event loop closures
    // TODO: handle event buffer
    pub fn send_event(&self, event: Event<T>) {
        let start_cause = StartCause::Poll; // TODO: this is obviously not right
        self.handle_start(StartCause::Poll);
        self.handle_event(event);
        self.handle_event(Event::EventsCleared);
    }

    fn handle_start(&self, start: StartCause) {
        let is_handling = if Some(ref runner) = *self.runner.borrow() {
            runner.handling
        } else {
            false
        };
        self.handle_event(Event::StartCause(is_handling));
    }

    fn handle_event(&self, event: Event<T>) {
        match *self.runner.borrow_mut() {
            Some(ref mut runner) if !runner.handling => {
                runner.handling = true;
                let closed = runner.control == ControlFlow::Exit;
                // TODO: bracket this in control flow events?
                (runner.event_handler)(event, &mut runner.control);
                if closed {
                    runner.control = ControlFlow::Exit;
                }
                runner.handling = false;
            }
            _ => self.events.borrow_mut().push_back(event)
        }
        if self.runner.borrow().is_some() {
            if let Some(event) = self.events.borrow_mut().pop_front() {
                self.handle_event(event);
            }
        }
    }

}

<|endoftext|>


<commit_before>extern crate clerk;

use std::cell::RefCell;
use std::collections::VecDeque;

use clerk::{DefaultLines, Delay, Display, DisplayControlBuilder, EntryModeBuilder,
            FunctionSetBuilder, Init, ReadMode, Receive, SeekCgRamFrom, SeekFrom, Send, ShiftTo,
            WriteMode};

struct ConnectionMock {
    init_calls: RefCell<u8>,
    send_bytes: RefCell<Vec<WriteMode>>,
    receivable_bytes: RefCell<VecDeque<u8>>,
}

impl Default for ConnectionMock {
    fn default() -> Self {
        ConnectionMock {
            init_calls: RefCell::new(0),
            send_bytes: RefCell::new(vec![]),
            receivable_bytes: RefCell::new(VecDeque::new()),
        }
    }
}

impl ConnectionMock {
    fn set_read_value(&self, value: u8) {
        self.receivable_bytes.borrow_mut().push_back(value)
    }
}

impl Init for ConnectionMock {
    fn init(&self) {
        let mut init_calls = self.init_calls.borrow_mut();

        *init_calls += 1;
    }
}

impl Send for ConnectionMock {
    fn send(&self, mode: WriteMode) {
        let mut send_bytes = self.send_bytes.borrow_mut();

        send_bytes.push(mode);
    }
}

impl Receive for ConnectionMock {
    fn receive(&self, _: ReadMode) -> u8 {
        self.receivable_bytes.borrow_mut().pop_front().unwrap()
    }
}

pub struct CustomDelayMock;

impl Delay for CustomDelayMock {
    fn delay_ns(_: u16) {
        // mhh
    }
}

fn setup_display() -> Display<ConnectionMock, DefaultLines> {
    Display::new(ConnectionMock::default())
}

#[test]
fn init() {
    let lcd = setup_display();

    lcd.init(&FunctionSetBuilder::default());

    let connection = lcd.get_connection();

    let init_calls = connection.init_calls.borrow_mut();
    assert_eq!(*init_calls, 1);

    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0x33));
    assert_eq!(send_bytes[1], WriteMode::Command(0x32));
    assert_eq!(send_bytes[2], WriteMode::Command(0x20));
    assert_eq!(send_bytes[3], WriteMode::Command(0x01));
}

#[test]
fn set_entry_mode() {
    let lcd = setup_display();

    lcd.set_entry_mode(&EntryModeBuilder::default());

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0000_0110));
}

#[test]
fn test_set_display_control() {
    let lcd = setup_display();

    lcd.set_display_control(&DisplayControlBuilder::default());

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0000_1100));
}

#[test]
fn test_shift_cursor_left() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_0000));
}

#[test]
fn test_shift_cursor_left_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes.len(), 0);
}

#[test]
fn test_shift_cursor_right() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_multiple() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(2));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_0100));
    assert_eq!(send_bytes[1], WriteMode::Command(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes.len(), 0);
}

#[test]
fn test_shift_left() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Left(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_1000));
}

#[test]
fn test_shift_right() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Right(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_1100));
}

#[test]
fn test_clear() {
    let lcd = setup_display();

    lcd.clear();

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0x01));
}

#[test]
fn test_seek_from_home() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(3));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1000_0011));
}

#[test]
fn test_seek_from_current() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(2));
    lcd.seek(SeekFrom::Current(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1000_0010));
    assert_eq!(send_bytes[1], WriteMode::Command(0b1000_0011));
}

#[test]
fn test_seek_from_line() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Line {
        line: DefaultLines::Two,
        bytes: 3,
    });

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1100_0011));
}

#[test]
fn test_seek_cgram_from_home() {
    let lcd = setup_display();

    let lcd = lcd.seek_cgram(3);

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0100_0011));
}

#[test]
fn test_seek_cgram_from_current() {
    let lcd = setup_display();

    let mut lcd = lcd.seek_cgram(2);
    lcd.seek(SeekCgRamFrom::Current(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0100_0010));
    assert_eq!(send_bytes[1], WriteMode::Command(0b0100_0011));
}

// #[test]
// #[ignore]
// TODO: needs clarification: line does not make sense here,  For 5×8 dots, eight character
// patterns can be written, and for 5×10 dots, four character patterns can be written
// fn test_seek_cgram_from_line() {
//     let mut lcd = setup_display();

//     lcd.seek_cgram(SeekFrom::Home(2));
//     lcd.seek_cgram(SeekFrom::Current(1));

//     let connection = lcd.get_connection();
//     let send_bytes = connection.send_bytes.borrow_mut();
//     assert_eq!(send_bytes[0], WriteMode::Command(0b0100_0010));
//     assert_eq!(send_bytes[1], WriteMode::Command(0b0100_0011));
// }

#[test]
fn test_write() {
    let mut lcd = setup_display();

    lcd.write(123);

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Data(123));
}

#[test]
fn test_write_updates_address_counter() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(0));
    lcd.write(12);
    lcd.write(34);
    lcd.seek(SeekFrom::Current(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[3], WriteMode::Command(0b1000_0010));
}

#[test]
fn test_write_message() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Data(b'H'));
    assert_eq!(send_bytes[1], WriteMode::Data(b'i'));
}

#[test]
fn test_write_message_increments_address_counter() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");
    lcd.seek(SeekFrom::Current(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[2], WriteMode::Command(0b1000_0010));
}

#[test]
fn test_read() {
    let expected = 42;

    let connection = ConnectionMock::default();
    connection.set_read_value(expected);

    let mut lcd: Display<ConnectionMock, DefaultLines> = Display::new(connection);
    let input = lcd.read_byte();
    assert_eq!(input, expected);
}

#[test]
fn test_read_increments_address_counter() {
    let connection = ConnectionMock::default();

    connection.set_read_value(4);
    connection.set_read_value(2);

    let mut lcd: Display<ConnectionMock, DefaultLines> = Display::new(connection);

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1000_0001));
    assert_eq!(send_bytes[1], WriteMode::Command(0b1000_0010));
}
<commit_msg>Fix tests<commit_after>extern crate clerk;

use std::cell::RefCell;
use std::collections::VecDeque;

use clerk::{DefaultLines, Delay, Display, DisplayControlBuilder, EntryModeBuilder,
            FunctionSetBuilder, Init, ReadMode, Receive, SeekCgRamFrom, SeekFrom, Send, ShiftTo,
            WriteMode};

struct ConnectionMock {
    init_calls: RefCell<u8>,
    send_bytes: RefCell<Vec<WriteMode>>,
    receivable_bytes: RefCell<VecDeque<u8>>,
}

impl Default for ConnectionMock {
    fn default() -> Self {
        ConnectionMock {
            init_calls: RefCell::new(0),
            send_bytes: RefCell::new(vec![]),
            receivable_bytes: RefCell::new(VecDeque::new()),
        }
    }
}

impl ConnectionMock {
    fn set_read_value(&self, value: u8) {
        self.receivable_bytes.borrow_mut().push_back(value)
    }
}

impl Init for ConnectionMock {
    fn init(&self) {
        let mut init_calls = self.init_calls.borrow_mut();

        *init_calls += 1;
    }
}

impl Send for ConnectionMock {
    fn send(&self, mode: WriteMode) {
        let mut send_bytes = self.send_bytes.borrow_mut();

        send_bytes.push(mode);
    }
}

impl Receive for ConnectionMock {
    fn receive(&self, _: ReadMode) -> u8 {
        self.receivable_bytes.borrow_mut().pop_front().unwrap()
    }
}

pub struct CustomDelayMock;

impl Delay for CustomDelayMock {
    fn delay_ns(_: u16) {
        // mhh
    }
}

fn setup_display() -> Display<ConnectionMock, DefaultLines> {
    Display::new(ConnectionMock::default())
}

#[test]
fn init() {
    let lcd = setup_display();

    lcd.init(&FunctionSetBuilder::default());

    let connection = lcd.get_connection();

    let init_calls = connection.init_calls.borrow_mut();
    assert_eq!(*init_calls, 1);

    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0x33));
    assert_eq!(send_bytes[1], WriteMode::Command(0x32));
    assert_eq!(send_bytes[2], WriteMode::Command(0x20));
    assert_eq!(send_bytes[3], WriteMode::Command(0x01));
}

#[test]
fn set_entry_mode() {
    let lcd = setup_display();

    lcd.set_entry_mode(&EntryModeBuilder::default());

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0000_0110));
}

#[test]
fn test_set_display_control() {
    let lcd = setup_display();

    lcd.set_display_control(&DisplayControlBuilder::default());

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0000_1100));
}

#[test]
fn test_shift_cursor_left() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_0000));
}

#[test]
fn test_shift_cursor_left_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes.len(), 0);
}

#[test]
fn test_shift_cursor_right() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_multiple() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(2));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_0100));
    assert_eq!(send_bytes[1], WriteMode::Command(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes.len(), 0);
}

#[test]
fn test_shift_left() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Left(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_1000));
}

#[test]
fn test_shift_right() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Right(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0001_1100));
}

#[test]
fn test_clear() {
    let lcd = setup_display();

    lcd.clear();

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0x01));
}

#[test]
fn test_seek_from_home() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(3));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1000_0011));
}

#[test]
fn test_seek_from_current() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(2));
    lcd.seek(SeekFrom::Current(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1000_0010));
    assert_eq!(send_bytes[1], WriteMode::Command(0b1000_0011));
}

#[test]
fn test_seek_from_line() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Line {
        line: DefaultLines::Two,
        offset: 3,
    });

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1100_0011));
}

#[test]
fn test_set_cgram_address_from_home() {
    let lcd = setup_display();

    let lcd = lcd.set_cgram_address(3);

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0100_0011));
}

#[test]
fn test_seek_cgram_from_current() {
    let lcd = setup_display();

    let mut lcd = lcd.set_cgram_address(2);
    lcd.seek(SeekCgRamFrom::Current(1));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b0100_0010));
    assert_eq!(send_bytes[1], WriteMode::Command(0b0100_0011));
}

#[test]
fn test_write() {
    let mut lcd = setup_display();

    lcd.write(123);

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Data(123));
}

#[test]
fn test_write_updates_address_counter() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(0));
    lcd.write(12);
    lcd.write(34);
    lcd.seek(SeekFrom::Current(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[3], WriteMode::Command(0b1000_0010));
}

#[test]
fn test_write_message() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Data(b'H'));
    assert_eq!(send_bytes[1], WriteMode::Data(b'i'));
}

#[test]
fn test_write_message_increments_address_counter() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");
    lcd.seek(SeekFrom::Current(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[2], WriteMode::Command(0b1000_0010));
}

#[test]
fn test_read() {
    let expected = 42;

    let connection = ConnectionMock::default();
    connection.set_read_value(expected);

    let mut lcd: Display<ConnectionMock, DefaultLines> = Display::new(connection);
    let input = lcd.read_byte();
    assert_eq!(input, expected);
}

#[test]
fn test_read_increments_address_counter() {
    let connection = ConnectionMock::default();

    connection.set_read_value(4);
    connection.set_read_value(2);

    let mut lcd: Display<ConnectionMock, DefaultLines> = Display::new(connection);

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    let connection = lcd.get_connection();
    let send_bytes = connection.send_bytes.borrow_mut();
    assert_eq!(send_bytes[0], WriteMode::Command(0b1000_0001));
    assert_eq!(send_bytes[1], WriteMode::Command(0b1000_0010));
}
<|endoftext|>


<commit_before>//!
//! A dynamic environment is used when we want to be able define more tools later on.
//!
//! A dynamic environment is modified by a tool: `define-tool` will take the name of a tool in its
//! execution environment and define it with a new name in the dynamic environment it belongs to.
//!

use std::sync::*;
use std::collections::*;
use std::result::Result;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;
use super::functional_tool::*;
use super::list_tools::*;

///
/// Input to the `define-tool` tool
///
#[derive(Serialize, Deserialize)]
pub struct DefineToolInput {
    /// Name of the tool in the execution environment to copy to the dynamic environment
    pub source_name: String,

    /// Name that should be given to the tool in the dynamic environment (or None if the name should be left the same)
    pub target_name: Option<String>
}

impl DefineToolInput {
    pub fn new(source_name: &str, target_name: Option<&str>) -> DefineToolInput {
        DefineToolInput {
            source_name: String::from(source_name),
            target_name: target_name.map(|n| String::from(n))
        }
    }
}

///
/// Tool from a dynamic environment
///
#[derive(Clone)]
struct DynamicTool {
    tool: Arc<Box<Tool>>
}

impl Tool for DynamicTool {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        self.tool.invoke_json(input, environment)
    }
}

impl DynamicTool {
    fn new(tool: Box<Tool>) -> DynamicTool {
        DynamicTool { tool: Arc::new(tool) }
    }
}

///
/// Structure used to store the tools in a dynamic environment
///
struct DynamicToolMap {
    tools: HashMap<String, DynamicTool>
}

impl DynamicToolMap {
    fn new() -> DynamicToolMap {
        DynamicToolMap { tools: HashMap::new() }
    }
}

#[derive(Clone)]
pub struct DynamicEnvironment {
    /// The tools defined in this environment
    tools: Arc<Mutex<DynamicToolMap>>
}

impl DynamicEnvironment {
    pub fn new() -> DynamicEnvironment {
        DynamicEnvironment { tools: Arc::new(Mutex::new(DynamicToolMap::new())) }
    }
}

impl DynamicEnvironment {
    ///
    /// Copies a tool from a source environment into this dynamic environment
    ///
    pub fn define_tool(&self, source_name: &str, target_name: &str, source_environment: &Environment) -> Result<(), Value> {
        let source = source_environment.get_json_tool(source_name);

        match source {
            Ok(source) => {
                let mut map = self.tools.lock().unwrap();
                map.tools.insert(String::from(target_name), DynamicTool::new(source));

                Ok(())
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Could not find source tool",
                    "description":  erm.message()
                }])
            }
        }
    }

    ///
    /// Lists the tools in this environment
    ///
    pub fn list_tools(&self) -> ListToolsResult {
        // Collect the names from the map
        let map = self.tools.lock().unwrap();
        let mut defined_names: Vec<String> = map.tools.keys().map(|s| s.clone()).collect();

        // We also define define-tool and list-tools in a dynamic environment
        defined_names.push(String::from(super::tool_name::DEFINE_TOOL));
        defined_names.push(String::from(super::tool_name::LIST_TOOLS));

        // Remove duplicates
        defined_names.sort();
        defined_names.dedup();

        ListToolsResult::with_name_strings(defined_names)
    }
}

impl Environment for DynamicEnvironment {
    fn get_json_tool(&self, name: &str) -> Result<Box<Tool>, RetrieveToolError> {
        // Try to get the tool from the map
        let map = self.tools.lock().unwrap();
        let tool = map.tools.get(name);

        // Always use the mapped tool if available (so it's possible to redefine define-tool and list-tools if we want)
        match tool {
            Some(tool) => Ok(Box::new(tool.clone())),

            None => {
                match name {
                    super::tool_name::DEFINE_TOOL => {
                        // Cloning the environment creates a new reference to the map that we can use in the tool
                        let target_environment = self.clone();

                        // Generate a define-tool tool when this is requested (calls through to define_tool)
                        Ok(Box::new(make_dynamic_tool(move |input: DefineToolInput, source_environment| {
                            target_environment.define_tool(&input.source_name.clone(), &input.target_name.unwrap_or(input.source_name), source_environment)
                        })))
                    },

                    super::tool_name::LIST_TOOLS => {
                        // Cloning the environment creates a new reference to the map that we can use in the tool
                        let target_environment = self.clone();

                        // List the tools on request
                        Ok(Box::new(make_pure_tool(move |_: ()| {
                            target_environment.list_tools()
                        })))
                    }

                    // TODO: add the ability to delete tools too (including our own define-tool and list-tools)

                    _ => Err(RetrieveToolError::not_found())
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::empty_environment::*;
    use super::super::static_environment::*;
    use super::super::basic_toolset::*;

    #[test]
    fn can_list_tools() {
        let env         = DynamicEnvironment::new();
        let list_tools  = env.get_typed_tool("list-tools").unwrap();
        let list_result = list_tools.invoke((), &env);

        assert!(list_result == Ok(ListToolsResult::with_names(vec![ "define-tool", "list-tools" ])));
    }

    #[test]
    fn can_define_tool() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());

        // Define a new tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", None), &new_env);
        assert!(define_result == Ok(()));

        // Should now exist
        let new_tool = dynamic_env.get_typed_tool("test");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_define_tool_with_new_name() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());
        assert!(dynamic_env.get_json_tool("new-tool").is_err());

        // Define a new tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("new-tool")), &new_env);
        assert!(define_result == Ok(()));

        // Should now exist
        assert!(dynamic_env.get_json_tool("test").is_err());
        let new_tool = dynamic_env.get_typed_tool("new-tool");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_replace_define_tool() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Define a new tool, overwriting the 'define-tool' tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("define-tool")), &new_env);
        assert!(define_result == Ok(()));

        // Define-tool should now be our new tool instead of its default implementation
        let new_tool = dynamic_env.get_typed_tool("define-tool");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_replace_list_tools() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Define a new tool, overwriting the 'list-tools' tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("list-tools")), &new_env);
        assert!(define_result == Ok(()));

        // List-tools should now be our new tool instead of its default implementation
        let new_tool = dynamic_env.get_typed_tool("list-tools");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn new_tools_are_added_to_list() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();
        let list_tools  = dynamic_env.get_typed_tool("list-tools").unwrap();

        // List is initially just the two tools
        let initial_list_result = list_tools.invoke((), &dynamic_env);
        assert!(initial_list_result == Ok(ListToolsResult::with_names(vec![ "define-tool", "list-tools" ])));

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());
        assert!(dynamic_env.get_json_tool("new-tool").is_err());

        // Define a new tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("new-tool")), &new_env);
        assert!(define_result == Ok(()));

        // Should now be in the list
        let final_list_result = list_tools.invoke((), &dynamic_env);
        assert!(final_list_result == Ok(ListToolsResult::with_names(vec![ "define-tool", "list-tools", "new-tool" ])));
    }
}
<commit_msg>Add a way to define tools directly<commit_after>//!
//! A dynamic environment is used when we want to be able define more tools later on.
//!
//! A dynamic environment is modified by a tool: `define-tool` will take the name of a tool in its
//! execution environment and define it with a new name in the dynamic environment it belongs to.
//!

use std::sync::*;
use std::collections::*;
use std::result::Result;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;
use super::functional_tool::*;
use super::list_tools::*;

///
/// Input to the `define-tool` tool
///
#[derive(Serialize, Deserialize)]
pub struct DefineToolInput {
    /// Name of the tool in the execution environment to copy to the dynamic environment
    pub source_name: String,

    /// Name that should be given to the tool in the dynamic environment (or None if the name should be left the same)
    pub target_name: Option<String>
}

impl DefineToolInput {
    pub fn new(source_name: &str, target_name: Option<&str>) -> DefineToolInput {
        DefineToolInput {
            source_name: String::from(source_name),
            target_name: target_name.map(|n| String::from(n))
        }
    }
}

///
/// Tool from a dynamic environment
///
#[derive(Clone)]
struct DynamicTool {
    tool: Arc<Box<Tool>>
}

impl Tool for DynamicTool {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        self.tool.invoke_json(input, environment)
    }
}

impl DynamicTool {
    fn new(tool: Box<Tool>) -> DynamicTool {
        DynamicTool { tool: Arc::new(tool) }
    }
}

///
/// Structure used to store the tools in a dynamic environment
///
struct DynamicToolMap {
    tools: HashMap<String, DynamicTool>
}

impl DynamicToolMap {
    fn new() -> DynamicToolMap {
        DynamicToolMap { tools: HashMap::new() }
    }
}

#[derive(Clone)]
pub struct DynamicEnvironment {
    /// The tools defined in this environment
    tools: Arc<Mutex<DynamicToolMap>>
}

impl DynamicEnvironment {
    pub fn new() -> DynamicEnvironment {
        DynamicEnvironment { tools: Arc::new(Mutex::new(DynamicToolMap::new())) }
    }
}

impl DynamicEnvironment {
    ///
    /// Defines a new tool in this environment
    ///
    pub fn define(&self, name: &str, tool: Box<Tool>) {
        let mut map = self.tools.lock().unwrap();
        map.tools.insert(String::from(name), DynamicTool::new(tool));
    }

    ///
    /// Copies a tool from a source environment into this dynamic environment
    ///
    pub fn define_tool(&self, source_name: &str, target_name: &str, source_environment: &Environment) -> Result<(), Value> {
        let source_tool = source_environment.get_json_tool(source_name);

        match source_tool {
            Ok(source_tool) => {
                self.define(target_name, source_tool);

                Ok(())
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Could not find source tool",
                    "description":  erm.message()
                }])
            }
        }
    }

    ///
    /// Lists the tools in this environment
    ///
    pub fn list_tools(&self) -> ListToolsResult {
        // Collect the names from the map
        let map = self.tools.lock().unwrap();
        let mut defined_names: Vec<String> = map.tools.keys().map(|s| s.clone()).collect();

        // We also define define-tool and list-tools in a dynamic environment
        defined_names.push(String::from(super::tool_name::DEFINE_TOOL));
        defined_names.push(String::from(super::tool_name::LIST_TOOLS));

        // Remove duplicates
        defined_names.sort();
        defined_names.dedup();

        ListToolsResult::with_name_strings(defined_names)
    }
}

impl Environment for DynamicEnvironment {
    fn get_json_tool(&self, name: &str) -> Result<Box<Tool>, RetrieveToolError> {
        // Try to get the tool from the map
        let map = self.tools.lock().unwrap();
        let tool = map.tools.get(name);

        // Always use the mapped tool if available (so it's possible to redefine define-tool and list-tools if we want)
        match tool {
            Some(tool) => Ok(Box::new(tool.clone())),

            None => {
                match name {
                    super::tool_name::DEFINE_TOOL => {
                        // Cloning the environment creates a new reference to the map that we can use in the tool
                        let target_environment = self.clone();

                        // Generate a define-tool tool when this is requested (calls through to define_tool)
                        Ok(Box::new(make_dynamic_tool(move |input: DefineToolInput, source_environment| {
                            target_environment.define_tool(&input.source_name.clone(), &input.target_name.unwrap_or(input.source_name), source_environment)
                        })))
                    },

                    super::tool_name::LIST_TOOLS => {
                        // Cloning the environment creates a new reference to the map that we can use in the tool
                        let target_environment = self.clone();

                        // List the tools on request
                        Ok(Box::new(make_pure_tool(move |_: ()| {
                            target_environment.list_tools()
                        })))
                    }

                    // TODO: add the ability to delete tools too (including our own define-tool and list-tools)

                    _ => Err(RetrieveToolError::not_found())
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::empty_environment::*;
    use super::super::static_environment::*;
    use super::super::basic_toolset::*;

    #[test]
    fn can_list_tools() {
        let env         = DynamicEnvironment::new();
        let list_tools  = env.get_typed_tool("list-tools").unwrap();
        let list_result = list_tools.invoke((), &env);

        assert!(list_result == Ok(ListToolsResult::with_names(vec![ "define-tool", "list-tools" ])));
    }

    #[test]
    fn can_define_tool() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());

        // Define a new tool
        dynamic_env.define("test", Box::new(make_pure_tool(|x: i32| x+1)));

        // Should now exist
        let new_tool = dynamic_env.get_typed_tool("test");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_define_tool_using_tool() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());

        // Define a new tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", None), &new_env);
        assert!(define_result == Ok(()));

        // Should now exist
        let new_tool = dynamic_env.get_typed_tool("test");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_define_tool_with_new_name() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());
        assert!(dynamic_env.get_json_tool("new-tool").is_err());

        // Define a new tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("new-tool")), &new_env);
        assert!(define_result == Ok(()));

        // Should now exist
        assert!(dynamic_env.get_json_tool("test").is_err());
        let new_tool = dynamic_env.get_typed_tool("new-tool");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_replace_define_tool() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Define a new tool, overwriting the 'define-tool' tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("define-tool")), &new_env);
        assert!(define_result == Ok(()));

        // Define-tool should now be our new tool instead of its default implementation
        let new_tool = dynamic_env.get_typed_tool("define-tool");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn can_replace_list_tools() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Define a new tool, overwriting the 'list-tools' tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("list-tools")), &new_env);
        assert!(define_result == Ok(()));

        // List-tools should now be our new tool instead of its default implementation
        let new_tool = dynamic_env.get_typed_tool("list-tools");
        assert!(new_tool.is_ok());
        assert!(new_tool.unwrap().invoke(2, &dynamic_env) == Ok(3));
    }

    #[test]
    fn new_tools_are_added_to_list() {
        // Create a dynamic environment
        let dynamic_env = DynamicEnvironment::new();
        let define_tool = dynamic_env.get_typed_tool("define-tool").unwrap();
        let list_tools  = dynamic_env.get_typed_tool("list-tools").unwrap();

        // List is initially just the two tools
        let initial_list_result = list_tools.invoke((), &dynamic_env);
        assert!(initial_list_result == Ok(ListToolsResult::with_names(vec![ "define-tool", "list-tools" ])));

        // Then a static environment to copy our tool from
        let new_env     = StaticEnvironment::from_toolset(BasicToolSet::from(vec![
            ("test", make_pure_tool(|x: i32| x+1))
        ]), &EmptyEnvironment::new());

        // Initially there is no tool with this name...
        assert!(dynamic_env.get_json_tool("test").is_err());
        assert!(dynamic_env.get_json_tool("new-tool").is_err());

        // Define a new tool
        let define_result = define_tool.invoke(DefineToolInput::new("test", Some("new-tool")), &new_env);
        assert!(define_result == Ok(()));

        // Should now be in the list
        let final_list_result = list_tools.invoke((), &dynamic_env);
        assert!(final_list_result == Ok(ListToolsResult::with_names(vec![ "define-tool", "list-tools", "new-tool" ])));
    }
}
<|endoftext|>