use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistorySpec {
pub source: String,
pub remote: String,
pub cleaned: String,
#[serde(rename = "commit")]
pub commits: Vec<CommitSpec>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitSpec {
pub message: String,
#[serde(default)]
pub hints: Option<String>,
#[serde(default)]
pub history: Vec<HistoryEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HistoryEntry {
Started,
CommitCreated(String),
Stuck(String),
Resolved(String),
Response(String),
Complete,
}
impl HistorySpec {
pub fn from_toml(content: &str) -> Result<Self, toml::de::Error> {
toml::from_str(content)
}
pub fn to_toml(&self) -> Result<String, toml::ser::Error> {
toml::to_string_pretty(self)
}
#[must_use]
pub fn next_pending_commit(&self) -> Option<usize> {
self.commits.iter().position(|c| !c.is_complete())
}
}
impl CommitSpec {
#[must_use]
pub fn is_complete(&self) -> bool {
matches!(self.history.last(), Some(HistoryEntry::Complete))
}
#[must_use]
pub fn is_stuck(&self) -> bool {
matches!(self.history.last(), Some(HistoryEntry::Stuck(_)))
}
#[must_use]
pub fn is_started(&self) -> bool {
matches!(self.history.last(), Some(HistoryEntry::Started))
}
#[must_use]
pub fn is_resolved(&self) -> bool {
matches!(
self.history.last(),
Some(HistoryEntry::Resolved(_) | HistoryEntry::Response(_))
)
}
#[must_use]
pub fn resolution_note(&self) -> Option<&str> {
match self.history.last() {
Some(HistoryEntry::Resolved(note) | HistoryEntry::Response(note)) => Some(note),
_ => None,
}
}
}