This commit is contained in:
Bob Farrell 2024-05-12 12:33:37 +01:00
parent 91e0b44404
commit bd02977600
7 changed files with 50 additions and 27 deletions

View File

@ -23,8 +23,8 @@
.hash = "1220d4f1c2472769b0d689ea878f41f0a66cb07f28569a138aea2c0a648a5c90dd4e",
},
.httpz = .{
.url = "https://github.com/karlseguin/http.zig/archive/206a34c0ee35a07b89d000f630b2f1e0f7c98119.tar.gz",
.hash = "1220768b5925b4e13f73c036f1ca18b4a7d987ffaf5e825af6443d5d4ed8e37e7dfd",
.url = "https://github.com/karlseguin/http.zig/archive/34f1aa8a1486478414e876f65364a501d73c8a76.tar.gz",
.hash = "12205404dd8bdd98c659e844385154eb28116c1be073103fc94739bc99fa912323e8",
},
},

View File

@ -10,7 +10,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
// * `src/app/mailers/welcome/html.zmpl`
// * `src/app/mailers/welcome/text.zmpl`
// All mailer templates have access to the same template data as a view template.
const mail = request.mail("welcome", .{ .to = &.{"hello.dev"} });
const mail = request.mail("welcome", .{ .to = &.{"hello@jetzig.dev"} });
// Deliver the email asynchronously via a built-in mail Job. Use `.now` to send the email
// synchronously (i.e. before the request has returned).

View File

