Artifact 1f5e773731c0388eba6f0e7c3b86489873ddafa4285ff36dd61562a88b990ef9:
- File
src/artifact.zig
— part of check-in
[0ae7fb2622]
at
2026-07-08 21:53:01
on branch trunk
— Scaffold zit: Zig project + RFC references + module layout
Fossil-in-Zig, ported from the four RFC drafts (docs/rfcs/). Layout mirrors the RFC layering: delta -> artifact -> repostate -> sync. delta int codec + checksum and artifact naming/Z-card implemented with RFC-derived test vectors; everything else stubbed against its section. Builds clean; zig build test green. See docs/HANDOFF.md. (user: dmestas, size: 4357) [annotate] [blame] [check-ins using]
//! Artifact Format — draft-fossil-artifact-format-00. //! //! An artifact is an immutable byte sequence named by the lowercase-hex hash //! of its content (Section 3). Structural artifacts are line-oriented "cards". //! This module owns the canonical vocabulary for the whole suite: naming, the //! card grammar, the structural types, and the two checksums (Z, R). //! //! Scope note: zit targets the version-control substrate — the check-in //! (manifest), cluster, and tag/control artifact types. The collaboration //! types (wiki, ticket, forum, attachment, technote) are carried opaquely. const std = @import("std"); /// Card-type letters. Only the version-control cards are interpreted; others /// are recognised syntactically and otherwise carried opaquely. pub const CardType = enum(u8) { B = 'B', // baseline (delta manifest) C = 'C', // comment D = 'D', // date (mtime) F = 'F', // file M = 'M', // cluster member N = 'N', // mimetype P = 'P', // parent(s) Q = 'Q', // cherrypick R = 'R', // aggregate content checksum (MD5) T = 'T', // tag U = 'U', // user Z = 'Z', // artifact self-checksum (MD5), always last }; /// Structural artifact types in scope for the version-control substrate. pub const ArtifactType = enum { manifest, // a check-in cluster, // a set of artifact hashes (sync housekeeping) control, // a tag/branch mutation other, // recognised but carried opaquely (wiki/ticket/forum/...) }; /// Hash algorithm that names an artifact (Section 3, Section 7 hash policy). /// The canonical (writer) form is always lowercase hex. pub const HashAlgo = enum { sha1, sha3_256 }; /// Compute the artifact name: the lowercase-hex hash of `content`. /// Writes into `out` and returns the populated slice (40 hex for SHA-1, /// 64 hex for SHA3-256). pub fn artifactName(algo: HashAlgo, content: []const u8, out: *[64]u8) []u8 { switch (algo) { .sha1 => { var digest: [20]u8 = undefined; std.crypto.hash.Sha1.hash(content, &digest, .{}); const hex = std.fmt.bytesToHex(digest, .lower); @memcpy(out[0..hex.len], &hex); return out[0..hex.len]; }, .sha3_256 => { var digest: [32]u8 = undefined; std.crypto.hash.sha3.Sha3_256.hash(content, &digest, .{}); const hex = std.fmt.bytesToHex(digest, .lower); @memcpy(out[0..hex.len], &hex); return out[0..hex.len]; }, } } /// The Z-card value: lowercase-hex MD5 of all stripped-content bytes that /// precede the `Z ` line (Section 6, Z card). pub fn selfChecksum(stripped_prefix: []const u8, out: *[32]u8) []u8 { var digest: [16]u8 = undefined; std.crypto.hash.Md5.hash(stripped_prefix, &digest, .{}); const hex = std.fmt.bytesToHex(digest, .lower); @memcpy(out, &hex); return out; } // --------------------------------------------------------------------------- // Manifest parsing (Sections 4, 5) — TODO // --------------------------------------------------------------------------- pub const Manifest = struct { // Populated by parse(): cards, type, name. Skeleton for now. kind: ArtifactType = .other, }; pub const ParseError = error{ SyntaxError, NotImplemented }; /// Parse a structural artifact from its bytes. See Section 4 (card syntax), /// Section 5 (types + per-type validity). Not yet implemented. pub fn parse(allocator: std.mem.Allocator, bytes: []const u8) ParseError!Manifest { _ = allocator; _ = bytes; return ParseError.NotImplemented; } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- test "artifact name = lowercase SHA3-256 (RFC example vector)" { var out: [64]u8 = undefined; // draft-fossil-artifact-format-00, R-card example: SHA3-256("hello world\n"). const name = artifactName(.sha3_256, "hello world\n", &out); try std.testing.expectEqualStrings( "a8009a7a528d87778c356da3a55d964719e818666a04e4f960c9e2439e35f138", name, ); } test "self-checksum = lowercase MD5 (empty input vector)" { var out: [32]u8 = undefined; const z = selfChecksum("", &out); try std.testing.expectEqualStrings("d41d8cd98f00b204e9800998ecf8427e", z); }