1 use self::Channel::*;
2 use std::fmt::{self, Debug};
3
4 pub enum ParseResult {
5 Success(Version),
6 OopsClippy,
7 Unrecognized,
8 }
9
10 #[cfg_attr(test, derive(PartialEq))]
11 pub struct Version {
12 pub minor: u16,
13 pub patch: u16,
14 pub channel: Channel,
15 }
16
17 #[cfg_attr(test, derive(PartialEq))]
18 pub enum Channel {
19 Stable,
20 Beta,
21 Nightly(Date),
22 Dev,
23 }
24
25 #[cfg_attr(test, derive(PartialEq))]
26 pub struct Date {
27 pub year: u16,
28 pub month: u8,
29 pub day: u8,
30 }
31
parse(string: &str) -> ParseResult32 pub fn parse(string: &str) -> ParseResult {
33 let last_line = string.lines().last().unwrap_or(string);
34 let mut words = last_line.trim().split(' ');
35
36 match words.next() {
37 Some("rustc") => {}
38 Some(word) if word.starts_with("clippy") => return ParseResult::OopsClippy,
39 Some(_) | None => return ParseResult::Unrecognized,
40 }
41
42 parse_words(&mut words).map_or(ParseResult::Unrecognized, ParseResult::Success)
43 }
44
parse_words(words: &mut dyn Iterator<Item = &str>) -> Option<Version>45 fn parse_words(words: &mut dyn Iterator<Item = &str>) -> Option<Version> {
46 let mut version_channel = words.next()?.split('-');
47 let version = version_channel.next()?;
48 let channel = version_channel.next();
49
50 let mut digits = version.split('.');
51 let major = digits.next()?;
52 if major != "1" {
53 return None;
54 }
55 let minor = digits.next()?.parse().ok()?;
56 let patch = digits.next().unwrap_or("0").parse().ok()?;
57
58 let channel = match channel {
59 None => Stable,
60 Some(channel) if channel == "dev" => Dev,
61 Some(channel) if channel.starts_with("beta") => Beta,
62 Some(channel) if channel == "nightly" => match words.next() {
63 Some(hash) if hash.starts_with('(') => match words.next() {
64 None if hash.ends_with(')') => Dev,
65 Some(date) if date.ends_with(')') => {
66 let mut date = date[..date.len() - 1].split('-');
67 let year = date.next()?.parse().ok()?;
68 let month = date.next()?.parse().ok()?;
69 let day = date.next()?.parse().ok()?;
70 match date.next() {
71 None => Nightly(Date { year, month, day }),
72 Some(_) => return None,
73 }
74 }
75 None | Some(_) => return None,
76 },
77 Some(_) => return None,
78 None => Dev,
79 },
80 Some(_) => return None,
81 };
82
83 Some(Version {
84 minor,
85 patch,
86 channel,
87 })
88 }
89
90 impl Debug for Version {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result91 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
92 formatter
93 .debug_struct("crate::version::Version")
94 .field("minor", &self.minor)
95 .field("patch", &self.patch)
96 .field("channel", &self.channel)
97 .finish()
98 }
99 }
100
101 impl Debug for Channel {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result102 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
103 match self {
104 Channel::Stable => formatter.write_str("crate::version::Channel::Stable"),
105 Channel::Beta => formatter.write_str("crate::version::Channel::Beta"),
106 Channel::Nightly(date) => formatter
107 .debug_tuple("crate::version::Channel::Nightly")
108 .field(date)
109 .finish(),
110 Channel::Dev => formatter.write_str("crate::version::Channel::Dev"),
111 }
112 }
113 }
114
115 impl Debug for Date {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result116 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
117 formatter
118 .debug_struct("crate::date::Date")
119 .field("year", &self.year)
120 .field("month", &self.month)
121 .field("day", &self.day)
122 .finish()
123 }
124 }
125