diff options
author | Edwin Cheng <[email protected]> | 2020-03-26 20:26:34 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2020-03-31 15:20:18 +0100 |
commit | 503cbd3f4b54f3be224d7a4221fa023f0e35d228 (patch) | |
tree | 101dcec6de8da790766a6e76d8f983242d8fcbfd /crates/ra_proc_macro/src/process.rs | |
parent | fa3c7742af9fbfe5146f4158a6119fa727dcc87a (diff) |
Implement ra_proc_macro client logic
Diffstat (limited to 'crates/ra_proc_macro/src/process.rs')
-rw-r--r-- | crates/ra_proc_macro/src/process.rs | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs new file mode 100644 index 000000000..a9095af11 --- /dev/null +++ b/crates/ra_proc_macro/src/process.rs | |||
@@ -0,0 +1,202 @@ | |||
1 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
2 | use ra_tt::Subtree; | ||
3 | |||
4 | use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; | ||
5 | use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | ||
6 | |||
7 | use io::{BufRead, BufReader}; | ||
8 | use std::{ | ||
9 | io::{self, Write}, | ||
10 | path::{Path, PathBuf}, | ||
11 | process::{Child, Command, Stdio}, | ||
12 | thread::spawn, | ||
13 | }; | ||
14 | |||
15 | #[derive(Debug, Default)] | ||
16 | pub(crate) struct ProcMacroProcessSrv { | ||
17 | inner: Option<Handle>, | ||
18 | } | ||
19 | |||
20 | struct Task { | ||
21 | req: Message, | ||
22 | result_tx: Sender<Message>, | ||
23 | } | ||
24 | |||
25 | #[derive(Debug)] | ||
26 | struct Handle { | ||
27 | sender: Sender<Task>, | ||
28 | } | ||
29 | |||
30 | struct Process { | ||
31 | path: PathBuf, | ||
32 | child: Child, | ||
33 | } | ||
34 | |||
35 | impl Process { | ||
36 | fn run(process_path: &Path) -> Result<Process, io::Error> { | ||
37 | let child = Command::new(process_path.clone()) | ||
38 | .stdin(Stdio::piped()) | ||
39 | .stdout(Stdio::piped()) | ||
40 | .spawn()?; | ||
41 | |||
42 | Ok(Process { path: process_path.into(), child }) | ||
43 | } | ||
44 | |||
45 | fn restart(&mut self) -> Result<(), io::Error> { | ||
46 | let _ = self.child.kill(); | ||
47 | self.child = | ||
48 | Command::new(self.path.clone()).stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; | ||
49 | Ok(()) | ||
50 | } | ||
51 | |||
52 | fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { | ||
53 | let stdin = self.child.stdin.take()?; | ||
54 | let stdout = self.child.stdout.take()?; | ||
55 | let read = BufReader::new(stdout); | ||
56 | |||
57 | Some((stdin, read)) | ||
58 | } | ||
59 | } | ||
60 | |||
61 | impl ProcMacroProcessSrv { | ||
62 | pub fn run(process_path: &Path) -> Result<ProcMacroProcessSrv, io::Error> { | ||
63 | let process = Process::run(process_path)?; | ||
64 | |||
65 | let (task_tx, task_rx) = bounded(0); | ||
66 | |||
67 | let _ = spawn(move || { | ||
68 | client_loop(task_rx, process); | ||
69 | }); | ||
70 | Ok(ProcMacroProcessSrv { inner: Some(Handle { sender: task_tx }) }) | ||
71 | } | ||
72 | |||
73 | pub fn find_proc_macros( | ||
74 | &self, | ||
75 | dylib_path: &Path, | ||
76 | ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> { | ||
77 | let task = ListMacrosTask { lib: dylib_path.to_path_buf() }; | ||
78 | |||
79 | let result: ListMacrosResult = self.send_task("list_macros", task)?; | ||
80 | Ok(result.macros) | ||
81 | } | ||
82 | |||
83 | pub fn custom_derive( | ||
84 | &self, | ||
85 | dylib_path: &Path, | ||
86 | subtree: &Subtree, | ||
87 | derive_name: &str, | ||
88 | ) -> Result<Subtree, ra_tt::ExpansionError> { | ||
89 | let task = ExpansionTask { | ||
90 | macro_body: subtree.clone(), | ||
91 | macro_name: derive_name.to_string(), | ||
92 | attributes: None, | ||
93 | lib: dylib_path.to_path_buf(), | ||
94 | }; | ||
95 | |||
96 | let result: ExpansionResult = self.send_task("custom_derive", task)?; | ||
97 | Ok(result.expansion) | ||
98 | } | ||
99 | |||
100 | pub fn send_task<'a, T, R>(&self, method: &str, task: T) -> Result<R, ra_tt::ExpansionError> | ||
101 | where | ||
102 | T: serde::Serialize, | ||
103 | R: serde::de::DeserializeOwned + Default, | ||
104 | { | ||
105 | let handle = match &self.inner { | ||
106 | None => return Err(ra_tt::ExpansionError::Unknown("No handle is found.".to_string())), | ||
107 | Some(it) => it, | ||
108 | }; | ||
109 | |||
110 | let msg = serde_json::to_value(task).unwrap(); | ||
111 | |||
112 | // FIXME: use a proper request id | ||
113 | let id = 0; | ||
114 | let req = Request { id: id.into(), method: method.into(), params: msg }; | ||
115 | |||
116 | let (result_tx, result_rx) = bounded(0); | ||
117 | |||
118 | handle.sender.send(Task { req: req.into(), result_tx }).unwrap(); | ||
119 | let response = result_rx.recv().unwrap(); | ||
120 | |||
121 | match response { | ||
122 | Message::Request(_) => { | ||
123 | return Err(ra_tt::ExpansionError::Unknown( | ||
124 | "Return request from ra_proc_srv".into(), | ||
125 | )) | ||
126 | } | ||
127 | Message::Response(res) => { | ||
128 | if let Some(err) = res.error { | ||
129 | return Err(ra_tt::ExpansionError::ExpansionError(err.message)); | ||
130 | } | ||
131 | match res.result { | ||
132 | None => Ok(R::default()), | ||
133 | Some(res) => { | ||
134 | let result: R = serde_json::from_value(res) | ||
135 | .map_err(|err| ra_tt::ExpansionError::JsonError(err.to_string()))?; | ||
136 | Ok(result) | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | fn client_loop(task_rx: Receiver<Task>, mut process: Process) { | ||
145 | let (mut stdin, mut stdout) = match process.stdio() { | ||
146 | None => return, | ||
147 | Some(it) => it, | ||
148 | }; | ||
149 | |||
150 | loop { | ||
151 | let task = match task_rx.recv() { | ||
152 | Ok(task) => task, | ||
153 | Err(_) => break, | ||
154 | }; | ||
155 | |||
156 | let res = match send_message(&mut stdin, &mut stdout, task.req) { | ||
157 | Ok(res) => res, | ||
158 | Err(_err) => { | ||
159 | let res = Response { | ||
160 | id: 0.into(), | ||
161 | result: None, | ||
162 | error: Some(ResponseError { | ||
163 | code: ErrorCode::ServerErrorEnd as i32, | ||
164 | message: "Server closed".into(), | ||
165 | data: None, | ||
166 | }), | ||
167 | }; | ||
168 | if task.result_tx.send(res.into()).is_err() { | ||
169 | break; | ||
170 | } | ||
171 | // Restart the process | ||
172 | if process.restart().is_err() { | ||
173 | break; | ||
174 | } | ||
175 | let stdio = match process.stdio() { | ||
176 | None => break, | ||
177 | Some(it) => it, | ||
178 | }; | ||
179 | stdin = stdio.0; | ||
180 | stdout = stdio.1; | ||
181 | continue; | ||
182 | } | ||
183 | }; | ||
184 | |||
185 | if let Some(res) = res { | ||
186 | if task.result_tx.send(res).is_err() { | ||
187 | break; | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | let _ = process.child.kill(); | ||
193 | } | ||
194 | |||
195 | fn send_message( | ||
196 | mut writer: &mut impl Write, | ||
197 | mut reader: &mut impl BufRead, | ||
198 | msg: Message, | ||
199 | ) -> Result<Option<Message>, io::Error> { | ||
200 | msg.write(&mut writer)?; | ||
201 | Ok(Message::read(&mut reader)?) | ||
202 | } | ||