Lindenii Project Forge
Login

hare-git

Git library for Hare

Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.

/git/ident.ha (raw)

use bytes;
use errors;
use encoding::utf8;
use strings;
use strconv;

// Author/committer identity and its associated timestamp
// and timezone offset.
export type ident = struct {
	name: []u8,
	email: []u8,
	when: i64,
	ofs: i32,
};

// Frees resources associated with an [[ident]].
export fn ident_finish(p: ident) void = {
	free(p.name);
	free(p.email);
};

// Parses an [[ident]] from its canonical byte-slice representation.
fn ident_parse(
	line: []u8,
) (ident | errors::invalid | strconv::invalid | strconv::overflow | utf8::invalid | 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(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(tz_b[1..3])?, strconv::base::DEC)?;
	const mm = strconv::stou32(strings::fromutf8(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 };
};

// Returns the canonical form for an ident.
export fn ident_serialize(p: ident) ([]u8 | nomem) = {
	const whens = strconv::i64tos(p.when);
	const whenb = strings::toutf8(whens);

	let mins = p.ofs;
	let sign: u8 = '+';
	if (mins < 0) {
		sign = '-';
		mins = -mins;
	};
	let hh: u32 = ((mins / 60): u32);
	let mm: u32 = ((mins % 60): u32);

	let outlen = len(p.name) + 2z + len(p.email) + 2z + len(whenb) + 1z + 5z;
	let out = alloc([0u8...], outlen)?;
	let pos = 0z;

	out[pos .. pos + len(p.name)] = p.name;
	pos += len(p.name);

	out[pos] = ' ';
	pos += 1z;

	out[pos] = '<';
	pos += 1z;

	out[pos .. pos + len(p.email)] = p.email;
	pos += len(p.email);

	out[pos] = '>';
	pos += 1z;

	out[pos] = ' ';
	pos += 1z;

	out[pos .. pos + len(whenb)] = whenb;
	pos += len(whenb);

	out[pos] = ' ';
	pos += 1z;

	out[pos] = sign;
	pos += 1z;

	out[pos] = ('0' + (hh / 10u32): u8);
	pos += 1z;
	out[pos] = ('0' + (hh % 10u32): u8);
	pos += 1z;

	out[pos] = ('0' + (mm / 10u32): u8);
	pos += 1z;
	out[pos] = ('0' + (mm % 10u32): u8);
	pos += 1z;

	return out;
};