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