1use std::{
4 borrow::Cow,
5 cmp::{self, Ordering},
6 fmt::{self, Display},
7};
8
9use sway_types::{SourceEngine, SourceId};
10
11pub fn get_file_name(source_engine: &SourceEngine, source_id: Option<&SourceId>) -> Option<String> {
15 match source_id {
16 Some(source_id) => source_engine.get_file_name(source_id),
17 None => None,
18 }
19}
20
21pub fn number_to_str(number: usize) -> String {
24 match number {
25 0 => "zero".to_string(),
26 1 => "one".to_string(),
27 2 => "two".to_string(),
28 3 => "three".to_string(),
29 4 => "four".to_string(),
30 5 => "five".to_string(),
31 6 => "six".to_string(),
32 7 => "seven".to_string(),
33 8 => "eight".to_string(),
34 9 => "nine".to_string(),
35 10 => "ten".to_string(),
36 _ => format!("{number}"),
37 }
38}
39
40pub enum Enclosing {
41 #[allow(dead_code)]
42 None,
43 DoubleQuote,
44}
45
46impl Display for Enclosing {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 write!(
49 f,
50 "{}",
51 match self {
52 Self::None => "",
53 Self::DoubleQuote => "\"",
54 },
55 )
56 }
57}
58
59pub enum Indent {
60 #[allow(dead_code)]
61 None,
62 Single,
63 Double,
64}
65
66impl Display for Indent {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(
69 f,
70 "{}",
71 match self {
72 Self::None => "",
73 Self::Single => " ",
74 Self::Double => " ",
75 },
76 )
77 }
78}
79
80pub fn sequence_to_str<T>(sequence: &[T], enclosing: Enclosing, max_items: usize) -> String
94where
95 T: Display,
96{
97 sequence_to_str_impl(sequence, enclosing, max_items, "and")
98}
99
100pub fn sequence_to_str_or<T>(sequence: &[T], enclosing: Enclosing, max_items: usize) -> String
114where
115 T: Display,
116{
117 sequence_to_str_impl(sequence, enclosing, max_items, "or")
118}
119
120fn sequence_to_str_impl<T>(
121 sequence: &[T],
122 enclosing: Enclosing,
123 max_items: usize,
124 and_or: &str,
125) -> String
126where
127 T: Display,
128{
129 assert!(
130 !sequence.is_empty(),
131 "Sequence to display must not be empty."
132 );
133 assert!(
134 max_items > 0,
135 "Maximum number of items to display must be greater than zero."
136 );
137
138 let max_items = cmp::min(max_items, sequence.len());
139
140 let (to_display, remaining) = sequence.split_at(max_items);
141
142 let fmt_item = |item: &T| format!("{enclosing}{item}{enclosing}");
143
144 if !remaining.is_empty() {
145 format!(
146 "{}, {} {} more",
147 to_display
148 .iter()
149 .map(fmt_item)
150 .collect::<Vec<_>>()
151 .join(", "),
152 and_or,
153 number_to_str(remaining.len())
154 )
155 } else {
156 match to_display {
157 [] => unreachable!("There must be at least one item in the sequence."),
158 [item] => fmt_item(item),
159 [first_item, second_item] => {
160 format!(
161 "{} {} {}",
162 fmt_item(first_item),
163 and_or,
164 fmt_item(second_item)
165 )
166 }
167 _ => format!(
168 "{}, {} {}",
169 to_display
170 .split_last()
171 .unwrap()
172 .1
173 .iter()
174 .map(fmt_item)
175 .collect::<Vec::<_>>()
176 .join(", "),
177 and_or,
178 fmt_item(to_display.last().unwrap())
179 ),
180 }
181 }
182}
183
184pub fn sequence_to_list<T>(sequence: &[T], indent: Indent, max_items: usize) -> Vec<String>
202where
203 T: Display,
204{
205 assert!(
206 !sequence.is_empty(),
207 "Sequence to display must not be empty."
208 );
209 assert!(
210 max_items > 0,
211 "Maximum number of items to display must be greater than zero."
212 );
213
214 let mut result = vec![];
215
216 let max_items = cmp::min(max_items, sequence.len());
217 let (to_display, remaining) = sequence.split_at(max_items);
218 for item in to_display {
219 result.push(format!("{indent}- {item}"));
220 }
221 if !remaining.is_empty() {
222 result.push(format!(
223 "{indent}- and {} more",
224 number_to_str(remaining.len())
225 ));
226 }
227
228 result
229}
230
231pub fn plural_s(count: usize) -> &'static str {
234 if count == 1 {
235 ""
236 } else {
237 "s"
238 }
239}
240
241pub fn is_are(count: usize) -> &'static str {
243 if count == 1 {
244 "is"
245 } else {
246 "are"
247 }
248}
249
250pub fn singular_plural<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str {
252 if count == 1 {
253 singular
254 } else {
255 plural
256 }
257}
258
259pub fn call_path_suffix_with_args(call_path: &String) -> Cow<String> {
270 match call_path.rfind(':') {
271 Some(index) if index < call_path.len() - 1 => {
272 Cow::Owned(call_path.split_at(index + 1).1.to_string())
273 }
274 _ => Cow::Borrowed(call_path),
275 }
276}
277
278pub fn a_or_an(word: &'static str) -> &'static str {
286 let is_a = in_definite::is_an(word);
287 match is_a {
288 in_definite::Is::An => "an ",
289 in_definite::Is::A => "a ",
290 in_definite::Is::None => "",
291 }
292}
293
294pub fn ascii_sentence_case(text: &String) -> Cow<String> {
296 if text.is_empty() || text.chars().next().unwrap().is_uppercase() {
297 Cow::Borrowed(text)
298 } else {
299 let mut result = text.clone();
300 result[0..1].make_ascii_uppercase();
301 Cow::Owned(result.to_owned())
302 }
303}
304
305pub fn first_line(text: &str, with_ellipses: bool) -> Cow<str> {
321 if !text.contains('\n') {
322 Cow::Borrowed(text)
323 } else {
324 let index_of_new_line = text.find('\n').unwrap();
325 Cow::Owned(text[..index_of_new_line].to_string() + if with_ellipses { "..." } else { "" })
326 }
327}
328
329pub fn did_you_mean<T, I>(v: &str, possible_values: I, max_num_of_suggestions: usize) -> Vec<String>
336where
337 T: AsRef<str>,
338 I: IntoIterator<Item = T>,
339{
340 let mut candidates: Vec<_> = possible_values
341 .into_iter()
342 .map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
343 .filter(|(confidence, _)| *confidence > 0.7)
345 .collect();
346 candidates.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
347 candidates
348 .into_iter()
349 .take(max_num_of_suggestions)
350 .map(|(_, pv)| pv)
351 .collect()
352}