1use std::{
9 fmt::{Display, Formatter},
10 hash::{Hash, Hasher},
11 str::FromStr,
12};
13
14use serde::{Deserialize, Serialize, Serializer};
15
16pub use crate::ParseError;
17use crate::utils::TryFromStrVisitor;
18
19#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
24pub enum Extension {
25 Backports,
27 Security,
29 Updates,
31 ProposedUpdates,
33}
34
35impl AsRef<str> for Extension {
36 fn as_ref(&self) -> &str {
37 match self {
38 Self::Backports => "backports",
39 Self::Security => "security",
40 Self::Updates => "updates",
41 Self::ProposedUpdates => "proposed-updates",
42 }
43 }
44}
45
46impl Display for Extension {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{}", self.as_ref())
49 }
50}
51
52impl TryFrom<&str> for Extension {
53 type Error = ParseError;
54
55 fn try_from(value: &str) -> Result<Self, Self::Error> {
56 match value {
57 "backports" => Ok(Self::Backports),
58 "security" => Ok(Self::Security),
59 "updates" => Ok(Self::Updates),
60 "proposed-updates" => Ok(Self::ProposedUpdates),
61 _ => Err(ParseError::InvalidExtension),
62 }
63 }
64}
65
66impl FromStr for Extension {
67 type Err = ParseError;
68
69 fn from_str(s: &str) -> Result<Self, Self::Err> {
70 Self::try_from(s)
71 }
72}
73
74pub trait WithExtension {
76 fn with_extension(&self, extension: Extension) -> Self;
80
81 fn without_extension(&self) -> Self;
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
91pub enum Suite {
92 Unstable,
94 Testing(Option<Extension>),
96 Stable(Option<Extension>),
98 OldStable(Option<Extension>),
100 Experimental,
102}
103
104impl WithExtension for Suite {
105 fn with_extension(&self, extension: Extension) -> Self {
106 match self {
107 Self::Unstable | Self::Experimental => *self,
108 Self::Testing(_) => Self::Testing(Some(extension)),
109 Self::Stable(_) => Self::Stable(Some(extension)),
110 Self::OldStable(_) => Self::OldStable(Some(extension)),
111 }
112 }
113
114 fn without_extension(&self) -> Self {
115 match self {
116 Self::Unstable | Self::Experimental => *self,
117 Self::Testing(_) => Self::Testing(None),
118 Self::Stable(_) => Self::Stable(None),
119 Self::OldStable(_) => Self::OldStable(None),
120 }
121 }
122}
123
124impl Display for Suite {
125 fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
126 match self {
127 Self::Unstable => write!(f, "unstable"),
128 Self::Testing(None) => write!(f, "testing"),
129 Self::Stable(None) => write!(f, "stable"),
130 Self::OldStable(None) => write!(f, "oldstable"),
131 Self::Experimental => write!(f, "experimental"),
132 Self::Testing(Some(ext)) => write!(f, "testing-{ext}"),
133 Self::Stable(Some(Extension::ProposedUpdates)) => write!(f, "proposed-updates"),
135 Self::Stable(Some(ext)) => write!(f, "stable-{ext}"),
136 Self::OldStable(Some(ext)) => write!(f, "oldstable-{ext}"),
137 }
138 }
139}
140
141impl TryFrom<&str> for Suite {
142 type Error = ParseError;
143
144 fn try_from(value: &str) -> Result<Self, Self::Error> {
145 match value {
146 "unstable" => Ok(Self::Unstable),
147 "testing" => Ok(Self::Testing(None)),
148 "stable" => Ok(Self::Stable(None)),
149 "oldstable" => Ok(Self::OldStable(None)),
150 "proposed-updates" => Ok(Self::Stable(Some(Extension::ProposedUpdates))),
152 "experimental" => Ok(Self::Experimental),
153 _ => {
154 let s = value.split_once('-').ok_or(ParseError::InvalidSuite)?;
155 let ext = Extension::try_from(s.1)?;
156 match s.0 {
157 "testing" => Ok(Self::Testing(Some(ext))),
158 "stable" => Ok(Self::Stable(Some(ext))),
159 "oldstable" => Ok(Self::OldStable(Some(ext))),
160 _ => Err(ParseError::InvalidSuite),
161 }
162 }
163 }
164 }
165}
166
167impl FromStr for Suite {
168 type Err = ParseError;
169
170 fn from_str(s: &str) -> Result<Self, Self::Err> {
171 Self::try_from(s)
172 }
173}
174
175impl Serialize for Suite {
176 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177 where
178 S: Serializer,
179 {
180 serializer.serialize_str(&self.to_string())
181 }
182}
183
184impl<'de> Deserialize<'de> for Suite {
185 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
186 where
187 D: serde::Deserializer<'de>,
188 {
189 deserializer.deserialize_str(TryFromStrVisitor::new("a suite name"))
190 }
191}
192
193#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
197pub enum Codename {
198 Sid,
200 Forky(Option<Extension>),
202 Trixie(Option<Extension>),
204 Bookworm(Option<Extension>),
206 RCBuggy,
208}
209
210impl WithExtension for Codename {
211 fn with_extension(&self, extension: Extension) -> Self {
212 match self {
213 Self::Sid | Self::RCBuggy => *self,
214 Self::Trixie(_) => Self::Trixie(Some(extension)),
215 Self::Bookworm(_) => Self::Bookworm(Some(extension)),
216 Self::Forky(_) => Self::Forky(Some(extension)),
217 }
218 }
219
220 fn without_extension(&self) -> Self {
221 match self {
222 Self::Sid | Self::RCBuggy => *self,
223 Self::Trixie(_) => Self::Trixie(None),
224 Self::Bookworm(_) => Self::Bookworm(None),
225 Self::Forky(_) => Self::Forky(None),
226 }
227 }
228}
229
230impl Display for Codename {
231 fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
232 match self {
233 Self::Sid => write!(f, "sid"),
234 Self::Trixie(None) => write!(f, "trixie"),
235 Self::Bookworm(None) => write!(f, "bookworm"),
236 Self::Forky(None) => write!(f, "forky"),
237 Self::RCBuggy => write!(f, "rc-buggy"),
238 Self::Trixie(Some(ext)) => write!(f, "trixie-{ext}"),
239 Self::Bookworm(Some(ext)) => write!(f, "bookworm-{ext}"),
240 Self::Forky(Some(ext)) => write!(f, "forky-{ext}"),
241 }
242 }
243}
244
245impl TryFrom<&str> for Codename {
246 type Error = ParseError;
247
248 fn try_from(value: &str) -> Result<Self, Self::Error> {
249 match value {
250 "sid" => Ok(Self::Sid),
251 "trixie" => Ok(Self::Trixie(None)),
252 "bookworm" => Ok(Self::Bookworm(None)),
253 "forky" => Ok(Self::Forky(None)),
254 "rc-buggy" => Ok(Self::RCBuggy),
255 _ => {
256 let s = value.split_once('-').ok_or(ParseError::InvalidCodename)?;
257 let ext = Extension::try_from(s.1)?;
258 match s.0 {
259 "trixie" => Ok(Self::Trixie(Some(ext))),
260 "bookworm" => Ok(Self::Bookworm(Some(ext))),
261 "forky" => Ok(Self::Forky(Some(ext))),
262 _ => Err(ParseError::InvalidCodename),
263 }
264 }
265 }
266 }
267}
268
269impl FromStr for Codename {
270 type Err = ParseError;
271
272 fn from_str(s: &str) -> Result<Self, Self::Err> {
273 Self::try_from(s)
274 }
275}
276
277impl From<Suite> for Codename {
278 fn from(suite: Suite) -> Self {
279 match suite {
280 Suite::Unstable => Self::Sid,
281 Suite::Testing(ext) => Self::Forky(ext),
282 Suite::Stable(ext) => Self::Trixie(ext),
283 Suite::OldStable(ext) => Self::Bookworm(ext),
284 Suite::Experimental => Self::RCBuggy,
285 }
286 }
287}
288
289impl From<Codename> for Suite {
290 fn from(codename: Codename) -> Self {
291 match codename {
292 Codename::Sid => Self::Unstable,
293 Codename::Forky(ext) => Self::Testing(ext),
294 Codename::Trixie(ext) => Self::Stable(ext),
295 Codename::Bookworm(ext) => Self::OldStable(ext),
296 Codename::RCBuggy => Self::Experimental,
297 }
298 }
299}
300
301impl Serialize for Codename {
302 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303 where
304 S: Serializer,
305 {
306 serializer.serialize_str(&self.to_string())
307 }
308}
309
310impl<'de> Deserialize<'de> for Codename {
311 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
312 where
313 D: serde::Deserializer<'de>,
314 {
315 deserializer.deserialize_str(TryFromStrVisitor::new("a codename"))
316 }
317}
318
319#[derive(Clone, Copy, Debug, Eq)]
323pub enum SuiteOrCodename {
324 Suite(Suite),
326 Codename(Codename),
328}
329
330impl SuiteOrCodename {
331 pub const UNSTABLE: Self = Self::Suite(Suite::Unstable);
333 pub const TESTING: Self = Self::Suite(Suite::Testing(None));
335 pub const STABLE: Self = Self::Suite(Suite::Stable(None));
337 pub const OLDSTABLE: Self = Self::Suite(Suite::OldStable(None));
339 pub const EXPERIMENTAL: Self = Self::Suite(Suite::Experimental);
341 pub const STABLE_PU: Self = Self::Suite(Suite::Stable(Some(Extension::ProposedUpdates)));
343 pub const OLDSTABLE_PU: Self = Self::Suite(Suite::OldStable(Some(Extension::ProposedUpdates)));
345 pub const STABLE_BACKPORTS: Self = Self::Suite(Suite::Stable(Some(Extension::Backports)));
347}
348
349impl PartialEq for SuiteOrCodename {
350 fn eq(&self, other: &Self) -> bool {
351 match (self, other) {
352 (Self::Suite(l0), Self::Suite(r0)) => l0 == r0,
353 (Self::Codename(l0), Self::Codename(r0)) => l0 == r0,
354 (Self::Suite(l0), Self::Codename(r0)) => Suite::from(*r0) == *l0,
355 (Self::Codename(l0), Self::Suite(r0)) => Suite::from(*l0) == *r0,
356 }
357 }
358}
359
360impl Hash for SuiteOrCodename {
361 fn hash<H>(&self, state: &mut H)
362 where
363 H: Hasher,
364 {
365 match self {
366 Self::Suite(suite) => suite.hash(state),
367 Self::Codename(codename) => Suite::from(*codename).hash(state),
368 }
369 }
370}
371
372impl WithExtension for SuiteOrCodename {
373 fn with_extension(&self, extension: Extension) -> Self {
374 match self {
375 Self::Suite(suite) => Self::Suite(suite.with_extension(extension)),
376 Self::Codename(suite) => Self::Codename(suite.with_extension(extension)),
377 }
378 }
379
380 fn without_extension(&self) -> Self {
381 match self {
382 Self::Suite(suite) => Self::Suite(suite.without_extension()),
383 Self::Codename(suite) => Self::Codename(suite.without_extension()),
384 }
385 }
386}
387
388impl From<Codename> for SuiteOrCodename {
389 fn from(codename: Codename) -> Self {
390 Self::Codename(codename)
391 }
392}
393
394impl From<Suite> for SuiteOrCodename {
395 fn from(suite: Suite) -> Self {
396 Self::Suite(suite)
397 }
398}
399
400impl TryFrom<&str> for SuiteOrCodename {
401 type Error = ParseError;
402
403 fn try_from(value: &str) -> Result<Self, Self::Error> {
404 match Suite::try_from(value) {
405 Ok(suite) => Ok(Self::Suite(suite)),
406 Err(_) => match Codename::try_from(value) {
407 Ok(codename) => Ok(Self::Codename(codename)),
408 Err(_) => Err(ParseError::InvalidSuiteOrCodename),
409 },
410 }
411 }
412}
413
414impl FromStr for SuiteOrCodename {
415 type Err = ParseError;
416
417 fn from_str(s: &str) -> Result<Self, Self::Err> {
418 Self::try_from(s)
419 }
420}
421
422impl Display for SuiteOrCodename {
423 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
424 match self {
425 Self::Suite(suite) => suite.fmt(f),
426 Self::Codename(codename) => codename.fmt(f),
427 }
428 }
429}
430
431impl From<SuiteOrCodename> for Suite {
432 fn from(value: SuiteOrCodename) -> Self {
433 match value {
434 SuiteOrCodename::Suite(suite) => suite,
435 SuiteOrCodename::Codename(codename) => Self::from(codename),
436 }
437 }
438}
439
440impl From<SuiteOrCodename> for Codename {
441 fn from(value: SuiteOrCodename) -> Self {
442 match value {
443 SuiteOrCodename::Suite(suite) => Self::from(suite),
444 SuiteOrCodename::Codename(codename) => codename,
445 }
446 }
447}
448
449impl Serialize for SuiteOrCodename {
450 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
451 where
452 S: Serializer,
453 {
454 serializer.serialize_str(&self.to_string())
455 }
456}
457
458impl<'de> Deserialize<'de> for SuiteOrCodename {
459 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
460 where
461 D: serde::Deserializer<'de>,
462 {
463 deserializer.deserialize_str(TryFromStrVisitor::new("a suite or a codename"))
464 }
465}
466
467#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash)]
469#[serde(rename_all = "lowercase")]
470pub enum MultiArch {
471 Allowed,
473 Foreign,
475 No,
477 Same,
479}
480
481impl AsRef<str> for MultiArch {
482 fn as_ref(&self) -> &str {
483 match self {
484 Self::Allowed => "allowed",
485 Self::Foreign => "foreign",
486 Self::No => "no",
487 Self::Same => "same",
488 }
489 }
490}
491
492impl Display for MultiArch {
493 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
494 write!(f, "{}", self.as_ref())
495 }
496}
497
498impl TryFrom<&str> for MultiArch {
499 type Error = ParseError;
500
501 fn try_from(value: &str) -> Result<Self, Self::Error> {
502 match value {
503 "allowed" => Ok(Self::Allowed),
504 "foreign" => Ok(Self::Foreign),
505 "no" => Ok(Self::No),
506 "same" => Ok(Self::Same),
507 _ => Err(ParseError::InvalidMultiArch),
508 }
509 }
510}
511
512impl FromStr for MultiArch {
513 type Err = ParseError;
514
515 fn from_str(s: &str) -> Result<Self, Self::Err> {
516 Self::try_from(s)
517 }
518}
519
520#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash)]
522#[serde(rename_all = "lowercase")]
523pub enum Component {
524 Main,
526 Contrib,
528 #[serde(rename = "non-free")]
530 NonFree,
531 #[serde(rename = "non-free-firmware")]
533 NonFreeFirmware,
534}
535
536impl AsRef<str> for Component {
537 fn as_ref(&self) -> &str {
538 match self {
539 Self::Main => "main",
540 Self::Contrib => "contrib",
541 Self::NonFree => "non-free",
542 Self::NonFreeFirmware => "non-free-firmware",
543 }
544 }
545}
546
547impl Display for Component {
548 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
549 write!(f, "{}", self.as_ref())
550 }
551}
552
553impl TryFrom<&str> for Component {
554 type Error = ParseError;
555
556 fn try_from(value: &str) -> Result<Self, Self::Error> {
557 match value {
558 "main" => Ok(Self::Main),
559 "contrib" => Ok(Self::Contrib),
560 "non-free" => Ok(Self::NonFree),
561 "non-free-firmware" => Ok(Self::NonFreeFirmware),
562 _ => Err(ParseError::InvalidComponent),
563 }
564 }
565}
566
567impl FromStr for Component {
568 type Err = ParseError;
569
570 fn from_str(s: &str) -> Result<Self, Self::Err> {
571 Self::try_from(s)
572 }
573}
574
575#[cfg(test)]
576mod test {
577 use std::hash::DefaultHasher;
578
579 use super::*;
580
581 #[test]
582 fn suite_from_str() {
583 assert_eq!(Suite::try_from("unstable").unwrap(), Suite::Unstable);
584 assert_eq!(Suite::try_from("stable").unwrap(), Suite::Stable(None));
585 assert_eq!(
586 Suite::try_from("stable-backports").unwrap(),
587 Suite::Stable(Some(Extension::Backports))
588 );
589 }
590
591 #[test]
592 fn codename_from_str() {
593 assert_eq!(Codename::try_from("sid").unwrap(), Codename::Sid);
594 assert_eq!(Codename::try_from("forky").unwrap(), Codename::Forky(None));
595 assert_eq!(
596 Codename::try_from("forky-backports").unwrap(),
597 Codename::Forky(Some(Extension::Backports))
598 );
599 }
600
601 #[test]
602 fn codename_from_suite() {
603 assert_eq!(Codename::from(Suite::Unstable), Codename::Sid);
604 assert_eq!(
605 Codename::from(Suite::Stable(Some(Extension::Backports))),
606 Codename::Trixie(Some(Extension::Backports))
607 );
608 }
609
610 #[test]
611 fn suite_from_codename() {
612 assert_eq!(Suite::from(Codename::Sid), Suite::Unstable);
613 assert_eq!(
614 Suite::from(Codename::Trixie(Some(Extension::Backports))),
615 Suite::Stable(Some(Extension::Backports))
616 );
617 }
618
619 #[test]
620 fn suite_or_codename_from_str() {
621 assert_eq!(
622 SuiteOrCodename::try_from("unstable").unwrap(),
623 SuiteOrCodename::from(Suite::Unstable)
624 );
625 assert_eq!(
626 SuiteOrCodename::try_from("sid").unwrap(),
627 SuiteOrCodename::from(Codename::Sid)
628 );
629 }
630
631 #[test]
632 fn suite_or_codename_eq() {
633 assert_eq!(
634 SuiteOrCodename::UNSTABLE,
635 SuiteOrCodename::Codename(Codename::Sid)
636 );
637 assert_eq!(
638 SuiteOrCodename::STABLE_PU,
639 SuiteOrCodename::Codename(Codename::Trixie(Some(Extension::ProposedUpdates)))
640 );
641 }
642
643 #[test]
644 fn suite_or_codename_hash() {
645 let mut hasher_1 = DefaultHasher::new();
646 let mut hasher_2 = DefaultHasher::new();
647
648 SuiteOrCodename::UNSTABLE.hash(&mut hasher_1);
649 SuiteOrCodename::Codename(Codename::Sid).hash(&mut hasher_2);
650 assert_eq!(hasher_1.finish(), hasher_2.finish());
651 }
652
653 #[test]
654 fn multi_arch_from_str() {
655 assert_eq!(MultiArch::try_from("foreign").unwrap(), MultiArch::Foreign);
656 }
657
658 #[test]
659 fn compoment_from_str() {
660 assert_eq!(Component::try_from("main").unwrap(), Component::Main);
661 }
662}