1use super::event_path::{EventPath, base64_standard};
20use super::json_record::{ObjectWriter, string_array};
21use super::transaction_journal::{
23 AddedPath, Base, Conclusion, FileIdentity, IndexLockRecord, JOURNAL_SCHEMA_VERSION,
24 LandingOperation, LandingRecord, LockIdentity, PreparedRecord, PreparingRecord, RefFormat,
25 SymbolicHead, TransactionMode,
26};
27
28fn encode_base(base: &Base) -> String {
38 let mut writer: ObjectWriter = ObjectWriter::new();
39 match base {
40 Base::Unborn => {
41 writer.string("kind", "unborn");
42 }
43 Base::Commit(oid) => {
44 writer.string("kind", "commit").string("oid", oid.as_str());
45 }
46 }
47 return writer.finish();
48}
49
50fn encode_identity(identity: &FileIdentity) -> String {
60 let mut writer: ObjectWriter = ObjectWriter::new();
61 writer
62 .string("device", identity.device.as_str())
63 .string("inode", identity.inode.as_str());
64 return writer.finish();
65}
66
67fn encode_lock(lock: &LockIdentity) -> String {
77 let mut writer: ObjectWriter = ObjectWriter::new();
78 writer
79 .string("device", lock.file.device.as_str())
80 .string("inode", lock.file.inode.as_str())
81 .string("fsId", lock.fs_id.as_str());
82 return writer.finish();
83}
84
85fn push_path_list(writer: &mut ObjectWriter, key: &str, bytes_key: &str, paths: &[Vec<u8>]) {
98 let event_paths: Vec<EventPath> = paths.iter().map(|path| { return EventPath::from_git_bytes(path.as_slice()); }).collect();
99 let texts: Vec<String> = event_paths.iter().map(|path| { return String::from(path.text()); }).collect();
100 writer.raw(key, string_array(texts.as_slice()).as_str());
101 if event_paths.iter().any(|path| { return path.exact().is_some(); }) {
102 let encoded: Vec<String> = event_paths.iter().map(|path| { return base64_standard(path.bytes()); }).collect();
103 writer.raw(bytes_key, string_array(encoded.as_slice()).as_str());
104 }
105}
106
107fn encode_added_paths(records: &[AddedPath]) -> String {
117 let mut text: String = String::from("[");
118 for (index, record) in records.iter().enumerate() {
119 if index > 0 {
120 text.push(',');
121 }
122 let mut writer: ObjectWriter = ObjectWriter::new();
123 let path: EventPath = EventPath::from_git_bytes(record.path.as_slice());
124 writer.string("path", path.text());
125 if let Some(exact) = path.exact() {
126 writer.string("pathBytes", base64_standard(exact).as_str());
127 }
128 writer
129 .string("gitMode", record.git_mode.as_str())
130 .string("originalOid", record.original_oid.as_str())
131 .string("intendedOid", record.intended_oid.as_str());
132 text.push_str(writer.finish().as_str());
133 }
134 text.push(']');
135 return text;
136}
137
138fn record_head(state: &str) -> ObjectWriter {
149 let mut writer: ObjectWriter = ObjectWriter::new();
150 writer
151 .integer("schemaVersion", JOURNAL_SCHEMA_VERSION)
152 .string("state", state);
153 return writer;
154}
155
156pub fn mode_name(mode: TransactionMode) -> &'static str {
166 match mode {
167 TransactionMode::ExplicitPath => return "explicit-path",
168 TransactionMode::Index => return "index",
169 }
170}
171
172pub fn conclusion_name(conclusion: Conclusion) -> &'static str {
182 match conclusion {
183 Conclusion::None => return "none",
184 Conclusion::Amend => return "amend",
185 Conclusion::Merge => return "merge",
186 Conclusion::CherryPick => return "cherry-pick",
187 Conclusion::Revert => return "revert",
188 }
189}
190
191pub fn ref_format_name(format: RefFormat) -> &'static str {
201 match format {
202 RefFormat::Files => return "files",
203 RefFormat::Reftable => return "reftable",
204 }
205}
206
207pub fn encode_preparing(record: &PreparingRecord) -> String {
217 let mut writer: ObjectWriter = record_head("preparing");
218 writer
219 .string("transactionId", record.transaction_id.as_str())
220 .string("mode", mode_name(record.mode))
221 .raw("base", encode_base(&record.base).as_str());
222 let head: String = match &record.symbolic_head {
223 SymbolicHead::Branch(name) => {
224 let mut nested: ObjectWriter = ObjectWriter::new();
225 nested.string("kind", "branch").string("ref", name.as_str());
226 nested.finish()
227 }
228 SymbolicHead::Detached => String::from("{\"kind\":\"detached\"}"),
229 };
230 writer
231 .raw("symbolicHead", head.as_str())
232 .string("targetRef", record.target_ref.as_str())
233 .string("conclusion", conclusion_name(record.conclusion))
234 .string("repositoryRoot", record.repository_root.as_str())
235 .string("gitDir", record.git_dir.as_str())
236 .string("commonDir", record.common_dir.as_str())
237 .string("realIndexPath", record.real_index_path.as_str())
238 .string("objectDirectory", record.object_directory.as_str())
239 .string("refFormat", ref_format_name(record.ref_format))
240 .string("emptyTreeOid", record.empty_tree_oid.as_str())
241 .string("shadowPath", record.shadow_path.as_str());
242 push_path_list(&mut writer, "selectedPathspecs", "selectedPathspecBytes", record.selected_pathspecs.as_slice());
243 writer.string("invokedAt", record.invoked_at.as_str());
244 return format!("{}\n", writer.finish());
245}
246
247pub fn encode_prepared(record: &PreparedRecord) -> String {
257 let mut writer: ObjectWriter = record_head("prepared");
258 writer
259 .string("shadowPath", record.shadow_path.as_str())
260 .string("preparedOid", record.prepared_oid.as_str())
261 .boolean("signed", record.signed)
262 .string("intendedTreeOid", record.intended_tree_oid.as_str());
263 push_path_list(&mut writer, "committedPaths", "committedPathBytes", record.committed_paths.as_slice());
264 writer
265 .raw(
266 "addedPaths",
267 encode_added_paths(record.added_paths.as_slice()).as_str(),
268 )
269 .raw(
270 "selectedWorktreePaths",
271 encode_added_paths(record.selected_worktree_paths.as_slice()).as_str(),
272 );
273 return format!("{}\n", writer.finish());
274}
275
276pub fn encode_index_lock(record: &IndexLockRecord) -> String {
286 let mut writer: ObjectWriter = record_head("index-locked");
287 writer
288 .integer("attempt", record.attempt)
289 .raw("lock", encode_lock(&record.lock).as_str());
290 return format!("{}\n", writer.finish());
291}
292
293pub fn encode_landing(record: &LandingRecord) -> String {
306 let mut writer: ObjectWriter = record_head("landing");
307 writer.integer("attempt", record.attempt).string(
308 "operation",
309 match record.operation {
310 LandingOperation::Commit => "commit",
311 LandingOperation::NormalizeOnly => "normalize-only",
312 },
313 );
314 writer.raw("expectedOld", encode_base(&record.expected_old).as_str());
315 if let Some(oid) = &record.new_oid {
316 writer.string("newOid", oid.as_str());
317 }
318 writer
319 .string("landedTreeOid", record.landed_tree_oid.as_str())
320 .raw(
321 "preLandingIndex",
322 encode_identity(&record.pre_landing_index).as_str(),
323 )
324 .raw("postIndex", encode_identity(&record.post_index).as_str())
325 .raw("lock", encode_lock(&record.lock).as_str());
326 if let Some(pack) = &record.pack_name {
327 writer.string("packName", pack.as_str());
328 }
329 writer
330 .raw(
331 "addedPaths",
332 encode_added_paths(record.added_paths.as_slice()).as_str(),
333 )
334 .raw(
335 "selectedWorktreePaths",
336 encode_added_paths(record.selected_worktree_paths.as_slice()).as_str(),
337 );
338 return format!("{}\n", writer.finish());
339}
340
341pub fn encode_ref_updated(landed_oid: &str) -> String {
351 let mut writer: ObjectWriter = record_head("ref-updated");
352 writer.string("landedOid", landed_oid);
353 return format!("{}\n", writer.finish());
354}
355
356#[cfg(test)]
358#[path = "transaction_journal_encode_tests.rs"]
359pub(crate) mod tests;