1use crate::common::shell;
2use crate::finder::structures::SuggestionType;
3use crate::prelude::*;
4use shell::EOF;
5use std::process::Stdio;
6
7fn apply_map(text: String, map_fn: Option<String>) -> Result<String> {
8 if let Some(m) = map_fn {
9 let cmd = if CONFIG.shell().contains("fish") {
10 format!(r#"printf "%s" "{text}" | {m}"#)
11 } else {
12 format!(
13 r#"_navi_input() {{
14cat <<'{EOF}'
15{text}
16{EOF}
17}}
18
19_navi_map_fn() {{
20 {m}
21}}
22
23_navi_nonewline() {{
24 printf "%s" "$(cat)"
25}}
26
27_navi_input | _navi_map_fn | _navi_nonewline"#
28 )
29 };
30
31 let output = shell::out()
32 .arg(cmd.as_str())
33 .stderr(Stdio::inherit())
34 .output()
35 .context("Failed to execute map function")?;
36
37 String::from_utf8(output.stdout).context("Invalid utf8 output for map function")
38 } else {
39 Ok(text)
40 }
41}
42
43fn get_column(text: String, column: Option<u8>, delimiter: Option<&str>) -> String {
44 if let Some(c) = column {
45 let mut result = String::from("");
46 let re = regex::Regex::new(delimiter.unwrap_or(r"\s\s+")).expect("Invalid regex");
47 for line in text.split('\n') {
48 if (line).is_empty() {
49 continue;
50 }
51 let mut parts = re.split(line).skip((c - 1) as usize);
52 if !result.is_empty() {
53 result.push('\n');
54 }
55 result.push_str(parts.next().unwrap_or(""));
56 }
57 result
58 } else {
59 text
60 }
61}
62
63pub fn process(
64 text: String,
65 column: Option<u8>,
66 delimiter: Option<&str>,
67 map_fn: Option<String>,
68) -> Result<String> {
69 apply_map(get_column(text, column, delimiter), map_fn)
70}
71
72pub(super) fn parse_output_single(mut text: String, suggestion_type: SuggestionType) -> Result<String> {
73 Ok(match suggestion_type {
74 SuggestionType::SingleSelection => text
75 .lines()
76 .next()
77 .context("No sufficient data for single selection")?
78 .to_string(),
79 SuggestionType::MultipleSelections | SuggestionType::Disabled | SuggestionType::SnippetSelection => {
80 let len = text.len();
81 if len > 1 {
82 text.truncate(len - 1);
83 }
84 text
85 }
86 SuggestionType::SingleRecommendation => {
87 let lines: Vec<&str> = text.lines().collect();
88
89 match (lines.first(), lines.get(1), lines.get(2)) {
90 (Some(one), Some(termination), Some(two))
91 if *termination == "enter" || termination.is_empty() =>
92 {
93 if two.is_empty() {
94 (*one).to_string()
95 } else {
96 (*two).to_string()
97 }
98 }
99 (Some(one), Some(termination), None) if *termination == "enter" || termination.is_empty() => {
100 (*one).to_string()
101 }
102 (Some(one), Some(termination), _) if *termination == "tab" => (*one).to_string(),
103 _ => "".to_string(),
104 }
105 }
106 })
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_parse_output1() {
115 let text = "palo\n".to_string();
116 let output = parse_output_single(text, SuggestionType::SingleSelection).unwrap();
117 assert_eq!(output, "palo");
118 }
119
120 #[test]
121 fn test_parse_output2() {
122 let text = "\nenter\npalo".to_string();
123 let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
124 assert_eq!(output, "palo");
125 }
126
127 #[test]
128 fn test_parse_recommendation_output_1() {
129 let text = "\nenter\npalo".to_string();
130 let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
131 assert_eq!(output, "palo");
132 }
133
134 #[test]
135 fn test_parse_recommendation_output_2() {
136 let text = "p\nenter\npalo".to_string();
137 let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
138 assert_eq!(output, "palo");
139 }
140
141 #[test]
142 fn test_parse_recommendation_output_3() {
143 let text = "peter\nenter\n".to_string();
144 let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
145 assert_eq!(output, "peter");
146 }
147
148 #[test]
149 fn test_parse_output3() {
150 let text = "p\ntab\npalo".to_string();
151 let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
152 assert_eq!(output, "p");
153 }
154
155 #[test]
156 fn test_parse_snippet_request() {
157 let text = "enter\nssh ⠀login to a server and forward to ssh key (d… ⠀ssh -A <user>@<server> ⠀ssh ⠀login to a server and forward to ssh key (dangerous but useful for bastion hosts) ⠀ssh -A <user>@<server> ⠀\n".to_string();
158 let output = parse_output_single(text, SuggestionType::SnippetSelection).unwrap();
159 assert_eq!(output, "enter\nssh ⠀login to a server and forward to ssh key (d… ⠀ssh -A <user>@<server> ⠀ssh ⠀login to a server and forward to ssh key (dangerous but useful for bastion hosts) ⠀ssh -A <user>@<server> ⠀");
160 }
161}