1use crate::application::ports::RuntimeVcsPort;
15use crate::json::Value as JsonValue;
16use crate::storage::transaction::snapshot::Xid;
17use crate::RedDBResult;
18
19pub type CommitHash = String;
25
26pub type RefName = String;
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Author {
31 pub name: String,
32 pub email: String,
33}
34
35#[derive(Debug, Clone)]
36pub struct Commit {
37 pub hash: CommitHash,
38 pub root_xid: Xid,
39 pub parents: Vec<CommitHash>,
40 pub height: u64,
41 pub author: Author,
42 pub committer: Author,
43 pub message: String,
44 pub timestamp_ms: i64,
46 pub signature: Option<String>,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum RefKind {
51 Branch,
52 Tag,
53 Head,
54}
55
56#[derive(Debug, Clone)]
57pub struct Ref {
58 pub name: RefName,
59 pub kind: RefKind,
60 pub target: String,
63 pub protected: bool,
64}
65
66#[derive(Debug, Clone)]
71pub struct CreateCommitInput {
72 pub connection_id: u64,
74 pub message: String,
75 pub author: Author,
76 pub committer: Option<Author>,
78 pub amend: bool,
81 pub allow_empty: bool,
84}
85
86#[derive(Debug, Clone)]
87pub struct CreateBranchInput {
88 pub name: String,
89 pub from: Option<String>,
92 pub connection_id: u64,
93}
94
95#[derive(Debug, Clone)]
96pub struct CreateTagInput {
97 pub name: String,
98 pub target: String,
100 pub annotation: Option<String>,
101}
102
103#[derive(Debug, Clone)]
104pub enum CheckoutTarget {
105 Branch(String),
107 Commit(CommitHash),
109 Tag(String),
111}
112
113#[derive(Debug, Clone)]
114pub struct CheckoutInput {
115 pub connection_id: u64,
116 pub target: CheckoutTarget,
117 pub force: bool,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum MergeStrategy {
123 Auto,
125 NoFastForward,
127 FastForwardOnly,
129}
130
131#[derive(Debug, Clone)]
132pub struct MergeOpts {
133 pub strategy: MergeStrategy,
134 pub message: Option<String>,
136 pub abort_on_conflict: bool,
139}
140
141impl Default for MergeOpts {
142 fn default() -> Self {
143 Self {
144 strategy: MergeStrategy::Auto,
145 message: None,
146 abort_on_conflict: false,
147 }
148 }
149}
150
151#[derive(Debug, Clone)]
152pub struct MergeInput {
153 pub connection_id: u64,
154 pub from: String,
156 pub opts: MergeOpts,
157 pub author: Author,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum ResetMode {
162 Soft,
164 Mixed,
166 Hard,
168}
169
170#[derive(Debug, Clone)]
171pub struct ResetInput {
172 pub connection_id: u64,
173 pub target: String,
174 pub mode: ResetMode,
175}
176
177#[derive(Debug, Clone, Default)]
178pub struct LogRange {
179 pub to: Option<String>,
181 pub from: Option<String>,
183 pub limit: Option<usize>,
184 pub skip: Option<usize>,
185 pub no_merges: bool,
187}
188
189#[derive(Debug, Clone)]
190pub struct LogInput {
191 pub connection_id: u64,
192 pub range: LogRange,
193}
194
195#[derive(Debug, Clone)]
196pub struct DiffInput {
197 pub from: String,
199 pub to: String,
200 pub collection: Option<String>,
202 pub summary_only: bool,
204}
205
206#[derive(Debug, Clone)]
207pub struct StatusInput {
208 pub connection_id: u64,
209}
210
211#[derive(Debug, Clone)]
216pub enum AsOfSpec {
217 Commit(CommitHash),
218 Branch(String),
219 Tag(String),
220 TimestampMs(i64),
222 Snapshot(Xid),
224}
225
226#[derive(Debug, Clone)]
232pub struct DiffEntry {
233 pub collection: String,
234 pub entity_id: String,
235 pub change: DiffChange,
236}
237
238#[derive(Debug, Clone)]
239pub enum DiffChange {
240 Added { after: JsonValue },
241 Removed { before: JsonValue },
242 Modified { before: JsonValue, after: JsonValue },
243}
244
245#[derive(Debug, Clone, Default)]
246pub struct Diff {
247 pub from: CommitHash,
248 pub to: CommitHash,
249 pub entries: Vec<DiffEntry>,
250 pub added: usize,
251 pub removed: usize,
252 pub modified: usize,
253}
254
255#[derive(Debug, Clone)]
259pub struct MergeOutcome {
260 pub merge_commit: Option<Commit>,
261 pub fast_forward: bool,
262 pub conflicts: Vec<Conflict>,
263 pub merge_state_id: Option<String>,
264}
265
266impl MergeOutcome {
267 pub fn is_clean(&self) -> bool {
268 self.conflicts.is_empty()
269 }
270}
271
272#[derive(Debug, Clone)]
273pub struct Conflict {
274 pub id: String,
275 pub collection: String,
276 pub entity_id: String,
277 pub base: JsonValue,
278 pub ours: JsonValue,
279 pub theirs: JsonValue,
280 pub conflicting_paths: Vec<String>,
281 pub merge_state_id: String,
282}
283
284#[derive(Debug, Clone)]
285pub struct Status {
286 pub connection_id: u64,
287 pub head_ref: Option<RefName>,
288 pub head_commit: Option<CommitHash>,
289 pub detached: bool,
290 pub staged_changes: usize,
291 pub working_changes: usize,
292 pub unresolved_conflicts: usize,
293 pub merge_state_id: Option<String>,
294}
295
296pub struct VcsUseCases<'a, P: ?Sized> {
301 runtime: &'a P,
302}
303
304impl<'a, P: RuntimeVcsPort + ?Sized> VcsUseCases<'a, P> {
305 pub fn new(runtime: &'a P) -> Self {
306 Self { runtime }
307 }
308
309 pub fn commit(&self, input: CreateCommitInput) -> RedDBResult<Commit> {
310 self.runtime.vcs_commit(input)
311 }
312
313 pub fn branch_create(&self, input: CreateBranchInput) -> RedDBResult<Ref> {
314 self.runtime.vcs_branch_create(input)
315 }
316
317 pub fn branch_list(&self) -> RedDBResult<Vec<Ref>> {
318 self.runtime.vcs_list_refs(Some("refs/heads/"))
319 }
320
321 pub fn branch_delete(&self, name: &str) -> RedDBResult<()> {
322 self.runtime.vcs_branch_delete(name)
323 }
324
325 pub fn tag(&self, input: CreateTagInput) -> RedDBResult<Ref> {
326 self.runtime.vcs_tag_create(input)
327 }
328
329 pub fn tag_list(&self) -> RedDBResult<Vec<Ref>> {
330 self.runtime.vcs_list_refs(Some("refs/tags/"))
331 }
332
333 pub fn checkout(&self, input: CheckoutInput) -> RedDBResult<Ref> {
334 self.runtime.vcs_checkout(input)
335 }
336
337 pub fn merge(&self, input: MergeInput) -> RedDBResult<MergeOutcome> {
338 self.runtime.vcs_merge(input)
339 }
340
341 pub fn cherry_pick(
342 &self,
343 connection_id: u64,
344 commit: &str,
345 author: Author,
346 ) -> RedDBResult<MergeOutcome> {
347 self.runtime.vcs_cherry_pick(connection_id, commit, author)
348 }
349
350 pub fn revert(&self, connection_id: u64, commit: &str, author: Author) -> RedDBResult<Commit> {
351 self.runtime.vcs_revert(connection_id, commit, author)
352 }
353
354 pub fn reset(&self, input: ResetInput) -> RedDBResult<()> {
355 self.runtime.vcs_reset(input)
356 }
357
358 pub fn log(&self, input: LogInput) -> RedDBResult<Vec<Commit>> {
359 self.runtime.vcs_log(input)
360 }
361
362 pub fn diff(&self, input: DiffInput) -> RedDBResult<Diff> {
363 self.runtime.vcs_diff(input)
364 }
365
366 pub fn status(&self, input: StatusInput) -> RedDBResult<Status> {
367 self.runtime.vcs_status(input)
368 }
369
370 pub fn lca(&self, a: &str, b: &str) -> RedDBResult<Option<CommitHash>> {
371 self.runtime.vcs_lca(a, b)
372 }
373
374 pub fn conflicts_list(&self, merge_state_id: &str) -> RedDBResult<Vec<Conflict>> {
375 self.runtime.vcs_conflicts_list(merge_state_id)
376 }
377
378 pub fn conflict_resolve(&self, conflict_id: &str, resolved: JsonValue) -> RedDBResult<()> {
379 self.runtime.vcs_conflict_resolve(conflict_id, resolved)
380 }
381
382 pub fn resolve_as_of(&self, spec: AsOfSpec) -> RedDBResult<Xid> {
383 self.runtime.vcs_resolve_as_of(spec)
384 }
385
386 pub fn set_versioned(&self, collection: &str, enabled: bool) -> RedDBResult<()> {
389 self.runtime.vcs_set_versioned(collection, enabled)
390 }
391
392 pub fn list_versioned(&self) -> RedDBResult<Vec<String>> {
394 self.runtime.vcs_list_versioned()
395 }
396
397 pub fn is_versioned(&self, collection: &str) -> RedDBResult<bool> {
399 self.runtime.vcs_is_versioned(collection)
400 }
401
402 pub fn resolve_commitish(&self, spec: &str) -> RedDBResult<CommitHash> {
405 self.runtime.vcs_resolve_commitish(spec)
406 }
407}