1use std::{
2 fmt::{Display, Formatter},
3 str::FromStr,
4};
5
6use serde::{Deserialize, Serialize};
7use strum::{Display, EnumString, VariantNames};
8use winnow::{
9 ModalResult,
10 Parser,
11 ascii::{Caseless, space1},
12 combinator::{alt, cut_err, eof, not, repeat_till},
13 error::{StrContext, StrContextValue},
14 token::one_of,
15};
16
17use crate::Error;
18
19#[derive(
52 Clone,
53 Debug,
54 Deserialize,
55 Display,
56 Eq,
57 Hash,
58 Ord,
59 PartialEq,
60 PartialOrd,
61 Serialize,
62 VariantNames,
63)]
64#[strum(serialize_all = "lowercase")]
65#[serde(rename_all = "lowercase")]
66pub enum SystemArchitecture {
67 Aarch64,
69 Arm,
71 Armv6h,
73 Armv7h,
75 I386,
77 I486,
79 I686,
81 Pentium4,
83 Riscv32,
85 Riscv64,
87 X86_64,
89 #[strum(to_string = "x86_64_v2")]
91 X86_64V2,
92 #[strum(to_string = "x86_64_v3")]
94 X86_64V3,
95 #[strum(to_string = "x86_64_v4")]
97 X86_64V4,
98 #[strum(transparent)]
100 #[serde(untagged)]
101 Unknown(UnknownArchitecture),
102}
103
104impl SystemArchitecture {
105 pub fn parser(input: &mut &str) -> ModalResult<SystemArchitecture> {
109 alt((
110 ("aarch64", eof).value(SystemArchitecture::Aarch64),
112 ("arm", eof).value(SystemArchitecture::Arm),
113 ("armv6h", eof).value(SystemArchitecture::Armv6h),
114 ("armv7h", eof).value(SystemArchitecture::Armv7h),
115 ("i386", eof).value(SystemArchitecture::I386),
116 ("i486", eof).value(SystemArchitecture::I486),
117 ("i686", eof).value(SystemArchitecture::I686),
118 ("pentium4", eof).value(SystemArchitecture::Pentium4),
119 ("riscv32", eof).value(SystemArchitecture::Riscv32),
120 ("riscv64", eof).value(SystemArchitecture::Riscv64),
121 ("x86_64", eof).value(SystemArchitecture::X86_64),
122 ("x86_64_v2", eof).value(SystemArchitecture::X86_64V2),
123 ("x86_64_v3", eof).value(SystemArchitecture::X86_64V3),
124 ("x86_64_v4", eof).value(SystemArchitecture::X86_64V4),
125 UnknownArchitecture::parser.map(SystemArchitecture::Unknown),
126 ))
127 .parse_next(input)
128 }
129}
130
131impl FromStr for SystemArchitecture {
132 type Err = Error;
133
134 fn from_str(s: &str) -> Result<SystemArchitecture, Self::Err> {
142 Ok(Self::parser.parse(s)?)
143 }
144}
145
146#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
150pub struct UnknownArchitecture(String);
151
152impl UnknownArchitecture {
153 pub fn new(name: &str) -> Result<Self, Error> {
155 Self::from_str(name)
156 }
157
158 pub fn inner(&self) -> &str {
160 &self.0
161 }
162
163 pub fn parser(input: &mut &str) -> ModalResult<Self> {
174 cut_err(not(alt((eof, space1))))
176 .context(StrContext::Label("system architecture"))
177 .context(StrContext::Expected(StrContextValue::Description(
178 "a non empty string.",
179 )))
180 .parse_next(input)?;
181
182 cut_err(not((Caseless("any"), eof)))
184 .context(StrContext::Label(
185 "system architecture. 'any' has a special meaning and is not allowed here.",
186 ))
187 .parse_next(input)?;
188
189 let alphanum = |c: char| c.is_ascii_alphanumeric();
190 let special_chars = ['_'];
191
192 cut_err(repeat_till(0.., one_of((alphanum, special_chars)), eof))
193 .map(|(r, _)| r)
194 .map(Self)
195 .context(StrContext::Label("character in system architecture"))
196 .context(StrContext::Expected(StrContextValue::Description(
197 "a string containing only ASCII alphanumeric characters and underscores.",
198 )))
199 .parse_next(input)
200 }
201}
202
203impl From<UnknownArchitecture> for SystemArchitecture {
204 fn from(value: UnknownArchitecture) -> Self {
206 SystemArchitecture::Unknown(value)
207 }
208}
209
210impl From<UnknownArchitecture> for Architecture {
211 fn from(value: UnknownArchitecture) -> Self {
213 Architecture::Some(value.into())
214 }
215}
216
217impl FromStr for UnknownArchitecture {
218 type Err = Error;
219
220 fn from_str(s: &str) -> Result<UnknownArchitecture, Self::Err> {
228 Ok(Self::parser.parse(s)?)
229 }
230}
231
232impl Display for UnknownArchitecture {
233 fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
234 write!(fmt, "{}", self.inner())
235 }
236}
237
238impl AsRef<str> for UnknownArchitecture {
239 fn as_ref(&self) -> &str {
240 self.inner()
241 }
242}
243
244#[derive(
279 Clone,
280 Debug,
281 Deserialize,
282 Display,
283 Eq,
284 Hash,
285 Ord,
286 PartialEq,
287 PartialOrd,
288 Serialize,
289 VariantNames,
290)]
291#[strum(serialize_all = "lowercase")]
292#[serde(rename_all = "lowercase")]
293pub enum Architecture {
294 Any,
296 #[strum(transparent)]
298 #[serde(untagged)]
299 Some(SystemArchitecture),
300}
301
302impl Architecture {
303 pub fn parser(input: &mut &str) -> ModalResult<Architecture> {
307 alt((
308 (Caseless("any"), eof).value(Architecture::Any),
309 SystemArchitecture::parser.map(Architecture::Some),
310 ))
311 .context(StrContext::Label("alpm-architecture"))
312 .parse_next(input)
313 }
314}
315
316impl FromStr for Architecture {
317 type Err = Error;
318
319 fn from_str(s: &str) -> Result<Architecture, Self::Err> {
327 Ok(Self::parser.parse(s)?)
328 }
329}
330
331impl From<SystemArchitecture> for Architecture {
332 fn from(value: SystemArchitecture) -> Self {
334 Architecture::Some(value)
335 }
336}
337
338#[derive(
347 Clone,
348 Debug,
349 Deserialize,
350 EnumString,
351 Eq,
352 Hash,
353 Ord,
354 PartialEq,
355 PartialOrd,
356 Serialize,
357 VariantNames,
358)]
359#[strum(serialize_all = "lowercase")]
360#[serde(rename_all = "lowercase")]
361pub enum Architectures {
362 Any,
364 #[strum(transparent)]
366 #[serde(untagged)]
367 Some(Vec<SystemArchitecture>),
368}
369
370impl Architectures {
371 pub fn len(&self) -> usize {
373 match self {
374 Architectures::Any => 1,
375 Architectures::Some(archs) => archs.len(),
376 }
377 }
378
379 pub fn is_empty(&self) -> bool {
381 match self {
382 Architectures::Any => false,
383 Architectures::Some(archs) => archs.is_empty(),
384 }
385 }
386}
387
388impl Display for Architectures {
389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 match self {
392 Architectures::Any => {
393 write!(f, "any")
394 }
395 Architectures::Some(archs) => {
396 write!(
397 f,
398 "{}",
399 archs
400 .iter()
401 .map(ToString::to_string)
402 .collect::<Vec<_>>()
403 .join(", ")
404 )
405 }
406 }
407 }
408}
409
410impl From<Architecture> for Architectures {
411 fn from(value: Architecture) -> Self {
413 match value {
414 Architecture::Any => Architectures::Any,
415 Architecture::Some(arch) => Architectures::Some(vec![arch]),
416 }
417 }
418}
419
420impl From<&Architectures> for Vec<Architecture> {
421 fn from(value: &Architectures) -> Self {
423 match value {
424 Architectures::Any => vec![Architecture::Any],
425 Architectures::Some(archs) => {
426 archs.clone().into_iter().map(Architecture::Some).collect()
427 }
428 }
429 }
430}
431
432impl IntoIterator for &Architectures {
433 type Item = Architecture;
434 type IntoIter = std::vec::IntoIter<Architecture>;
435 fn into_iter(self) -> Self::IntoIter {
437 let vec: Vec<Architecture> = self.into();
438 vec.into_iter()
439 }
440}
441
442impl TryFrom<Vec<&Architecture>> for Architectures {
443 type Error = Error;
444
445 fn try_from(value: Vec<&Architecture>) -> Result<Self, Self::Error> {
452 if value.contains(&&Architecture::Any) {
453 if value.len() > 1 {
454 Err(Error::InvalidArchitectures {
455 architectures: value.iter().map(|&v| v.clone()).collect(),
456 context: "'any' cannot be used in combination with other architectures.",
457 })
458 } else {
459 Ok(Architectures::Any)
460 }
461 } else {
462 let archs: Vec<SystemArchitecture> = value
463 .into_iter()
464 .map(|arch| {
465 if let Architecture::Some(specific) = arch {
466 specific.clone()
467 } else {
468 unreachable!()
470 }
471 })
472 .collect();
473 Ok(Architectures::Some(archs))
474 }
475 }
476}
477
478impl TryFrom<Vec<Architecture>> for Architectures {
479 type Error = Error;
480
481 fn try_from(value: Vec<Architecture>) -> Result<Self, Self::Error> {
485 value.iter().collect::<Vec<&Architecture>>().try_into()
486 }
487}
488
489#[derive(
520 Clone, Copy, Debug, Deserialize, Display, EnumString, Eq, Ord, PartialEq, PartialOrd, Serialize,
521)]
522#[strum(serialize_all = "lowercase")]
523pub enum ElfArchitectureFormat {
524 #[strum(to_string = "32")]
526 Bit32 = 32,
527 #[strum(to_string = "64")]
529 Bit64 = 64,
530}
531
532#[cfg(test)]
533mod tests {
534 use std::str::FromStr;
535
536 use rstest::rstest;
537 use strum::ParseError;
538
539 use super::*;
540
541 const ERROR_ANY: &str = concat!(
543 "any\n",
544 "^\n",
545 "invalid system architecture. 'any' has a special meaning and is not allowed here."
546 );
547
548 const ERROR_FOO: &str = concat!(
550 "f oo\n",
551 " ^\n",
552 "invalid character in system architecture\n",
553 "expected a string containing only ASCII alphanumeric characters and underscores."
554 );
555
556 #[rstest]
557 #[case(SystemArchitecture::Aarch64.into(), Architecture::Some(SystemArchitecture::Aarch64))]
558 #[case(SystemArchitecture::from_str("f_oo").unwrap().into(), Architecture::Some(SystemArchitecture::from_str("f_oo").unwrap()))]
559 fn system_architecture_into_architecture(
560 #[case] left: Architecture,
561 #[case] right: Architecture,
562 ) {
563 assert_eq!(left, right);
564 }
565
566 #[rstest]
567 #[case("aarch64", Ok(SystemArchitecture::Aarch64))]
568 #[case("f_oo", UnknownArchitecture::new("f_oo").map(From::from))]
569 #[case("f oo", Err(Error::ParseError(ERROR_FOO.to_string())))]
570 #[case("any", Err(Error::ParseError(ERROR_ANY.to_string())))]
571 fn system_architecture_from_string(
572 #[case] s: &str,
573 #[case] arch: Result<SystemArchitecture, Error>,
574 ) {
575 assert_eq!(SystemArchitecture::from_str(s), arch);
576 }
577
578 #[rstest]
579 #[case(SystemArchitecture::Aarch64, "aarch64")]
580 #[case(SystemArchitecture::from_str("f_o_o").unwrap(), "f_o_o")]
581 fn system_architecture_format_string(#[case] arch: SystemArchitecture, #[case] arch_str: &str) {
582 assert_eq!(arch_str, format!("{arch}"));
583 }
584
585 #[rstest]
586 #[case("any", Ok(Architecture::Any))]
587 #[case("aarch64", Ok(SystemArchitecture::Aarch64.into()))]
588 #[case("arm", Ok(SystemArchitecture::Arm.into()))]
589 #[case("armv6h", Ok(SystemArchitecture::Armv6h.into()))]
590 #[case("armv7h", Ok(SystemArchitecture::Armv7h.into()))]
591 #[case("i386", Ok(SystemArchitecture::I386.into()))]
592 #[case("i486", Ok(SystemArchitecture::I486.into()))]
593 #[case("i686", Ok(SystemArchitecture::I686.into()))]
594 #[case("pentium4", Ok(SystemArchitecture::Pentium4.into()))]
595 #[case("riscv32", Ok(SystemArchitecture::Riscv32.into()))]
596 #[case("riscv64", Ok(SystemArchitecture::Riscv64.into()))]
597 #[case("x86_64", Ok(SystemArchitecture::X86_64.into()))]
598 #[case("x86_64_v2", Ok(SystemArchitecture::X86_64V2.into()))]
599 #[case("x86_64_v3", Ok(SystemArchitecture::X86_64V3.into()))]
600 #[case("x86_64_v4", Ok(SystemArchitecture::X86_64V4.into()))]
601 #[case("foo", UnknownArchitecture::new("foo").map(From::from))]
602 #[case("f_oo", UnknownArchitecture::new("f_oo").map(From::from))]
603 #[case("f oo", Err(Error::ParseError(ERROR_FOO.to_string())))]
604 fn architecture_from_string(#[case] s: &str, #[case] arch: Result<Architecture, Error>) {
605 assert_eq!(Architecture::from_str(s), arch);
606 }
607
608 #[rstest]
609 #[case(Architecture::Any, "any")]
610 #[case(SystemArchitecture::Aarch64.into(), "aarch64")]
611 #[case(Architecture::from_str("foo").unwrap(), "foo")]
612 fn architecture_format_string(#[case] arch: Architecture, #[case] arch_str: &str) {
613 assert_eq!(arch_str, format!("{arch}"));
614 }
615
616 #[rstest]
617 #[case(vec![Architecture::Any], Ok(Architectures::Any))]
618 #[case(vec![SystemArchitecture::Aarch64.into()], Ok(Architectures::Some(vec![SystemArchitecture::Aarch64])))]
619 #[case(vec![SystemArchitecture::Arm.into(), SystemArchitecture::I386.into()], Ok(Architectures::Some(vec![SystemArchitecture::Arm, SystemArchitecture::I386])))]
620 #[case(vec![SystemArchitecture::Arm.into(), SystemArchitecture::Arm.into()], Ok(Architectures::Some(vec![SystemArchitecture::Arm, SystemArchitecture::Arm])))]
622 #[case(vec![Architecture::Any, SystemArchitecture::I386.into()], Err(Error::InvalidArchitectures {
623 architectures: vec![Architecture::Any, SystemArchitecture::I386.into()],
624 context: "'any' cannot be used in combination with other architectures.",
625 }))]
626 #[case(vec![Architecture::Any, Architecture::Any], Err(Error::InvalidArchitectures {
627 architectures: vec![Architecture::Any, Architecture::Any],
628 context: "'any' cannot be used in combination with other architectures.",
629 }))]
630 #[case(vec![], Ok(Architectures::Some(vec![])))]
631 fn architectures_from_vec(
632 #[case] archs: Vec<Architecture>,
633 #[case] expected: Result<Architectures, Error>,
634 ) {
635 assert_eq!(archs.try_into(), expected);
636 }
637
638 #[rstest]
639 #[case(Architectures::Any, "any")]
640 #[case(Architectures::Some(vec![SystemArchitecture::Aarch64]), "aarch64")]
641 #[case(Architectures::Some(vec![SystemArchitecture::Arm, SystemArchitecture::I386]), "arm, i386")]
642 #[case(Architectures::Some(vec![]), "")]
643 fn architectures_format_display(#[case] archs: Architectures, #[case] archs_str: &str) {
644 assert_eq!(archs_str, format!("{archs}"));
645 }
646
647 #[rstest]
648 #[case("32", Ok(ElfArchitectureFormat::Bit32))]
649 #[case("64", Ok(ElfArchitectureFormat::Bit64))]
650 #[case("foo", Err(ParseError::VariantNotFound))]
651 fn elf_architecture_format_from_string(
652 #[case] s: &str,
653 #[case] arch: Result<ElfArchitectureFormat, ParseError>,
654 ) {
655 assert_eq!(ElfArchitectureFormat::from_str(s), arch);
656 }
657
658 #[rstest]
659 #[case(ElfArchitectureFormat::Bit32, "32")]
660 #[case(ElfArchitectureFormat::Bit64, "64")]
661 fn elf_architecture_format_display(
662 #[case] arch: ElfArchitectureFormat,
663 #[case] arch_str: &str,
664 ) {
665 assert_eq!(arch_str, format!("{arch}"));
666 }
667}