use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Status {
Open,
InProgress,
Blocked,
Closed,
Deferred,
#[serde(untagged)]
Other(String),
}
impl Status {
pub fn rank(&self) -> u8 {
match self {
Status::InProgress => 0,
Status::Blocked => 1,
Status::Open => 2,
Status::Deferred => 3,
Status::Closed => 4,
Status::Other(_) => 5,
}
}
pub fn is_closed(&self) -> bool {
matches!(self, Status::Closed)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Edge {
ParentChild,
Blocks,
#[serde(untagged)]
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dependency {
pub on: String,
pub edge: Edge,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct Unreadable {
pub read: String,
pub cause: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bead {
pub id: String,
pub title: String,
pub status: Status,
pub priority: u8,
pub issue_type: String,
pub parent: Option<String>,
pub dependencies: Vec<Dependency>,
pub metadata: BTreeMap<String, String>,
pub values: BTreeMap<String, String>,
pub created_by: Option<String>,
pub assignee: Option<String>,
pub labels: Vec<String>,
pub description: Option<String>,
pub notes: Option<String>,
pub created_at: Option<DateTime<Utc>>,
pub updated_at: Option<DateTime<Utc>>,
pub started_at: Option<DateTime<Utc>>,
pub closed_at: Option<DateTime<Utc>>,
pub defer_until: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PaneStatus {
Idle,
Working,
Blocked,
Done,
#[serde(untagged)]
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct PaneKey {
pub session: String,
pub id: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pane {
pub session: String,
pub pane_id: String,
pub cwd: PathBuf,
pub display_agent: Option<String>,
pub title: Option<String>,
pub state_labels: BTreeMap<String, String>,
pub agent_status: PaneStatus,
cwd_in_the_main_working_tree: Option<PathBuf>,
}
impl Pane {
pub fn answered(
session: String,
pane_id: String,
cwd: PathBuf,
agent_status: PaneStatus,
) -> Self {
Self {
session,
pane_id,
cwd,
display_agent: None,
title: None,
state_labels: BTreeMap::new(),
agent_status,
cwd_in_the_main_working_tree: None,
}
}
pub fn key(&self) -> PaneKey {
PaneKey {
session: self.session.clone(),
id: self.pane_id.clone(),
}
}
pub fn with_cwd_in_the_main_working_tree(mut self, cwd: Option<PathBuf>) -> Self {
self.cwd_in_the_main_working_tree = cwd;
self
}
pub fn cwd_in_the_main_working_tree(&self) -> Option<&Path> {
self.cwd_in_the_main_working_tree.as_deref()
}
pub fn caption(&self) -> Option<&str> {
let state = match &self.agent_status {
PaneStatus::Idle => "idle",
PaneStatus::Working => "working",
PaneStatus::Blocked => "blocked",
PaneStatus::Done => "done",
PaneStatus::Other(s) => s.as_str(),
};
self.state_labels
.get(state)
.map(String::as_str)
.or(self.title.as_deref())
}
}
#[cfg(feature = "testing")]
pub mod testing {
use super::{PaneKey, Unreadable};
pub const A_SESSION: &str = "default";
pub fn an_unreadable() -> Unreadable {
Unreadable {
read: "list".to_string(),
cause: "invalid type: null, expected a string at line 1 column 25".to_string(),
}
}
pub fn key(id: &str) -> PaneKey {
PaneKey {
session: A_SESSION.to_string(),
id: id.to_string(),
}
}
}