~sircmpwn/hare-message

2f3f36111e08190ccca9f84fda923593c3c7233c — Drew DeVault 10 months ago dfedbc8
header: flesh out a bit more
1 files changed, 67 insertions(+), 5 deletions(-)

M message/header.ha
M message/header.ha => message/header.ha +67 -5
@@ 18,10 18,11 @@ fn new_header_field(key: str, val: str, raw: []u8) header_field = {
	};
};

fn header_field_finish(hf: *header_field) void = {
fn header_field_destroy(hf: *header_field) void = {
	free(hf.raw);
	free(hf.key);
	free(hf.val);
	free(hf);
};

export type header = struct {


@@ 43,9 44,9 @@ export fn new_header() header = {
// Frees state associated with a [[header]].
export fn header_finish(head: *header) void = {
	for (let i = 0z; i < len(head.fields); i += 1) {
		header_field_finish(head.fields[i]);
		free(head.fields[i]);
		header_field_destroy(head.fields[i]);
	};
	free(head.fields);

	for (let i = 0z; i < len(head.map); i += 1) {
		for (let j = 0z; j < len(head.map[i]); j += 1) {


@@ 125,6 126,50 @@ export fn header_get(head: *header, key: str) str = {
	assert(header_get(&head, "foobar") == "");
};

// Returns all of the header values associated with a given key. The returned
// slice must be freed by the caller, but the strings within it are borrowed
// from the header -- use free(), not [[strings::freeall]].
//
// Returns an empty slice if no values are set for the given key.
export fn header_values(head: *header, key: str) []str = {
	const key = canonical_mime_header_key(key);
	defer free(key);

	const i = fnv::string64(key) % HEADER_BUCKETS;
	const bucket = &head.map[i];
	for (let i = 0z; i < len(bucket); i += 1) {
		const map = &bucket[i];
		if (map.key != key) {
			continue;
		};

		let vals: []str = alloc([], len(map.fields));
		for (let i = 0z; i < len(map.fields); i += 1) {
			append(vals, map.fields[i].val);
		};
		return vals;
	};
	return [];
};

@test fn header_values() void = {
	const head = new_header();
	defer header_finish(&head);

	header_add(&head, "content-type", "text/plain");
	header_add(&head, "Content-Type", "text/html");

	const values = header_values(&head, "Content-Type");
	defer free(values);
	assert(len(values) == 2);
	assert(values[0] == "text/plain");
	assert(values[1] == "text/html");

	const values = header_values(&head, "foobar");
	defer free(values);
	assert(len(values) == 0);
};

// Deletes all header values associated with a key.
export fn header_del(head: *header, key: str) void = {
	const key = canonical_mime_header_key(key);


@@ 134,6 179,8 @@ export fn header_del(head: *header, key: str) void = {
	const bucket = &head.map[i];
	for (let i = 0z; i < len(bucket); i += 1) {
		if (bucket[i].key == key) {
			free(bucket[i].key);
			free(bucket[i].fields);
			delete(bucket[i]);
			break;
		};


@@ 143,8 190,8 @@ export fn header_del(head: *header, key: str) void = {
		if (head.fields[i].key != key) {
			continue;
		};
		header_field_finish(head.fields[i]);
		free(head.fields[i]);
		header_field_destroy(head.fields[i]);
		delete(head.fields[i]);
		i -= 1;
	};
};


@@ 171,3 218,18 @@ export fn header_set(head: *header, key: str, val: str) void = {
	header_del(head, key);
	header_add(head, key, val);
};

@test fn header_set() void = {
	const head = new_header();
	defer header_finish(&head);

	header_add(&head, "Content-Type", "text/plain");
	header_add(&head, "Content-Type", "text/html");

	header_set(&head, "Content-Type", "text/x-hare");
	const vals = header_values(&head, "Content-Type");
	defer free(vals);

	assert(len(vals) == 1);
	assert(vals[0] == "text/x-hare");
};