| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const top = @This(); | 3 | const top = @This(); |
| | 4 | const time = @import("time"); |
| 4 | | 5 | |
| 5 | // 40 is length of sha1 hash | 6 | // 40 is length of sha1 hash |
| 6 | pub const Id = *const [40]u8; | 7 | pub const Id = *const [40]u8; |
| ... | @@ -99,8 +100,8 @@ pub fn parseCommit(alloc: std.mem.Allocator, commitfile: string) !Commit { | ... | @@ -99,8 +100,8 @@ pub fn parseCommit(alloc: std.mem.Allocator, commitfile: string) !Commit { |
| 99 | const k = line[0..space]; | 100 | const k = line[0..space]; |
| 100 | | 101 | |
| 101 | if (std.mem.eql(u8, k, "tree")) result.tree = .{ .id = line[space + 1 ..][0..40] }; | 102 | if (std.mem.eql(u8, k, "tree")) result.tree = .{ .id = line[space + 1 ..][0..40] }; |
| 102 | if (std.mem.eql(u8, k, "author")) result.author = line[space + 1 ..]; | 103 | if (std.mem.eql(u8, k, "author")) result.author = try parseCommitUserAndAt(line[space + 1 ..]); |
| 103 | if (std.mem.eql(u8, k, "committer")) result.committer = line[space + 1 ..]; | 104 | if (std.mem.eql(u8, k, "committer")) result.committer = try parseCommitUserAndAt(line[space + 1 ..]); |
| 104 | if (std.mem.eql(u8, k, "parent")) try parents.append(.{ .id = line[space + 1 ..][0..40] }); | 105 | if (std.mem.eql(u8, k, "parent")) try parents.append(.{ .id = line[space + 1 ..][0..40] }); |
| 105 | } | 106 | } |
| 106 | result.parents = try parents.toOwnedSlice(); | 107 | result.parents = try parents.toOwnedSlice(); |
| ... | @@ -108,13 +109,39 @@ pub fn parseCommit(alloc: std.mem.Allocator, commitfile: string) !Commit { | ... | @@ -108,13 +109,39 @@ pub fn parseCommit(alloc: std.mem.Allocator, commitfile: string) !Commit { |
| 108 | return result; | 109 | return result; |
| 109 | } | 110 | } |
| 110 | | 111 | |
| | 112 | fn parseCommitUserAndAt(input: string) !Commit.UserAndAt { |
| | 113 | // Mitchell Hashimoto <mitchell.hashimoto@gmail.com> 1680797363 -0700 |
| | 114 | // first and second part is https://datatracker.ietf.org/doc/html/rfc5322#section-3.4 |
| | 115 | // third part is unix epoch timestamp |
| | 116 | // fourth part is TZ |
| | 117 | var maybe_bad_parser = std.mem.splitBackwards(u8, input, " "); |
| | 118 | const tz_part = maybe_bad_parser.next() orelse return error.BadCommitTz; |
| | 119 | const time_part = maybe_bad_parser.next() orelse return error.BadCommitTime; |
| | 120 | const email_part = maybe_bad_parser.next() orelse return error.BadCommitEmail; |
| | 121 | const name_part = maybe_bad_parser.rest(); |
| | 122 | _ = tz_part; |
| | 123 | std.debug.assert(email_part[0] == '<'); |
| | 124 | std.debug.assert(email_part[email_part.len - 1] == '>'); |
| | 125 | return .{ |
| | 126 | .name = name_part, |
| | 127 | .email = std.mem.trim(u8, email_part, "<>"), |
| | 128 | .at = time.DateTime.initUnix(try std.fmt.parseInt(u64, time_part, 10)), |
| | 129 | }; |
| | 130 | } |
| | 131 | |
| 111 | pub const Commit = struct { | 132 | pub const Commit = struct { |
| 112 | tree: TreeId, | 133 | tree: TreeId, |
| 113 | parents: []const CommitId, | 134 | parents: []const CommitId, |
| 114 | author: string, | 135 | author: UserAndAt, |
| 115 | committer: string, | 136 | committer: UserAndAt, |
| 116 | gpgsig: string, | 137 | gpgsig: string, |
| 117 | message: string, | 138 | message: string, |
| | 139 | |
| | 140 | pub const UserAndAt = struct { |
| | 141 | name: string, |
| | 142 | email: string, |
| | 143 | at: time.DateTime, |
| | 144 | }; |
| 118 | }; | 145 | }; |
| 119 | | 146 | |
| 120 | pub fn parseTree(alloc: std.mem.Allocator, treefile: string) !Tree { | 147 | pub fn parseTree(alloc: std.mem.Allocator, treefile: string) !Tree { |