Skip to main content

onevcs/
session.rs

1//! A session: the per-run clone and worktree a change is made in, and what is
2//! left behind when one does not finish.
3
4use std::path::PathBuf;
5
6use serde::{Deserialize, Serialize};
7use url::Url;
8
9/// What to open a session over.
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct SessionRequest {
12    /// The repository: an identity key, a registered alias, or a path.
13    pub repo: String,
14    /// The branch to work on. Absent means one is derived.
15    pub branch: Option<String>,
16    /// The base to cut it from. Absent means the identity's registered base.
17    pub base: Option<String>,
18    /// Which registered checkout to clone from. Absent means the identity's
19    /// default execution checkout.
20    pub execution_checkout: Option<String>,
21}
22
23/// The handle a session is adopted, published, and closed by.
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
25#[serde(transparent)]
26pub struct SessionToken(pub String);
27
28/// An open session: a per-run shared clone plus a worktree, held under an
29/// occupancy lease.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub struct Session {
32    /// The handle this session is addressed by.
33    pub token: SessionToken,
34    /// The worktree the change is made in.
35    pub worktree: PathBuf,
36    /// The branch the worktree has checked out.
37    pub branch: String,
38    /// The base that branch was cut from.
39    pub base: String,
40}
41
42/// Where a session is in its life.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "kebab-case")]
45pub enum Lifecycle {
46    /// It has a worktree and its work has not been published or released.
47    Open,
48    /// Its worktree is gone and its branch has been handed back. The session is
49    /// still addressable, because the branch it names is still the only record of
50    /// the work.
51    Closed,
52}
53
54/// Everything the implementation that opened a session records about it.
55///
56/// A [`Session`] is the handle a caller was given; this is what the repository side
57/// knows about it afterwards, and it is why the record had to come through the
58/// interface: which repository the session belongs to, whether it is still open,
59/// and whether its branch carries an incomplete-step marker are all questions a
60/// command asks between opening a session and publishing it.
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct SessionRecord {
63    /// The session itself.
64    pub session: Session,
65    /// The identity key the session belongs to.
66    // llmlint: ignore[invalid_states_unrepresentable] an identity key is a `String`
67    // everywhere this contract spells one — `Recoverable::identity`, `Identity::origin`,
68    // and the registry document's own map key — so a newtype here would disagree with the
69    // types it names and add a public item the contract does not. Every value written here
70    // came out of a registry the implementation resolved against, which is where the key
71    // is normalized and decided.
72    pub identity: String,
73    /// Where the session is in its life.
74    pub lifecycle: Lifecycle,
75    /// What the session's branch carries now — an adopted session that was left
76    /// dirty carries [`Provenance::IncompleteStep`], and must pass the merge-path
77    /// gate through `onevcs recover` before it may be published.
78    pub provenance: Provenance,
79}
80
81/// Why a branch was preserved, and therefore what recovering it must do.
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(rename_all = "kebab-case")]
84pub enum Provenance {
85    /// The step finished; the branch is ready to publish as it stands.
86    Complete,
87    /// The step did not finish. Its work was committed behind an incomplete-step
88    /// marker and must pass the merge-path gate before it may be published.
89    IncompleteStep,
90}
91
92/// A branch that holds work outside its session.
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94pub struct PreservedBranch {
95    /// The branch the work is on.
96    pub branch: String,
97    /// The base it was cut from.
98    pub base: String,
99    /// Why it was preserved.
100    pub provenance: Provenance,
101    /// The change request it belongs to, when one was opened. Host-neutral: a
102    /// GitHub pull request and a GitLab merge request both land here.
103    pub change_url: Option<Url>,
104    /// The base that change request targets, which for a stacked branch is the
105    /// branch below it rather than the root.
106    pub change_base: Option<String>,
107}
108
109/// Which preserved work to look for.
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(rename_all = "kebab-case")]
112pub enum Scope {
113    /// Every registered identity.
114    All,
115    /// One repository: an identity key, a registered alias, or a path.
116    Repo(String),
117}
118
119/// Preserved work that has not been published, and what would land it.
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121pub struct Recoverable {
122    /// The identity the work belongs to.
123    pub identity: String,
124    /// The branch and why it was preserved.
125    pub branch: PreservedBranch,
126    /// The checkout the branch can be reached from.
127    pub checkout: PathBuf,
128    /// Why the workstream stopped.
129    pub stopped_because: String,
130    /// The argv that lands it, ready to run.
131    pub recover_command: Vec<String>,
132}