Skip to content

Commit 7e22f9f

Browse files
refactor methods into submodule, impl simple msg loop
1 parent 0a18a6b commit 7e22f9f

File tree

7 files changed

+346
-196
lines changed

7 files changed

+346
-196
lines changed

src/server/method.rs

-147
This file was deleted.

src/server/method/mod.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
pub mod package;
2+
pub mod project;
3+
4+
use serde::de::DeserializeOwned;
5+
use serde::{Deserialize, Serialize};
6+
7+
use self::package::PackageMethod;
8+
use self::project::ProjectMethod;
9+
use super::Error;
10+
11+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
12+
pub enum Method {
13+
Exit,
14+
Project(ProjectMethod),
15+
Package(PackageMethod),
16+
}
17+
18+
/// Method namespace registration.
19+
impl Method {
20+
pub fn from_value(method: &str, value: serde_json::Value) -> Result<Self, Error> {
21+
let mut split = method.split('/');
22+
let (namespace, name) = (
23+
split
24+
.next()
25+
.ok_or_else(|| Error::InvalidMethod(method.into()))?,
26+
split
27+
.next()
28+
.ok_or_else(|| Error::InvalidMethod(method.into()))?,
29+
);
30+
31+
// Route namespaces to the appropriate enum variants for construction.
32+
Ok(match namespace {
33+
"exit" => Self::Exit,
34+
"project" => Self::Project(ProjectMethod::from_value(name, value)?),
35+
"package" => Self::Package(PackageMethod::from_value(name, value)?),
36+
x => Err(Error::InvalidMethod(x.into()))?,
37+
})
38+
}
39+
}
40+
41+
pub fn parse_value<T: DeserializeOwned>(value: serde_json::Value) -> Result<T, serde_json::Error> {
42+
serde_json::from_value(value)
43+
}

src/server/method/package.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
use super::Error;
4+
5+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
6+
pub enum PackageMethod {
7+
/// Get metadata about this package.
8+
GetMetadata,
9+
/// Determine if the package exists within the cache.
10+
IsCached,
11+
}
12+
13+
impl PackageMethod {
14+
pub fn from_value(method: &str, value: serde_json::Value) -> Result<Self, Error> {
15+
Ok(match method {
16+
"get_metadata" => Self::GetMetadata,
17+
"is_cached" => Self::IsCached,
18+
x => Err(Error::InvalidMethod(x.into()))?,
19+
})
20+
}
21+
}

src/server/method/project.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::path::PathBuf;
2+
3+
use serde::{Deserialize, Serialize};
4+
5+
use crate::ts::package_reference::PackageReference;
6+
7+
use super::Error;
8+
9+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
10+
pub enum ProjectMethod {
11+
/// Set the project directory context. This locks the project, if it exists.
12+
SetContext(SetContext),
13+
/// Release the project directory context. This unlocks the project, if a lock exists.
14+
ReleaseContext,
15+
/// Get project metadata.
16+
GetMetadata,
17+
/// Add one or more packages to the project.
18+
AddPackages(AddPackages),
19+
/// Remove one or more packages from the project.
20+
RemovePackages(RemovePackages),
21+
/// Get a list of currently installed packages.
22+
GetPackages,
23+
/// Determine if the current context is a valid project.
24+
IsValid,
25+
}
26+
27+
impl ProjectMethod {
28+
pub fn from_value(method: &str, value: serde_json::Value) -> Result<Self, Error> {
29+
Ok(match method {
30+
"set_context" => Self::SetContext(super::parse_value(value)?),
31+
"release_context" => Self::ReleaseContext,
32+
"get_metadata" => Self::GetMetadata,
33+
"add_packages" => Self::AddPackages(super::parse_value(value)?),
34+
"remove_packages" => Self::RemovePackages(super::parse_value(value)?),
35+
"get_packages" => Self::GetPackages,
36+
"is_valid" => Self::IsValid,
37+
x => Err(Error::InvalidMethod(x.into()))?,
38+
})
39+
}
40+
}
41+
42+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
43+
pub struct SetContext {
44+
path: PathBuf,
45+
}
46+
47+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
48+
pub struct AddPackages {
49+
packages: Vec<PackageReference>,
50+
}
51+
52+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
53+
pub struct RemovePackages {
54+
packages: Vec<PackageReference>,
55+
}

src/server/mod.rs

+84-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,91 @@
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;
19
mod method;
210
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+
}
352

453
/// The daemon's entrypoint. This is a psuedo event loop which does the following in step:
554
/// 1. Read JSON-RPC input(s) from stdin.
655
/// 2. Route each input.
756
/// 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

Comments
 (0)