1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
pub use wasi_worker::{FileOptions, ServiceOptions, ServiceWorker};
pub use yew::agent::{Agent, AgentLink, FromWorker, HandlerId, Packed, Public, ToWorker};
use std::io;
use wasi_worker::Handler;
use yew::agent::{AgentScope, AgentLifecycleEvent, Responder};
pub struct WASIAgent<T: Agent<Reach = Public>> {
scope: AgentScope<T>,
}
impl<T: Agent<Reach = Public>> WASIAgent<T> {
pub fn new() -> Self {
Self {
scope: AgentScope::<T>::new(),
}
}
}
pub trait ThreadedWASI {
fn run(&self) -> io::Result<()>;
}
impl<T: Agent<Reach = Public>> ThreadedWASI for WASIAgent<T> {
fn run(&self) -> io::Result<()> {
let responder = WASIResponder {};
let link = AgentLink::connect(&self.scope, responder);
let upd = AgentLifecycleEvent::Create(link);
self.scope.send(upd);
let loaded: FromWorker<T::Output> = FromWorker::WorkerLoaded;
let loaded = loaded.pack();
ServiceWorker::post_message(&loaded)
}
}
impl<T: Agent<Reach = Public>> Handler for WASIAgent<T> {
fn on_message(&self, data: &[u8]) -> io::Result<()> {
let msg = ToWorker::<T::Input>::unpack(&data);
match msg {
ToWorker::Connected(id) => {
let upd = AgentLifecycleEvent::Connected(id);
self.scope.send(upd);
}
ToWorker::ProcessInput(id, value) => {
let upd = AgentLifecycleEvent::Input(value, id);
self.scope.send(upd);
}
ToWorker::Disconnected(id) => {
let upd = AgentLifecycleEvent::Disconnected(id);
self.scope.send(upd);
}
ToWorker::Destroy => {
let upd = AgentLifecycleEvent::Destroy;
self.scope.send(upd);
std::process::exit(1);
}
};
Ok(())
}
}
struct WASIResponder {}
impl<T: Agent<Reach = Public>> Responder<T> for WASIResponder {
fn respond(&self, id: HandlerId, output: T::Output) {
let msg = FromWorker::ProcessOutput(id, output);
let data = msg.pack();
if let Err(err) = ServiceWorker::post_message(&data) {
eprintln!("Worker failed to send message: {:?}", err);
};
}
}
#[cfg(test)]
mod tests {
use super::*;
use wasi_worker::{FileOptions, ServiceOptions};
struct MyAgent;
impl Agent for MyAgent {
type Reach = Public;
type Message = String;
type Input = String;
type Output = String;
fn create(_link: AgentLink<Self>) -> Self {
MyAgent {}
}
fn update(&mut self, _msg: Self::Message) {
}
fn handle_input(&mut self, _msg: Self::Input, _who: HandlerId) {
}
}
#[test]
fn it_works() {
let opt = ServiceOptions {
output: FileOptions::File("./testdata/output.bin".to_string()),
cleanup: true,
};
ServiceWorker::initialize(opt).expect("ServiceWorker::initialize");
ServiceWorker::set_message_handler(Box::new(WASIAgent::<MyAgent>::new()));
let message = b"check";
ServiceWorker::post_message(message).expect("ServiceWorker::post_message");
let data = std::fs::read("./testdata/output.bin").expect("Read testdata/output.bin");
assert_eq!(data, message);
}
}