#![allow(dead_code)]
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct OfferedRow {
pub baseline_passed: Option<bool>,
pub baseline_check_passed: Option<bool>,
pub primary: DependencyRef,
pub offered: Option<OfferedVersion>,
pub test: TestExecution,
pub transitive: Vec<TransitiveTest>,
}
impl OfferedRow {
pub fn is_regression(&self) -> bool {
matches!(self.baseline_passed, Some(true)) && !self.test_passed()
}
pub fn test_passed(&self) -> bool {
self.test.commands.iter().all(|cmd| cmd.result.passed)
}
pub fn is_baseline(&self) -> bool {
self.offered.is_none()
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct DependencyRef {
pub dependent_name: String, pub dependent_version: String, pub spec: String, pub resolved_version: String, pub resolved_source: VersionSource, pub used_offered_version: bool, }
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct OfferedVersion {
pub version: String, pub forced: bool, pub patch_depth: crate::compile::PatchDepth, }
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TestExecution {
pub commands: Vec<TestCommand>, }
impl TestExecution {
pub fn new() -> Self {
Self { commands: Vec::new() }
}
pub fn add_command(&mut self, command: TestCommand) {
self.commands.push(command);
}
pub fn all_passed(&self) -> bool {
self.commands.iter().all(|cmd| cmd.result.passed)
}
pub fn first_failure(&self) -> Option<&TestCommand> {
self.commands.iter().find(|cmd| !cmd.result.passed)
}
}
impl Default for TestExecution {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TestCommand {
pub command: CommandType,
pub features: Vec<String>,
pub result: CommandResult,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum CommandType {
Fetch,
Check,
Test,
}
impl CommandType {
pub fn as_str(&self) -> &'static str {
match self {
CommandType::Fetch => "fetch",
CommandType::Check => "check",
CommandType::Test => "test",
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CommandResult {
pub passed: bool,
pub duration: f64,
pub failures: Vec<CrateFailure>, }
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CrateFailure {
pub crate_name: String,
pub error_message: String,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TransitiveTest {
pub dependency: DependencyRef,
pub depth: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum VersionSource {
CratesIo,
Local,
Git,
}
impl VersionSource {
pub fn as_str(&self) -> &'static str {
match self {
VersionSource::CratesIo => "crates.io",
VersionSource::Local => "local",
VersionSource::Git => "git",
}
}
}
pub fn extract_error_with_fallback(
diagnostics: &[crate::error_extract::Diagnostic],
stderr: &str,
_max_lines: usize,
) -> String {
let msg = crate::error_extract::extract_error_summary(diagnostics, 0); if !msg.is_empty() {
msg
} else {
stderr.to_string()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Version {
Semver(String),
Git { rev: String },
Latest,
}
impl Version {
pub fn display(&self) -> String {
match self {
Version::Semver(v) => v.clone(),
Version::Git { rev } => format!("git:{}", rev),
Version::Latest => "latest".to_string(),
}
}
pub fn is_resolved(&self) -> bool {
!matches!(self, Version::Latest)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum CrateSource {
Registry,
Local { path: std::path::PathBuf },
Git { url: String, rev: Option<String> },
}
impl CrateSource {
pub fn as_str(&self) -> &'static str {
match self {
CrateSource::Registry => "registry",
CrateSource::Local { .. } => "local",
CrateSource::Git { .. } => "git",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct VersionedCrate {
pub name: String,
pub version: Version,
pub source: CrateSource,
}
impl VersionedCrate {
pub fn from_registry(name: impl Into<String>, version: impl Into<String>) -> Self {
Self { name: name.into(), version: Version::Semver(version.into()), source: CrateSource::Registry }
}
pub fn from_local(name: impl Into<String>, version: impl Into<String>, path: std::path::PathBuf) -> Self {
Self { name: name.into(), version: Version::Semver(version.into()), source: CrateSource::Local { path } }
}
pub fn latest_from_registry(name: impl Into<String>) -> Self {
Self { name: name.into(), version: Version::Latest, source: CrateSource::Registry }
}
pub fn display(&self) -> String {
format!("{} {}", self.name, self.version.display())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum OverrideMode {
None,
Patch,
Force,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct VersionSpec {
pub crate_ref: VersionedCrate,
pub override_mode: OverrideMode,
pub is_baseline: bool,
}
impl VersionSpec {
pub fn baseline(crate_ref: VersionedCrate) -> Self {
Self { crate_ref, override_mode: OverrideMode::None, is_baseline: true }
}
pub fn with_patch(crate_ref: VersionedCrate) -> Self {
Self { crate_ref, override_mode: OverrideMode::Patch, is_baseline: false }
}
pub fn with_force(crate_ref: VersionedCrate) -> Self {
Self { crate_ref, override_mode: OverrideMode::Force, is_baseline: false }
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TestMatrix {
pub base_crate: String,
pub base_versions: Vec<VersionSpec>,
pub dependents: Vec<VersionSpec>,
pub staging_dir: std::path::PathBuf,
pub skip_check: bool,
pub skip_test: bool,
pub error_lines: usize,
pub patch_transitive: bool,
}
impl TestMatrix {
pub fn test_count(&self) -> usize {
self.base_versions.len() * self.dependents.len()
}
pub fn test_pairs(&self) -> impl Iterator<Item = (&VersionSpec, &VersionSpec)> {
self.base_versions.iter().flat_map(move |v| self.dependents.iter().map(move |d| (v, d)))
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BaselineComparison {
pub baseline_passed: bool,
pub baseline_version: String,
pub baseline_fetch_passed: bool,
pub baseline_check_passed: Option<bool>, pub baseline_test_passed: Option<bool>, }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TestResult {
pub base_version: VersionedCrate,
pub dependent: VersionedCrate,
pub execution: crate::compile::ThreeStepResult,
pub baseline: Option<BaselineComparison>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum TestStatus {
Baseline { passed: bool },
Passed,
Regressed,
Fixed,
StillBroken,
}
impl TestResult {
pub fn status(&self) -> TestStatus {
let current_passed = self.execution.is_success();
match &self.baseline {
None => {
TestStatus::Baseline { passed: current_passed }
}
Some(cmp) => match (cmp.baseline_passed, current_passed) {
(true, true) => TestStatus::Passed,
(true, false) => TestStatus::Regressed,
(false, true) => TestStatus::Fixed,
(false, false) => TestStatus::StillBroken,
},
}
}
pub fn is_baseline(&self) -> bool {
self.baseline.is_none()
}
pub fn passed(&self) -> bool {
self.execution.is_success()
}
pub fn is_step_regression(&self) -> bool {
let Some(cmp) = &self.baseline else {
return false; };
if cmp.baseline_fetch_passed && !self.execution.fetch.success {
return true;
}
if let Some(true) = cmp.baseline_check_passed
&& let Some(ref check) = self.execution.check
&& !check.success
{
return true;
}
if let Some(true) = cmp.baseline_test_passed
&& let Some(ref test) = self.execution.test
&& !test.success
{
return true;
}
false
}
pub fn regression_step(&self) -> Option<&'static str> {
let Some(cmp) = &self.baseline else {
return None;
};
if cmp.baseline_fetch_passed && !self.execution.fetch.success {
return Some("fetch");
}
if let Some(true) = cmp.baseline_check_passed
&& let Some(ref check) = self.execution.check
&& !check.success
{
return Some("check");
}
if let Some(true) = cmp.baseline_test_passed
&& let Some(ref test) = self.execution.test
&& !test.success
{
return Some("test");
}
None
}
}
pub fn compile_result_to_command(
compile_result: &crate::compile::CompileResult,
command_type: CommandType,
crate_name: &str,
max_error_lines: usize,
) -> TestCommand {
let failures = if !compile_result.success {
let error_msg =
extract_error_with_fallback(&compile_result.diagnostics, &compile_result.stderr, max_error_lines);
vec![CrateFailure { crate_name: crate_name.to_string(), error_message: error_msg }]
} else {
vec![]
};
TestCommand {
command: command_type,
features: vec![],
result: CommandResult {
passed: compile_result.success,
duration: compile_result.duration.as_secs_f64(),
failures,
},
}
}