lib/sandbox/src/change.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const Allocator = std.mem.Allocator;
4
5 pub const Operation = enum {
6 put,
7 delete,
8 };
9
10 pub const Kind = enum {
11 file,
12 directory,
13 sym_link,
14 other,
15 };
16
17 pub const Entry = struct {
18 path: []u8,
19 kind: Kind,
20 hash: []u8 = &.{},
21 target: []u8 = &.{},
22 bytes: u64 = 0,
23
24 pub fn clone(self: Entry, allocator: Allocator) Allocator.Error!Entry {
25 const path = try allocator.dupe(u8, self.path);
26 errdefer allocator.free(path);
27 const hash = try allocator.dupe(u8, self.hash);
28 errdefer allocator.free(hash);
29 const target = try allocator.dupe(u8, self.target);
30 return .{
31 .path = path,
32 .kind = self.kind,
33 .hash = hash,
34 .target = target,
35 .bytes = self.bytes,
36 };
37 }
38
39 pub fn deinit(self: *Entry, allocator: Allocator) void {
40 allocator.free(self.path);
41 allocator.free(self.hash);
42 allocator.free(self.target);
43 self.* = undefined;
44 }
45 };
46
47 pub const Change = struct {
48 operation: Operation,
49 entry: Entry,
50
51 pub fn clone(self: Change, allocator: Allocator) Allocator.Error!Change {
52 return .{
53 .operation = self.operation,
54 .entry = try self.entry.clone(allocator),
55 };
56 }
57
58 pub fn deinit(self: *Change, allocator: Allocator) void {
59 self.entry.deinit(allocator);
60 self.* = undefined;
61 }
62 };
63
64 pub const Set = struct {
65 allocator: Allocator,
66 items: std.ArrayList(Change) = .empty,
67
68 pub fn init(allocator: Allocator) Set {
69 return .{ .allocator = allocator };
70 }
71
72 pub fn deinit(self: *Set) void {
73 for (self.items.items) |*item| item.deinit(self.allocator);
74 self.items.deinit(self.allocator);
75 self.* = undefined;
76 }
77
78 pub fn append(self: *Set, operation: Operation, entry: Entry) Allocator.Error!void {
79 try self.items.append(self.allocator, .{
80 .operation = operation,
81 .entry = try entry.clone(self.allocator),
82 });
83 }
84
85 pub fn len(self: *const Set) usize {
86 return self.items.items.len;
87 }
88
89 pub fn get(self: *const Set, index: usize) ?Change {
90 if (index >= self.items.items.len) return null;
91 return self.items.items[index];
92 }
93
94 pub fn find(self: *const Set, path: []const u8) ?Change {
95 for (self.items.items) |item| {
96 if (std.mem.eql(u8, item.entry.path, path)) return item;
97 }
98 return null;
99 }
100 };
101
102 pub fn sameEntry(left: Entry, right: Entry) bool {
103 return left.kind == right.kind and
104 left.bytes == right.bytes and
105 std.mem.eql(u8, left.path, right.path) and
106 std.mem.eql(u8, left.hash, right.hash) and
107 std.mem.eql(u8, left.target, right.target);
108 }
109
110 test "change set owns cloned entries" {
111 var set = Set.init(std.testing.allocator);
112 defer set.deinit();
113
114 var path = try std.testing.allocator.dupe(u8, "a.txt");
115 defer std.testing.allocator.free(path);
116 var hash = try std.testing.allocator.dupe(u8, "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
117 defer std.testing.allocator.free(hash);
118
119 try set.append(.put, .{
120 .path = path,
121 .kind = .file,
122 .hash = hash,
123 .bytes = 4,
124 });
125 path[0] = 'b';
126 hash["sha256:".len] = 'b';
127
128 const found = set.find("a.txt").?;
129 try std.testing.expectEqual(Operation.put, found.operation);
130 try std.testing.expectEqual(Kind.file, found.entry.kind);
131 try std.testing.expectEqualStrings("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", found.entry.hash);
132 }