Skip to main content

sloop/domain/
ticket.rs

1use serde::{Deserialize, Serialize};
2
3use crate::outcome::Outcome;
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6pub struct TicketSnapshot {
7    pub id: String,
8    pub name: String,
9    pub blocked_by: Vec<String>,
10    pub worktree: Option<String>,
11    pub target: Option<String>,
12    pub model: Option<String>,
13    pub effort: Option<String>,
14    pub body: String,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum TicketState {
19    Ready,
20    Held,
21    Claimed,
22    Merged,
23    Failed,
24    NeedsReview,
25}
26
27impl TicketState {
28    pub fn as_str(self) -> &'static str {
29        match self {
30            Self::Ready => "ready",
31            Self::Held => "held",
32            Self::Claimed => "claimed",
33            Self::Merged => "merged",
34            Self::Failed => "failed",
35            Self::NeedsReview => "needs_review",
36        }
37    }
38
39    pub fn after_outcome(outcome: Outcome) -> Self {
40        match outcome {
41            Outcome::Merged => Self::Merged,
42            Outcome::Failed => Self::Failed,
43            Outcome::NeedsReview => Self::NeedsReview,
44            Outcome::Cancelled | Outcome::RateLimited | Outcome::Orphaned => Self::Ready,
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::TicketState;
52    use crate::outcome::Outcome;
53
54    #[test]
55    fn outcomes_determine_the_next_ticket_state() {
56        for (outcome, expected) in [
57            (Outcome::Merged, TicketState::Merged),
58            (Outcome::Failed, TicketState::Failed),
59            (Outcome::NeedsReview, TicketState::NeedsReview),
60            (Outcome::Cancelled, TicketState::Ready),
61            (Outcome::RateLimited, TicketState::Ready),
62            (Outcome::Orphaned, TicketState::Ready),
63        ] {
64            assert_eq!(TicketState::after_outcome(outcome), expected);
65        }
66    }
67}