command_autocomplete/
carapace.rs

1use crate::connection::{ConnRequest, SendError, Transport};
2use crate::types::{CompleteParams, CompleteResult, CompletionValue, Error, ShutdownResult};
3use clap::Args;
4use serde::{Deserialize, Serialize};
5use std::process::Command;
6
7#[derive(Debug, Args)]
8pub struct CarapaceArgs {}
9
10pub fn run_carapace(_args: CarapaceArgs) -> anyhow::Result<()> {
11    let (transport, join_handle) = Transport::stdio();
12    {
13        let (_, receiver) = crate::connection::new_connection(transport);
14        while let Some(req) = receiver.next_request() {
15            match handle_request(req) {
16                Ok(LoopAction::Continue) => continue,
17                Ok(LoopAction::Stop) => break,
18                Err(_) => {
19                    log::warn!("the connection closed unexpectedly, stopping the receving loop");
20                    break;
21                }
22            }
23        }
24    }
25    join_handle.join()?;
26    Ok(())
27}
28enum LoopAction {
29    Continue,
30    Stop,
31}
32
33fn handle_request(req: ConnRequest) -> Result<LoopAction, SendError> {
34    match req.inner().method.as_str() {
35        "complete" => match serde_json::from_value(req.inner().params.clone()) {
36            Ok(params) => {
37                req.reply(handle_complete_request(params))?;
38            }
39            Err(err) => {
40                req.reply_err(Error::invalid_request(format!(
41                    "invalid params for complete request: {err}"
42                )))?;
43            }
44        },
45        "shutdown" => {
46            req.reply_ok(ShutdownResult {})?;
47            return Ok(LoopAction::Stop);
48        }
49        _ => {
50            let method = req.inner().method.clone();
51            req.reply_err(Error {
52                code: "UNKNOWN_REQUEST".to_string(),
53                message: format!("method {} is not recognized", method),
54            })?;
55        }
56    }
57    Ok(LoopAction::Continue)
58}
59
60fn handle_complete_request(params: CompleteParams) -> Result<CompleteResult, Error> {
61    if params.args.is_empty() {
62        return Err(Error::invalid_request(
63            "params.args is empty, required at least one element",
64        ));
65    }
66
67    let mut args = Vec::new();
68    args.push(params.args[0].clone());
69    args.push("export".into());
70    args.extend_from_slice(&params.args);
71    let output = Command::new("carapace")
72        .args(args)
73        .output()
74        .map_err(|e| Error::internal(format!("failed to run carapace command: {e}")))?;
75    if !output.status.success() {
76        return Err(Error::internal("carapace command failed"));
77    }
78
79    let carapace_export: CarapaceExport = serde_json::from_slice(&output.stdout)
80        .map_err(|e| Error::internal(format!("output from carapace can't be parsed: {e}")))?;
81
82    Ok(CompleteResult {
83        values: carapace_export
84            .values
85            .into_iter()
86            .map(|x| CompletionValue {
87                value: x.value,
88                description: x.description,
89            })
90            .collect(),
91    })
92}
93
94#[derive(Debug, Deserialize, Serialize)]
95struct CarapaceExport {
96    pub values: Vec<CarapaceValue>,
97}
98
99#[derive(Debug, Deserialize, Serialize)]
100struct CarapaceValue {
101    pub value: String,
102    pub display: Option<String>,
103    pub description: Option<String>,
104    pub tag: Option<String>,
105}