use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Outcome {
Ok,
Failed,
Skipped,
Cancelled,
}
impl Outcome {
pub fn label(self) -> &'static str {
match self {
Self::Ok => "ok",
Self::Failed => "failed",
Self::Skipped => "skipped",
Self::Cancelled => "cancelled",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Level {
Info,
Warn,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Stream {
Stdout,
Stderr,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PlannedSuite {
pub suite: String,
pub steps: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skip: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Record {
CommandStarted {
argv: Vec<String>,
cwd: String,
version: String,
pid: u32,
},
CommandFinished {
outcome: Outcome,
exit_code: i32,
duration_ms: u64,
},
Stdout { text: String },
Stderr { text: String },
Message {
level: Level,
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
code: Option<String>,
},
Failure {
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
code: Option<String>,
},
PhaseStarted {
phase: String,
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
total: Option<u64>,
},
PhaseFinished {
phase: String,
outcome: Outcome,
duration_ms: u64,
},
Progress {
#[serde(skip_serializing_if = "Option::is_none")]
phase: Option<String>,
done: u64,
#[serde(skip_serializing_if = "Option::is_none")]
total: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
},
SubprocessStarted {
id: u64,
argv: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
cwd: Option<String>,
pid: u32,
},
SubprocessOutput {
id: u64,
stream: Stream,
text: String,
},
SubprocessFinished {
id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
exit_code: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
signal: Option<i32>,
duration_ms: u64,
},
TestGroupPlanned {
group: String,
label: String,
suites: Vec<PlannedSuite>,
},
TestStarted {
suite: String,
group: String,
step: String,
#[serde(skip_serializing_if = "Option::is_none")]
log: Option<String>,
},
TestFinished {
suite: String,
group: String,
step: String,
outcome: Outcome,
duration_ms: u64,
#[serde(skip_serializing_if = "Option::is_none")]
log: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message: Option<String>,
},
TestSummary {
passed: u64,
failed: u64,
skipped: u64,
duration_ms: u64,
},
CatalogueTest {
#[serde(skip_serializing_if = "Option::is_none")]
number: Option<i64>,
name: String,
repo: String,
language: String,
file: String,
line: i64,
description: String,
},
Table {
title: String,
columns: Vec<String>,
rows: Vec<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
verdict: Option<Verdict>,
},
Artifact {
kind: String,
path: String,
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
},
WorktreeCheckout {
action: String,
worktree: String,
repository: String,
#[serde(skip_serializing_if = "Option::is_none")]
branch: Option<String>,
commit: String,
},
WorktreeLayout {
project: String,
worktree: String,
path: String,
#[serde(skip_serializing_if = "Option::is_none")]
branch: Option<String>,
shared: Vec<String>,
cloned: Vec<String>,
missing: Vec<String>,
broken: Vec<String>,
},
PeerState {
peer: String,
name: String,
fingerprint: String,
address: String,
state: String,
#[serde(skip_serializing_if = "Option::is_none")]
detail: Option<String>,
},
WorkspacePeerState {
peer: String,
workspace: String,
presence: String,
desired: String,
repositories: usize,
blocked: usize,
conflicts: usize,
},
PeerService {
peer: String,
service: String,
switch: String,
},
SyncState {
peer: String,
workspace: String,
repository: String,
desired: String,
observed: String,
#[serde(skip_serializing_if = "Option::is_none")]
blocked: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
reason: Option<String>,
pending: usize,
conflicts: usize,
#[serde(skip_serializing_if = "Option::is_none")]
branch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
head: Option<String>,
},
SyncProgress {
peer: String,
workspace: String,
repository: String,
files: usize,
bytes: u64,
},
SyncConflict {
peer: String,
workspace: String,
repository: String,
path: String,
here: String,
there: String,
},
SyncDiff {
peer: String,
workspace: String,
repository: String,
path: String,
direction: String,
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<u64>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Verdict {
pub message: String,
pub level: Level,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Line {
pub seq: u64,
pub ts: String,
#[serde(flatten)]
pub record: Record,
}
#[cfg(test)]
#[path = "record_tests.rs"]
mod tests;