probe-run 0.2.3

Runs embedded programs just like native ones
use std::borrow::Cow;

use colored::Colorize;

#[derive(Debug, PartialEq)]
pub(crate) enum Toolchain<'p> {
    One52(One52<'p>),
    Verbatim(&'p str),
}

impl<'p> Toolchain<'p> {
    pub(crate) fn from_str(input: &str) -> Toolchain {
        if let Some(toolchain) = One52::from_str(input) {
            Toolchain::One52(toolchain)
        } else {
            Toolchain::Verbatim(input)
        }
    }

    pub(crate) fn format_highlight(&self) -> Cow<str> {
        match self {
            Toolchain::One52(toolchain) => toolchain.format_highlight().into(),
            Toolchain::Verbatim(toolchain) => Cow::Borrowed(toolchain),
        }
    }

    pub(crate) fn format_short(&self) -> Cow<str> {
        match self {
            Toolchain::One52(toolchain) => toolchain.format_short(),
            Toolchain::Verbatim(toolchain) => Cow::Borrowed(toolchain),
        }
    }
}

#[derive(Debug, PartialEq)]
pub(crate) struct One52<'p> {
    pub(crate) channel: Channel<'p>,
    pub(crate) host: &'p str,
}

impl<'p> One52<'p> {
    fn from_str(input: &'p str) -> Option<Self> {
        let (maybe_channel, rest) = input.split_once('-')?;

        Some(if maybe_channel == "stable" {
            One52 {
                channel: Channel::Stable,
                host: rest,
            }
        } else if maybe_channel == "beta" {
            One52 {
                channel: Channel::Beta,
                host: rest,
            }
        } else if maybe_channel == "nightly" {
            let maybe_year = rest.split('-').next()?;

            if maybe_year.starts_with("20") {
                let mut count = 0;
                let at_third_dash = |c: char| {
                    c == '-' && {
                        count += 1;
                        count == 3
                    }
                };

                let mut parts = rest.split(at_third_dash);

                let date = parts.next()?;
                let host = parts.next()?;
                One52 {
                    channel: Channel::Nightly { date: Some(date) },
                    host,
                }
            } else {
                One52 {
                    channel: Channel::Nightly { date: None },
                    host: rest,
                }
            }
        } else if maybe_channel.contains('.') {
            One52 {
                channel: Channel::Version(maybe_channel),
                host: rest,
            }
        } else {
            return None;
        })
    }

    fn format_highlight(&self) -> String {
        format!(
            "{}{}{}",
            self.format_short().bold(),
            "-".dimmed(),
            self.host.dimmed()
        )
    }

    fn format_short(&self) -> Cow<str> {
        match self.channel {
            Channel::Beta => "beta".into(),
            Channel::Nightly { date } => {
                if let Some(date) = date {
                    format!("nightly-{}", date).into()
                } else {
                    "nightly".into()
                }
            }
            Channel::Stable => "stable".into(),
            Channel::Version(version) => version.into(),
        }
    }
}

#[derive(Debug, PartialEq)]
pub(crate) enum Channel<'p> {
    Beta,
    Nightly { date: Option<&'p str> },
    Stable,
    Version(&'p str),
}

#[cfg(test)]
mod tests {
    use super::*;

    use rstest::rstest;

    #[rstest]
    #[case("stable-x86_64-unknown-linux-gnu", Channel::Stable, "stable")]
    #[case("beta-x86_64-unknown-linux-gnu", Channel::Beta, "beta")]
    #[case("nightly-x86_64-unknown-linux-gnu", Channel::Nightly { date: None }, "nightly")]
    #[case(
        "nightly-2021-05-01-x86_64-unknown-linux-gnu",
        Channel::Nightly { date: Some("2021-05-01") },
        "nightly-2021-05-01",
    )]
    #[case(
        "1.52.1-x86_64-unknown-linux-gnu",
        Channel::Version("1.52.1"),
        "1.52.1"
    )]
    fn end_to_end(
        #[case] input: &str,
        #[case] channel: Channel,
        #[case] expected_short_format: &str,
    ) {
        let toolchain = Toolchain::from_str(input);
        let expected = Toolchain::One52(One52 {
            channel,
            host: "x86_64-unknown-linux-gnu",
        });

        assert_eq!(expected, toolchain);

        let formatted_string = toolchain.format_short();

        assert_eq!(expected_short_format, formatted_string);
    }
}