1use 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#[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}