command_autocomplete/
nushell.rs

1use crate::connection::{ConnectionSender, Transport};
2use crate::types::{CompleteParams, CompleteResult, Error};
3use anyhow::Context;
4use clap::Args;
5use serde_json::json;
6use std::process::{Command, Stdio};
7
8#[derive(Debug, Args)]
9pub struct NushellArgs {
10    /// args of the command that is being completed
11    #[arg(last = true)]
12    command: Vec<String>,
13}
14
15pub fn run_nushell(args: NushellArgs) -> anyhow::Result<()> {
16    // TODO: make it customizable (we should actually invoke a router)
17    let mut child = Command::new("command-autocomplete")
18        .args(["router"])
19        .stdin(Stdio::piped())
20        .stdout(Stdio::piped())
21        .spawn()?;
22
23    let stdin = child.stdin.take().context("missing stdin")?;
24    let stdout = child.stdout.take().context("missing stdout")?;
25
26    let (transport, join_handle) = Transport::raw(stdout, stdin);
27    let (sender, receiver) = crate::connection::new_connection(transport);
28
29    let recv_join_handle = std::thread::spawn(move || {
30        // This is required to read the incoming responses.
31        while let Some(req) = receiver.next_request() {
32            let r = req.reply_err(Error::invalid_request("no requests expected"));
33            if r.is_err() {
34                log::warn!("The connection closed unexpectedly, stopping the receving loop");
35                break;
36            }
37        }
38    });
39    if let Err(err) = complete_and_shutdown(args, sender) {
40        log::error!("Completion failed, will kill subprocess: {}", err);
41        if let Err(err) = child.kill() {
42            log::warn!("Failed to kill subprocess: {err}")
43        }
44    }
45
46    if let Err(err) = child.wait() {
47        log::warn!("Failed to wait for the subprocess: {err}");
48    }
49    if let Err(err) = recv_join_handle.join() {
50        log::warn!("receiving thread failed: {:?}", err);
51    }
52    if let Err(err) = join_handle.join() {
53        log::warn!("connection threads failed: {:?}", err);
54    }
55    Ok(())
56}
57
58fn complete_and_shutdown(args: NushellArgs, sender: ConnectionSender) -> anyhow::Result<()> {
59    // TODO: handle unwrap
60    let res_handle = sender
61        .send(
62            "complete",
63            CompleteParams {
64                args: args.command.clone(),
65            },
66        )
67        .context("complete command failed")?;
68
69    // TODO: handle unwrap
70    let result: CompleteResult = res_handle.wait().context("complete command failed")?;
71
72    // TODO: handle unwrap
73    sender.shutdown().unwrap().wait().unwrap();
74
75    println!(
76        "{}",
77        json!(result
78            .values
79            .into_iter()
80            .map(|v| {
81                json! ({
82                    "value": v.value,
83                    "description": v.description,
84                })
85            })
86            .collect::<Vec<_>>())
87    );
88    Ok(())
89}