1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum TestScenario {
12 W1ClearDimensionWinner,
15 W2SameDimensionsDifferentSize,
17 W3SameDimensionsSameSize,
19 W4SomeMissingDimensions,
21 W5OnlyOneHasDimensions,
23 W6AllMissingDimensions,
25 W7ThreePlusDuplicates,
27 W8SamePixelsDifferentAspect,
29
30 C1WinnerLacksGpsLoserHas,
33 C2WinnerLacksDatetimeLoserHas,
35 C3WinnerLacksDescriptionLoserHas,
37 C4WinnerLacksAllLoserHasAll,
39 C5BothHaveGps,
41 C6MultipleLosersContribute,
43 C7NoLoserHasNeeded,
45 C8WinnerHasEverything,
47
48 F1GpsConflict,
51 F2GpsWithinThreshold,
53 F3TimezoneConflict,
55 F4CameraConflict,
57 F5CaptureTimeConflict,
59 F6MultipleConflicts,
61 F7NoConflicts,
63
64 X1SingleAssetGroup,
67 X2LargeGroup,
69 X3LargeFile,
71 X4SpecialCharsFilename,
73 X5Video,
75 X7Png,
77 X9UnicodeDescription,
79 X10VeryOldDate,
81 X11FutureDate,
83}
84
85impl TestScenario {
86 pub fn all() -> Vec<TestScenario> {
88 vec![
89 Self::W1ClearDimensionWinner,
91 Self::W2SameDimensionsDifferentSize,
92 Self::W3SameDimensionsSameSize,
93 Self::W4SomeMissingDimensions,
94 Self::W5OnlyOneHasDimensions,
95 Self::W6AllMissingDimensions,
96 Self::W7ThreePlusDuplicates,
97 Self::W8SamePixelsDifferentAspect,
98 Self::C1WinnerLacksGpsLoserHas,
100 Self::C2WinnerLacksDatetimeLoserHas,
101 Self::C3WinnerLacksDescriptionLoserHas,
102 Self::C4WinnerLacksAllLoserHasAll,
103 Self::C5BothHaveGps,
104 Self::C6MultipleLosersContribute,
105 Self::C7NoLoserHasNeeded,
106 Self::C8WinnerHasEverything,
107 Self::F1GpsConflict,
109 Self::F2GpsWithinThreshold,
110 Self::F3TimezoneConflict,
111 Self::F4CameraConflict,
112 Self::F5CaptureTimeConflict,
113 Self::F6MultipleConflicts,
114 Self::F7NoConflicts,
115 Self::X1SingleAssetGroup,
117 Self::X2LargeGroup,
118 Self::X3LargeFile,
119 Self::X4SpecialCharsFilename,
120 Self::X5Video,
121 Self::X7Png,
122 Self::X9UnicodeDescription,
123 Self::X10VeryOldDate,
124 Self::X11FutureDate,
125 ]
126 }
127
128 pub fn code(&self) -> &'static str {
130 match self {
131 Self::W1ClearDimensionWinner => "w1",
132 Self::W2SameDimensionsDifferentSize => "w2",
133 Self::W3SameDimensionsSameSize => "w3",
134 Self::W4SomeMissingDimensions => "w4",
135 Self::W5OnlyOneHasDimensions => "w5",
136 Self::W6AllMissingDimensions => "w6",
137 Self::W7ThreePlusDuplicates => "w7",
138 Self::W8SamePixelsDifferentAspect => "w8",
139 Self::C1WinnerLacksGpsLoserHas => "c1",
140 Self::C2WinnerLacksDatetimeLoserHas => "c2",
141 Self::C3WinnerLacksDescriptionLoserHas => "c3",
142 Self::C4WinnerLacksAllLoserHasAll => "c4",
143 Self::C5BothHaveGps => "c5",
144 Self::C6MultipleLosersContribute => "c6",
145 Self::C7NoLoserHasNeeded => "c7",
146 Self::C8WinnerHasEverything => "c8",
147 Self::F1GpsConflict => "f1",
148 Self::F2GpsWithinThreshold => "f2",
149 Self::F3TimezoneConflict => "f3",
150 Self::F4CameraConflict => "f4",
151 Self::F5CaptureTimeConflict => "f5",
152 Self::F6MultipleConflicts => "f6",
153 Self::F7NoConflicts => "f7",
154 Self::X1SingleAssetGroup => "x1",
155 Self::X2LargeGroup => "x2",
156 Self::X3LargeFile => "x3",
157 Self::X4SpecialCharsFilename => "x4",
158 Self::X5Video => "x5",
159 Self::X7Png => "x7",
160 Self::X9UnicodeDescription => "x9",
161 Self::X10VeryOldDate => "x10",
162 Self::X11FutureDate => "x11",
163 }
164 }
165
166 pub fn category(&self) -> &'static str {
168 match self {
169 Self::W1ClearDimensionWinner
170 | Self::W2SameDimensionsDifferentSize
171 | Self::W3SameDimensionsSameSize
172 | Self::W4SomeMissingDimensions
173 | Self::W5OnlyOneHasDimensions
174 | Self::W6AllMissingDimensions
175 | Self::W7ThreePlusDuplicates
176 | Self::W8SamePixelsDifferentAspect => "Winner Selection",
177 Self::C1WinnerLacksGpsLoserHas
178 | Self::C2WinnerLacksDatetimeLoserHas
179 | Self::C3WinnerLacksDescriptionLoserHas
180 | Self::C4WinnerLacksAllLoserHasAll
181 | Self::C5BothHaveGps
182 | Self::C6MultipleLosersContribute
183 | Self::C7NoLoserHasNeeded
184 | Self::C8WinnerHasEverything => "Consolidation",
185 Self::F1GpsConflict
186 | Self::F2GpsWithinThreshold
187 | Self::F3TimezoneConflict
188 | Self::F4CameraConflict
189 | Self::F5CaptureTimeConflict
190 | Self::F6MultipleConflicts
191 | Self::F7NoConflicts => "Conflicts",
192 Self::X1SingleAssetGroup
193 | Self::X2LargeGroup
194 | Self::X3LargeFile
195 | Self::X4SpecialCharsFilename
196 | Self::X5Video
197 | Self::X7Png
198 | Self::X9UnicodeDescription
199 | Self::X10VeryOldDate
200 | Self::X11FutureDate => "Edge Cases",
201 }
202 }
203}
204
205impl fmt::Display for TestScenario {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 let name = match self {
208 Self::W1ClearDimensionWinner => "W1: Clear dimension winner",
209 Self::W2SameDimensionsDifferentSize => "W2: Same dimensions, different size",
210 Self::W3SameDimensionsSameSize => "W3: Same dimensions, same size",
211 Self::W4SomeMissingDimensions => "W4: Some missing dimensions",
212 Self::W5OnlyOneHasDimensions => "W5: Only one has dimensions",
213 Self::W6AllMissingDimensions => "W6: All missing dimensions",
214 Self::W7ThreePlusDuplicates => "W7: 3+ duplicates",
215 Self::W8SamePixelsDifferentAspect => "W8: Same pixels, different aspect",
216 Self::C1WinnerLacksGpsLoserHas => "C1: Winner lacks GPS, loser has",
217 Self::C2WinnerLacksDatetimeLoserHas => "C2: Winner lacks datetime, loser has",
218 Self::C3WinnerLacksDescriptionLoserHas => "C3: Winner lacks description, loser has",
219 Self::C4WinnerLacksAllLoserHasAll => "C4: Winner lacks all, loser has all",
220 Self::C5BothHaveGps => "C5: Both have GPS",
221 Self::C6MultipleLosersContribute => "C6: Multiple losers contribute",
222 Self::C7NoLoserHasNeeded => "C7: No loser has needed",
223 Self::C8WinnerHasEverything => "C8: Winner has everything",
224 Self::F1GpsConflict => "F1: GPS conflict",
225 Self::F2GpsWithinThreshold => "F2: GPS within threshold",
226 Self::F3TimezoneConflict => "F3: Timezone conflict",
227 Self::F4CameraConflict => "F4: Camera conflict",
228 Self::F5CaptureTimeConflict => "F5: Capture time conflict",
229 Self::F6MultipleConflicts => "F6: Multiple conflicts",
230 Self::F7NoConflicts => "F7: No conflicts",
231 Self::X1SingleAssetGroup => "X1: Single asset group",
232 Self::X2LargeGroup => "X2: Large group (10+)",
233 Self::X3LargeFile => "X3: Large file (>50MB)",
234 Self::X4SpecialCharsFilename => "X4: Special chars in filename",
235 Self::X5Video => "X5: Video",
236 Self::X7Png => "X7: PNG",
237 Self::X9UnicodeDescription => "X9: Unicode description",
238 Self::X10VeryOldDate => "X10: Very old date (<1990)",
239 Self::X11FutureDate => "X11: Future date",
240 };
241 write!(f, "{}", name)
242 }
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct ScenarioMatch {
248 pub scenario: TestScenario,
250 pub duplicate_id: String,
252 pub details: String,
254}