1use nom::{
6 branch::alt,
7 bytes::complete::{tag_no_case, take_while_m_n},
8 character::complete::{alphanumeric1, char as nomchar, digit1, satisfy},
9 combinator::opt,
10 multi::many0,
11 sequence::{preceded, terminated, tuple},
12 IResult, Parser,
13};
14
15use crate::requirements::{LocalVersionPart, Version};
16
17pub fn epoch(input: &str) -> IResult<&str, u64> {
18 terminated(digit1, nomchar('!'))
19 .map(|s: &str| s.parse::<u64>().unwrap())
20 .parse(input)
21}
22
23pub fn release(input: &str) -> IResult<&str, Vec<u64>> {
24 digit1
25 .and(many0(preceded(nomchar('.'), digit1)))
26 .map(|(first, mut rest)| {
27 rest.insert(0, first);
28 rest.into_iter()
29 .map(|s: &str| s.parse::<u64>().unwrap())
30 .collect()
31 })
32 .parse(input)
33}
34
35pub fn pre_l(input: &str) -> IResult<&str, &str> {
37 alt((
39 tag_no_case("alpha"),
40 tag_no_case("preview"),
41 tag_no_case("beta"),
42 tag_no_case("a"),
43 tag_no_case("b"),
44 tag_no_case("c"),
45 tag_no_case("rc"),
46 tag_no_case("pre"),
47 ))(input)
48}
49
50pub fn pre(input: &str) -> IResult<&str, (String, u64)> {
52 tuple((
53 take_while_m_n(0, 1, |c| "-_.".contains(c)),
54 pre_l,
55 take_while_m_n(0, 1, |c| "-_.".contains(c)),
56 opt(digit1),
57 ))
58 .map(|(_, l, _, n)| {
59 let number = n.map_or(0u64, |s| s.parse().unwrap());
60 let letter = l.to_lowercase();
61 match letter.as_str() {
62 "alpha" => ("a".to_string(), number),
63 "beta" => ("b".to_string(), number),
64 "c" | "pre" | "preview" => ("rc".to_string(), number),
65 _ => (letter, number),
66 }
67 })
68 .parse(input)
69}
70
71pub fn post_l(input: &str) -> IResult<&str, &str> {
72 alt((tag_no_case("post"), tag_no_case("rev"), tag_no_case("r")))(input)
73}
74
75pub fn post(input: &str) -> IResult<&str, (String, u64)> {
76 alt((
77 preceded(nomchar('-'), digit1)
78 .map(|s: &str| ("post".to_string(), s.parse::<u64>().unwrap())),
79 tuple((
80 take_while_m_n(0, 1, |c| "-_.".contains(c)),
81 post_l,
82 take_while_m_n(0, 1, |c| "-_.".contains(c)),
83 opt(digit1),
84 ))
85 .map(|(_, _, _, n)| ("post".to_string(), n.map_or(0u64, |s| s.parse().unwrap()))),
86 ))(input)
87}
88
89pub fn dev(input: &str) -> IResult<&str, (String, u64)> {
90 tuple((
91 take_while_m_n(0, 1, |c| "-_.".contains(c)),
92 tag_no_case("dev"),
93 take_while_m_n(0, 1, |c| "-_.".contains(c)),
94 opt(digit1),
95 ))
96 .map(|(_, _, _, n)| {
97 (
98 "dev".to_string(),
99 n.map_or(0u64, |s: &str| s.parse().unwrap()),
100 )
101 })
102 .parse(input)
103}
104
105pub fn local(input: &str) -> IResult<&str, Vec<LocalVersionPart>> {
107 preceded(
108 nomchar('+'),
109 alphanumeric1
110 .and(many0(preceded(
111 satisfy(|c| "-_.".contains(c)),
112 alphanumeric1,
113 )))
114 .map(|(first, mut rest)| {
115 rest.insert(0, first);
116 rest.into_iter()
117 .map(|s: &str| match s.parse::<u64>() {
118 Ok(n) => LocalVersionPart::Num(n),
119 Err(_) => LocalVersionPart::LowerStr(s.to_lowercase()),
120 })
121 .collect()
122 }),
123 )(input)
124}
125
126pub fn version_scheme(input: &str) -> IResult<&str, Version> {
127 tuple((
128 opt(nomchar('v')),
129 opt(epoch),
130 release,
131 opt(pre),
132 opt(post),
133 opt(dev),
134 opt(local),
135 ))
136 .map(|(_, e, r, p, po, de, lo)| Version {
137 epoch: e.unwrap_or(0u64),
138 release: r,
139 pre: p,
140 post: po,
141 dev: de,
142 local: lo,
143 })
144 .parse(input)
145}