1use std::fmt::Display;
2
3use itertools::Itertools as _;
4
5use crate::{UpError, UpResult, YesAnd};
6
7pub fn yes_and<T>(yes: T, and: &str) -> UpResult<T> {
8 Ok(YesAnd {
9 yes,
10 and,
11 could_also: vec![],
12 })
13}
14
15pub fn yes_and_also<T, AlsoT: Display>(
16 yes: T,
17 and: &str,
18 also: impl IntoIterator<Item = AlsoT>,
19) -> UpResult<T> {
20 Ok(YesAnd {
21 yes,
22 and,
23 could_also: also.into_iter().map(|a| a.to_string()).collect(),
24 })
25}
26
27pub fn go_on<'any, AnyT, GoOnT: Display>(
28 suggestions: impl IntoIterator<Item = GoOnT>,
29) -> UpResult<'any, AnyT> {
30 Err(UpError::GoOn {
31 go_on: suggestions.into_iter().map(|g| g.to_string()).collect(),
32 })
33}
34
35pub fn oops<'any, AnyT, MessageT: Display>(message: MessageT) -> UpResult<'any, AnyT> {
36 Err(UpError::Oops {
37 message: message.to_string(),
38 })
39}
40
41macro_rules! oops {
42 ($tt:tt) => {
43 Err(UpError::Oops {
44 message: format!($tt),
45 })
46 };
47}
48
49pub fn strip_matching_prefixes<'haystack>(
61 needle: &str,
62 haystack: impl IntoIterator<Item = &'haystack str>,
63) -> Vec<String> {
64 haystack
65 .into_iter()
66 .filter_map(|hay| {
67 hay.strip_prefix(needle).and_then(|s| match s {
68 "" => None,
69 _ => Some(s),
70 })
71 })
72 .map(String::from)
73 .collect()
74}
75
76pub fn chars_needed_to_complete<'needle>(
87 needle: &'needle str,
88 haystack: &str,
89) -> Option<&'needle str> {
90 use itertools::EitherOrBoth::{Both, Left, Right};
91 for chars in needle.char_indices().zip_longest(haystack.chars()) {
92 match chars {
93 Both((_, l), r) if l == r => continue, Both(..) => return None, Left((ix, _)) => return Some(&needle[ix..]),
96 Right(_) => return None,
97 }
98 }
99 Some("") }
101
102#[test]
103fn test_chars_needed_to_complete() {
104 assert_eq!(chars_needed_to_complete("tag", "ta"), Some("g"));
105 assert_eq!(chars_needed_to_complete("foo", ""), Some("foo"));
106 assert_eq!(chars_needed_to_complete("foo", "bar"), None);
107 assert_eq!(chars_needed_to_complete("foo", "food"), None);
108}