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}