1use std::path::Path;
2
3use anyhow::anyhow;
4use clap::{Arg, Command};
5
6use crate::{
7 app::{client_loop, create_app_proc, ClientId},
8 client::client_main,
9 config::Config,
10 host::socket::{bind_server_socket, connect_client_socket},
11 js::js_vm::JsVm,
12 kernel::{
13 kernel::Kernel,
14 kernel_message::KernelCommand,
15 proc::{ProcInit, ProcStatus},
16 },
17 keymap::Keymap,
18 lualib::init_std,
19 protocol::{CltToSrv, SrvToClt},
20 settings::Settings,
21};
22
23async fn run_server() -> anyhow::Result<()> {
24 let settings = Settings::default();
25 let mut keymap = Keymap::new();
26 settings.add_to_keymap(&mut keymap)?;
27 let config = Config::make_default(&settings)?;
28
29 let _logger = {
30 let logger_str = if cfg!(debug_assertions) {
31 "debug"
32 } else {
33 "warn"
34 };
35 let logger = flexi_logger::Logger::try_with_str(logger_str)
36 .unwrap()
37 .log_to_file(flexi_logger::FileSpec::default().suppress_timestamp())
38 .append()
39 .duplicate_to_stdout(flexi_logger::Duplicate::All);
40
41 std::panic::set_hook(Box::new(|info| {
42 let stacktrace = std::backtrace::Backtrace::capture();
43 log::error!("Got panic. @info:{}\n@stackTrace:{}", info, stacktrace);
44 }));
45
46 logger.use_utc().start().unwrap()
47 };
48
49 #[cfg(unix)]
50 crate::process::unix_processes_waiter::UnixProcessesWaiter::init()?;
51 let mut kernel = Kernel::new();
52 kernel.spawn_proc(|pc| {
53 let app_proc_id = create_app_proc(config, keymap, &pc);
54 let (sender, _receiver) = tokio::sync::mpsc::unbounded_channel();
55
56 let app_sender = pc.get_proc_sender(app_proc_id);
57
58 tokio::spawn(async move {
59 let mut last_client_id = 0;
60
61 let mut server_socket = match bind_server_socket().await {
62 Ok(server_socket) => {
63 log::info!("Server is listening.");
64 server_socket
65 }
66 Err(err) => {
67 log::error!("Failed to bind the server: {:?}", err);
68 pc.send(KernelCommand::Quit);
69 return;
70 }
71 };
72 log::debug!("Waiting for clients...");
73 loop {
74 match server_socket.accept().await {
75 Ok(socket) => {
76 last_client_id += 1;
77 let client_id = ClientId(last_client_id);
78 let app_sender = app_sender.clone();
79 tokio::spawn(async move {
80 client_loop(client_id, app_sender, socket).await;
81 });
82 }
83 Err(err) => {
84 log::info!("Server socket accept error: {}", err);
85 break;
86 }
87 }
88 }
89 });
90
91 ProcInit {
92 sender,
93 stop_on_quit: false,
94 status: ProcStatus::Down,
95 deps: Vec::new(),
96 }
97 });
98
99 kernel.run().await;
100 #[cfg(unix)]
101 crate::process::unix_processes_waiter::UnixProcessesWaiter::uninit()?;
102
103 Ok(())
104}
105
106pub async fn dekit_main() -> anyhow::Result<()> {
107 println!("* Welcome to dekit — playground for future features *\n");
108
109 let cmd = clap::command!()
110 .subcommands([
111 Command::new("attach"),
112 Command::new("up"),
113 Command::new("down"),
114 Command::new("server").subcommands([
115 Command::new("run"),
116 Command::new("start"),
117 Command::new("stop"),
118 Command::new("status"),
119 ]),
120 ])
121 .arg(
122 Arg::new("files")
123 .action(clap::ArgAction::Append)
124 .trailing_var_arg(true),
125 );
126 let matches = cmd.get_matches();
127
128 match matches.subcommand() {
129 Some(("attach", _sub_m)) => {
130 let (sender, receiver) =
131 connect_client_socket::<CltToSrv, SrvToClt>(false).await?;
132 client_main(sender, receiver).await?;
133 }
134 Some(("up", _sub_m)) => {
135 println!("Up.");
136 }
137 Some(("down", _sub_m)) => {
138 println!("Down.");
139 }
140 Some(("server", sub_m)) => match sub_m.subcommand() {
141 Some(("run", _sub_m)) => {
142 run_server().await?;
143 }
144 Some(("start", _sub_m)) => {
145 println!("Start server.");
146 }
147 Some(("stop", _sub_m)) => {
148 println!("Stop server.");
149 }
150 Some(("status", _sub_m)) => {
151 println!("Server status.");
152 }
153 _ => {
154 println!("Expected more arguments after `dk server`");
155 }
156 },
157 Some((arg, _sub_m)) => {
158 println!("Unknown: {}", arg);
159 }
160 None => {
161 println!("None.");
162 let paths = matches
163 .get_many::<String>("files")
164 .map(|p| p.collect::<Vec<_>>())
165 .unwrap_or_default();
166 println!("paths = {:?}", paths);
167
168 #[allow(clippy::collapsible_if)]
169 if let Some(first) = paths.first() {
170 if first.ends_with(".lua") {
172 println!("Running the script.");
173
174 let src = std::fs::read_to_string(first)?;
175
176 let lua = mlua::Lua::new();
177 let cancel = tokio_util::sync::CancellationToken::new();
178 lua.set_app_data(cancel.clone());
179 lua
180 .globals()
181 .set("std", init_std(&lua).map_err(|e| anyhow!("{}", e))?)
182 .map_err(|e| anyhow!("{}", e))?;
183 println!("After std init.");
184
185 let chunk = lua.load(src.clone());
186 let f: mlua::Function = chunk.eval().map_err(|e| anyhow!("{}", e))?;
187 let r = f
188 .call_async::<mlua::Value>(())
189 .await
190 .map_err(|e| anyhow!("{}", e))?;
191 println!("-> {:?}", r);
192 cancel.cancel();
193 }
194 else if first.ends_with(".js") {
196 println!("Running the script.");
197
198 let src = std::fs::read_to_string(first)?;
199
200 let vm = JsVm::new().await?;
201 let root =
202 vm.eval_file(Path::new("dekit.js"), src.as_bytes()).await?;
203
204 let r: anyhow::Result<()> =
205 rquickjs::async_with!(vm.context => |ctx| {
206 run_module_main(&ctx, &root).await
207 })
208 .await;
209 r?;
210 }
211 }
212 }
213 }
214
215 Ok(())
216}
217
218async fn run_module_main(
219 ctx: &rquickjs::Ctx<'_>,
220 root: &rquickjs::Persistent<rquickjs::Object<'static>>,
221) -> anyhow::Result<()> {
222 let m = root.clone().restore(ctx)?;
223 let main = m.get::<_, rquickjs::Value>("main")?;
224
225 let val = match main.type_of() {
226 rquickjs::Type::Constructor => main
227 .into_constructor()
228 .unwrap()
229 .call::<_, rquickjs::Value>(()),
230 rquickjs::Type::Function => main.into_function().unwrap().call(()),
231 t => anyhow::bail!("Exported `main` is not a function ({}).", t.as_str()),
232 }?;
233
234 let val = if let Some(promise) = val.clone().into_promise() {
235 promise.into_future::<rquickjs::Value<'_>>().await?
236 } else {
237 val
238 };
239
240 println!("-> {:?}", val);
241 Ok(())
242}