use crate::weak_error;
use itertools::Itertools;
use object::{Object, ObjectSection};
use once_cell::sync;
use regex::Regex;
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
pub struct Version(pub (u32, u32, u32));
#[macro_export]
macro_rules! version_specialized {
($name: ident, $description: literal) => {
#[doc=$description]
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
pub struct $name($crate::version::Version);
impl std::ops::Deref for $name {
type Target = $crate::version::Version;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl PartialEq<$crate::version::Version> for $name {
fn eq(&self, other: &$crate::version::Version) -> bool {
let v: &$crate::version::Version = &*self;
v.eq(other)
}
}
impl PartialOrd<$crate::version::Version> for $name {
fn partial_cmp(&self, other: &$crate::version::Version) -> Option<std::cmp::Ordering> {
let v: &$crate::version::Version = &*self;
v.partial_cmp(other)
}
}
impl $name {
pub fn new(major: u32, minor: u32, patch: u32) -> $name {
$name(Version((major, minor, patch)))
}
}
};
}
version_specialized!(RustVersion, "Compiler SemVer version");
impl RustVersion {
pub fn parse(s: &str) -> Option<Self> {
static V_RE: sync::Lazy<Regex> = sync::Lazy::new(|| {
Regex::new(r"rustc version (\d+)\.(\d+)\.(\d+)").expect("must compile")
});
if let Some((_, [major, minor, patch])) = V_RE.captures_iter(s).next().map(|c| c.extract())
{
let major = weak_error!(major.parse::<u32>())?;
let minor = weak_error!(minor.parse::<u32>())?;
let patch = weak_error!(patch.parse::<u32>())?;
return Some(Self(Version((major, minor, patch))));
}
None
}
}
impl Default for RustVersion {
fn default() -> Self {
RustVersion(Version((1, 75, 0)))
}
}
#[macro_export]
macro_rules! _version_switch {
($lang_v: expr, ($major_1: expr, $minor_1: expr) ..) => {
$lang_v >= $crate::version::Version(($major_1, $minor_1, 0))
&& $lang_v <= $crate::version::Version(($major_1, u32::MAX, u32::MAX))
};
($lang_v: expr, .. ($major_2: expr, $minor_2: expr)) => {
$lang_v >= $crate::version::Version((1, 0, 0))
&& $lang_v < $crate::version::Version(($major_2, $minor_2, 0))
};
($lang_v: expr, ($major_1: expr, $minor_1: expr) .. ($major_2: expr, $minor_2: expr)) => {
$lang_v >= $crate::version::Version(($major_1, $minor_1, 0))
&& $lang_v < $crate::version::Version(($major_2, $minor_2, 0))
};
}
#[macro_export]
macro_rules! version_switch {
($lang_v: expr, $($(($major_1: tt.$minor_1: tt))? .. $(($major_2: tt.$minor_2: tt))? => $code: expr),+ $(,)?) => {
{
$(
if $crate::_version_switch!($lang_v, $(($major_1, $minor_1))? .. $(($major_2, $minor_2))?) {
Some($code)
} else
)*
{
None
}
}
};
}
macro_rules! supported {
($($ver_major: tt . $ver_minor: expr);+ $(;)?) => {
&[
$(
(RustVersion(Version(($ver_major, $ver_minor, 0))), RustVersion(Version(($ver_major, $ver_minor, u32::MAX)))),
)*
]
};
}
static SUPPORTED_RUSTC: &[(RustVersion, RustVersion)] = supported!(
1 . 75;
1 . 76;
1 . 77;
1 . 78;
1 . 79;
1 . 80;
1 . 81;
1 . 82;
1 . 83;
1 . 84;
1 . 85;
1 . 86;
1 . 87;
1 . 88;
1 . 89;
1 . 90;
1 . 91;
1 . 92;
1 . 93;
1 . 94;
1 . 95;
);
pub fn supported_versions_to_string() -> String {
format!(
"[{}]",
SUPPORTED_RUSTC
.iter()
.map(|(v, _)| format!("{}.{}.x", v.0.0.0, v.0.0.1))
.join(", ")
)
}
pub fn probe_file(obj: &object::File) -> bool {
let Some(comment_sect) = obj.section_by_name(".comment") else {
return true;
};
let Ok(data) = comment_sect.data() else {
return true;
};
let Ok(string_data) = std::str::from_utf8(data) else {
return true;
};
if let Some(version) = RustVersion::parse(string_data) {
return SUPPORTED_RUSTC
.iter()
.any(|(v_min, v_max)| version >= *v_min && version <= *v_max);
}
true
}