demo/
demo.rs

1// This is the same example also used in the README.md. When updating this, don't forget updating
2// the README.md as well. This is mainly used to test the code and generate the output shown.
3
4use core::fmt;
5
6use error_stack::{Context, Report, Result, ResultExt};
7
8#[derive(Debug)]
9struct ParseExperimentError;
10
11impl fmt::Display for ParseExperimentError {
12    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
13        fmt.write_str("invalid experiment description")
14    }
15}
16
17impl Context for ParseExperimentError {}
18
19// Reason: false-positive, try_fold is fail-fast, our implementation is fail-slow.
20#[expect(clippy::manual_try_fold)]
21fn parse_experiment(description: &str) -> Result<Vec<(u64, u64)>, ParseExperimentError> {
22    let values = description
23        .split(' ')
24        .map(|value| {
25            value
26                .parse::<u64>()
27                .attach_printable_lazy(|| format!("{value:?} could not be parsed as experiment"))
28        })
29        .map(|value| value.map(|ok| (ok, 2 * ok)))
30        .fold(Ok(vec![]), |accum, value| match (accum, value) {
31            (Ok(mut accum), Ok(value)) => {
32                accum.push(value);
33
34                Ok(accum)
35            }
36            (Ok(_), Err(err)) => Err(err),
37            (Err(accum), Ok(_)) => Err(accum),
38            (Err(mut accum), Err(err)) => {
39                accum.extend_one(err);
40
41                Err(accum)
42            }
43        })
44        .change_context(ParseExperimentError)?;
45
46    Ok(values)
47}
48
49#[derive(Debug)]
50struct ExperimentError;
51
52impl fmt::Display for ExperimentError {
53    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
54        fmt.write_str("experiment error: could not run experiment")
55    }
56}
57
58impl Context for ExperimentError {}
59
60#[expect(
61    clippy::manual_try_fold,
62    reason = "false-positive, try_fold is fail-fast, our implementation is fail-slow"
63)]
64fn start_experiments(
65    experiment_ids: &[usize],
66    experiment_descriptions: &[&str],
67) -> Result<Vec<u64>, ExperimentError> {
68    let experiments = experiment_ids
69        .iter()
70        .map(|exp_id| {
71            let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
72                Report::new(ExperimentError)
73                    .attach_printable(format!("experiment {exp_id} has no valid description"))
74            })?;
75
76            let experiments = parse_experiment(description)
77                .attach_printable(format!("experiment {exp_id} could not be parsed"))
78                .change_context(ExperimentError)?;
79
80            let experiments = experiments
81                .into_iter()
82                .map(|(a, b)| move || a * b)
83                .collect::<Vec<_>>();
84
85            Ok(experiments)
86        })
87        .fold(
88            Ok(vec![]),
89            |accum: Result<_, ExperimentError>, value| match (accum, value) {
90                (Ok(mut accum), Ok(value)) => {
91                    accum.extend(value);
92
93                    Ok(accum)
94                }
95                (Ok(_), Err(err)) => Err(err),
96                (Err(accum), Ok(_)) => Err(accum),
97                (Err(mut accum), Err(err)) => {
98                    accum.extend_one(err);
99
100                    Err(accum)
101                }
102            },
103        )
104        .attach_printable("unable to set up experiments")?;
105
106    Ok(experiments.iter().map(|experiment| experiment()).collect())
107}
108
109fn main() -> Result<(), ExperimentError> {
110    let experiment_ids = &[0, 2, 3];
111    let experiment_descriptions = &["10", "20", "3o 4a"];
112    start_experiments(experiment_ids, experiment_descriptions)?;
113
114    Ok(())
115}