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