@ -5,7 +5,7 @@ const builtin = @import("builtin");
const types = @import("types.zig");
const codes = .{
.escape = "\x1B[",
.escape = "\x1b[",
.reset = "0;0",
.black = "0;30",
.red = "0;31",
@ -17,6 +17,17 @@ const codes = .{
.white = "0;37",
};
pub fn colorize(color: std.io.tty.Color, buf: []u8, input: []const u8, target: std.fs.File) ![]const u8 {
const config = std.io.tty.detectConfig(target);
var stream = std.io.fixedBufferStream(buf);
const writer = stream.writer();
try config.setColor(writer, color);
try writer.writeAll(input);
try config.setColor(writer, .white);
return stream.getWritten();
}
fn wrap(comptime attribute: []const u8, comptime message: []const u8) []const u8 {
if (builtin.os.tag == .windows) {
return message;

View File

@ -5,15 +5,14 @@ const httpz = @import("httpz");
const jetzig = @import("../../jetzig.zig");
allocator: std.mem.Allocator,
httpz_headers: *HttpzKeyValue,
httpz_headers: *httpz.key_value.KeyValue,
new_headers: std.ArrayList(Header),
const Headers = @This();
const Header = struct { name: []const u8, value: []const u8 };
const HttpzKeyValue = std.meta.fieldInfo(httpz.Request, std.meta.FieldEnum(httpz.Request).headers).type;
const max_bytes_header_name = jetzig.config.get(u8, "max_bytes_header_name");
pub fn init(allocator: std.mem.Allocator, httpz_headers: *HttpzKeyValue) Headers {
pub fn init(allocator: std.mem.Allocator, httpz_headers: *httpz.key_value.KeyValue) Headers {
return .{
.allocator = allocator,
.httpz_headers = httpz_headers,
@ -78,7 +77,7 @@ pub fn append(self: *Headers, name: []const u8, value: []const u8) !void {
test "append (deprecated)" {
const allocator = std.testing.allocator;
var httpz_headers = try HttpzKeyValue.init(allocator, 10);
var httpz_headers = try httpz.key_value.KeyValue.init(allocator, 10);
var headers = Headers.init(allocator, &httpz_headers);
defer headers.deinit();
try headers.append("foo", "bar");
@ -87,7 +86,7 @@ test "append (deprecated)" {
test "add" {
const allocator = std.testing.allocator;
var httpz_headers = try HttpzKeyValue.init(allocator, 10);
var httpz_headers = try httpz.key_value.KeyValue.init(allocator, 10);
var headers = Headers.init(allocator, &httpz_headers);
defer headers.deinit();
try headers.append("foo", "bar");
@ -96,7 +95,7 @@ test "add" {
test "get with multiple headers (bugfix regression test)" {
const allocator = std.testing.allocator;
var httpz_headers = try HttpzKeyValue.init(allocator, 10);
var httpz_headers = try httpz.key_value.KeyValue.init(allocator, 10);
var headers = Headers.init(allocator, &httpz_headers);
defer headers.deinit();
try headers.append("foo", "bar");
@ -106,7 +105,7 @@ test "get with multiple headers (bugfix regression test)" {
test "getAll" {
const allocator = std.testing.allocator;
var httpz_headers = try HttpzKeyValue.init(allocator, 10);
var httpz_headers = try httpz.key_value.KeyValue.init(allocator, 10);
var headers = Headers.init(allocator, &httpz_headers);
defer headers.deinit();
try headers.append("foo", "bar");
@ -119,7 +118,7 @@ test "getAll" {
test "add too many headers" {
const allocator = std.testing.allocator;
var httpz_headers = try HttpzKeyValue.init(allocator, 10);
var httpz_headers = try httpz.key_value.KeyValue.init(allocator, 10);
var headers = Headers.init(allocator, &httpz_headers);
defer headers.deinit();
for (0..10) |_| try headers.append("foo", "bar");
@ -129,7 +128,7 @@ test "add too many headers" {
test "case-insensitive matching" {
const allocator = std.testing.allocator;
var httpz_headers = try HttpzKeyValue.init(allocator, 10);
var httpz_headers = try httpz.key_value.KeyValue.init(allocator, 10);
var headers = Headers.init(allocator, &httpz_headers);
defer headers.deinit();
try headers.append("Content-Type", "bar");

View File

@ -437,6 +437,14 @@ const RequestMail = struct {
}
};
/// Create a new email from the mailer named `name` (`app/mailers/<name>.zig`). Pass delivery
/// params to override defaults defined my mailer (`to`, `from`, `subject`, etc.).
/// Must call `deliver` on the returned `RequestMail` to send the email.
/// Example:
/// ```zig
/// const mail = request.mail("welcome", .{ .to = &.{"hello@jetzig.dev"} });
/// try mail.deliver(.background, .{});
/// ```
pub fn mail(self: *Request, name: []const u8, mail_params: jetzig.mail.MailParams) RequestMail {
return .{
.request = self,

View File

@ -44,16 +44,14 @@ pub fn log(
var timestamp_buf: [256]u8 = undefined;
const iso8601 = try timestamp.iso8601(&timestamp_buf);
const colorized = switch (level) {
.TRACE, .DEBUG, .INFO => self.stdout_colorized,
.WARN, .ERROR, .FATAL => self.stderr_colorized,
};
const level_formatted = if (colorized) colorizedLogLevel(level) else @tagName(level);
var level_buf: [16]u8 = undefined;
const formatted_level = try colorizedLogLevel(level, &level_buf, self.log_queue.reader.stdout_file);
const target = jetzig.loggers.logTarget(level);
try self.log_queue.print(
"{s: >5} [{s}] {s}\n",
.{ level_formatted, iso8601, output },
.{ formatted_level, iso8601, output },
target,
);
}
@ -87,8 +85,11 @@ pub fn logRequest(self: DevelopmentLogger, request: *const jetzig.http.Request)
var timestamp_buf: [256]u8 = undefined;
const iso8601 = try timestamp.iso8601(&timestamp_buf);
var level_buf: [16]u8 = undefined;
const formatted_level = try colorizedLogLevel(.INFO, &level_buf, self.log_queue.reader.stdout_file);
try self.log_queue.print("{s: >5} [{s}] [{s}/{s}/{s}] {s}\n", .{
if (self.stdout_colorized) colorizedLogLevel(.INFO) else @tagName(.INFO),
formatted_level,
iso8601,
formatted_duration,
request.fmtMethod(self.stdout_colorized),
@ -97,13 +98,13 @@ pub fn logRequest(self: DevelopmentLogger, request: *const jetzig.http.Request)
}, .stdout);
}
fn colorizedLogLevel(comptime level: LogLevel) []const u8 {
fn colorizedLogLevel(comptime level: LogLevel, buf: []u8, file: std.fs.File) ![]const u8 {
return switch (level) {
.TRACE => jetzig.colors.white(@tagName(level)),
.DEBUG => jetzig.colors.cyan(@tagName(level)),
.INFO => jetzig.colors.blue(@tagName(level) ++ " "), // Keep logs neatly aligned
.WARN => jetzig.colors.yellow(@tagName(level) ++ " "), // "
.ERROR => jetzig.colors.red(@tagName(level)),
.FATAL => jetzig.colors.red(@tagName(level)),
.TRACE => jetzig.colors.colorize(.white, buf, @tagName(level), file),
.DEBUG => jetzig.colors.colorize(.cyan, buf, @tagName(level), file),
.INFO => jetzig.colors.colorize(.blue, buf, @tagName(level) ++ " ", file),
.WARN => jetzig.colors.colorize(.yellow, buf, @tagName(level) ++ " ", file),
.ERROR => jetzig.colors.colorize(.red, buf, @tagName(level), file),
.FATAL => jetzig.colors.colorize(.red, buf, @tagName(level), file),
};
}

View File

@ -21,6 +21,8 @@ buffer_pool: std.ArrayList(*Buffer),
position: usize,
stdout_is_tty: bool = undefined,
stderr_is_tty: bool = undefined,
stdout_colorize: bool = undefined,
stderr_colorize: bool = undefined,
state: enum { pending, ready } = .pending,
const LogQueue = @This();
@ -79,6 +81,8 @@ pub fn setFiles(self: *LogQueue, stdout_file: std.fs.File, stderr_file: std.fs.F
};
self.stdout_is_tty = stdout_file.isTty();
self.stderr_is_tty = stderr_file.isTty();
self.stdout_colorize = std.io.tty.detectConfig(stdout_file) != .no_color;
self.stderr_colorize = std.io.tty.detectConfig(stderr_file) != .no_color;
self.state = .ready;
}