1use anyhow::Result;
2use miniserde::{json, Deserialize, Serialize};
3use std::{
4 fmt::{self, Display},
5 path::{Path, PathBuf},
6};
7
8pub mod setup;
9
10pub struct Completions {
11 pub completions: Vec<String>,
12}
13
14impl Display for Completions {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
16 let last = self.completions.len() - 1;
17
18 for (index, completion) in self.completions.iter().enumerate() {
19 if index == last {
20 write!(f, "{}", completion)?;
21 } else {
22 writeln!(f, "{}", completion)?;
23 }
24 }
25
26 Ok(())
27 }
28}
29
30#[derive(Serialize, Deserialize, Debug, Clone)]
31pub struct CompletionData {
32 #[serde(rename = "cli-name")]
33 pub cli_name: String,
35 pub commands: Option<Vec<CompletionItem>>,
37}
38
39#[derive(Serialize, Deserialize, Debug, Clone)]
40pub struct CompletionItem {
41 pub name: String,
43 pub flags: Vec<String>,
45}
46
47pub struct Completer {
48 pub completion_data: CompletionData,
50 pub config_path: PathBuf,
52}
53
54impl Completer {
55 pub fn new<P: AsRef<Path>>(config_path: P) -> Result<Completer> {
56 let text = std::fs::read_to_string(&config_path).unwrap();
57 let completion_data = json::from_str::<CompletionData>(&text)?;
58
59 let completer = Completer {
60 config_path: config_path.as_ref().to_owned(),
61 completion_data,
62 };
63
64 Ok(completer)
65 }
66
67 pub fn from<P: AsRef<Path>>(p: P) -> Result<Completer> {
68 Self::new(p.as_ref().to_owned())
69 }
70
71 pub fn get_completions(&self) -> Completions {
72 let args = std::env::args().collect::<Vec<String>>();
73 let mut completions = vec![];
74
75 if args.len() > 2 {
76 let current_word = args[2].replace("--word=", "");
77
78 let mut line = args[4].to_string();
79
80 let position = args[6].parse::<u64>().unwrap();
81
82 if position > line.len() as u64 {
83 line.push_str(" ");
84 }
85
86 let split = line.split(" ").collect::<Vec<&str>>();
87
88 match split.len() {
89 2 => {
90 if current_word == "" {
92 for command in self.completion_data.commands.as_ref().unwrap().iter() {
93 completions.push(command.name.to_string());
94 }
95 } else {
96 for command in self.completion_data.commands.as_ref().unwrap().iter() {
97 if command.name.starts_with(¤t_word) {
98 completions.push(command.name.to_string());
99 }
100 }
101 }
102 }
103 _ => {
104 if current_word.starts_with("--") || current_word == "" {
107 for command in self.completion_data.commands.as_ref().unwrap().iter() {
108 if command.name == split[1] {
109 for flag in command.flags.iter() {
110 if flag.starts_with(¤t_word) {
111 completions.push(flag.to_string());
112 }
113 }
114 }
115 }
116 }
117 }
118 }
119 }
120
121 Completions { completions }
122 }
123
124 pub fn install(&self) -> Result<()> {
125 setup::install(&self)?;
126 Ok(())
127 }
128
129 pub fn display_completions(completions: &Vec<String>) {
130 for completion in completions.iter() {
131 println!("{}", completion);
132 }
133 }
134}