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