1#[cfg(feature = "arbitrary")]
2use arbitrary::Arbitrary;
3use bounded_static::ToStatic;
4use nom::{
5 bytes::complete::tag,
6 character::complete::digit1,
7 combinator::{map, opt},
8 sequence::tuple,
9 IResult,
10};
11
12#[cfg(feature = "arbitrary")]
13use crate::fuzz_eq::FuzzEq;
14use crate::i18n::ContainsUtf8;
15use crate::print::{Formatter, Print, ToStringFromPrint};
16use crate::text::whitespace::cfws;
17use eml_codec_derives::instrument_input;
18
19#[derive(Clone, ContainsUtf8, Debug, PartialEq, ToStatic, ToStringFromPrint)]
20#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
21#[contains_utf8(false)]
22pub struct Version {
23 pub major: u64,
24 pub minor: u64,
25}
26
27impl Default for Version {
28 fn default() -> Self {
29 Self { major: 1, minor: 0 }
30 }
31}
32#[cfg(feature = "arbitrary")]
33impl FuzzEq for Version {
34 fn fuzz_eq(&self, other: &Self) -> bool {
35 self == other
36 }
37}
38
39#[instrument_input("tracing")]
40pub fn version(input: &[u8]) -> IResult<&[u8], Version> {
41 let (rest, (_, major, _, _, _, minor, _)) = tuple((
42 opt(cfws),
43 map(digit1, ascii_to_u64),
44 opt(cfws),
45 tag(b"."),
46 opt(cfws),
47 map(digit1, ascii_to_u64),
48 opt(cfws),
49 ))(input)?;
50 Ok((rest, Version { major, minor }))
51}
52
53fn ascii_to_u64(c: &[u8]) -> u64 {
54 str::from_utf8(c).unwrap().parse().unwrap()
55}
56
57impl Print for Version {
58 fn print(&self, fmt: &mut impl Formatter) {
59 fmt.write_bytes(self.major.to_string().as_bytes());
60 fmt.write_bytes(b".");
61 fmt.write_bytes(self.minor.to_string().as_bytes())
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_version() {
71 assert_eq!(
72 version(b"1.0"),
73 Ok((&b""[..], Version { major: 1, minor: 0 })),
74 );
75
76 assert_eq!(
77 version(b" 1.0 (produced by MetaSend Vx.x)"),
78 Ok((&b""[..], Version { major: 1, minor: 0 })),
79 );
80
81 assert_eq!(
82 version(b"(produced by MetaSend Vx.x) 1.0"),
83 Ok((&b""[..], Version { major: 1, minor: 0 })),
84 );
85
86 assert_eq!(
87 version(b"1.(produced by MetaSend Vx.x)0"),
88 Ok((&b""[..], Version { major: 1, minor: 0 })),
89 );
90 }
91}