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) -> Option { 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() } }