Lindenii Project Forge
Add some tree and log handling functions
use fs; use io; use errors; use strconv; use encoding::utf8; export type error = ( fs::error | io::error | errors::invalid | strconv::invalid | strconv::overflow | utf8::invalid | nomem );
use bytes; use crypto::sha256; use errors;
use fs; use io;
use strconv; use strings; export type tree = struct { oid: oid, entries: []tree_entry, }; export fn tree_finish(t: tree) void = { for (let entry .. t.entries) { tree_entry_finish(entry); }; free(t.entries); }; export type tree_entry = struct { mode: u32, name: []u8, oid: oid, }; export fn tree_entry_finish(te: tree_entry) void = { free(te.name); }; export fn parse_tree(id: oid, body: []u8) (tree | errors::invalid | strconv::invalid | strconv::overflow | nomem) = { let entries: []tree_entry = []; let i = 0z; for (i < len(body)) { const sp = match (bytes::index(body[i..], ' ')) { case let j: size => yield j; case void => return errors::invalid; }; const mode_b = body[i .. i+sp]; i += sp + 1z; const nul = match (bytes::index(body[i..], 0u8)) { case let j: size => yield j; case void => return errors::invalid; }; const name_b = body[i .. i+nul]; i += nul + 1z; if (i + sha256::SZ > len(body)) return errors::invalid; let child: oid = [0...]; child[..] = body[i .. i+sha256::SZ]; i += sha256::SZ; const mode_s = strings::fromutf8_unsafe(mode_b); const mode = strconv::stou32(mode_s, strconv::base::OCT)?; const name = alloc(name_b...)?; append(entries, tree_entry { mode = mode, name = name, oid = child })!; }; return tree { oid = id, entries = entries }; };
fn tree_entry_by_name_raw(t: *const tree, name: []const u8) (*const tree_entry | void) = { for (let i = 0z; i < len(t.entries); i += 1z) { if (bytes::equal(t.entries[i].name, name)) { return &t.entries[i]; }; }; return void; }; export fn tree_at_path( r: repo, root: *const tree, path: const []u8, ) (tree | blob | errors::invalid | fs::error | io::error | strconv::invalid | strconv::overflow | nomem) = { if (len(path) == 0) { match (read_object(r, root.oid)) { case let t: tree => return t; case => return errors::invalid; }; }; let cur: tree = *root; let owned_cur = false; defer if (owned_cur) { tree_finish(cur); }; let i = 0z; for (i < len(path)) { let j = match (bytes::index(path[i..], '/')) { case let k: size => yield i + k; case void => yield len(path); }; if (j == i) { return errors::invalid; }; let comp = path[i..j]; let entp = tree_entry_by_name_raw(&cur, comp); let ent: *const tree_entry = match (entp) { case let p: *const tree_entry => yield p; case void => return errors::invalid; }; let last = (j == len(path)); if (last) { match (read_object(r, ent.oid)) { case let t: tree => if (owned_cur) { tree_finish(cur); }; return t; case let b: blob => if (owned_cur) { tree_finish(cur); }; return b; case => if (owned_cur) { tree_finish(cur); }; return errors::invalid; }; } else { match (read_object(r, ent.oid)) { case let t: tree => if (owned_cur) { tree_finish(cur); }; cur = t; owned_cur = true; case => if (owned_cur) { tree_finish(cur); }; return errors::invalid; }; i = j + 1z; if (i >= len(path)) { return errors::invalid; }; }; }; return errors::invalid; };
use bytes; use errors; use fs; use io; use strings; export fn resolve_ref(r: repo, refname: const str) (oid | error) = { { match (fs::open(r.root, refname)) { case let fh: io::handle => defer io::close(fh)!; let b = io::drain(fh)?; defer free(b); let n = if (len(b) > 0 && b[len(b)-1] == '\n') { yield len(b) - 1z; } else { yield len(b); }; let s = strings::fromutf8(b[..n])?; return parse_oid(s)?; case let fe: fs::error => if (!(fe is errors::noentry)) { return fe; }; }; }; match (fs::open(r.root, "packed-refs")) { case let fh: io::handle => defer io::close(fh)!; let pr = io::drain(fh)?; defer free(pr); let want = strings::toutf8(refname); let i = 0z; for (i < len(pr)) { let e = match (bytes::index(pr[i..], '\n')) { case let j: size => yield i + j; case void => yield len(pr); }; let line = pr[i..e]; if (len(line) >= 1 && (line[0] == '#' || line[0] == '^')) { void; } else if (len(line) >= 66z) { let sp = bytes::index(line, ' '); if (sp is size) { let k = (sp: size); if (k == 64z && k + 1z < len(line)) { let name_b = line[k + 1z..]; if (bytes::equal(name_b, want)) { let hexs = strings::fromutf8(line[..k])?; return parse_oid(hexs)?; }; }; }; }; i = if (e < len(pr) && pr[e] == '\n') { yield e + 1z; } else { yield e; }; }; return errors::invalid; case let fe: fs::error => if (fe is errors::noentry) { return errors::invalid; }; return fe; }; }; export fn head_oid(r: repo) (oid | error) = { let fh = fs::open(r.root, "HEAD")?; defer io::close(fh)!; let b = io::drain(fh)?; defer free(b); let n = if (len(b) > 0 && b[len(b)-1] == '\n') { yield len(b) - 1z; } else { yield len(b); }; let line = b[..n]; const pfx = strings::toutf8("ref: "); if (len(line) >= len(pfx) && bytes::hasprefix(line, pfx)) { let rn = strings::fromutf8(line[len(pfx)..])?; return resolve_ref(r, rn); }; let s = strings::fromutf8(line)?; return parse_oid(s)?; };
use errors; use fs; use io; use strconv; export fn recent_commits(r: repo, start: oid, limit: int) ([]commit | error) = { if (limit <= 0) { let empty: []commit = []; return empty; }; let out: []commit = []; let cur = start; for (let n = 0; n < limit; n += 1) { match (read_object(r, cur)) { case let c: commit => append(out, c)!; if (len(c.parents) == 0) { return out; }; cur = c.parents[0]; case let b: blob => blob_finish(b); return errors::invalid; case let t: tree => tree_finish(t); return errors::invalid; case let e: (fs::error | io::error | errors::invalid | strconv::invalid | strconv::overflow | nomem) => return (e: error); }; }; return out; };