// 1001: Simple Event Loop
// Poll events, dispatch enum handlers, accumulate state
use std::collections::VecDeque;
// --- Event enum ---
#[derive(Debug, Clone, PartialEq)]
enum Event {
Click { x: i32, y: i32 },
KeyPress(char),
Timer(String),
NetworkData(String),
Quit,
}
// --- Application state ---
#[derive(Debug, Clone, PartialEq)]
struct AppState {
clicks: u32,
keys: String,
timers: u32,
network_msgs: Vec<String>,
}
impl AppState {
fn new() -> Self {
AppState { clicks: 0, keys: String::new(), timers: 0, network_msgs: Vec::new() }
}
}
// --- Pure functional dispatch: one event โ next state ---
fn dispatch(state: AppState, event: &Event) -> AppState {
match event {
Event::Click { .. } => AppState { clicks: state.clicks + 1, ..state },
Event::KeyPress(c) => AppState { keys: format!("{}{}", state.keys, c), ..state },
Event::Timer(_) => AppState { timers: state.timers + 1, ..state },
Event::NetworkData(msg) => {
let mut msgs = state.network_msgs.clone();
msgs.push(msg.clone());
AppState { network_msgs: msgs, ..state }
}
Event::Quit => state, // handled by loop
}
}
// --- Approach 1: Functional event loop over a Vec ---
fn run_event_loop(events: Vec<Event>, init: AppState) -> AppState {
events.iter().fold(init, |state, event| {
if event == &Event::Quit { state } // stop processing new events via fold
else { dispatch(state, event) }
})
}
// Better version that actually stops at Quit:
fn run_until_quit(events: Vec<Event>, mut state: AppState) -> AppState {
for event in events {
match event {
Event::Quit => break,
e => state = dispatch(state, &e),
}
}
state
}
// --- Approach 2: Event loop with a queue (mutable, real-world style) ---
struct EventLoop {
queue: VecDeque<Event>,
state: AppState,
}
impl EventLoop {
fn new(state: AppState) -> Self {
EventLoop { queue: VecDeque::new(), state }
}
fn push(&mut self, event: Event) {
self.queue.push_back(event);
}
fn push_many(&mut self, events: Vec<Event>) {
for e in events { self.queue.push_back(e); }
}
fn run(&mut self) {
while let Some(event) = self.queue.pop_front() {
match event {
Event::Quit => break,
e => self.state = dispatch(self.state.clone(), &e),
}
}
}
}
fn main() {
let events = vec![
Event::Click { x: 10, y: 20 },
Event::KeyPress('h'),
Event::KeyPress('i'),
Event::Timer("heartbeat".to_string()),
Event::NetworkData("hello".to_string()),
Event::Click { x: 5, y: 5 },
Event::NetworkData("world".to_string()),
Event::Timer("refresh".to_string()),
Event::Quit,
Event::Click { x: 0, y: 0 }, // ignored after Quit
];
let final_state = run_until_quit(events, AppState::new());
println!("clicks={} keys={} timers={} msgs={}",
final_state.clicks, final_state.keys,
final_state.timers, final_state.network_msgs.len());
}
#[cfg(test)]
mod tests {
use super::*;
fn test_events() -> Vec<Event> {
vec![
Event::Click { x: 10, y: 20 },
Event::KeyPress('h'),
Event::KeyPress('i'),
Event::Timer("heartbeat".to_string()),
Event::NetworkData("hello".to_string()),
Event::Click { x: 5, y: 5 },
Event::NetworkData("world".to_string()),
Event::Timer("refresh".to_string()),
Event::Quit,
Event::Click { x: 0, y: 0 }, // ignored
]
}
#[test]
fn test_run_until_quit() {
let state = run_until_quit(test_events(), AppState::new());
assert_eq!(state.clicks, 2);
assert_eq!(state.keys, "hi");
assert_eq!(state.timers, 2);
assert_eq!(state.network_msgs.len(), 2);
}
#[test]
fn test_quit_stops_processing() {
let events = vec![
Event::Click { x: 0, y: 0 },
Event::Quit,
Event::Click { x: 0, y: 0 }, // should not be processed
];
let state = run_until_quit(events, AppState::new());
assert_eq!(state.clicks, 1);
}
#[test]
fn test_event_loop_queue() {
let mut el = EventLoop::new(AppState::new());
el.push_many(test_events());
el.run();
assert_eq!(el.state.clicks, 2);
assert_eq!(el.state.keys, "hi");
}
#[test]
fn test_dispatch_click() {
let s = dispatch(AppState::new(), &Event::Click { x: 5, y: 5 });
assert_eq!(s.clicks, 1);
}
#[test]
fn test_dispatch_network() {
let s = dispatch(AppState::new(), &Event::NetworkData("test".to_string()));
assert_eq!(s.network_msgs, vec!["test"]);
}
#[test]
fn test_empty_events() {
let state = run_until_quit(vec![], AppState::new());
assert_eq!(state, AppState::new());
}
}
(* 1001: Simple Event Loop *)
(* Poll events, dispatch enum handlers, process until Quit *)
(* --- Event types --- *)
type event =
| Click of int * int (* x, y *)
| KeyPress of char
| Timer of string (* timer name *)
| NetworkData of string (* payload *)
| Quit
type 'state handler = {
on_click: int -> int -> 'state -> 'state;
on_key: char -> 'state -> 'state;
on_timer: string -> 'state -> 'state;
on_network: string -> 'state -> 'state;
}
(* --- Event loop: dispatch events to handlers, accumulate state --- *)
let run_event_loop ~handler ~init events =
let rec loop state = function
| [] -> state
| Quit :: _ -> state
| Click (x, y) :: rest -> loop (handler.on_click x y state) rest
| KeyPress c :: rest -> loop (handler.on_key c state) rest
| Timer name :: rest -> loop (handler.on_timer name state) rest
| NetworkData s :: rest -> loop (handler.on_network s state) rest
in
loop init events
(* --- Approach 1: Pure functional event loop --- *)
type app_state = {
clicks: int;
keys: string;
timers: int;
network_msgs: string list;
}
let initial_state = { clicks = 0; keys = ""; timers = 0; network_msgs = [] }
let app_handler = {
on_click = (fun _x _y s -> { s with clicks = s.clicks + 1 });
on_key = (fun c s -> { s with keys = s.keys ^ String.make 1 c });
on_timer = (fun _name s -> { s with timers = s.timers + 1 });
on_network = (fun msg s -> { s with network_msgs = msg :: s.network_msgs });
}
let () =
let events = [
Click (10, 20);
KeyPress 'h';
KeyPress 'i';
Timer "heartbeat";
NetworkData "hello";
Click (5, 5);
NetworkData "world";
Timer "refresh";
Quit;
Click (0, 0); (* ignored after Quit *)
] in
let final_state = run_event_loop ~handler:app_handler ~init:initial_state events in
assert (final_state.clicks = 2);
assert (final_state.keys = "hi");
assert (final_state.timers = 2);
assert (List.length final_state.network_msgs = 2);
Printf.printf "Approach 1: clicks=%d keys=%s timers=%d msgs=%d\n"
final_state.clicks final_state.keys final_state.timers
(List.length final_state.network_msgs)
(* --- Approach 2: Stateful event loop with mutation --- *)
let () =
let q = Queue.create () in
List.iter (Queue.push q) [Click(1,1); KeyPress 'x'; Timer "t1"; Quit];
(* Queue is FIFO โ push order is preserved when popping *)
let event_count = ref 0 in
let rec loop () =
if Queue.is_empty q then ()
else match Queue.pop q with
| Quit -> ()
| _ -> incr event_count; loop ()
in
loop ();
assert (!event_count = 3);
Printf.printf "Approach 2 (stateful loop): %d events processed\n" !event_count
let () = Printf.printf "โ All tests passed\n"