mime: extract string parser

This commit is contained in:
Pierre Tachoire
2024-05-06 12:44:45 +02:00
parent 28a87c2a47
commit e42b03acd8
3 changed files with 97 additions and 87 deletions

View File

@@ -1,6 +1,10 @@
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const strparser = @import("../str/parser.zig");
const Reader = strparser.Reader;
const trim = strparser.trim;
const Self = @This(); const Self = @This();
const MimeError = error{ const MimeError = error{
@@ -21,91 +25,6 @@ pub const Empty = Self{ .mtype = "", .msubtype = "" };
pub const HTML = Self{ .mtype = "text", .msubtype = "html" }; pub const HTML = Self{ .mtype = "text", .msubtype = "html" };
pub const Javascript = Self{ .mtype = "application", .msubtype = "javascript" }; pub const Javascript = Self{ .mtype = "application", .msubtype = "javascript" };
const reader = struct {
s: []const u8,
i: usize = 0,
fn until(self: *reader, c: u8) []const u8 {
const ln = self.s.len;
const start = self.i;
while (self.i < ln) {
if (c == self.s[self.i]) return self.s[start..self.i];
self.i += 1;
}
return self.s[start..self.i];
}
fn tail(self: *reader) []const u8 {
if (self.i > self.s.len) return "";
defer self.i = self.s.len;
return self.s[self.i..];
}
fn skip(self: *reader) bool {
if (self.i >= self.s.len) return false;
self.i += 1;
return true;
}
};
test "reader.skip" {
var r = reader{ .s = "foo" };
try testing.expect(r.skip());
try testing.expect(r.skip());
try testing.expect(r.skip());
try testing.expect(!r.skip());
try testing.expect(!r.skip());
}
test "reader.tail" {
var r = reader{ .s = "foo" };
try testing.expectEqualStrings("foo", r.tail());
try testing.expectEqualStrings("", r.tail());
}
test "reader.until" {
var r = reader{ .s = "foo.bar.baz" };
try testing.expectEqualStrings("foo", r.until('.'));
_ = r.skip();
try testing.expectEqualStrings("bar", r.until('.'));
_ = r.skip();
try testing.expectEqualStrings("baz", r.until('.'));
r = reader{ .s = "foo" };
try testing.expectEqualStrings("foo", r.until('.'));
r = reader{ .s = "" };
try testing.expectEqualStrings("", r.until('.'));
}
fn trim(s: []const u8) []const u8 {
const ln = s.len;
if (ln == 0) {
return "";
}
var start: usize = 0;
while (start < ln) {
if (!std.ascii.isWhitespace(s[start])) break;
start += 1;
}
var end: usize = ln;
while (end > 0) {
if (!std.ascii.isWhitespace(s[end - 1])) break;
end -= 1;
}
return s[start..end];
}
test "trim" {
try testing.expectEqualStrings("", trim(""));
try testing.expectEqualStrings("foo", trim("foo"));
try testing.expectEqualStrings("foo", trim(" \n\tfoo"));
try testing.expectEqualStrings("foo", trim("foo \n\t"));
}
// https://mimesniff.spec.whatwg.org/#http-token-code-point // https://mimesniff.spec.whatwg.org/#http-token-code-point
fn isHTTPCodePoint(c: u8) bool { fn isHTTPCodePoint(c: u8) bool {
return switch (c) { return switch (c) {
@@ -133,7 +52,7 @@ pub fn parse(s: []const u8) Self.MimeError!Self {
if (ln > 255) return MimeError.TooBig; if (ln > 255) return MimeError.TooBig;
var res = Self{ .mtype = "", .msubtype = "" }; var res = Self{ .mtype = "", .msubtype = "" };
var r = reader{ .s = s }; var r = Reader{ .s = s };
res.mtype = trim(r.until('/')); res.mtype = trim(r.until('/'));
if (res.mtype.len == 0) return MimeError.Invalid; if (res.mtype.len == 0) return MimeError.Invalid;
@@ -150,7 +69,7 @@ pub fn parse(s: []const u8) Self.MimeError!Self {
// parse well known parameters. // parse well known parameters.
// don't check invalid parameter format. // don't check invalid parameter format.
var rp = reader{ .s = res.params }; var rp = Reader{ .s = res.params };
while (true) { while (true) {
const name = trim(rp.until('=')); const name = trim(rp.until('='));
if (!rp.skip()) return res; if (!rp.skip()) return res;

View File

@@ -264,6 +264,9 @@ test {
const dumpTest = @import("browser/dump.zig"); const dumpTest = @import("browser/dump.zig");
std.testing.refAllDecls(dumpTest); std.testing.refAllDecls(dumpTest);
const mimeTest = @import("browser/mime.zig");
std.testing.refAllDecls(mimeTest);
const cssTest = @import("css/css.zig"); const cssTest = @import("css/css.zig");
std.testing.refAllDecls(cssTest); std.testing.refAllDecls(cssTest);

88
src/str/parser.zig Normal file
View File

@@ -0,0 +1,88 @@
// some utils to parser strings.
const std = @import("std");
const testing = std.testing;
pub const Reader = struct {
s: []const u8,
i: usize = 0,
pub fn until(self: *Reader, c: u8) []const u8 {
const ln = self.s.len;
const start = self.i;
while (self.i < ln) {
if (c == self.s[self.i]) return self.s[start..self.i];
self.i += 1;
}
return self.s[start..self.i];
}
pub fn tail(self: *Reader) []const u8 {
if (self.i > self.s.len) return "";
defer self.i = self.s.len;
return self.s[self.i..];
}
pub fn skip(self: *Reader) bool {
if (self.i >= self.s.len) return false;
self.i += 1;
return true;
}
};
test "Reader.skip" {
var r = Reader{ .s = "foo" };
try testing.expect(r.skip());
try testing.expect(r.skip());
try testing.expect(r.skip());
try testing.expect(!r.skip());
try testing.expect(!r.skip());
}
test "Reader.tail" {
var r = Reader{ .s = "foo" };
try testing.expectEqualStrings("foo", r.tail());
try testing.expectEqualStrings("", r.tail());
}
test "Reader.until" {
var r = Reader{ .s = "foo.bar.baz" };
try testing.expectEqualStrings("foo", r.until('.'));
_ = r.skip();
try testing.expectEqualStrings("bar", r.until('.'));
_ = r.skip();
try testing.expectEqualStrings("baz", r.until('.'));
r = Reader{ .s = "foo" };
try testing.expectEqualStrings("foo", r.until('.'));
r = Reader{ .s = "" };
try testing.expectEqualStrings("", r.until('.'));
}
pub fn trim(s: []const u8) []const u8 {
const ln = s.len;
if (ln == 0) {
return "";
}
var start: usize = 0;
while (start < ln) {
if (!std.ascii.isWhitespace(s[start])) break;
start += 1;
}
var end: usize = ln;
while (end > 0) {
if (!std.ascii.isWhitespace(s[end - 1])) break;
end -= 1;
}
return s[start..end];
}
test "trim" {
try testing.expectEqualStrings("", trim(""));
try testing.expectEqualStrings("foo", trim("foo"));
try testing.expectEqualStrings("foo", trim(" \n\tfoo"));
try testing.expectEqualStrings("foo", trim("foo \n\t"));
}