use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Target(pub String);
impl Target {
#[must_use]
pub fn short(&self) -> String {
let parts: Vec<&str> = self.0.split('-').collect();
match parts.as_slice() {
[arch, _vendor, os, ..] => format!("{arch}-{os}"),
[arch, os] => format!("{arch}-{os}"),
_ => self.0.clone(),
}
}
}
impl fmt::Display for Target {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RustcVersion {
pub version: semver::Version,
pub commit_hash: String,
pub llvm_version: String,
}
impl RustcVersion {
#[must_use]
pub fn short(&self) -> String {
self.version.to_string()
}
}
impl fmt::Display for RustcVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.version, self.commit_hash)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema)]
pub struct Profile {
pub opt_level: String,
pub debuginfo: u32,
pub debug_assertions: bool,
pub overflow_checks: bool,
pub panic: PanicStrategy,
#[serde(default, skip_serializing_if = "StripLevel::is_none")]
pub strip: StripLevel,
}
impl Profile {
#[must_use]
pub fn divergence(&self, requested: &Self) -> Option<(String, String)> {
let mut cached = Vec::new();
let mut wanted = Vec::new();
let mut note = |field: &str, mine: String, theirs: String| {
if mine != theirs {
cached.push(format!("{field}={mine}"));
wanted.push(format!("{field}={theirs}"));
}
};
note(
"opt-level",
self.opt_level.clone(),
requested.opt_level.clone(),
);
note(
"debuginfo",
self.debuginfo.to_string(),
requested.debuginfo.to_string(),
);
note(
"debug-assertions",
self.debug_assertions.to_string(),
requested.debug_assertions.to_string(),
);
note(
"overflow-checks",
self.overflow_checks.to_string(),
requested.overflow_checks.to_string(),
);
note(
"panic",
format!("{:?}", self.panic).to_lowercase(),
format!("{:?}", requested.panic).to_lowercase(),
);
note(
"strip",
format!("{:?}", self.strip).to_lowercase(),
format!("{:?}", requested.strip).to_lowercase(),
);
if cached.is_empty() {
return None;
}
Some((cached.join(", "), wanted.join(", ")))
}
#[must_use]
pub fn is_debug(&self) -> bool {
self.opt_level == "0" && self.debug_assertions
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema)]
pub enum PanicStrategy {
Unwind,
Abort,
}
impl PanicStrategy {
#[must_use]
pub const fn as_str(&self) -> &str {
match self {
Self::Unwind => "unwind",
Self::Abort => "abort",
}
}
}
impl fmt::Display for PanicStrategy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema,
)]
#[serde(rename_all = "lowercase")]
pub enum StripLevel {
#[default]
None,
Debuginfo,
Symbols,
}
impl StripLevel {
#[must_use]
pub const fn as_str(&self) -> &str {
match self {
Self::None => "none",
Self::Debuginfo => "debuginfo",
Self::Symbols => "symbols",
}
}
#[must_use]
pub const fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
impl fmt::Display for StripLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(test)]
mod profile_divergence_tests {
use super::{PanicStrategy, Profile, StripLevel};
fn dev() -> Profile {
Profile {
opt_level: "0".to_owned(),
debuginfo: 2,
debug_assertions: true,
overflow_checks: true,
panic: PanicStrategy::Unwind,
strip: StripLevel::None,
}
}
#[test]
fn equal_profiles_do_not_diverge() {
assert_eq!(dev().divergence(&dev()), None);
}
#[test]
fn only_the_diverging_fields_are_named() {
let requested = Profile {
debuginfo: 1,
..dev()
};
assert_eq!(
dev().divergence(&requested),
Some(("debuginfo=2".to_owned(), "debuginfo=1".to_owned()))
);
}
#[test]
fn several_diverging_fields_stay_in_parallel() {
let requested = Profile {
opt_level: "3".to_owned(),
debuginfo: 0,
..dev()
};
assert_eq!(
dev().divergence(&requested),
Some((
"opt-level=0, debuginfo=2".to_owned(),
"opt-level=3, debuginfo=0".to_owned()
))
);
}
}