Artifact 33fc46ed05eb2a04d3d7f6e8d34615efe76365f9771ce9a78b21376ab0d006a3:
- File
src/attachstub.zig
— part of check-in
[9033c13f21]
at
2026-09-17 17:04:25
on branch trunk
— Remove issue-tracker numbers from source and documents
The tracker is disposable and does not live with the source, so a number is a dangling pointer. Every zit#NN and #NN reference in code comments, test names, help text, smoke labels, identifiers, release notes and errata is replaced by enough context to stand alone, or by a short plain statement of the problem. No behaviour changed. (user: dmestas, size: 4205) [annotate] [blame] [check-ins using]
//! The quarry attachment stub. //! //! quarry never attaches file bytes to a hosted repository. It attaches a //! small text blob that says where the bytes are — a content hash, a size, //! a mimetype, a name — so the A card stays a valid fossil artifact, the //! store stays small, and nothing is a phantom on sync. This module only //! RECOGNISES that shape: `attach list` and `zit artifact` report such a //! blob as external instead of as its own hundred-odd bytes. A reader that //! does not recognise it sees a small text file under the attachment's name, //! which is the degraded-but-honest case. Nothing here writes one; quarry //! writes it through `zit attach add - --name NAME --mimetype TYPE < stub`. //! //! ``` //! quarry-attachment 1 //! sha256 4f9a…e01c //! size 18349022 //! type video/mp4 //! name screen-recording.mp4 //! ``` //! //! One header line naming the scheme and version, then `key value` lines. //! Unknown keys are ignored (forward compatibility); a missing required key //! or a malformed value is not a stub. const std = @import("std"); pub const header = "quarry-attachment 1\n"; /// A stub is a pointer, so it is small; anything larger that happens to /// begin with the header is a text file, not a stub. pub const max_len: usize = 4096; pub const Stub = struct { /// 64 lowercase hex — the external bytes' SHA-256. sha256: []const u8, size: u64, /// May be empty when the stub carries no `type` line. mimetype: []const u8, name: []const u8, }; /// Recognise `bytes` as a stub, borrowing its fields, or null. pub fn parse(bytes: []const u8) ?Stub { if (bytes.len > max_len or !std.mem.startsWith(u8, bytes, header)) return null; var sha: ?[]const u8 = null; var size: ?u64 = null; var mimetype: []const u8 = ""; var name: ?[]const u8 = null; var it = std.mem.splitScalar(u8, bytes[header.len..], '\n'); while (it.next()) |line| { if (line.len == 0) continue; const sp = std.mem.indexOfScalar(u8, line, ' ') orelse return null; const key = line[0..sp]; const value = line[sp + 1 ..]; if (value.len == 0) return null; if (std.mem.eql(u8, key, "sha256")) { if (value.len != 64) return null; for (value) |c| if (!std.ascii.isHex(c) or std.ascii.isUpper(c)) return null; sha = value; } else if (std.mem.eql(u8, key, "size")) { size = std.fmt.parseInt(u64, value, 10) catch return null; } else if (std.mem.eql(u8, key, "type")) { mimetype = value; } else if (std.mem.eql(u8, key, "name")) { name = value; } } return .{ .sha256 = sha orelse return null, .size = size orelse return null, .mimetype = mimetype, .name = name orelse return null, }; } test "a well-formed stub parses; unknown keys are ignored" { const s = parse(header ++ "sha256 " ++ ("4f" ** 32) ++ "\nsize 18349022\ntype video/mp4\nname screen-recording.mp4\nfuture yes\n").?; try std.testing.expectEqualStrings("4f" ** 32, s.sha256); try std.testing.expectEqual(@as(u64, 18349022), s.size); try std.testing.expectEqualStrings("video/mp4", s.mimetype); try std.testing.expectEqualStrings("screen-recording.mp4", s.name); } test "not a stub: wrong header, missing key, bad hash, bad size, too large" { try std.testing.expect(parse("quarry-attachment 2\nsha256 " ++ ("4f" ** 32) ++ "\nsize 1\nname a\n") == null); try std.testing.expect(parse(header ++ "size 1\nname a\n") == null); // no sha256 try std.testing.expect(parse(header ++ "sha256 abc\nsize 1\nname a\n") == null); try std.testing.expect(parse(header ++ "sha256 " ++ ("4F" ** 32) ++ "\nsize 1\nname a\n") == null); // uppercase try std.testing.expect(parse(header ++ "sha256 " ++ ("4f" ** 32) ++ "\nsize big\nname a\n") == null); try std.testing.expect(parse(header ++ "sha256 " ++ ("4f" ** 32) ++ "\nsize 1\n") == null); // no name try std.testing.expect(parse("PNG\x89 not text") == null); const big = header ++ "sha256 " ++ ("4f" ** 32) ++ "\nsize 1\nname a\n" ++ ("x" ** max_len); try std.testing.expect(parse(big) == null); }