//! Handle process life-time and message passing for proc-macro client use std::{ convert::{TryFrom, TryInto}, ffi::{OsStr, OsString}, io::{self, BufRead, BufReader, Write}, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, }; use paths::{AbsPath, AbsPathBuf}; use stdx::JodChild; use crate::{ msg::{ErrorCode, Message, Request, Response, ResponseError}, rpc::{ListMacrosResult, ListMacrosTask, ProcMacroKind}, }; #[derive(Debug)] pub(crate) struct ProcMacroProcessSrv { process: Process, stdin: ChildStdin, stdout: BufReader, } impl ProcMacroProcessSrv { pub(crate) fn run( process_path: AbsPathBuf, args: impl IntoIterator>, ) -> io::Result { let mut process = Process::run(process_path, args)?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); let srv = ProcMacroProcessSrv { process, stdin, stdout }; Ok(srv) } pub(crate) fn find_proc_macros( &mut self, dylib_path: &AbsPath, ) -> Result, tt::ExpansionError> { let task = ListMacrosTask { lib: dylib_path.to_path_buf().into() }; let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?; Ok(result.macros) } pub(crate) fn send_task(&mut self, req: Request) -> Result where R: TryFrom, { let mut buf = String::new(); let res = match send_request(&mut self.stdin, &mut self.stdout, req, &mut buf) { Ok(res) => res, Err(err) => { let result = self.process.child.try_wait(); tracing::error!( "proc macro server crashed, server process state: {:?}, server request error: {:?}", result, err ); let res = Response::Error(ResponseError { code: ErrorCode::ServerErrorEnd, message: "proc macro server crashed".into(), }); Some(res) } }; match res { Some(Response::Error(err)) => Err(tt::ExpansionError::ExpansionError(err.message)), Some(res) => Ok(res.try_into().map_err(|err| { tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err)) })?), None => Err(tt::ExpansionError::Unknown("Empty result".into())), } } } #[derive(Debug)] struct Process { child: JodChild, } impl Process { fn run( path: AbsPathBuf, args: impl IntoIterator>, ) -> io::Result { let args: Vec = args.into_iter().map(|s| s.as_ref().into()).collect(); let child = JodChild(mk_child(&path, &args)?); Ok(Process { child }) } fn stdio(&mut self) -> Option<(ChildStdin, BufReader)> { let stdin = self.child.stdin.take()?; let stdout = self.child.stdout.take()?; let read = BufReader::new(stdout); Some((stdin, read)) } } fn mk_child( path: &AbsPath, args: impl IntoIterator>, ) -> io::Result { Command::new(path.as_os_str()) .args(args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) .spawn() } fn send_request( mut writer: &mut impl Write, mut reader: &mut impl BufRead, req: Request, buf: &mut String, ) -> io::Result> { req.write(&mut writer)?; Response::read(&mut reader, buf) }