1use thiserror::Error;
2
3use crate::geometry::Geometry;
4
5use super::{Datagram, DatagramOption};
6
7#[derive(Debug, PartialEq)]
8#[doc(hidden)]
9pub struct CombinedOperationGenerator<O1, O2> {
10 pub o1: O1,
11 pub o2: O2,
12}
13
14#[derive(Error, Debug, PartialEq)]
15#[doc(hidden)]
16pub enum CombinedError<E1, E2> {
17 #[error("{0}")]
18 E1(E1),
19 #[error("{0}")]
20 E2(E2),
21}
22
23impl<G1, G2, D1, D2, E1, E2> Datagram for (D1, D2)
24where
25 D1: Datagram<G = G1, Error = E1>,
26 D2: Datagram<G = G2, Error = E2>,
27{
28 type G = CombinedOperationGenerator<D1::G, D2::G>;
29 type Error = CombinedError<E1, E2>;
30
31 fn operation_generator(
32 self,
33 geometry: &Geometry,
34 parallel: bool,
35 ) -> Result<Self::G, Self::Error> {
36 match (
37 self.0.operation_generator(geometry, parallel),
38 self.1.operation_generator(geometry, parallel),
39 ) {
40 (Ok(g1), Ok(g2)) => Ok(CombinedOperationGenerator { o1: g1, o2: g2 }),
41 (Err(e1), _) => Err(Self::Error::E1(e1)),
42 (_, Err(e2)) => Err(Self::Error::E2(e2)),
43 }
44 }
45
46 fn option(&self) -> DatagramOption {
47 DatagramOption {
48 timeout: self.0.option().timeout.max(self.1.option().timeout),
49 parallel_threshold: self
50 .0
51 .option()
52 .parallel_threshold
53 .min(self.1.option().parallel_threshold),
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 use std::time::Duration;
63
64 #[derive(Debug)]
65 pub struct TestDatagram {
66 pub option: DatagramOption,
67 pub result: Result<(), ()>,
68 }
69
70 impl Datagram for TestDatagram {
71 type G = ();
72 type Error = ();
73
74 fn operation_generator(self, _: &Geometry, _: bool) -> Result<Self::G, Self::Error> {
75 self.result
76 }
77
78 fn option(&self) -> DatagramOption {
79 self.option
80 }
81 }
82
83 #[rstest::rstest]
84 #[case(Ok(CombinedOperationGenerator { o1: (), o2: () }), Ok(()), Ok(()))]
85 #[case(Err(CombinedError::E1(())), Err(()), Ok(()))]
86 #[case(Err(CombinedError::E2(())), Ok(()), Err(()))]
87 #[test]
88 fn operation_generator(
89 #[case] expect: Result<CombinedOperationGenerator<(), ()>, CombinedError<(), ()>>,
90 #[case] result1: Result<(), ()>,
91 #[case] result2: Result<(), ()>,
92 ) {
93 assert_eq!(
94 expect,
95 (
96 TestDatagram {
97 option: DatagramOption::default(),
98 result: result1,
99 },
100 TestDatagram {
101 option: DatagramOption::default(),
102 result: result2,
103 }
104 )
105 .operation_generator(&Geometry::new(Default::default()), false)
106 );
107 }
108
109 #[rstest::rstest]
110 #[case(
111 Duration::from_millis(200),
112 Duration::from_millis(100),
113 Duration::from_millis(200)
114 )]
115 #[case(
116 Duration::from_millis(200),
117 Duration::from_millis(200),
118 Duration::from_millis(100)
119 )]
120 #[test]
121 fn timeout(#[case] expect: Duration, #[case] timeout1: Duration, #[case] timeout2: Duration) {
122 assert_eq!(
123 expect,
124 (
125 TestDatagram {
126 option: DatagramOption {
127 timeout: timeout1,
128 parallel_threshold: 0,
129 },
130 result: Ok(()),
131 },
132 TestDatagram {
133 option: DatagramOption {
134 timeout: timeout2,
135 parallel_threshold: 0,
136 },
137 result: Ok(()),
138 }
139 )
140 .option()
141 .timeout
142 );
143 }
144
145 #[rstest::rstest]
146 #[case(100, 100, 200)]
147 #[case(100, 200, 100)]
148 #[test]
149 fn parallel_threshold(
150 #[case] expect: usize,
151 #[case] threshold1: usize,
152 #[case] threshold2: usize,
153 ) {
154 assert_eq!(
155 expect,
156 (
157 TestDatagram {
158 option: DatagramOption {
159 timeout: Duration::ZERO,
160 parallel_threshold: threshold1,
161 },
162 result: Ok(()),
163 },
164 TestDatagram {
165 option: DatagramOption {
166 timeout: Duration::ZERO,
167 parallel_threshold: threshold2,
168 },
169 result: Ok(()),
170 }
171 )
172 .option()
173 .parallel_threshold
174 );
175 }
176}