1use std::{fmt, str::FromStr};
2
3use crate::media_events::MediaEvent;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
8pub enum ScanSseEventType {
9 Started,
10 Progress,
11 Quiescing,
12 Completed,
13 Failed,
14}
15
16impl ScanSseEventType {
17 pub const fn event_name(self) -> &'static str {
18 match self {
19 Self::Started => "scan.started",
20 Self::Progress => "scan.progress",
21 Self::Quiescing => "scan.quiescing",
22 Self::Completed => "scan.completed",
23 Self::Failed => "scan.failed",
24 }
25 }
26}
27
28impl fmt::Display for ScanSseEventType {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.write_str(self.event_name())
31 }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct ParseScanSseEventTypeError {
36 invalid_value: String,
37}
38
39impl ParseScanSseEventTypeError {
40 pub fn new(value: &str) -> Self {
41 Self {
42 invalid_value: value.to_string(),
43 }
44 }
45}
46
47impl fmt::Display for ParseScanSseEventTypeError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "invalid scan SSE event type: {}", self.invalid_value)
50 }
51}
52
53impl std::error::Error for ParseScanSseEventTypeError {}
54
55impl FromStr for ScanSseEventType {
56 type Err = ParseScanSseEventTypeError;
57
58 fn from_str(value: &str) -> Result<Self, Self::Err> {
59 match value {
60 "scan.started" => Ok(Self::Started),
61 "scan.progress" => Ok(Self::Progress),
62 "scan.quiescing" => Ok(Self::Quiescing),
63 "scan.completed" => Ok(Self::Completed),
64 "scan.failed" => Ok(Self::Failed),
65 other => Err(ParseScanSseEventTypeError::new(other)),
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
73pub enum MediaSseEventType {
74 MovieAdded,
75 MovieBatchFinalized,
76 SeriesAdded,
77 SeriesBundleFinalized,
78 SeasonAdded,
79 EpisodeAdded,
80 MovieUpdated,
81 SeriesUpdated,
82 SeasonUpdated,
83 EpisodeUpdated,
84 MediaDeleted,
85 Scan(ScanSseEventType),
86}
87
88impl MediaSseEventType {
89 pub const fn event_name(self) -> &'static str {
90 match self {
91 Self::MovieAdded => "media.movie_added",
92 Self::MovieBatchFinalized => "media.movie_batch_finalized",
93 Self::SeriesAdded => "media.series_added",
94 Self::SeriesBundleFinalized => "media.series_bundle_finalized",
95 Self::SeasonAdded => "media.season_added",
96 Self::EpisodeAdded => "media.episode_added",
97 Self::MovieUpdated => "media.movie_updated",
98 Self::SeriesUpdated => "media.series_updated",
99 Self::SeasonUpdated => "media.season_updated",
100 Self::EpisodeUpdated => "media.episode_updated",
101 Self::MediaDeleted => "media.deleted",
102 Self::Scan(kind) => kind.event_name(),
103 }
104 }
105}
106
107impl fmt::Display for MediaSseEventType {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 f.write_str(self.event_name())
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct ParseMediaSseEventTypeError {
115 invalid_value: String,
116}
117
118impl ParseMediaSseEventTypeError {
119 pub fn new(value: &str) -> Self {
120 Self {
121 invalid_value: value.to_string(),
122 }
123 }
124}
125
126impl fmt::Display for ParseMediaSseEventTypeError {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "invalid media SSE event type: {}", self.invalid_value)
129 }
130}
131
132impl std::error::Error for ParseMediaSseEventTypeError {}
133
134impl FromStr for MediaSseEventType {
135 type Err = ParseMediaSseEventTypeError;
136
137 fn from_str(value: &str) -> Result<Self, Self::Err> {
138 match value {
139 "media.movie_added" => Ok(Self::MovieAdded),
140 "media.movie_batch_finalized" => Ok(Self::MovieBatchFinalized),
141 "media.series_added" => Ok(Self::SeriesAdded),
142 "media.series_bundle_finalized" => Ok(Self::SeriesBundleFinalized),
143 "media.season_added" => Ok(Self::SeasonAdded),
144 "media.episode_added" => Ok(Self::EpisodeAdded),
145 "media.movie_updated" => Ok(Self::MovieUpdated),
146 "media.series_updated" => Ok(Self::SeriesUpdated),
147 "media.season_updated" => Ok(Self::SeasonUpdated),
148 "media.episode_updated" => Ok(Self::EpisodeUpdated),
149 "media.deleted" => Ok(Self::MediaDeleted),
150 other => match ScanSseEventType::from_str(other) {
151 Ok(kind) => Ok(Self::Scan(kind)),
152 Err(_) => Err(ParseMediaSseEventTypeError::new(other)),
153 },
154 }
155 }
156}
157
158impl MediaEvent {
159 pub fn sse_event_type(&self) -> MediaSseEventType {
160 match self {
161 MediaEvent::MovieAdded { .. } => MediaSseEventType::MovieAdded,
162 MediaEvent::MovieBatchFinalized { .. } => {
163 MediaSseEventType::MovieBatchFinalized
164 }
165 MediaEvent::SeriesBundleFinalized { .. } => {
166 MediaSseEventType::SeriesBundleFinalized
167 }
168 MediaEvent::SeriesAdded { .. } => MediaSseEventType::SeriesAdded,
169 MediaEvent::MovieUpdated { .. } => MediaSseEventType::MovieUpdated,
170 MediaEvent::SeriesUpdated { .. } => {
171 MediaSseEventType::SeriesUpdated
172 }
173 MediaEvent::MediaDeleted { .. } => MediaSseEventType::MediaDeleted,
174 MediaEvent::ScanStarted { .. } => {
175 MediaSseEventType::Scan(ScanSseEventType::Started)
176 }
177 MediaEvent::ScanProgress { .. } => {
178 MediaSseEventType::Scan(ScanSseEventType::Progress)
179 }
180 MediaEvent::ScanCompleted { .. } => {
181 MediaSseEventType::Scan(ScanSseEventType::Completed)
182 }
183 MediaEvent::ScanFailed { .. } => {
184 MediaSseEventType::Scan(ScanSseEventType::Failed)
185 }
186 }
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
193pub enum ImageSseEventType {
194 Ready,
195}
196
197impl ImageSseEventType {
198 pub const fn event_name(self) -> &'static str {
199 match self {
200 Self::Ready => "image.ready",
201 }
202 }
203}
204
205impl fmt::Display for ImageSseEventType {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 f.write_str(self.event_name())
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct ParseImageSseEventTypeError {
213 invalid_value: String,
214}
215
216impl ParseImageSseEventTypeError {
217 pub fn new(value: &str) -> Self {
218 Self {
219 invalid_value: value.to_string(),
220 }
221 }
222}
223
224impl fmt::Display for ParseImageSseEventTypeError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(f, "invalid image SSE event type: {}", self.invalid_value)
227 }
228}
229
230impl std::error::Error for ParseImageSseEventTypeError {}
231
232impl FromStr for ImageSseEventType {
233 type Err = ParseImageSseEventTypeError;
234
235 fn from_str(value: &str) -> Result<Self, Self::Err> {
236 match value {
237 "image.ready" => Ok(Self::Ready),
238 other => Err(ParseImageSseEventTypeError::new(other)),
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::{ImageSseEventType, MediaSseEventType, ScanSseEventType};
246 use std::str::FromStr;
247
248 #[test]
249 fn scan_event_name_roundtrip() {
250 for (name, value) in [
251 ("scan.started", ScanSseEventType::Started),
252 ("scan.progress", ScanSseEventType::Progress),
253 ("scan.quiescing", ScanSseEventType::Quiescing),
254 ("scan.completed", ScanSseEventType::Completed),
255 ("scan.failed", ScanSseEventType::Failed),
256 ] {
257 assert_eq!(value.event_name(), name);
258 assert_eq!(ScanSseEventType::from_str(name).unwrap(), value);
259 }
260 }
261
262 #[test]
263 fn media_event_name_roundtrip() {
264 for (name, value) in [
265 ("media.movie_added", MediaSseEventType::MovieAdded),
266 (
267 "media.movie_batch_finalized",
268 MediaSseEventType::MovieBatchFinalized,
269 ),
270 ("media.series_added", MediaSseEventType::SeriesAdded),
271 (
272 "media.series_bundle_finalized",
273 MediaSseEventType::SeriesBundleFinalized,
274 ),
275 ("media.season_added", MediaSseEventType::SeasonAdded),
276 ("media.episode_added", MediaSseEventType::EpisodeAdded),
277 ("media.movie_updated", MediaSseEventType::MovieUpdated),
278 ("media.series_updated", MediaSseEventType::SeriesUpdated),
279 ("media.season_updated", MediaSseEventType::SeasonUpdated),
280 ("media.episode_updated", MediaSseEventType::EpisodeUpdated),
281 ("media.deleted", MediaSseEventType::MediaDeleted),
282 (
283 "scan.started",
284 MediaSseEventType::Scan(ScanSseEventType::Started),
285 ),
286 (
287 "scan.progress",
288 MediaSseEventType::Scan(ScanSseEventType::Progress),
289 ),
290 (
291 "scan.quiescing",
292 MediaSseEventType::Scan(ScanSseEventType::Quiescing),
293 ),
294 (
295 "scan.completed",
296 MediaSseEventType::Scan(ScanSseEventType::Completed),
297 ),
298 (
299 "scan.failed",
300 MediaSseEventType::Scan(ScanSseEventType::Failed),
301 ),
302 ] {
303 assert_eq!(value.event_name(), name);
304 assert_eq!(MediaSseEventType::from_str(name).unwrap(), value);
305 }
306 }
307
308 #[test]
309 fn image_event_name_roundtrip() {
310 {
311 let (name, value) = ("image.ready", ImageSseEventType::Ready);
312 assert_eq!(value.event_name(), name);
313 assert_eq!(ImageSseEventType::from_str(name).unwrap(), value);
314 }
315 }
316}