agent_migrate_core/transaction/
plan.rs1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4use serde::{Deserialize, Serialize};
5
6use crate::BundleRelativePath;
7use crate::transaction::SnapshotId;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum ConflictPolicy {
12 Fail,
13 Replace,
14 Merge,
15 Rename,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum ChangeKind {
21 Add,
22 Identical,
23 Merge,
24 Replace,
25 Rename,
26 Skip,
27 Conflict,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct PlannedWrite {
32 pub path: BundleRelativePath,
33 pub content: Vec<u8>,
34 pub mode: u32,
35}
36
37impl PlannedWrite {
38 #[must_use]
39 pub const fn new(path: BundleRelativePath, content: Vec<u8>) -> Self {
40 Self {
41 path,
42 content,
43 mode: 0o600,
44 }
45 }
46
47 #[must_use]
48 pub const fn with_mode(mut self, mode: u32) -> Self {
49 self.mode = mode & 0o777;
50 self
51 }
52}
53
54#[derive(Debug, Clone, Default, PartialEq, Eq)]
55pub struct WritePlan {
56 writes: BTreeMap<BundleRelativePath, PlannedWrite>,
57}
58
59impl WritePlan {
60 pub fn insert(&mut self, write: PlannedWrite) {
61 let _previous = self.writes.insert(write.path.clone(), write);
62 }
63
64 #[must_use]
65 pub fn get(&self, path: &BundleRelativePath) -> Option<&PlannedWrite> {
66 self.writes.get(path)
67 }
68
69 #[must_use]
70 pub fn writes(&self) -> impl ExactSizeIterator<Item = &PlannedWrite> {
71 self.writes.values()
72 }
73
74 #[must_use]
75 pub fn len(&self) -> usize {
76 self.writes.len()
77 }
78
79 #[must_use]
80 pub fn is_empty(&self) -> bool {
81 self.writes.is_empty()
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct TransactionOptions {
87 root: PathBuf,
88 state_root: PathBuf,
89 conflict_policy: ConflictPolicy,
90 dry_run: bool,
91}
92
93impl TransactionOptions {
94 #[must_use]
95 pub fn new(root: &Path, state_root: &Path) -> Self {
96 Self {
97 root: root.to_path_buf(),
98 state_root: state_root.to_path_buf(),
99 conflict_policy: ConflictPolicy::Fail,
100 dry_run: false,
101 }
102 }
103
104 #[must_use]
105 pub const fn with_conflict_policy(mut self, conflict_policy: ConflictPolicy) -> Self {
106 self.conflict_policy = conflict_policy;
107 self
108 }
109
110 #[must_use]
111 pub const fn dry_run(mut self) -> Self {
112 self.dry_run = true;
113 self
114 }
115
116 #[must_use]
117 pub fn root(&self) -> &Path {
118 &self.root
119 }
120
121 #[must_use]
122 pub fn state_root(&self) -> &Path {
123 &self.state_root
124 }
125
126 #[must_use]
127 pub const fn conflict_policy(&self) -> ConflictPolicy {
128 self.conflict_policy
129 }
130
131 #[must_use]
132 pub const fn is_dry_run(&self) -> bool {
133 self.dry_run
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub struct TransactionResult {
139 pub snapshot_id: SnapshotId,
140 pub dry_run: bool,
141 pub writes: usize,
142}