Refactor jsonrpc module

This commit is contained in:
Patrick Förster 2020-02-28 21:25:52 +01:00
parent a815408518
commit c9ce4069e5
12 changed files with 816 additions and 18 deletions

129
Cargo.lock generated
View file

@ -36,11 +36,6 @@ name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
@ -69,6 +64,15 @@ name = "cfg-if"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chashmap"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "chrono"
version = "0.4.10"
@ -93,6 +97,11 @@ dependencies = [
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
@ -230,6 +239,15 @@ name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jsonrpc-derive"
version = "0.1.0"
dependencies = [
"futures-boxed 0.1.0",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -277,10 +295,9 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.71.0"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
@ -293,6 +310,11 @@ name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.3.3"
@ -409,6 +431,34 @@ name = "once_cell"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "owning_ref"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -460,6 +510,18 @@ dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.7.3"
@ -481,6 +543,19 @@ dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_core"
version = "0.5.1"
@ -497,6 +572,14 @@ dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
@ -596,6 +679,14 @@ name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "smallvec"
version = "1.2.0"
@ -612,6 +703,11 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "static_assertions"
version = "0.3.4"
@ -682,11 +778,13 @@ version = "1.10.0"
dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-boxed 0.1.0",
"jsonrpc-derive 0.1.0",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lsp-types 0.71.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
@ -884,14 +982,15 @@ dependencies = [
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45"
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780"
@ -913,8 +1012,9 @@ dependencies = [
"checksum lexical-core 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f"
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum lsp-types 0.71.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efa6b75633b0c3412ee36fc416e6d9c1e4ff576b536217f4ac3f34ac83d9e564"
"checksum lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa3268fbe8beb2795c2fb327bf44f4f3d24f5fe9ebc18d7e2980afd444d72bcf"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
@ -928,6 +1028,9 @@ dependencies = [
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
@ -936,10 +1039,14 @@ dependencies = [
"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
@ -953,8 +1060,10 @@ dependencies = [
"checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573"
"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
"checksum stderrlog 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32e5ee9b90a5452c570a0b0ac1c99ae9498db7e56e33d74366de7f2a7add7f25"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"

View file

@ -11,17 +11,20 @@ edition = "2018"
[workspace]
members = [
"crates/futures_boxed"
"crates/futures_boxed",
"crates/jsonrpc_derive"
]
[dependencies]
byteorder = "1.3"
bytes = "0.5"
chashmap = "2.2"
clap = "2.33"
futures = "0.3"
futures-boxed = { path = "crates/futures_boxed" }
jsonrpc-derive = { path = "crates/jsonrpc_derive" }
log = "0.4"
lsp-types = "0.71"
lsp-types = { version = "0.61.0", features = ["proposed"] }
nom = "5.1"
once_cell = "1.3"
serde = { version = "1.0", features = ["derive"] }

View file

@ -0,0 +1,15 @@
[package]
name = "jsonrpc-derive"
version = "0.1.0"
authors = [
"Eric Förster <efoerster@users.noreply.github.com>",
"Patrick Förster <pfoerster@users.noreply.github.com>"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
futures-boxed = { path = "../futures_boxed" }
syn = "1.0"
quote = "1.0"

View file

@ -0,0 +1,210 @@
#![recursion_limit = "128"]
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use std::str::FromStr;
use syn::{export::TokenStream2, *};
macro_rules! unwrap {
($input:expr, $arm:pat => $value:expr) => {{
match $input {
$arm => $value,
_ => panic!(),
}
}};
}
enum MethodKind {
Request,
Notification,
}
struct MethodMeta {
pub name: String,
pub kind: MethodKind,
}
impl MethodMeta {
pub fn parse(attr: &Attribute) -> Self {
let meta = attr.parse_meta().unwrap();
if meta.path().get_ident().unwrap() != "jsonrpc_method" {
panic!("Expected jsonrpc_method attribute");
}
let nested = unwrap!(meta, Meta::List(x) => x.nested);
let name = unwrap!(&nested[0], NestedMeta::Lit(Lit::Str(x)) => x.value());
let kind = {
let lit = unwrap!(&nested[1], NestedMeta::Meta(Meta::NameValue(x)) => &x.lit);
let kind = unwrap!(lit, Lit::Str(x) => x.value());
match kind.as_str() {
"request" => MethodKind::Request,
"notification" => MethodKind::Notification,
_ => panic!(
"Invalid method kind. Valid options are \"request\" and \"notification\""
),
}
};
Self { name, kind }
}
}
#[proc_macro_attribute]
pub fn jsonrpc_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
#[proc_macro_attribute]
pub fn jsonrpc_server(_attr: TokenStream, item: TokenStream) -> TokenStream {
let impl_: ItemImpl = parse_macro_input!(item);
let generics = &impl_.generics;
let self_ty = &impl_.self_ty;
let (requests, notifications) = generate_server_skeletons(&impl_.items);
let tokens = quote! {
#impl_
impl #generics crate::jsonrpc::RequestHandler for #self_ty {
#[boxed]
async fn handle_request(&self, request: crate::jsonrpc::Request) -> crate::jsonrpc::Response {
use crate::jsonrpc::*;
match request.method.as_str() {
#(#requests),*,
_ => {
Response::error(Error::method_not_found_error(), Some(request.id))
}
}
}
#[boxed]
async fn handle_notification(&self, notification: crate::jsonrpc::Notification) {
match notification.method.as_str() {
#(#notifications),*,
_ => log::warn!("{}: {}", "Method not found", notification.method),
}
}
}
};
tokens.into()
}
#[proc_macro_attribute]
pub fn jsonrpc_client(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = TokenStream::from_str(&item.to_string().replace("async ", "")).unwrap();
let trait_: ItemTrait = parse_macro_input!(item);
let trait_ident = &trait_.ident;
let stubs = generate_client_stubs(&trait_.items);
let attr: AttributeArgs = parse_macro_input!(attr);
let struct_ident = unwrap!(attr.first().unwrap(), NestedMeta::Meta(Meta::Path(x)) => x);
let tokens = quote! {
#trait_
pub struct #struct_ident {
client: crate::jsonrpc::Client
}
impl #struct_ident
{
pub fn new(output: futures::channel::mpsc::Sender<String>) -> Self {
Self {
client: crate::jsonrpc::Client::new(output),
}
}
}
impl #trait_ident for #struct_ident
{
#(#stubs)*
}
impl crate::jsonrpc::ResponseHandler for #struct_ident
{
#[boxed]
async fn handle(&self, response: crate::jsonrpc::Response) -> () {
self.client.handle(response).await
}
}
};
tokens.into()
}
fn generate_server_skeletons(items: &Vec<ImplItem>) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
let mut requests = Vec::new();
let mut notifications = Vec::new();
for item in items {
let method = unwrap!(item, ImplItem::Method(x) => x);
if method.attrs.is_empty() {
continue;
}
let ident = &method.sig.ident;
let param_ty = unwrap!(&method.sig.inputs[1], FnArg::Typed(x) => &x.ty);
let meta = MethodMeta::parse(method.attrs.first().unwrap());
let name = &meta.name.as_str();
match meta.kind {
MethodKind::Request => {
requests.push(quote!(
#name => {
let handler = |param: #param_ty| async move {
self.#ident(param).await
};
crate::jsonrpc::handle_request(request, handler).await
}
));
}
MethodKind::Notification => {
notifications.push(quote!(
#name => {
let handler = |param: #param_ty| async move {
self.#ident(param).await;
};
crate::jsonrpc::handle_notification(notification, handler).await;
}
));
}
}
}
(requests, notifications)
}
fn generate_client_stubs(items: &Vec<TraitItem>) -> Vec<TokenStream2> {
let mut stubs = Vec::new();
for item in items {
let method = unwrap!(item, TraitItem::Method(x) => x);
let attrs = &method.attrs;
let sig = &method.sig;
let param = unwrap!(&sig.inputs[1], FnArg::Typed(x) => &x.pat);
let meta = MethodMeta::parse(attrs.first().unwrap());
let name = &meta.name;
let stub = match meta.kind {
MethodKind::Request => quote!(
#[boxed]
#sig {
let result = self.client.send_request(#name.to_owned(), #param).await?;
serde_json::from_value(result).map_err(|_| crate::jsonrpc::Error::deserialize_error())
}
),
MethodKind::Notification => quote!(
#[boxed]
#sig {
self.client.send_notification(#name.to_owned(), #param).await
}
),
};
stubs.push(stub);
}
stubs
}

77
src/jsonrpc/client.rs Normal file
View file

@ -0,0 +1,77 @@
use super::types::*;
use chashmap::CHashMap;
use futures::{
channel::{mpsc, oneshot},
prelude::*,
};
use futures_boxed::boxed;
use serde::Serialize;
use serde_json::json;
use std::sync::atomic::{AtomicU64, Ordering};
pub type Result<T> = std::result::Result<T, Error>;
pub trait ResponseHandler {
#[boxed]
async fn handle(&self, response: Response);
}
#[derive(Debug)]
pub struct Client {
output: mpsc::Sender<String>,
request_id: AtomicU64,
senders_by_id: CHashMap<Id, oneshot::Sender<Result<serde_json::Value>>>,
}
impl Client {
pub fn new(output: mpsc::Sender<String>) -> Self {
Self {
output,
request_id: AtomicU64::new(0),
senders_by_id: CHashMap::new(),
}
}
pub async fn send_request<T: Serialize>(
&self,
method: String,
params: T,
) -> Result<serde_json::Value> {
let id = self.request_id.fetch_add(1, Ordering::SeqCst);
let request = Request::new(method, json!(params), Id::Number(id));
let (result_tx, result_rx) = oneshot::channel();
self.senders_by_id.insert(request.id.clone(), result_tx);
self.send(Message::Request(request)).await;
result_rx.await.unwrap()
}
pub async fn send_notification<T: Serialize>(&self, method: String, params: T) {
let notification = Notification::new(method, json!(params));
self.send(Message::Notification(notification)).await;
}
async fn send(&self, message: Message) {
let mut output = self.output.clone();
let json = serde_json::to_string(&message).unwrap();
output.send(json).await.unwrap();
}
}
impl ResponseHandler for Client {
#[boxed]
async fn handle(&self, response: Response) {
let id = response.id.expect("Expected response with id");
let result = match response.error {
Some(why) => Err(why),
None => Ok(response.result.unwrap_or(serde_json::Value::Null)),
};
let result_tx = self
.senders_by_id
.remove(&id)
.expect("Unexpected response received");
result_tx.send(result).unwrap();
}
}

66
src/jsonrpc/mod.rs Normal file
View file

@ -0,0 +1,66 @@
pub mod client;
pub mod server;
mod types;
pub use self::{
client::{Client, ResponseHandler},
server::{handle_notification, handle_request, Middleware, RequestHandler},
types::*,
};
use futures::{channel::mpsc, prelude::*};
use log::error;
use std::sync::Arc;
pub struct MessageHandler<S, C> {
pub server: Arc<S>,
pub client: Arc<C>,
pub output: mpsc::Sender<String>,
}
impl<S, C> MessageHandler<S, C>
where
S: RequestHandler + Middleware + Send + Sync + 'static,
C: ResponseHandler + Send + Sync + 'static,
{
pub async fn handle(&mut self, json: &str) {
self.server.before_message().await;
match serde_json::from_str(json).map_err(|_| Error::parse_error()) {
Ok(Message::Request(request)) => {
let server = Arc::clone(&self.server);
let mut output = self.output.clone();
tokio::spawn(async move {
let response = server.handle_request(request).await;
if let Some(error) = response.error.as_ref() {
error!("{:?}", error);
}
let json = serde_json::to_string(&response).unwrap();
output.send(json).await.unwrap();
server.after_message().await;
});
}
Ok(Message::Notification(notification)) => {
self.server.handle_notification(notification).await;
self.after_message();
}
Ok(Message::Response(response)) => {
self.client.handle(response).await;
self.after_message();
}
Err(why) => {
let response = Response::error(why, None);
let json = serde_json::to_string(&response).unwrap();
self.output.send(json).await.unwrap();
self.after_message();
}
};
}
fn after_message(&self) {
let server = Arc::clone(&self.server);
tokio::spawn(async move {
server.after_message().await;
});
}
}

137
src/jsonrpc/server.rs Normal file
View file

@ -0,0 +1,137 @@
use super::types::*;
use futures::prelude::*;
use futures_boxed::boxed;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::json;
pub type Result<T> = std::result::Result<T, String>;
pub trait RequestHandler {
#[boxed]
async fn handle_request(&self, request: Request) -> Response;
#[boxed]
async fn handle_notification(&self, notification: Notification);
}
pub trait Middleware {
#[boxed]
async fn before_message(&self);
#[boxed]
async fn after_message(&self);
}
pub async fn handle_request<'a, H, F, I, O>(request: Request, handler: H) -> Response
where
H: Fn(I) -> F + Send + Sync + 'a,
F: Future<Output = Result<O>> + Send,
I: DeserializeOwned + Send,
O: Serialize,
{
let handle = |json| {
async move {
let params: I = serde_json::from_value(json).map_err(|_| Error::deserialize_error())?;
let result = handler(params).await.map_err(Error::internal_error)?;
Ok(result)
}
};
match handle(request.params).await {
Ok(result) => Response::result(json!(result), request.id),
Err(error) => Response::error(error, Some(request.id)),
}
}
pub async fn handle_notification<'a, H, F, I>(notification: Notification, handler: H)
where
H: Fn(I) -> F + Send + Sync + 'a,
F: Future<Output = ()> + Send,
I: DeserializeOwned + Send,
{
let error = Error::deserialize_error().message;
let params = serde_json::from_value(notification.params).expect(&error);
handler(params).await;
}
#[cfg(test)]
mod tests {
use super::*;
const METHOD_NAME: &str = "foo";
async fn increment(i: i32) -> Result<i32> {
Ok(i + 1)
}
async fn panic(_params: ()) {
panic!("success");
}
fn setup_request<T: Serialize>(value: T) -> Request {
Request {
jsonrpc: PROTOCOL_VERSION.to_owned(),
params: json!(value),
method: METHOD_NAME.to_owned(),
id: Id::Number(0),
}
}
fn setup_notification() -> Notification {
Notification {
jsonrpc: PROTOCOL_VERSION.to_owned(),
method: METHOD_NAME.to_owned(),
params: json!(()),
}
}
#[tokio::test]
async fn request_valid() {
let value = 42;
let request = setup_request(value);
let response = handle_request(request.clone(), increment).await;
let expected = Response {
jsonrpc: request.jsonrpc,
result: Some(json!(increment(value).await.unwrap())),
error: None,
id: Some(request.id),
};
assert_eq!(response, expected);
}
#[tokio::test]
async fn request_invalid_params() {
let request = setup_request((0, 0));
let response = handle_request(request.clone(), increment).await;
let expected = Response {
jsonrpc: request.jsonrpc.clone(),
result: None,
error: Some(Error::deserialize_error()),
id: Some(request.id),
};
assert_eq!(response, expected);
}
#[tokio::test]
#[should_panic(expected = "success")]
async fn notification_valid() {
let notification = setup_notification();
handle_notification(notification, panic).await;
}
#[tokio::test]
#[should_panic]
async fn notification_invalid_params() {
let notification = setup_notification();
let notification = Notification {
params: json!(0),
..notification
};
handle_notification(notification, panic).await;
}
}

144
src/jsonrpc/types.rs Normal file
View file

@ -0,0 +1,144 @@
use serde::{Deserialize, Serialize};
use serde_repr::*;
pub const PROTOCOL_VERSION: &str = "2.0";
#[derive(Debug, Eq, Hash, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Id {
Number(u64),
String(String),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
#[repr(i32)]
pub enum ErrorCode {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
RequestCancelled = -32800,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Error {
pub code: ErrorCode,
pub message: String,
#[serde(skip_serializing_if = "serde_json::Value::is_null")]
pub data: serde_json::Value,
}
impl Error {
pub fn parse_error() -> Self {
Self {
code: ErrorCode::ParseError,
message: "Could not parse the input".to_owned(),
data: serde_json::Value::Null,
}
}
pub fn method_not_found_error() -> Self {
Self {
code: ErrorCode::MethodNotFound,
message: "Method not found".to_owned(),
data: serde_json::Value::Null,
}
}
pub fn deserialize_error() -> Self {
Self {
code: ErrorCode::InvalidParams,
message: "Could not deserialize parameter object".to_owned(),
data: serde_json::Value::Null,
}
}
pub fn internal_error(message: String) -> Self {
Self {
code: ErrorCode::InternalError,
message,
data: serde_json::Value::Null,
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Request {
pub jsonrpc: String,
pub method: String,
pub params: serde_json::Value,
pub id: Id,
}
impl Request {
pub fn new(method: String, params: serde_json::Value, id: Id) -> Self {
Self {
jsonrpc: PROTOCOL_VERSION.to_owned(),
method,
params,
id,
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Response {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<Error>,
pub id: Option<Id>,
}
impl Response {
pub fn result(result: serde_json::Value, id: Id) -> Self {
Self {
jsonrpc: PROTOCOL_VERSION.to_owned(),
result: Some(result),
error: None,
id: Some(id),
}
}
pub fn error(error: Error, id: Option<Id>) -> Self {
Self {
jsonrpc: PROTOCOL_VERSION.to_owned(),
result: None,
error: Some(error),
id,
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Notification {
pub jsonrpc: String,
pub method: String,
pub params: serde_json::Value,
}
impl Notification {
pub fn new(method: String, params: serde_json::Value) -> Self {
Self {
jsonrpc: PROTOCOL_VERSION.to_owned(),
method,
params,
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Message {
Request(Request),
Notification(Notification),
Response(Response),
}

View file

@ -1,2 +1,3 @@
pub mod jsonrpc;
pub mod protocol;
pub mod tex;

35
src/protocol/client.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::jsonrpc::client::Result;
use futures_boxed::boxed;
use jsonrpc_derive::{jsonrpc_client, jsonrpc_method};
use lsp_types::*;
#[jsonrpc_client(LatexLspClient)]
pub trait LspClient {
#[jsonrpc_method("workspace/configuration", kind = "request")]
#[boxed]
async fn configuration(&self, params: ConfigurationParams) -> Result<serde_json::Value>;
#[jsonrpc_method("window/showMessage", kind = "notification")]
#[boxed]
async fn show_message(&self, params: ShowMessageParams);
#[jsonrpc_method("client/registerCapability", kind = "request")]
#[boxed]
async fn register_capability(&self, params: RegistrationParams) -> Result<()>;
#[jsonrpc_method("textDocument/publishDiagnostics", kind = "notification")]
#[boxed]
async fn publish_diagnostics(&self, params: PublishDiagnosticsParams);
#[jsonrpc_method("$/progress", kind = "notification")]
#[boxed]
async fn progress(&self, params: ProgressParams);
#[jsonrpc_method("window/workDoneProgress/create", kind = "request")]
#[boxed]
async fn work_done_progress_create(&self, params: WorkDoneProgressCreateParams) -> Result<()>;
#[jsonrpc_method("window/logMessage", kind = "notification")]
#[boxed]
async fn log_message(&self, params: LogMessageParams);
}

View file

@ -1,4 +1,5 @@
mod capabilities;
mod client;
mod codec;
mod options;
mod range;
@ -6,6 +7,7 @@ mod uri;
pub use self::{
capabilities::ClientCapabilitiesExt,
client::{LatexLspClient, LspClient},
codec::LspCodec,
options::*,
range::RangeExt,

View file

@ -4,13 +4,12 @@ mod miktex;
mod tectonic;
mod texlive;
pub use self::compile::{Artifacts, CompileError, CompileParams, Format};
pub use self::kpsewhich::{KpsewhichError, Resolver};
pub use self::{
compile::{Artifacts, CompileError, CompileParams, Format},
kpsewhich::{KpsewhichError, Resolver},
};
use self::compile::Compiler;
use self::miktex::Miktex;
use self::tectonic::Tectonic;
use self::texlive::Texlive;
use self::{compile::Compiler, miktex::Miktex, tectonic::Tectonic, texlive::Texlive};
use futures_boxed::boxed;
use std::{fmt, process::Stdio, sync::Arc};
use tokio::process::Command;