Lindenii Project Forge
Basic commit and ident parsing
export type ident = struct { name: []u8, email: []u8, when: i64, ofs: i32, }; export fn ident_finish(p: ident) void = { free(p.name); free(p.email); }; fn parse_ident( line: []u8, ) (ident | errors::invalid | strconv::invalid | strconv::overflow | nomem) = { let mlt = bytes::index(line, '<'); if (mlt is void) { return errors::invalid; }; let lt = mlt: size; let mgt_rel = bytes::index(line[lt + 1z..], '>'); if (mgt_rel is void) { return errors::invalid; }; let gt_rel = mgt_rel: size; const gt = lt + 1z + gt_rel; const name_b = line[..lt]; const email_b = line[lt + 1z .. gt]; let rest = line[gt + 1z..]; if (len(rest) == 0 || rest[0] != ' ') { return errors::invalid; }; rest = rest[1..]; let msp = bytes::index(rest, ' '); if (msp is void) { return errors::invalid; }; let sp = msp: size; const when_s = strings::fromutf8_unsafe(rest[..sp]); const tz_b = rest[sp + 1z..]; if (len(tz_b) < 5) { return errors::invalid; }; const when = strconv::stoi64(when_s)?; let sign: i32 = 1; if (tz_b[0] == '-') { sign = -1; }; const hh = strconv::stou32(strings::fromutf8_unsafe(tz_b[1..3]), strconv::base::DEC)?; const mm = strconv::stou32(strings::fromutf8_unsafe(tz_b[3..5]), strconv::base::DEC)?; const mins: i32 = (hh: i32) * 60 + (mm: i32); const ofs: i32 = sign * mins; let name = alloc(name_b...)?; let email = alloc(email_b...)?; return ident { name = name, email = email, when = when, ofs = ofs }; };
use bytes; use errors; use strconv; use strings;
export type commit = struct { oid: oid, tree: oid, parents: []oid,
author: person, committer: person,
author: ident, committer: ident,
message: []u8, // other raw headers? }; export fn commit_finish(c: commit) void = { free(c.parents);
person_finish(c.author); person_finish(c.committer);
ident_finish(c.author); ident_finish(c.committer);
free(c.message); };
export fn parse_commit( id: oid, body: []u8, ) (commit | errors::invalid | strconv::invalid | strconv::overflow | nomem) = { let c = commit { oid = id, tree = [0...], parents = [], author = ident { name = [], email = [], when = 0, ofs = 0 }, committer = ident { name = [], email = [], when = 0, ofs = 0 }, message = [], }; let i = 0z; for (true) { let mrel = bytes::index(body[i..], '\n'); if (mrel is void) { return errors::invalid; }; let rel = mrel: size; const line = body[i .. i + rel]; if (len(line) == 0) { i += rel + 1z; break; }; if (bytes::hasprefix(line, strings::toutf8("tree "))) { const hex = strings::fromutf8_unsafe(line[5..]); match (parse_oid(hex)) { case let o: oid => c.tree = o; case nomem => return nomem; case => return errors::invalid; }; } else if (bytes::hasprefix(line, strings::toutf8("parent "))) { const hex = strings::fromutf8_unsafe(line[7..]); match (parse_oid(hex)) { case let o: oid => append(c.parents, o)!; case nomem => return nomem; case => return errors::invalid; }; } else if (bytes::hasprefix(line, strings::toutf8("author "))) { const per = parse_ident(line[7..])?; ident_finish(c.author); c.author = per; } else if (bytes::hasprefix(line, strings::toutf8("committer "))) { const per = parse_ident(line[10..])?; ident_finish(c.committer); c.committer = per; } else if ( bytes::hasprefix(line, strings::toutf8("gpgsig ")) || bytes::hasprefix(line, strings::toutf8("gpgsig-sha256 ")) ) { i += rel + 1z; for (true) { if (i >= len(body)) { return errors::invalid; }; let mnext = bytes::index(body[i..], '\n'); if (mnext is void) { return errors::invalid; }; let next = mnext: size; if (body[i] != ' ') { break; }; i += next + 1z; }; continue; }; i += rel + 1z; }; c.message = alloc(body[i..]...)?; return c; };
export type person = struct { name: []u8, email: []u8, when: i64, ofs: i32, }; export fn person_finish(p: person) void = { free(p.name); free(p.email); };