conch_runtime_pshaw/spawn/
case.rs

1use crate::env::{LastStatusEnvironment, ReportErrorEnvironment, StringWrapper};
2use crate::error::IsFatalError;
3use crate::eval::{eval_as_pattern, TildeExpansion, WordEval, WordEvalConfig};
4use crate::spawn::ExitStatus;
5use crate::{Spawn, EXIT_ERROR, EXIT_SUCCESS};
6use futures_core::future::BoxFuture;
7use glob::MatchOptions;
8
9/// A grouping of patterns and body commands.
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct PatternBodyPair<W, C> {
12    /// Pattern alternatives to match against.
13    pub patterns: W,
14    /// The body commands to execute if the pattern matches.
15    pub body: C,
16}
17
18/// Spawns a `case` commands from a word to match number of case arms.
19///
20/// First the provided `word` will be evaluated and compared to each
21/// pattern of each case arm. The first arm which contains a pattern that
22/// matches the `word` will have its (and only its) body evaluated.
23///
24/// If no arms are matched, the `case` command will exit successfully.
25pub async fn case<'a, I, W, P, S, E>(
26    word: W,
27    arms: I,
28    env: &mut E,
29) -> Result<BoxFuture<'static, ExitStatus>, S::Error>
30where
31    I: Iterator<Item = PatternBodyPair<&'a [P], S>>,
32    W: WordEval<E>,
33    P: 'a + WordEval<E>,
34    P::Error: IsFatalError,
35    S: Spawn<E>,
36    S::Error: From<W::Error> + From<P::Error>,
37    E: ?Sized + LastStatusEnvironment + ReportErrorEnvironment,
38{
39    let cfg = WordEvalConfig {
40        tilde_expansion: TildeExpansion::First,
41        split_fields_further: false,
42    };
43
44    let match_opts = MatchOptions {
45        case_sensitive: true,
46        require_literal_separator: false,
47        require_literal_leading_dot: false,
48    };
49
50    let word = match word.eval_with_config(env, cfg).await {
51        Ok(w) => w.await.join().into_owned(),
52        Err(e) => {
53            env.set_last_status(EXIT_ERROR);
54            return Err(S::Error::from(e));
55        }
56    };
57
58    for arm in arms {
59        for pat in arm.patterns {
60            let pat = match eval_as_pattern(pat, env).await {
61                Ok(pat) => pat,
62                Err(e) => {
63                    if e.is_fatal() {
64                        return Err(S::Error::from(e));
65                    } else {
66                        env.report_error(&e).await;
67                        continue;
68                    }
69                }
70            };
71
72            if pat.matches_with(&word, match_opts) {
73                return arm.body.spawn(env).await;
74            }
75        }
76    }
77
78    Ok(Box::pin(async { EXIT_SUCCESS }))
79}