summaryrefslogtreecommitdiffstats
path: root/vendor/rustversion/build/rustc.rs
blob: 71a830b93a96b98f685ca2691c116dc805a48ce1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use self::Channel::*;
use std::fmt::{self, Debug};

pub enum ParseResult {
    Success(Version),
    OopsClippy,
    Unrecognized,
}

#[cfg_attr(test, derive(PartialEq))]
pub struct Version {
    pub minor: u16,
    pub patch: u16,
    pub channel: Channel,
}

#[cfg_attr(test, derive(PartialEq))]
pub enum Channel {
    Stable,
    Beta,
    Nightly(Date),
    Dev,
}

#[cfg_attr(test, derive(PartialEq))]
pub struct Date {
    pub year: u16,
    pub month: u8,
    pub day: u8,
}

pub fn parse(string: &str) -> ParseResult {
    let last_line = string.lines().last().unwrap_or(string);
    let mut words = last_line.trim().split(' ');

    match words.next() {
        Some("rustc") => {}
        Some(word) if word.starts_with("clippy") => return ParseResult::OopsClippy,
        Some(_) | None => return ParseResult::Unrecognized,
    }

    parse_words(&mut words).map_or(ParseResult::Unrecognized, ParseResult::Success)
}

fn parse_words(words: &mut dyn Iterator<Item = &str>) -> Option<Version> {
    let mut version_channel = words.next()?.split('-');
    let version = version_channel.next()?;
    let channel = version_channel.next();

    let mut digits = version.split('.');
    let major = digits.next()?;
    if major != "1" {
        return None;
    }
    let minor = digits.next()?.parse().ok()?;
    let patch = digits.next().unwrap_or("0").parse().ok()?;

    let channel = match channel {
        None => Stable,
        Some(channel) if channel == "dev" => Dev,
        Some(channel) if channel.starts_with("beta") => Beta,
        Some(channel) if channel == "nightly" => match words.next() {
            Some(hash) if hash.starts_with('(') => match words.next() {
                None if hash.ends_with(')') => Dev,
                Some(date) if date.ends_with(')') => {
                    let mut date = date[..date.len() - 1].split('-');
                    let year = date.next()?.parse().ok()?;
                    let month = date.next()?.parse().ok()?;
                    let day = date.next()?.parse().ok()?;
                    match date.next() {
                        None => Nightly(Date { year, month, day }),
                        Some(_) => return None,
                    }
                }
                None | Some(_) => return None,
            },
            Some(_) => return None,
            None => Dev,
        },
        Some(_) => return None,
    };

    Some(Version {
        minor,
        patch,
        channel,
    })
}

impl Debug for Version {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_struct("crate::version::Version")
            .field("minor", &self.minor)
            .field("patch", &self.patch)
            .field("channel", &self.channel)
            .finish()
    }
}

impl Debug for Channel {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Channel::Stable => formatter.write_str("crate::version::Channel::Stable"),
            Channel::Beta => formatter.write_str("crate::version::Channel::Beta"),
            Channel::Nightly(date) => formatter
                .debug_tuple("crate::version::Channel::Nightly")
                .field(date)
                .finish(),
            Channel::Dev => formatter.write_str("crate::version::Channel::Dev"),
        }
    }
}

impl Debug for Date {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_struct("crate::date::Date")
            .field("year", &self.year)
            .field("month", &self.month)
            .field("day", &self.day)
            .finish()
    }
}