|
| 1 | +use std::io::Write; |
| 2 | +use std::sync::mpsc::{self, Receiver, Sender}; |
| 3 | +use std::sync::RwLock; |
| 4 | +use std::{io, thread}; |
| 5 | + |
| 6 | +use self::proto::{Message, Request, Response}; |
| 7 | + |
| 8 | +mod lock; |
1 | 9 | mod method;
|
2 | 10 | mod proto;
|
| 11 | +mod route; |
| 12 | + |
| 13 | +trait ToJson { |
| 14 | + fn to_json(&self) -> Result<String, serde_json::Error>; |
| 15 | +} |
| 16 | + |
| 17 | +/// This error type exists to wrap library errors into a single easy-to-use package. |
| 18 | +#[derive(thiserror::Error, Debug)] |
| 19 | +#[repr(isize)] |
| 20 | +pub enum Error { |
| 21 | + /// A partial implementation of the error variants described by the JRPC spec. |
| 22 | + #[error("Failed to serialize JSON: {0:?}")] |
| 23 | + InvalidJson(#[from] serde_json::Error) = -32700, |
| 24 | + |
| 25 | + #[error("The method {0} is not valid.")] |
| 26 | + InvalidMethod(String) = -32601, |
| 27 | + |
| 28 | + #[error("Recieved invalid params for method {0}: {1}")] |
| 29 | + InvalidParams(String, String) = -32602, |
| 30 | + |
| 31 | + /// Wrapper error types and codes. |
| 32 | + #[error("${0:?}")] |
| 33 | + ProjectError(String) = 1000, |
| 34 | + |
| 35 | + #[error("{0:?}")] |
| 36 | + PackageError(String) = 2000, |
| 37 | +} |
| 38 | + |
| 39 | +impl Error { |
| 40 | + pub fn discriminant(&self) -> isize { |
| 41 | + // SAFETY: `Self` is `repr(isize)` with layout `repr(C)`, with each variant having an isize |
| 42 | + // as its first field, so we can access this value without a pointer offset. |
| 43 | + unsafe { *<*const _>::from(self).cast::<isize>() } |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +impl ToJson for Result<Response, Error> { |
| 48 | + fn to_json(&self) -> Result<String, serde_json::Error> { |
| 49 | + todo!() |
| 50 | + } |
| 51 | +} |
3 | 52 |
|
4 | 53 | /// The daemon's entrypoint. This is a psuedo event loop which does the following in step:
|
5 | 54 | /// 1. Read JSON-RPC input(s) from stdin.
|
6 | 55 | /// 2. Route each input.
|
7 | 56 | /// 3. Serialize the output and write to stdout.
|
8 |
| -pub async fn start() {} |
| 57 | +async fn start() { |
| 58 | + let stdin = io::stdin(); |
| 59 | + let mut line = String::new(); |
| 60 | + let (tx, rx) = mpsc::channel::<Result<Response, Error>>(); |
| 61 | + |
| 62 | + let cancel = RwLock::new(false); |
| 63 | + |
| 64 | + // Responses are published through the tx send channel. |
| 65 | + thread::spawn(move || respond_msg(rx, cancel)); |
| 66 | + |
| 67 | + loop { |
| 68 | + // Block the main thread until we have an input line available to be read. |
| 69 | + // This is ok because, in theory, tasks will be processed on background threads. |
| 70 | + if let Err(e) = stdin.read_line(&mut line) { |
| 71 | + panic!("") |
| 72 | + } |
| 73 | + let res = route(&line, tx.clone()).await; |
| 74 | + res.to_json().unwrap(); |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +fn respond_msg(rx: Receiver<Result<Response, Error>>, cancel: RwLock<bool>) { |
| 79 | + let mut stdout = io::stdout(); |
| 80 | + while let Ok(res) = rx.recv() { |
| 81 | + let msg = res.map(|x| serde_json::to_string(&x).unwrap()); |
| 82 | + stdout.write_all(msg.unwrap().as_bytes()); |
| 83 | + stdout.write_all("\n".as_bytes()); |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +/// Route and execute the request, returning the result. |
| 88 | +async fn route(line: &str, tx: Sender<Result<Response, Error>>) -> Result<Response, Error> { |
| 89 | + let req = Message::from_json(line); |
| 90 | + todo!() |
| 91 | +} |
0 commit comments