Skip to main content

agent_migrate_core/transaction/
plan.rs

1use 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}