use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionRequest {
pub repo: String,
pub branch: Option<String>,
pub base: Option<String>,
pub execution_checkout: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SessionToken(pub String);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Session {
pub token: SessionToken,
pub worktree: PathBuf,
pub branch: String,
pub base: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Lifecycle {
Open,
Closed,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionRecord {
pub session: Session,
pub identity: String,
pub lifecycle: Lifecycle,
pub provenance: Provenance,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionHolder {
pub token: SessionToken,
pub identity: String,
pub branch: String,
pub worktree: PathBuf,
pub owner_pid: u32,
pub state: Lifecycle,
pub liveness: Liveness,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Liveness {
Live,
Stale,
}
impl Liveness {
pub fn as_str(&self) -> &'static str {
match self {
Self::Live => "live",
Self::Stale => "stale",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Provenance {
Complete,
IncompleteStep,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PreservedBranch {
pub branch: String,
pub base: String,
pub provenance: Provenance,
pub change_url: Option<Url>,
pub change_base: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Scope {
All,
Repo(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Recoverable {
pub identity: String,
pub branch: PreservedBranch,
pub checkout: PathBuf,
pub stopped_because: String,
pub recover_command: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub held_by: Option<HeldBy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub net_negative: Option<NetNegative>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HeldBy {
pub token: SessionToken,
pub worktree: PathBuf,
pub holding: Holding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Holding {
OwnerRunning,
RunRootOccupied,
}
impl Holding {
pub fn because(&self) -> &'static str {
match self {
Self::OwnerRunning => "the process that opened it is still running",
Self::RunRootOccupied => "a command is working in its run root right now",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct LineChange {
pub added: u64,
pub removed: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "LineChange", into = "LineChange")]
pub struct NetNegative(LineChange);
impl NetNegative {
pub fn new(lines: LineChange) -> Option<Self> {
(lines.removed > lines.added).then_some(Self(lines))
}
pub fn added(&self) -> u64 {
self.0.added
}
pub fn removed(&self) -> u64 {
self.0.removed
}
}
impl TryFrom<LineChange> for NetNegative {
type Error = String;
fn try_from(lines: LineChange) -> std::result::Result<Self, Self::Error> {
Self::new(lines).ok_or_else(|| {
format!(
"{} line(s) added and {} removed is not a net-negative change",
lines.added, lines.removed
)
})
}
}
impl From<NetNegative> for LineChange {
fn from(value: NetNegative) -> Self {
value.0
}
}