command_autocomplete/
router.rs1use crate::connection::{ConnRequest, ResponseError, SendError, Transport};
2use crate::types::{CompleteParams, CompleteResult, Error, ShutdownResult};
3use clap::Args;
4use serde::{Deserialize, Serialize};
5use std::path::{Path, PathBuf};
6use std::process::Stdio;
7
8#[derive(Debug, Args)]
9pub struct RouterArgs {
10 config: Option<PathBuf>,
12 }
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
16pub struct Completer {
17 command: String,
18 args: Vec<String>,
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize)]
22pub struct Command {
23 name: String,
24 completer: Completer,
25}
26
27#[derive(Clone, Debug, Default, Deserialize, Serialize)]
28pub struct Config {
29 pub command: Vec<Command>,
30}
31
32pub fn run_router(_args: RouterArgs) -> anyhow::Result<()> {
33 let content = std::fs::read_to_string(
34 Path::new(&std::env::var("HOME")?).join(".config/command-autocomplete/completers.toml"),
35 );
36 let config = match content {
37 Ok(content) => toml::from_str(&content)?,
38 Err(_) => Config::default(),
40 };
41 let (transport, join_handle) = Transport::stdio();
42 {
43 let (_, receiver) = crate::connection::new_connection(transport);
44 let mut router = Router::new(config);
45 while let Some(req) = receiver.next_request() {
46 match router.handle_request(req) {
47 Ok(LoopAction::Continue) => continue,
48 Ok(LoopAction::Stop) => break,
49 Err(_) => {
50 log::warn!("the connection closed unexpectedly, stopping the receving loop");
51 break;
52 }
53 };
54 }
55 }
56 join_handle.join()?;
57 Ok(())
58}
59
60struct Router {
61 config: Config,
62}
63
64enum LoopAction {
65 Continue,
66 Stop,
67}
68
69impl Router {
70 fn new(config: Config) -> Self {
71 Router { config }
72 }
73
74 fn handle_request(&mut self, req: ConnRequest) -> Result<LoopAction, SendError> {
75 match req.inner().method.as_str() {
76 "complete" => match serde_json::from_value(req.inner().params.clone()) {
77 Ok(params) => {
78 req.reply(self.handle_complete_request(params))?;
79 }
80 Err(err) => {
81 req.reply_err(Error::invalid_request(format!(
82 "invalid params for complete request: {err}"
83 )))?;
84 }
85 },
86 "shutdown" => {
87 req.reply_ok(ShutdownResult {})?;
88 return Ok(LoopAction::Stop);
89 }
90 _ => {
91 let method = req.inner().method.clone();
92 req.reply_err(Error {
93 code: "UNKNOWN_REQUEST".to_string(),
94 message: format!("method {} is not recognized", method),
95 })?;
96 }
97 }
98 Ok(LoopAction::Continue)
99 }
100
101 fn completer(&self, params: &CompleteParams) -> Option<std::process::Command> {
102 if params.args.is_empty() {
103 return None;
104 }
105 for command in &self.config.command {
106 if command.name != params.args[0] {
107 continue;
108 }
109 let mut cmd = std::process::Command::new(&command.completer.command);
110 cmd.args(&command.completer.args);
111 return Some(cmd);
112 }
113 None
114 }
115
116 fn handle_complete_request(&mut self, params: CompleteParams) -> Result<CompleteResult, Error> {
117 let Some(mut command) = self.completer(¶ms) else {
118 if !params.args.is_empty() {
119 log::info!("completer for command {} not found", params.args[0]);
120 }
121 return Ok(CompleteResult { values: vec![] });
123 };
124 log::debug!("starting external completer: {:?}", command);
125
126 let mut child = command
128 .stdin(Stdio::piped())
129 .stdout(Stdio::piped())
130 .spawn()
131 .map_err(|e| Error::internal(format!("failed to start the completer: {e}")))?;
132
133 let stdin = child.stdin.take().ok_or_else(|| {
135 Error::internal("stdin missing in started process, this should never happen")
136 })?;
137 let stdout = child.stdout.take().ok_or_else(|| {
138 Error::internal("stdout missing in started process, this should never happen")
139 })?;
140
141 let (transport, join_handle) = Transport::raw(stdout, stdin);
142 let (sender, receiver) = crate::connection::new_connection(transport);
143
144 let recv_join_handle = std::thread::spawn(move || {
145 while let Some(req) = receiver.next_request() {
147 let r = req.reply_err(Error::invalid_request("no requests expected"));
148 if r.is_err() {
149 break;
150 }
151 }
152 log::debug!("receiver finished");
153 });
154 log::debug!("sending complete request to sub process");
156 let res = sender.send::<CompleteResult>("complete", params).unwrap();
157 log::debug!("waiting for complete response");
158 let res = res.wait().map_err(|e| match e {
159 ResponseError::Err(e) => e,
160 ResponseError::ChannelClosed => {
161 Error::internal("subprocess closed connection before providing completions")
162 }
163 ResponseError::DeserializationError(err) => Error::internal(format!(
164 "subprocess returned response that failed deserialization, error: {err}"
165 )),
166 });
167 log::debug!("received response: {:?}", res.is_ok());
168
169 sender.shutdown().unwrap().wait().unwrap();
171
172 join_handle.join().unwrap();
173 recv_join_handle.join().unwrap();
174 child.wait().unwrap();
175
176 res
178 }
179}