-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathlib.rs
More file actions
271 lines (246 loc) · 9.33 KB
/
lib.rs
File metadata and controls
271 lines (246 loc) · 9.33 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
pub(crate) mod api;
pub(crate) mod cli;
pub(crate) mod engine;
pub(crate) mod helpers;
pub(crate) mod permissions;
pub(crate) mod store;
pub(crate) mod wasm_host;
pub(crate) use engine::WasmEngine;
pub(crate) use wasm_host::WasmHost;
use anyhow::Result;
use api::host_api::repl::api::transport;
use clap::Parser;
use cli::{Cli, Commands};
use helpers::{StatusHandler, StdoutHandler};
use std::io::Write;
/// Main entry point for the REPL application
pub async fn run_async() -> Result<()> {
// Parse command line arguments
let cli = Cli::parse();
// Handle subcommands first
if let Some(command) = &cli.command {
match command {
Commands::GenerateCompletions { shell } => {
return handle_generate_completions(*shell);
}
}
}
// For REPL mode, repl_logic is required
let repl_logic = cli
.repl_logic
.ok_or_else(|| anyhow::anyhow!("--repl-logic is required when running in REPL mode"))?;
let debug = cli.debug;
let plugins = cli.plugins;
let dir = cli.dir;
let allow_net = cli.allow_net;
let allow_read = cli.allow_read;
let allow_write = cli.allow_write;
let allow_all = cli.allow_all;
// Create a new CLI struct for the remaining operations
let repl_cli = Cli {
command: None,
plugins,
repl_logic: Some(repl_logic.clone()),
debug,
dir,
allow_net,
allow_read,
allow_write,
allow_all,
};
println!("[Host] Starting REPL host...");
// Create a WASI context for the host
// Binding stdio, args, env, preopened dir ...
let wasi_ctx = WasmEngine::build_wasi_ctx(&repl_cli)?;
// Create the WebAssembly engine
let engine = WasmEngine::new()?;
// Create the host
let mut host = WasmHost::new(&engine, wasi_ctx, &repl_cli);
println!("[Host] Loading REPL logic from: {}", repl_logic);
// Override the REPL logic in the binary with the one passed by params
host.load_repl_logic(&engine, &repl_logic).await?;
// Load plugins
for plugin_source in &repl_cli.plugins {
println!("[Host] Loading plugin: {}", plugin_source);
host.load_plugin(&engine, plugin_source).await?;
}
let mut plugins_config: Vec<(String, String)> = Vec::new();
for (name, plugin_instance) in &host.plugins {
let man = plugin_instance
.plugin
.repl_api_plugin()
.call_man(&mut host.store)
.await?;
plugins_config.push((name.clone(), man));
host.store.data_mut().plugins_names.push(name.clone());
}
if debug {
eprintln!("[Host][Debug] Loaded plugins config: {:?}", plugins_config);
}
{
let mut repl_vars = host
.store
.data_mut()
.repl_vars
.lock()
.expect("Failed to acquire repl_vars lock");
repl_vars.insert("ROOT".to_string(), "/Users".to_string());
}
{
let mut repl_vars = host
.store
.data_mut()
.repl_vars
.lock()
.expect("Failed to acquire repl_vars lock");
repl_vars.insert("USER".to_string(), "Tophe".to_string());
repl_vars.insert("?".to_string(), "0".to_string());
}
if debug {
eprintln!(
"[Host][Debug] Loaded env vars: {:?}",
host.store
.data()
.repl_vars
.lock()
.expect("Failed to acquire repl_vars lock")
);
}
let Some(repl_logic) = host.repl_logic else {
return Err(anyhow::anyhow!("No REPL logic loaded"));
};
loop {
let mut line = String::new();
match host
.store
.data()
.repl_vars
.lock()
.expect("Failed to acquire repl_vars lock")
.get("?")
{
Some(last_status) => {
print!("repl({})> ", last_status);
}
None => {
print!("repl> ");
}
}
std::io::stdout().flush()?;
std::io::stdin().read_line(&mut line)?;
let result = repl_logic
.repl_api_repl_logic()
.call_readline(&mut host.store, &line)
.await?;
// todo retrieve list of reserved commands from the repl-logic guest
match result {
// The built-in commands run in the repl-logic-guest contain stdout, stderr and a status
// We only need to output them and set the $? variable
transport::ReadlineResponse::Ready(plugin_response) => {
if let Some(stdout) = plugin_response.stdout {
StdoutHandler::print_and_set_last_result(
&mut host.store.data_mut().repl_vars,
stdout,
);
}
if let Some(stderr) = plugin_response.stderr {
eprintln!("{}", stderr);
}
StatusHandler::set_exit_status(
&mut host.store.data_mut().repl_vars,
plugin_response.status
== api::host_api::repl::api::transport::ReplStatus::Success,
);
}
// The repl-logic-guest parses the command and payload (expanded variables)
// We run the command of the plugin from the host, which has access to
// - the plugins
// - the store
transport::ReadlineResponse::ToRun(parsed_line) => {
if debug {
eprintln!("[Host][Debug] To run: {:?}", parsed_line);
}
// empty line - do nothing
if parsed_line.command == "" {
continue;
}
// this is a man command for plugins, we run it from the host
if parsed_line.command == "man" {
let Some(plugin_instance) = host.plugins.get(&parsed_line.payload) else {
println!(
"Unknown command: {}. Try `help` to see available commands.",
parsed_line.payload
);
StatusHandler::set_exit_status(&mut host.store.data_mut().repl_vars, false);
continue;
};
let man = plugin_instance
.plugin
.repl_api_plugin()
.call_man(&mut host.store)
.await?;
StdoutHandler::print_and_set_last_result(
&mut host.store.data_mut().repl_vars,
man,
);
StatusHandler::set_exit_status(&mut host.store.data_mut().repl_vars, true);
continue;
}
// this is a plugin command, we run it from the host
match host.plugins.get(&parsed_line.command) {
Some(plugin_instance) => {
let result = plugin_instance
.plugin
.repl_api_plugin()
.call_run(&mut host.store, &parsed_line.payload)
.await?;
if let Ok(result) = result {
if let Some(stdout) = result.stdout {
StdoutHandler::print_and_set_last_result(
&mut host.store.data_mut().repl_vars,
stdout,
);
}
if let Some(stderr) = result.stderr {
eprintln!("{}", stderr);
}
StatusHandler::set_exit_status(
&mut host.store.data_mut().repl_vars,
result.status
== api::plugin_api::repl::api::transport::ReplStatus::Success,
);
} else {
eprintln!("Error: {:?}", result);
StatusHandler::set_exit_status(
&mut host.store.data_mut().repl_vars,
false,
);
}
}
None => {
println!(
"Unknown command: {}. Try `help` to see available commands.",
parsed_line.command
);
StatusHandler::set_exit_status(&mut host.store.data_mut().repl_vars, false);
continue;
}
}
}
}
}
}
/// Handle the generate-completions subcommand
fn handle_generate_completions(shell: cli::AvailableShells) -> Result<()> {
use clap::CommandFactory;
use clap_complete::{generate, Shell};
use cli::Cli;
let mut cmd = Cli::command();
let shell_type = match shell {
cli::AvailableShells::Bash => Shell::Bash,
cli::AvailableShells::Fish => Shell::Fish,
cli::AvailableShells::Zsh => Shell::Zsh,
};
generate(shell_type, &mut cmd, "pluginlab", &mut std::io::stdout());
Ok(())
}