From bd02977600d39e89b5ad30d49023bdc9d3b9e9b0 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sun, 12 May 2024 12:33:37 +0100 Subject: [PATCH] WIP --- build.zig.zon | 4 ++-- demo/src/app/views/mail.zig | 2 +- src/jetzig/colors.zig | 13 ++++++++++- src/jetzig/http/Headers.zig | 17 +++++++------- src/jetzig/http/Request.zig | 8 +++++++ src/jetzig/loggers/DevelopmentLogger.zig | 29 ++++++++++++------------ src/jetzig/loggers/LogQueue.zig | 4 ++++ 7 files changed, 50 insertions(+), 27 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 11cb13c..210410b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -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", }, }, diff --git a/demo/src/app/views/mail.zig b/demo/src/app/views/mail.zig index 24b93ce..5e8a2f3 100644 --- a/demo/src/app/views/mail.zig +++ b/demo/src/app/views/mail.zig @@ -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). diff --git a/src/jetzig/colors.zig b/src/jetzig/colors.zig index af08bb8..d8e9960 100644 --- a/src/jetzig/colors.zig +++ b/src/jetzig/colors.zig @@ -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; diff --git a/src/jetzig/http/Headers.zig b/src/jetzig/http/Headers.zig index ad54f7b..8db8c56 100644 --- a/src/jetzig/http/Headers.zig +++ b/src/jetzig/http/Headers.zig @@ -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"); diff --git a/src/jetzig/http/Request.zig b/src/jetzig/http/Request.zig index 6a30367..35de773 100644 --- a/src/jetzig/http/Request.zig +++ b/src/jetzig/http/Request.zig @@ -437,6 +437,14 @@ const RequestMail = struct { } }; +/// Create a new email from the mailer named `name` (`app/mailers/.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, diff --git a/src/jetzig/loggers/DevelopmentLogger.zig b/src/jetzig/loggers/DevelopmentLogger.zig index 28cb0ad..70a9e19 100644 --- a/src/jetzig/loggers/DevelopmentLogger.zig +++ b/src/jetzig/loggers/DevelopmentLogger.zig @@ -44,16 +44,14 @@ pub fn log( var timestamp_buf: [256]u8 = undefined; const iso8601 = try timestamp.iso8601(×tamp_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(×tamp_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), }; } diff --git a/src/jetzig/loggers/LogQueue.zig b/src/jetzig/loggers/LogQueue.zig index 6d9c40e..5d6350d 100644 --- a/src/jetzig/loggers/LogQueue.zig +++ b/src/jetzig/loggers/LogQueue.zig @@ -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; }