diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-31 15:30:24 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-31 15:30:24 +0100 |
commit | 7a546490ecb93b9da1cd888086f00a69ebd8d0aa (patch) | |
tree | 18c25777f7f0fad8f60f58bf7187db45fe3307fd /crates/ra_proc_macro | |
parent | fa3c7742af9fbfe5146f4158a6119fa727dcc87a (diff) | |
parent | 207903a1c33961c2014010f5678b1c6807e7f6d6 (diff) |
Merge #3738
3738: Implement ra_proc_macro client logic r=matklad a=edwin0cheng
This PR add the actual client logic for `ra_proc_macro` crate:
1. Define all necessary rpc serialization data structure, which include `ra_tt` related data and some task messages. Although adding `Serialize` and `Deserialize` trait to ra_tt directly seem to be much easier, we deliberately duplicate the `ra_tt` struct with `#[serde(with = "XXDef")]` for separation of code responsibility.
2. Define a simplified version of lsp base protocol for rpc, which basically copy from lsp-server code base.
3. Implement the actual `IO` for the client side progress spawning and message passing.
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_proc_macro')
-rw-r--r-- | crates/ra_proc_macro/Cargo.toml | 5 | ||||
-rw-r--r-- | crates/ra_proc_macro/src/lib.rs | 85 | ||||
-rw-r--r-- | crates/ra_proc_macro/src/msg.rs | 93 | ||||
-rw-r--r-- | crates/ra_proc_macro/src/process.rs | 196 | ||||
-rw-r--r-- | crates/ra_proc_macro/src/rpc.rs | 266 |
5 files changed, 627 insertions, 18 deletions
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml index bc2c37296..d009ceb82 100644 --- a/crates/ra_proc_macro/Cargo.toml +++ b/crates/ra_proc_macro/Cargo.toml | |||
@@ -10,3 +10,8 @@ doctest = false | |||
10 | 10 | ||
11 | [dependencies] | 11 | [dependencies] |
12 | ra_tt = { path = "../ra_tt" } | 12 | ra_tt = { path = "../ra_tt" } |
13 | serde = { version = "1.0", features = ["derive"] } | ||
14 | serde_json = "1.0" | ||
15 | log = "0.4.8" | ||
16 | crossbeam-channel = "0.4.0" | ||
17 | jod-thread = "0.1.1" | ||
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs index 5e21dd487..51fbb046a 100644 --- a/crates/ra_proc_macro/src/lib.rs +++ b/crates/ra_proc_macro/src/lib.rs | |||
@@ -5,55 +5,104 @@ | |||
5 | //! is used to provide basic infrastructure for communication between two | 5 | //! is used to provide basic infrastructure for communication between two |
6 | //! processes: Client (RA itself), Server (the external program) | 6 | //! processes: Client (RA itself), Server (the external program) |
7 | 7 | ||
8 | mod rpc; | ||
9 | mod process; | ||
10 | pub mod msg; | ||
11 | |||
12 | use process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | ||
8 | use ra_tt::{SmolStr, Subtree}; | 13 | use ra_tt::{SmolStr, Subtree}; |
14 | use rpc::ProcMacroKind; | ||
9 | use std::{ | 15 | use std::{ |
10 | path::{Path, PathBuf}, | 16 | path::{Path, PathBuf}, |
11 | sync::Arc, | 17 | sync::Arc, |
12 | }; | 18 | }; |
13 | 19 | ||
14 | #[derive(Debug, Clone, PartialEq, Eq)] | 20 | pub use rpc::{ExpansionResult, ExpansionTask}; |
21 | |||
22 | #[derive(Debug, Clone)] | ||
15 | pub struct ProcMacroProcessExpander { | 23 | pub struct ProcMacroProcessExpander { |
16 | process: Arc<ProcMacroProcessSrv>, | 24 | process: Arc<ProcMacroProcessSrv>, |
25 | dylib_path: PathBuf, | ||
17 | name: SmolStr, | 26 | name: SmolStr, |
18 | } | 27 | } |
19 | 28 | ||
29 | impl Eq for ProcMacroProcessExpander {} | ||
30 | impl PartialEq for ProcMacroProcessExpander { | ||
31 | fn eq(&self, other: &Self) -> bool { | ||
32 | self.name == other.name | ||
33 | && self.dylib_path == other.dylib_path | ||
34 | && Arc::ptr_eq(&self.process, &other.process) | ||
35 | } | ||
36 | } | ||
37 | |||
20 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { | 38 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { |
21 | fn expand( | 39 | fn expand( |
22 | &self, | 40 | &self, |
23 | _subtree: &Subtree, | 41 | subtree: &Subtree, |
24 | _attr: Option<&Subtree>, | 42 | _attr: Option<&Subtree>, |
25 | ) -> Result<Subtree, ra_tt::ExpansionError> { | 43 | ) -> Result<Subtree, ra_tt::ExpansionError> { |
26 | // FIXME: do nothing for now | 44 | self.process.custom_derive(&self.dylib_path, subtree, &self.name) |
27 | Ok(Subtree::default()) | ||
28 | } | 45 | } |
29 | } | 46 | } |
30 | 47 | ||
31 | #[derive(Debug, Clone, PartialEq, Eq)] | 48 | #[derive(Debug)] |
32 | pub struct ProcMacroProcessSrv { | 49 | enum ProcMacroClientKind { |
33 | path: PathBuf, | 50 | Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread }, |
51 | Dummy, | ||
34 | } | 52 | } |
35 | 53 | ||
36 | #[derive(Debug, Clone, PartialEq, Eq)] | 54 | #[derive(Debug)] |
37 | pub enum ProcMacroClient { | 55 | pub struct ProcMacroClient { |
38 | Process { process: Arc<ProcMacroProcessSrv> }, | 56 | kind: ProcMacroClientKind, |
39 | Dummy, | ||
40 | } | 57 | } |
41 | 58 | ||
42 | impl ProcMacroClient { | 59 | impl ProcMacroClient { |
43 | pub fn extern_process(process_path: &Path) -> ProcMacroClient { | 60 | pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> { |
44 | let process = ProcMacroProcessSrv { path: process_path.into() }; | 61 | let (thread, process) = ProcMacroProcessSrv::run(process_path)?; |
45 | ProcMacroClient::Process { process: Arc::new(process) } | 62 | Ok(ProcMacroClient { |
63 | kind: ProcMacroClientKind::Process { process: Arc::new(process), thread }, | ||
64 | }) | ||
46 | } | 65 | } |
47 | 66 | ||
48 | pub fn dummy() -> ProcMacroClient { | 67 | pub fn dummy() -> ProcMacroClient { |
49 | ProcMacroClient::Dummy | 68 | ProcMacroClient { kind: ProcMacroClientKind::Dummy } |
50 | } | 69 | } |
51 | 70 | ||
52 | pub fn by_dylib_path( | 71 | pub fn by_dylib_path( |
53 | &self, | 72 | &self, |
54 | _dylib_path: &Path, | 73 | dylib_path: &Path, |
55 | ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { | 74 | ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { |
56 | // FIXME: return empty for now | 75 | match &self.kind { |
57 | vec![] | 76 | ProcMacroClientKind::Dummy => vec![], |
77 | ProcMacroClientKind::Process { process, .. } => { | ||
78 | let macros = match process.find_proc_macros(dylib_path) { | ||
79 | Err(err) => { | ||
80 | eprintln!("Fail to find proc macro. Error: {:#?}", err); | ||
81 | return vec![]; | ||
82 | } | ||
83 | Ok(macros) => macros, | ||
84 | }; | ||
85 | |||
86 | macros | ||
87 | .into_iter() | ||
88 | .filter_map(|(name, kind)| { | ||
89 | // FIXME: Support custom derive only for now. | ||
90 | match kind { | ||
91 | ProcMacroKind::CustomDerive => { | ||
92 | let name = SmolStr::new(&name); | ||
93 | let expander: Arc<dyn ra_tt::TokenExpander> = | ||
94 | Arc::new(ProcMacroProcessExpander { | ||
95 | process: process.clone(), | ||
96 | name: name.clone(), | ||
97 | dylib_path: dylib_path.into(), | ||
98 | }); | ||
99 | Some((name, expander)) | ||
100 | } | ||
101 | _ => None, | ||
102 | } | ||
103 | }) | ||
104 | .collect() | ||
105 | } | ||
106 | } | ||
58 | } | 107 | } |
59 | } | 108 | } |
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs new file mode 100644 index 000000000..aa95bcc8f --- /dev/null +++ b/crates/ra_proc_macro/src/msg.rs | |||
@@ -0,0 +1,93 @@ | |||
1 | //! Defines messages for cross-process message based on `ndjson` wire protocol | ||
2 | |||
3 | use std::{ | ||
4 | convert::TryFrom, | ||
5 | io::{self, BufRead, Write}, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | rpc::{ListMacrosResult, ListMacrosTask}, | ||
10 | ExpansionResult, ExpansionTask, | ||
11 | }; | ||
12 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
13 | |||
14 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
15 | pub enum Request { | ||
16 | ListMacro(ListMacrosTask), | ||
17 | ExpansionMacro(ExpansionTask), | ||
18 | } | ||
19 | |||
20 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
21 | pub enum Response { | ||
22 | Error(ResponseError), | ||
23 | ListMacro(ListMacrosResult), | ||
24 | ExpansionMacro(ExpansionResult), | ||
25 | } | ||
26 | |||
27 | macro_rules! impl_try_from_response { | ||
28 | ($ty:ty, $tag:ident) => { | ||
29 | impl TryFrom<Response> for $ty { | ||
30 | type Error = &'static str; | ||
31 | fn try_from(value: Response) -> Result<Self, Self::Error> { | ||
32 | match value { | ||
33 | Response::$tag(res) => Ok(res), | ||
34 | _ => Err("Fail to convert from response"), | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | impl_try_from_response!(ListMacrosResult, ListMacro); | ||
42 | impl_try_from_response!(ExpansionResult, ExpansionMacro); | ||
43 | |||
44 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
45 | pub struct ResponseError { | ||
46 | pub code: ErrorCode, | ||
47 | pub message: String, | ||
48 | } | ||
49 | |||
50 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
51 | pub enum ErrorCode { | ||
52 | ServerErrorEnd, | ||
53 | ExpansionError, | ||
54 | } | ||
55 | |||
56 | pub trait Message: Sized + Serialize + DeserializeOwned { | ||
57 | fn read(r: &mut impl BufRead) -> io::Result<Option<Self>> { | ||
58 | let text = match read_json(r)? { | ||
59 | None => return Ok(None), | ||
60 | Some(text) => text, | ||
61 | }; | ||
62 | let msg = serde_json::from_str(&text)?; | ||
63 | Ok(Some(msg)) | ||
64 | } | ||
65 | fn write(self, w: &mut impl Write) -> io::Result<()> { | ||
66 | let text = serde_json::to_string(&self)?; | ||
67 | write_json(w, &text) | ||
68 | } | ||
69 | } | ||
70 | |||
71 | impl Message for Request {} | ||
72 | impl Message for Response {} | ||
73 | |||
74 | fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { | ||
75 | let mut buf = String::new(); | ||
76 | if inp.read_line(&mut buf)? == 0 { | ||
77 | return Ok(None); | ||
78 | } | ||
79 | // Remove ending '\n' | ||
80 | let buf = &buf[..buf.len() - 1]; | ||
81 | if buf.is_empty() { | ||
82 | return Ok(None); | ||
83 | } | ||
84 | Ok(Some(buf.to_string())) | ||
85 | } | ||
86 | |||
87 | fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> { | ||
88 | log::debug!("> {}", msg); | ||
89 | out.write_all(msg.as_bytes())?; | ||
90 | out.write_all(b"\n")?; | ||
91 | out.flush()?; | ||
92 | Ok(()) | ||
93 | } | ||
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs new file mode 100644 index 000000000..e8c85be38 --- /dev/null +++ b/crates/ra_proc_macro/src/process.rs | |||
@@ -0,0 +1,196 @@ | |||
1 | //! Handle process life-time and message passing for proc-macro client | ||
2 | |||
3 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
4 | use ra_tt::Subtree; | ||
5 | |||
6 | use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; | ||
7 | use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | ||
8 | |||
9 | use io::{BufRead, BufReader}; | ||
10 | use std::{ | ||
11 | convert::{TryFrom, TryInto}, | ||
12 | io::{self, Write}, | ||
13 | path::{Path, PathBuf}, | ||
14 | process::{Child, Command, Stdio}, | ||
15 | sync::{Arc, Weak}, | ||
16 | }; | ||
17 | |||
18 | #[derive(Debug, Default)] | ||
19 | pub(crate) struct ProcMacroProcessSrv { | ||
20 | inner: Option<Weak<Sender<Task>>>, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug)] | ||
24 | pub(crate) struct ProcMacroProcessThread { | ||
25 | // XXX: drop order is significant | ||
26 | sender: Arc<Sender<Task>>, | ||
27 | handle: jod_thread::JoinHandle<()>, | ||
28 | } | ||
29 | |||
30 | struct Task { | ||
31 | req: Request, | ||
32 | result_tx: Sender<Option<Response>>, | ||
33 | } | ||
34 | |||
35 | struct Process { | ||
36 | path: PathBuf, | ||
37 | child: Child, | ||
38 | } | ||
39 | |||
40 | impl Drop for Process { | ||
41 | fn drop(&mut self) { | ||
42 | let _ = self.child.kill(); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | impl Process { | ||
47 | fn run(process_path: &Path) -> Result<Process, io::Error> { | ||
48 | let child = Command::new(process_path.clone()) | ||
49 | .stdin(Stdio::piped()) | ||
50 | .stdout(Stdio::piped()) | ||
51 | .stderr(Stdio::null()) | ||
52 | .spawn()?; | ||
53 | |||
54 | Ok(Process { path: process_path.into(), child }) | ||
55 | } | ||
56 | |||
57 | fn restart(&mut self) -> Result<(), io::Error> { | ||
58 | let _ = self.child.kill(); | ||
59 | self.child = Command::new(self.path.clone()) | ||
60 | .stdin(Stdio::piped()) | ||
61 | .stdout(Stdio::piped()) | ||
62 | .stderr(Stdio::null()) | ||
63 | .spawn()?; | ||
64 | Ok(()) | ||
65 | } | ||
66 | |||
67 | fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { | ||
68 | let stdin = self.child.stdin.take()?; | ||
69 | let stdout = self.child.stdout.take()?; | ||
70 | let read = BufReader::new(stdout); | ||
71 | |||
72 | Some((stdin, read)) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl ProcMacroProcessSrv { | ||
77 | pub fn run( | ||
78 | process_path: &Path, | ||
79 | ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> { | ||
80 | let process = Process::run(process_path)?; | ||
81 | |||
82 | let (task_tx, task_rx) = bounded(0); | ||
83 | let handle = jod_thread::spawn(move || { | ||
84 | client_loop(task_rx, process); | ||
85 | }); | ||
86 | |||
87 | let task_tx = Arc::new(task_tx); | ||
88 | let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) }; | ||
89 | let thread = ProcMacroProcessThread { handle, sender: task_tx }; | ||
90 | |||
91 | Ok((thread, srv)) | ||
92 | } | ||
93 | |||
94 | pub fn find_proc_macros( | ||
95 | &self, | ||
96 | dylib_path: &Path, | ||
97 | ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> { | ||
98 | let task = ListMacrosTask { lib: dylib_path.to_path_buf() }; | ||
99 | |||
100 | let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?; | ||
101 | Ok(result.macros) | ||
102 | } | ||
103 | |||
104 | pub fn custom_derive( | ||
105 | &self, | ||
106 | dylib_path: &Path, | ||
107 | subtree: &Subtree, | ||
108 | derive_name: &str, | ||
109 | ) -> Result<Subtree, ra_tt::ExpansionError> { | ||
110 | let task = ExpansionTask { | ||
111 | macro_body: subtree.clone(), | ||
112 | macro_name: derive_name.to_string(), | ||
113 | attributes: None, | ||
114 | lib: dylib_path.to_path_buf(), | ||
115 | }; | ||
116 | |||
117 | let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?; | ||
118 | Ok(result.expansion) | ||
119 | } | ||
120 | |||
121 | pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError> | ||
122 | where | ||
123 | R: TryFrom<Response, Error = &'static str>, | ||
124 | { | ||
125 | let sender = match &self.inner { | ||
126 | None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())), | ||
127 | Some(it) => it, | ||
128 | }; | ||
129 | |||
130 | let (result_tx, result_rx) = bounded(0); | ||
131 | let sender = match sender.upgrade() { | ||
132 | None => { | ||
133 | return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into())) | ||
134 | } | ||
135 | Some(it) => it, | ||
136 | }; | ||
137 | sender.send(Task { req: req.into(), result_tx }).unwrap(); | ||
138 | let res = result_rx | ||
139 | .recv() | ||
140 | .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?; | ||
141 | |||
142 | match res { | ||
143 | Some(Response::Error(err)) => { | ||
144 | return Err(ra_tt::ExpansionError::ExpansionError(err.message)); | ||
145 | } | ||
146 | Some(res) => Ok(res.try_into().map_err(|err| { | ||
147 | ra_tt::ExpansionError::Unknown(format!( | ||
148 | "Fail to get response, reason : {:#?} ", | ||
149 | err | ||
150 | )) | ||
151 | })?), | ||
152 | None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())), | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | fn client_loop(task_rx: Receiver<Task>, mut process: Process) { | ||
158 | let (mut stdin, mut stdout) = match process.stdio() { | ||
159 | None => return, | ||
160 | Some(it) => it, | ||
161 | }; | ||
162 | |||
163 | for task in task_rx { | ||
164 | let Task { req, result_tx } = task; | ||
165 | |||
166 | match send_request(&mut stdin, &mut stdout, req) { | ||
167 | Ok(res) => result_tx.send(res).unwrap(), | ||
168 | Err(_err) => { | ||
169 | let res = Response::Error(ResponseError { | ||
170 | code: ErrorCode::ServerErrorEnd, | ||
171 | message: "Server closed".into(), | ||
172 | }); | ||
173 | result_tx.send(res.into()).unwrap(); | ||
174 | // Restart the process | ||
175 | if process.restart().is_err() { | ||
176 | break; | ||
177 | } | ||
178 | let stdio = match process.stdio() { | ||
179 | None => break, | ||
180 | Some(it) => it, | ||
181 | }; | ||
182 | stdin = stdio.0; | ||
183 | stdout = stdio.1; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | fn send_request( | ||
190 | mut writer: &mut impl Write, | ||
191 | mut reader: &mut impl BufRead, | ||
192 | req: Request, | ||
193 | ) -> Result<Option<Response>, io::Error> { | ||
194 | req.write(&mut writer)?; | ||
195 | Ok(Response::read(&mut reader)?) | ||
196 | } | ||
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs new file mode 100644 index 000000000..66b3f55db --- /dev/null +++ b/crates/ra_proc_macro/src/rpc.rs | |||
@@ -0,0 +1,266 @@ | |||
1 | //! Data struture serialization related stuffs for RPC | ||
2 | //! | ||
3 | //! Define all necessary rpc serialization data structure, | ||
4 | //! which include ra_tt related data and some task messages. | ||
5 | //! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier, | ||
6 | //! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")] | ||
7 | //! for separation of code responsibility. | ||
8 | |||
9 | use ra_tt::{ | ||
10 | Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId, | ||
11 | TokenTree, | ||
12 | }; | ||
13 | use serde::{Deserialize, Serialize}; | ||
14 | use std::path::PathBuf; | ||
15 | |||
16 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
17 | pub struct ListMacrosTask { | ||
18 | pub lib: PathBuf, | ||
19 | } | ||
20 | |||
21 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
22 | pub enum ProcMacroKind { | ||
23 | CustomDerive, | ||
24 | FuncLike, | ||
25 | Attr, | ||
26 | } | ||
27 | |||
28 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | ||
29 | pub struct ListMacrosResult { | ||
30 | pub macros: Vec<(String, ProcMacroKind)>, | ||
31 | } | ||
32 | |||
33 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
34 | pub struct ExpansionTask { | ||
35 | /// Argument of macro call. | ||
36 | /// | ||
37 | /// In custom derive that would be a struct or enum; in attribute-like macro - underlying | ||
38 | /// item; in function-like macro - the macro body. | ||
39 | #[serde(with = "SubtreeDef")] | ||
40 | pub macro_body: Subtree, | ||
41 | |||
42 | /// Names of macros to expand. | ||
43 | /// | ||
44 | /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In | ||
45 | /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`). | ||
46 | pub macro_name: String, | ||
47 | |||
48 | /// Possible attributes for the attribute-like macros. | ||
49 | #[serde(with = "opt_subtree_def")] | ||
50 | pub attributes: Option<Subtree>, | ||
51 | |||
52 | pub lib: PathBuf, | ||
53 | } | ||
54 | |||
55 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | ||
56 | pub struct ExpansionResult { | ||
57 | #[serde(with = "SubtreeDef")] | ||
58 | pub expansion: Subtree, | ||
59 | } | ||
60 | |||
61 | #[derive(Serialize, Deserialize)] | ||
62 | #[serde(remote = "DelimiterKind")] | ||
63 | enum DelimiterKindDef { | ||
64 | Parenthesis, | ||
65 | Brace, | ||
66 | Bracket, | ||
67 | } | ||
68 | |||
69 | #[derive(Serialize, Deserialize)] | ||
70 | #[serde(remote = "TokenId")] | ||
71 | struct TokenIdDef(u32); | ||
72 | |||
73 | #[derive(Serialize, Deserialize)] | ||
74 | #[serde(remote = "Delimiter")] | ||
75 | struct DelimiterDef { | ||
76 | #[serde(with = "TokenIdDef")] | ||
77 | pub id: TokenId, | ||
78 | #[serde(with = "DelimiterKindDef")] | ||
79 | pub kind: DelimiterKind, | ||
80 | } | ||
81 | |||
82 | #[derive(Serialize, Deserialize)] | ||
83 | #[serde(remote = "Subtree")] | ||
84 | struct SubtreeDef { | ||
85 | #[serde(default, with = "opt_delimiter_def")] | ||
86 | pub delimiter: Option<Delimiter>, | ||
87 | #[serde(with = "vec_token_tree")] | ||
88 | pub token_trees: Vec<TokenTree>, | ||
89 | } | ||
90 | |||
91 | #[derive(Serialize, Deserialize)] | ||
92 | #[serde(remote = "TokenTree")] | ||
93 | enum TokenTreeDef { | ||
94 | #[serde(with = "LeafDef")] | ||
95 | Leaf(Leaf), | ||
96 | #[serde(with = "SubtreeDef")] | ||
97 | Subtree(Subtree), | ||
98 | } | ||
99 | |||
100 | #[derive(Serialize, Deserialize)] | ||
101 | #[serde(remote = "Leaf")] | ||
102 | enum LeafDef { | ||
103 | #[serde(with = "LiteralDef")] | ||
104 | Literal(Literal), | ||
105 | #[serde(with = "PunctDef")] | ||
106 | Punct(Punct), | ||
107 | #[serde(with = "IdentDef")] | ||
108 | Ident(Ident), | ||
109 | } | ||
110 | |||
111 | #[derive(Serialize, Deserialize)] | ||
112 | #[serde(remote = "Literal")] | ||
113 | struct LiteralDef { | ||
114 | pub text: SmolStr, | ||
115 | #[serde(with = "TokenIdDef")] | ||
116 | pub id: TokenId, | ||
117 | } | ||
118 | |||
119 | #[derive(Serialize, Deserialize)] | ||
120 | #[serde(remote = "Punct")] | ||
121 | struct PunctDef { | ||
122 | pub char: char, | ||
123 | #[serde(with = "SpacingDef")] | ||
124 | pub spacing: Spacing, | ||
125 | #[serde(with = "TokenIdDef")] | ||
126 | pub id: TokenId, | ||
127 | } | ||
128 | |||
129 | #[derive(Serialize, Deserialize)] | ||
130 | #[serde(remote = "Spacing")] | ||
131 | enum SpacingDef { | ||
132 | Alone, | ||
133 | Joint, | ||
134 | } | ||
135 | |||
136 | #[derive(Serialize, Deserialize)] | ||
137 | #[serde(remote = "Ident")] | ||
138 | struct IdentDef { | ||
139 | pub text: SmolStr, | ||
140 | #[serde(with = "TokenIdDef")] | ||
141 | pub id: TokenId, | ||
142 | } | ||
143 | |||
144 | mod opt_delimiter_def { | ||
145 | use super::{Delimiter, DelimiterDef}; | ||
146 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
147 | |||
148 | pub fn serialize<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error> | ||
149 | where | ||
150 | S: Serializer, | ||
151 | { | ||
152 | #[derive(Serialize)] | ||
153 | struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter); | ||
154 | value.as_ref().map(Helper).serialize(serializer) | ||
155 | } | ||
156 | |||
157 | pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Delimiter>, D::Error> | ||
158 | where | ||
159 | D: Deserializer<'de>, | ||
160 | { | ||
161 | #[derive(Deserialize)] | ||
162 | struct Helper(#[serde(with = "DelimiterDef")] Delimiter); | ||
163 | let helper = Option::deserialize(deserializer)?; | ||
164 | Ok(helper.map(|Helper(external)| external)) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | mod opt_subtree_def { | ||
169 | use super::{Subtree, SubtreeDef}; | ||
170 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
171 | |||
172 | pub fn serialize<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error> | ||
173 | where | ||
174 | S: Serializer, | ||
175 | { | ||
176 | #[derive(Serialize)] | ||
177 | struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree); | ||
178 | value.as_ref().map(Helper).serialize(serializer) | ||
179 | } | ||
180 | |||
181 | pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Subtree>, D::Error> | ||
182 | where | ||
183 | D: Deserializer<'de>, | ||
184 | { | ||
185 | #[derive(Deserialize)] | ||
186 | struct Helper(#[serde(with = "SubtreeDef")] Subtree); | ||
187 | let helper = Option::deserialize(deserializer)?; | ||
188 | Ok(helper.map(|Helper(external)| external)) | ||
189 | } | ||
190 | } | ||
191 | |||
192 | mod vec_token_tree { | ||
193 | use super::{TokenTree, TokenTreeDef}; | ||
194 | use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; | ||
195 | |||
196 | pub fn serialize<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error> | ||
197 | where | ||
198 | S: Serializer, | ||
199 | { | ||
200 | #[derive(Serialize)] | ||
201 | struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree); | ||
202 | |||
203 | let items: Vec<_> = value.iter().map(Helper).collect(); | ||
204 | let mut seq = serializer.serialize_seq(Some(items.len()))?; | ||
205 | for element in items { | ||
206 | seq.serialize_element(&element)?; | ||
207 | } | ||
208 | seq.end() | ||
209 | } | ||
210 | |||
211 | pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<TokenTree>, D::Error> | ||
212 | where | ||
213 | D: Deserializer<'de>, | ||
214 | { | ||
215 | #[derive(Deserialize)] | ||
216 | struct Helper(#[serde(with = "TokenTreeDef")] TokenTree); | ||
217 | |||
218 | let helper = Vec::deserialize(deserializer)?; | ||
219 | Ok(helper.into_iter().map(|Helper(external)| external).collect()) | ||
220 | } | ||
221 | } | ||
222 | |||
223 | #[cfg(test)] | ||
224 | mod tests { | ||
225 | use super::*; | ||
226 | |||
227 | fn fixture_token_tree() -> Subtree { | ||
228 | let mut subtree = Subtree::default(); | ||
229 | subtree | ||
230 | .token_trees | ||
231 | .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into())); | ||
232 | subtree | ||
233 | .token_trees | ||
234 | .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into())); | ||
235 | subtree.token_trees.push(TokenTree::Subtree( | ||
236 | Subtree { | ||
237 | delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }), | ||
238 | token_trees: vec![], | ||
239 | } | ||
240 | .into(), | ||
241 | )); | ||
242 | subtree | ||
243 | } | ||
244 | |||
245 | #[test] | ||
246 | fn test_proc_macro_rpc_works() { | ||
247 | let tt = fixture_token_tree(); | ||
248 | let task = ExpansionTask { | ||
249 | macro_body: tt.clone(), | ||
250 | macro_name: Default::default(), | ||
251 | attributes: None, | ||
252 | lib: Default::default(), | ||
253 | }; | ||
254 | |||
255 | let json = serde_json::to_string(&task).unwrap(); | ||
256 | let back: ExpansionTask = serde_json::from_str(&json).unwrap(); | ||
257 | |||
258 | assert_eq!(task.macro_body, back.macro_body); | ||
259 | |||
260 | let result = ExpansionResult { expansion: tt.clone() }; | ||
261 | let json = serde_json::to_string(&result).unwrap(); | ||
262 | let back: ExpansionResult = serde_json::from_str(&json).unwrap(); | ||
263 | |||
264 | assert_eq!(result, back); | ||
265 | } | ||
266 | } | ||