1use std::fmt::{Display, Formatter};
9use std::str::FromStr;
10
11use serde::{Deserialize, Serialize, Serializer};
12
13use crate::utils::TryFromStrVisitor;
14pub use crate::ParseError;
15
16#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
21pub enum Extension {
22 Backports,
24 Security,
26 Updates,
28 ProposedUpdates,
30}
31
32impl AsRef<str> for Extension {
33 fn as_ref(&self) -> &str {
34 match self {
35 Self::Backports => "backports",
36 Self::Security => "security",
37 Self::Updates => "updates",
38 Self::ProposedUpdates => "proposed-updates",
39 }
40 }
41}
42
43impl Display for Extension {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 write!(f, "{}", self.as_ref())
46 }
47}
48
49impl TryFrom<&str> for Extension {
50 type Error = ParseError;
51
52 fn try_from(value: &str) -> Result<Self, Self::Error> {
53 match value {
54 "backports" => Ok(Self::Backports),
55 "security" => Ok(Self::Security),
56 "updates" => Ok(Self::Updates),
57 "proposed-updates" => Ok(Self::ProposedUpdates),
58 _ => Err(ParseError::InvalidExtension),
59 }
60 }
61}
62
63impl FromStr for Extension {
64 type Err = ParseError;
65
66 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 Self::try_from(s)
68 }
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
75pub enum Suite {
76 Unstable,
78 Testing(Option<Extension>),
80 Stable(Option<Extension>),
82 OldStable(Option<Extension>),
84 Experimental,
86}
87
88impl Suite {
89 pub fn with_extension(&self, extension: Extension) -> Self {
93 match self {
94 Self::Unstable | Self::Experimental => *self,
95 Self::Testing(_) => Self::Testing(Some(extension)),
96 Self::Stable(_) => Self::Stable(Some(extension)),
97 Self::OldStable(_) => Self::OldStable(Some(extension)),
98 }
99 }
100
101 pub fn without_extension(&self) -> Self {
105 match self {
106 Self::Unstable | Self::Experimental => *self,
107 Self::Testing(_) => Self::Testing(None),
108 Self::Stable(_) => Self::Stable(None),
109 Self::OldStable(_) => Self::OldStable(None),
110 }
111 }
112}
113
114impl Display for Suite {
115 fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
116 match self {
117 Self::Unstable => write!(f, "unstable"),
118 Self::Testing(None) => write!(f, "testing"),
119 Self::Stable(None) => write!(f, "stable"),
120 Self::OldStable(None) => write!(f, "oldstable"),
121 Self::Experimental => write!(f, "experimental"),
122 Self::Testing(Some(ext)) => write!(f, "testing-{ext}"),
123 Self::Stable(Some(ext)) => write!(f, "stable-{ext}"),
124 Self::OldStable(Some(ext)) => write!(f, "oldstable-{ext}"),
125 }
126 }
127}
128
129impl TryFrom<&str> for Suite {
130 type Error = ParseError;
131
132 fn try_from(value: &str) -> Result<Self, Self::Error> {
133 match value {
134 "unstable" => Ok(Self::Unstable),
135 "testing" => Ok(Self::Testing(None)),
136 "stable" => Ok(Self::Stable(None)),
137 "oldstable" => Ok(Self::OldStable(None)),
138 "proposed-updates" => Ok(Self::Stable(Some(Extension::ProposedUpdates))),
140 "experimental" => Ok(Self::Experimental),
141 _ => {
142 let s = value.split_once('-').ok_or(ParseError::InvalidSuite)?;
143 let ext = Extension::try_from(s.1)?;
144 match s.0 {
145 "testing" => Ok(Self::Testing(Some(ext))),
146 "stable" => Ok(Self::Stable(Some(ext))),
147 "oldstable" => Ok(Self::OldStable(Some(ext))),
148 _ => Err(ParseError::InvalidSuite),
149 }
150 }
151 }
152 }
153}
154
155impl FromStr for Suite {
156 type Err = ParseError;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 Self::try_from(s)
160 }
161}
162
163impl Serialize for Suite {
164 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
165 where
166 S: Serializer,
167 {
168 serializer.serialize_str(&self.to_string())
169 }
170}
171
172impl<'de> Deserialize<'de> for Suite {
173 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174 where
175 D: serde::Deserializer<'de>,
176 {
177 deserializer.deserialize_str(TryFromStrVisitor::<Self>::new("a suite name"))
178 }
179}
180
181#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
185pub enum Codename {
186 Sid,
188 Trixie(Option<Extension>),
190 Bookworm(Option<Extension>),
192 Bullseye(Option<Extension>),
194 RCBuggy,
196}
197
198impl Display for Codename {
199 fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
200 match self {
201 Self::Sid => write!(f, "sid"),
202 Self::Trixie(None) => write!(f, "trixie"),
203 Self::Bookworm(None) => write!(f, "bookworm"),
204 Self::Bullseye(None) => write!(f, "bullseye"),
205 Self::RCBuggy => write!(f, "rc-buggy"),
206 Self::Trixie(Some(ext)) => write!(f, "trixie-{ext}"),
207 Self::Bookworm(Some(ext)) => write!(f, "bookworm-{ext}"),
208 Self::Bullseye(Some(ext)) => write!(f, "bullseye-{ext}"),
209 }
210 }
211}
212
213impl TryFrom<&str> for Codename {
214 type Error = ParseError;
215
216 fn try_from(value: &str) -> Result<Self, Self::Error> {
217 match value {
218 "sid" => Ok(Self::Sid),
219 "trixie" => Ok(Self::Trixie(None)),
220 "bookworm" => Ok(Self::Bookworm(None)),
221 "bullseye" => Ok(Self::Bullseye(None)),
222 "rc-buggy" => Ok(Self::RCBuggy),
223 _ => {
224 let s = value.split_once('-').ok_or(ParseError::InvalidCodename)?;
225 let ext = Extension::try_from(s.1)?;
226 match s.0 {
227 "trixie" => Ok(Self::Trixie(Some(ext))),
228 "bookworm" => Ok(Self::Bookworm(Some(ext))),
229 "bullseye" => Ok(Self::Bullseye(Some(ext))),
230 _ => Err(ParseError::InvalidCodename),
231 }
232 }
233 }
234 }
235}
236
237impl FromStr for Codename {
238 type Err = ParseError;
239
240 fn from_str(s: &str) -> Result<Self, Self::Err> {
241 Self::try_from(s)
242 }
243}
244
245impl From<Suite> for Codename {
246 fn from(suite: Suite) -> Self {
247 match suite {
248 Suite::Unstable => Self::Sid,
249 Suite::Testing(ext) => Self::Trixie(ext),
250 Suite::Stable(ext) => Self::Bookworm(ext),
251 Suite::OldStable(ext) => Self::Bullseye(ext),
252 Suite::Experimental => Self::RCBuggy,
253 }
254 }
255}
256
257impl From<Codename> for Suite {
258 fn from(codename: Codename) -> Self {
259 match codename {
260 Codename::Sid => Self::Unstable,
261 Codename::Trixie(ext) => Self::Testing(ext),
262 Codename::Bookworm(ext) => Self::Stable(ext),
263 Codename::Bullseye(ext) => Self::OldStable(ext),
264 Codename::RCBuggy => Self::Experimental,
265 }
266 }
267}
268
269impl Serialize for Codename {
270 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
271 where
272 S: Serializer,
273 {
274 serializer.serialize_str(&self.to_string())
275 }
276}
277
278impl<'de> Deserialize<'de> for Codename {
279 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
280 where
281 D: serde::Deserializer<'de>,
282 {
283 deserializer.deserialize_str(TryFromStrVisitor::<Self>::new("a codename"))
284 }
285}
286
287#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
291pub enum SuiteOrCodename {
292 Suite(Suite),
294 Codename(Codename),
296}
297
298impl SuiteOrCodename {
299 pub const UNSTABLE: Self = Self::Suite(Suite::Unstable);
301 pub const TESTING: Self = Self::Suite(Suite::Testing(None));
303 pub const STABLE: Self = Self::Suite(Suite::Stable(None));
305 pub const OLDSTABLE: Self = Self::Suite(Suite::OldStable(None));
307 pub const EXPERIMENTAL: Self = Self::Suite(Suite::Experimental);
309 pub const STABLE_PU: Self = Self::Suite(Suite::Stable(Some(Extension::ProposedUpdates)));
311 pub const OLDSTABLE_PU: Self = Self::Suite(Suite::OldStable(Some(Extension::ProposedUpdates)));
313 pub const STABLE_BACKPORTS: Self = Self::Suite(Suite::Stable(Some(Extension::Backports)));
315}
316
317impl From<Codename> for SuiteOrCodename {
318 fn from(codename: Codename) -> Self {
319 Self::Codename(codename)
320 }
321}
322
323impl From<Suite> for SuiteOrCodename {
324 fn from(suite: Suite) -> Self {
325 Self::Suite(suite)
326 }
327}
328
329impl TryFrom<&str> for SuiteOrCodename {
330 type Error = ParseError;
331
332 fn try_from(value: &str) -> Result<Self, Self::Error> {
333 match Suite::try_from(value) {
334 Ok(suite) => Ok(Self::Suite(suite)),
335 Err(_) => match Codename::try_from(value) {
336 Ok(codename) => Ok(Self::Codename(codename)),
337 Err(_) => Err(ParseError::InvalidSuiteOrCodename),
338 },
339 }
340 }
341}
342
343impl FromStr for SuiteOrCodename {
344 type Err = ParseError;
345
346 fn from_str(s: &str) -> Result<Self, Self::Err> {
347 Self::try_from(s)
348 }
349}
350
351impl Display for SuiteOrCodename {
352 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
353 match self {
354 Self::Suite(suite) => suite.fmt(f),
355 Self::Codename(codename) => codename.fmt(f),
356 }
357 }
358}
359
360impl From<SuiteOrCodename> for Suite {
361 fn from(value: SuiteOrCodename) -> Self {
362 match value {
363 SuiteOrCodename::Suite(suite) => suite,
364 SuiteOrCodename::Codename(codename) => Self::from(codename),
365 }
366 }
367}
368
369impl From<SuiteOrCodename> for Codename {
370 fn from(value: SuiteOrCodename) -> Self {
371 match value {
372 SuiteOrCodename::Suite(suite) => Self::from(suite),
373 SuiteOrCodename::Codename(codename) => codename,
374 }
375 }
376}
377
378impl Serialize for SuiteOrCodename {
379 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
380 where
381 S: Serializer,
382 {
383 serializer.serialize_str(&self.to_string())
384 }
385}
386
387impl<'de> Deserialize<'de> for SuiteOrCodename {
388 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
389 where
390 D: serde::Deserializer<'de>,
391 {
392 deserializer.deserialize_str(TryFromStrVisitor::<Self>::new("a suite or a codename"))
393 }
394}
395
396#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash)]
398#[serde(rename_all = "lowercase")]
399pub enum MultiArch {
400 Allowed,
402 Foreign,
404 No,
406 Same,
408}
409
410impl AsRef<str> for MultiArch {
411 fn as_ref(&self) -> &str {
412 match self {
413 Self::Allowed => "allowed",
414 Self::Foreign => "foreign",
415 Self::No => "no",
416 Self::Same => "same",
417 }
418 }
419}
420
421impl Display for MultiArch {
422 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
423 write!(f, "{}", self.as_ref())
424 }
425}
426
427impl TryFrom<&str> for MultiArch {
428 type Error = ParseError;
429
430 fn try_from(value: &str) -> Result<Self, Self::Error> {
431 match value {
432 "allowed" => Ok(Self::Allowed),
433 "foreign" => Ok(Self::Foreign),
434 "no" => Ok(Self::No),
435 "same" => Ok(Self::Same),
436 _ => Err(ParseError::InvalidMultiArch),
437 }
438 }
439}
440
441impl FromStr for MultiArch {
442 type Err = ParseError;
443
444 fn from_str(s: &str) -> Result<Self, Self::Err> {
445 Self::try_from(s)
446 }
447}
448
449#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash)]
451#[serde(rename_all = "lowercase")]
452pub enum Component {
453 Main,
455 Contrib,
457 #[serde(rename = "non-free")]
459 NonFree,
460 #[serde(rename = "non-free-firmware")]
462 NonFreeFirmware,
463}
464
465impl AsRef<str> for Component {
466 fn as_ref(&self) -> &str {
467 match self {
468 Self::Main => "main",
469 Self::Contrib => "contrib",
470 Self::NonFree => "non-free",
471 Self::NonFreeFirmware => "non-free-firmware",
472 }
473 }
474}
475
476impl Display for Component {
477 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
478 write!(f, "{}", self.as_ref())
479 }
480}
481
482impl TryFrom<&str> for Component {
483 type Error = ParseError;
484
485 fn try_from(value: &str) -> Result<Self, Self::Error> {
486 match value {
487 "main" => Ok(Self::Main),
488 "contrib" => Ok(Self::Contrib),
489 "non-free" => Ok(Self::NonFree),
490 "non-free-firmware" => Ok(Self::NonFreeFirmware),
491 _ => Err(ParseError::InvalidComponent),
492 }
493 }
494}
495
496impl FromStr for Component {
497 type Err = ParseError;
498
499 fn from_str(s: &str) -> Result<Self, Self::Err> {
500 Self::try_from(s)
501 }
502}
503
504#[cfg(test)]
505mod test {
506 use super::*;
507
508 #[test]
509 fn suite_from_str() {
510 assert_eq!(Suite::try_from("unstable").unwrap(), Suite::Unstable);
511 assert_eq!(Suite::try_from("stable").unwrap(), Suite::Stable(None));
512 assert_eq!(
513 Suite::try_from("stable-backports").unwrap(),
514 Suite::Stable(Some(Extension::Backports))
515 );
516 }
517
518 #[test]
519 fn codename_from_str() {
520 assert_eq!(Codename::try_from("sid").unwrap(), Codename::Sid);
521 assert_eq!(
522 Codename::try_from("bullseye").unwrap(),
523 Codename::Bullseye(None)
524 );
525 assert_eq!(
526 Codename::try_from("bullseye-backports").unwrap(),
527 Codename::Bullseye(Some(Extension::Backports))
528 );
529 }
530
531 #[test]
532 fn codename_from_suite() {
533 assert_eq!(Codename::from(Suite::Unstable), Codename::Sid);
534 assert_eq!(
535 Codename::from(Suite::Stable(Some(Extension::Backports))),
536 Codename::Bookworm(Some(Extension::Backports))
537 );
538 }
539
540 #[test]
541 fn suite_from_codename() {
542 assert_eq!(Suite::from(Codename::Sid), Suite::Unstable);
543 assert_eq!(
544 Suite::from(Codename::Bookworm(Some(Extension::Backports))),
545 Suite::Stable(Some(Extension::Backports))
546 );
547 }
548
549 #[test]
550 fn suite_or_codename_from_str() {
551 assert_eq!(
552 SuiteOrCodename::try_from("unstable").unwrap(),
553 SuiteOrCodename::from(Suite::Unstable)
554 );
555 assert_eq!(
556 SuiteOrCodename::try_from("sid").unwrap(),
557 SuiteOrCodename::from(Codename::Sid)
558 );
559 }
560
561 #[test]
562 fn multi_arch_from_str() {
563 assert_eq!(MultiArch::try_from("foreign").unwrap(), MultiArch::Foreign);
564 }
565
566 #[test]
567 fn compoment_from_str() {
568 assert_eq!(Component::try_from("main").unwrap(), Component::Main);
569 }
570}