]> git.lizzy.rs Git - rust.git/blob - crates/proc_macro_api/src/process.rs
Remove proc macro management thread
[rust.git] / crates / proc_macro_api / src / process.rs
1 //! Handle process life-time and message passing for proc-macro client
2
3 use std::{
4     convert::{TryFrom, TryInto},
5     ffi::{OsStr, OsString},
6     fmt,
7     io::{self, BufRead, BufReader, Write},
8     path::{Path, PathBuf},
9     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
10     sync::Mutex,
11 };
12
13 use stdx::JodChild;
14
15 use crate::{
16     msg::{ErrorCode, Message, Request, Response, ResponseError},
17     rpc::{ListMacrosResult, ListMacrosTask, ProcMacroKind},
18 };
19
20 pub(crate) struct ProcMacroProcessSrv {
21     process: Mutex<Process>,
22     stdio: Mutex<(ChildStdin, BufReader<ChildStdout>)>,
23 }
24
25 impl fmt::Debug for ProcMacroProcessSrv {
26     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27         f.debug_struct("ProcMacroProcessSrv").field("process", &self.process).finish()
28     }
29 }
30
31 impl ProcMacroProcessSrv {
32     pub(crate) fn run(
33         process_path: PathBuf,
34         args: impl IntoIterator<Item = impl AsRef<OsStr>>,
35     ) -> io::Result<ProcMacroProcessSrv> {
36         let mut process = Process::run(process_path, args)?;
37         let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
38
39         let srv = ProcMacroProcessSrv {
40             process: Mutex::new(process),
41             stdio: Mutex::new((stdin, stdout)),
42         };
43
44         Ok(srv)
45     }
46
47     pub(crate) fn find_proc_macros(
48         &self,
49         dylib_path: &Path,
50     ) -> Result<Vec<(String, ProcMacroKind)>, tt::ExpansionError> {
51         let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
52
53         let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
54         Ok(result.macros)
55     }
56
57     pub(crate) fn send_task<R>(&self, req: Request) -> Result<R, tt::ExpansionError>
58     where
59         R: TryFrom<Response, Error = &'static str>,
60     {
61         let mut guard = self.stdio.lock().unwrap_or_else(|e| e.into_inner());
62         let stdio = &mut *guard;
63         let (stdin, stdout) = (&mut stdio.0, &mut stdio.1);
64
65         let mut buf = String::new();
66         let res = match send_request(stdin, stdout, req, &mut buf) {
67             Ok(res) => res,
68             Err(err) => {
69                 let mut process = self.process.lock().unwrap_or_else(|e| e.into_inner());
70                 log::error!(
71                     "proc macro server crashed, server process state: {:?}, server request error: {:?}",
72                     process.child.try_wait(),
73                     err
74                 );
75                 let res = Response::Error(ResponseError {
76                     code: ErrorCode::ServerErrorEnd,
77                     message: "proc macro server crashed".into(),
78                 });
79                 Some(res)
80             }
81         };
82
83         match res {
84             Some(Response::Error(err)) => Err(tt::ExpansionError::ExpansionError(err.message)),
85             Some(res) => Ok(res.try_into().map_err(|err| {
86                 tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err))
87             })?),
88             None => Err(tt::ExpansionError::Unknown("Empty result".into())),
89         }
90     }
91 }
92
93 #[derive(Debug)]
94 struct Process {
95     child: JodChild,
96 }
97
98 impl Process {
99     fn run(
100         path: PathBuf,
101         args: impl IntoIterator<Item = impl AsRef<OsStr>>,
102     ) -> io::Result<Process> {
103         let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
104         let child = JodChild(mk_child(&path, &args)?);
105         Ok(Process { child })
106     }
107
108     fn stdio(&mut self) -> Option<(ChildStdin, BufReader<ChildStdout>)> {
109         let stdin = self.child.stdin.take()?;
110         let stdout = self.child.stdout.take()?;
111         let read = BufReader::new(stdout);
112
113         Some((stdin, read))
114     }
115 }
116
117 fn mk_child(path: &Path, args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> io::Result<Child> {
118     Command::new(&path)
119         .args(args)
120         .stdin(Stdio::piped())
121         .stdout(Stdio::piped())
122         .stderr(Stdio::inherit())
123         .spawn()
124 }
125
126 fn send_request(
127     mut writer: &mut impl Write,
128     mut reader: &mut impl BufRead,
129     req: Request,
130     buf: &mut String,
131 ) -> io::Result<Option<Response>> {
132     req.write(&mut writer)?;
133     Response::read(&mut reader, buf)
134 }