Skip to main content

lib/
dekit.rs

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        // .lua
171        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        // .js
195        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()?;
201          let root = vm.eval_file(Path::new("dekit.js"), src.as_bytes())?;
202
203          let r: anyhow::Result<()> = vm.context.with(|ctx| {
204            let m = root.restore(&ctx)?;
205            let r = m.get::<_, rquickjs::Value>("main")?;
206            let r = match r.type_of() {
207              rquickjs::Type::Constructor => {
208                r.into_constructor().unwrap().call::<_, rquickjs::Value>(())
209              }
210              rquickjs::Type::Function => r.into_function().unwrap().call(()),
211              t => {
212                println!("Exported `main` is not a function ({}).", t.as_str());
213                Ok(rquickjs::Value::new_undefined(ctx.clone()))
214              }
215            };
216            println!("-> {:?}", r);
217            if let Err(rquickjs::Error::Exception) = r {
218              println!("Exc: {:?}", ctx.catch());
219            }
220            Ok(())
221          });
222          r?;
223        }
224      }
225    }
226  }
227
228  Ok(())
229}