Skip to content

Commit

Permalink
compile and fmt fixes, and share implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
dgv committed Nov 15, 2024
1 parent 2e79412 commit 60957b4
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 82 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ RUN ARCH=$(uname -m) &&\
rm zig-linux-$ARCH-0.13.0.tar.xz &&\
mv zig-linux-$ARCH-0.13.0 zig
ENV PATH="/home/$USER/zig:$PATH"
ENV TMPDIR="/tmp"
ENV LD_LIBRARY_PATH="/home/$USER"
RUN git clone https://codeberg.org/ziglings/exercises/ ziglings
RUN zig build -p . --prefix-exe-dir .
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# play.zig
[![zig version](https://img.shields.io/badge/0.13.0-orange?style=flat&logo=zig&label=Zig&color=%23eba742)](https://ziglang.org/download/)
[![reference Zig](https://img.shields.io/badge/deps%20-6-orange?color=%23eba742)](https://github.com/dgv/play.zig/blob/main/build.zig.zon)
[![zon deps](https://img.shields.io/badge/deps%20-7-orange?color=%23eba742)](https://github.com/dgv/play.zig/blob/main/build.zig.zon)
[![build](https://github.com/dgv/play.zig/actions/workflows/build.yml/badge.svg)](https://github.com/dgv/play.zig/actions/workflows/build.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Expand Down
12 changes: 4 additions & 8 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@ pub fn build(b: *std.Build) !void {
.target = target,
.optimize = optimize,
}).module("s3db"));
exe.root_module.addImport("tmpfile", b.dependency("tmpfile", .{
.target = target,
.optimize = optimize,
}).module("tmpfile"));
exe.root_module.addImport("zcmd", b.dependency("zcmd", .{
.target = target,
.optimize = optimize,
}).module("zcmd"));
exe.root_module.addImport("tmpfile", b.dependency("tmpfile", .{}).module("tmpfile"));
exe.root_module.addImport("zcmd", b.dependency("zcmd", .{}).module("zcmd"));
exe.root_module.addImport("string", b.dependency("zig-string", .{}).module("string"));
exe.root_module.addImport("ziglyph", b.dependency("ziglyph", .{}).module("ziglyph"));
exe.root_module.addImport("zmpl", b.dependency("zmpl", .{
.target = target,
.optimize = optimize,
Expand Down
8 changes: 8 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
.url = "https://github.com/dgv/tmpfile.zig/archive/refs/heads/master.zip",
.hash = "1220e06b43f1b50c5b354a279bfbb152fcca34990ec515f0c6039b988047f8e04339",
},
.@"zig-string" = .{
.url = "https://github.com/JakubSzark/zig-string/archive/refs/heads/master.zip",
.hash = "122047e740a48165ed1cdd10b8c595cb5b37d2ae2128364957ba0a8de5c7dc396adf",
},
.ziglyph = .{
.url = "https://codeberg.org/dude_the_builder/ziglyph/archive/v0.13.1.tar.gz",
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
},
},
.paths = .{
"static",
Expand Down
183 changes: 110 additions & 73 deletions main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const httpz = @import("httpz");
const zmpl = @import("zmpl");
const zcmd = @import("zcmd");
const tmp = @import("tmpfile");
const ziglyph = @import("ziglyph");
const string = @import("string");
const Collator = @import("ziglyph").Collator;

const max_snippet_size = 64 * 1024;
const gpa = global.allocator();
Expand All @@ -17,13 +18,20 @@ const hello =
\\pub fn main() !void {
\\ std.debug.print("Hello from Zig {}", .{builtin.zig_version});
\\}
\\
;

var global = std.heap.GeneralPurposeAllocator(.{}){};
var db: s3db.Db = undefined;
var ziglings_list = std.ArrayList(u8).init(std.heap.page_allocator);
var ziglings_list = std.ArrayList(u8).init(gpa);

fn loadZiglings(alloc: std.mem.Allocator, zigling: []const u8) !void {
var collator = try Collator.init(alloc);
defer collator.deinit();
var temp = std.ArrayList([]u8).init(alloc);
defer temp.deinit();

fn loadZiglings() !void {
_ = try ziglings_list.toOwnedSlice();
var dir = std.fs.cwd().openDir("ziglings/exercises", .{ .iterate = true }) catch |e| switch (e) {
error.FileNotFound => {
std.debug.print("ziglings dir not found\n", .{});
Expand All @@ -37,23 +45,27 @@ fn loadZiglings() !void {
continue;
}
const html = try std.fmt.allocPrint(
gpa,
"<option value=\"{s}\">{s}</option>",
//.{ gpa.dupe(u8, file.name), gpa.dupe(u8, file.name) },
.{ file.name, file.name },
alloc,
"<option value=\"{s}\" {s}>{s}</option>",
.{ file.name, if (std.mem.eql(u8, file.name, zigling)) "selected" else "", file.name },
);
try ziglings_list.appendSlice(html);
try temp.append(html);
}

std.mem.sort([]u8, temp.items, collator, Collator.ascendingCaseless);
for (temp.items) |t| {
try ziglings_list.appendSlice(try alloc.dupe(u8, t));
}
}

pub fn main() !void {
defer ziglings_list.deinit();
try loadZiglings();
// parse env
const addr = std.process.getEnvVarOwned(gpa, "ADDR") catch "0.0.0.0";
const port = try std.fmt.parseUnsigned(u16, std.process.getEnvVarOwned(gpa, "PORT") catch "3000", 10);
// init db
try initDb();
defer db.deinit();
// server config
var server = try httpz.Server().init(gpa, .{ .address = addr, .port = port, .request = .{
.max_form_count = 4,
Expand Down Expand Up @@ -82,28 +94,33 @@ fn edit(req: *httpz.Request, res: *httpz.Response) !void {
// /p/<snippet id>
const snippet = req.param("snippet") orelse "";
if (!std.mem.eql(u8, snippet, "")) {
content = get(snippet) catch hello;
content = get(snippet) catch {
res.status = 404;
res.body = "Snippet not found";
return;
};
}
const query = try req.query();
const zigling = query.get("zigling") orelse "";

if (!std.mem.eql(u8, zigling, "")) {
try loadZiglings(req.arena, zigling);
const path = try std.fmt.allocPrint(req.arena, "./ziglings/exercises/{s}", .{zigling});
var file = std.fs.cwd().openFile(path, .{}) catch {
res.status = 404;
res.body = "Not found";
res.body = "Zigling not found";
return;
};
content = try file.reader().readAllAlloc(req.arena, max_snippet_size);
defer file.close();
} else {
try loadZiglings(req.arena, zigling);
}

var d = zmpl.Data.init(res.arena);
defer d.deinit();
var root = try d.root(.object);
var c = try ziglyph.Collator.init(req.arena);
defer c.deinit();

//std.mem.sort(u8, ziglings_list.items, c, ziglyph.Collator.ascendingCaseless);
try root.put("snippet", content);
try root.put("ziglings", ziglings_list.items);
if (zmpl.find("edit")) |template| {
Expand Down Expand Up @@ -133,51 +150,78 @@ fn static(req: *httpz.Request, res: *httpz.Response) !void {
res.body = try req.arena.dupe(u8, content);
}

fn isUnreserved(c: u8) bool {
return switch (c) {
':', ',', '?', '#', '[', ']', '@' => true,
'!', '$', '&', '\'', '(', ')', '*', '+', ';', '=' => true,
'A'...'Z', 'a'...'z', '0'...'9', '-', '.', '_', '~' => true,
'/', ' ' => true,
else => false,
};
}

fn run(req: *httpz.Request, res: *httpz.Response) !void {
var timer = try std.time.Timer.start();
defer {
const elapsed = timer.lap() / 1000;
std.log.info("{any} {s} from {any} {d}ms", .{ req.method, req.url.raw, req.address, elapsed });
std.log.info("{any} {d} {s} from {any} {d}ms", .{ req.method, res.status, req.url.raw, req.address, elapsed });
}
var f: output = undefined;
const b = try req.formData();
const code = b.get("body") orelse "";
const isFmt = std.mem.containsAtLeast(u8, req.url.raw, 1, "fmt");
f = try if (isFmt) runFmt(req.arena, code) else runCompile(req.arena, code);
var err: []const u8 = "";
if (!std.mem.eql(u8, f.stderr.?, "")) {
err = f.stderr.?;
var idx = std.mem.indexOfAny(u8, ".zig:", err).?;
if (idx > 0) idx += 5;
std.debug.print("{s}\n", .{err[idx .. err.len - 1]});
err = err[idx .. err.len - 1];
var temp = std.ArrayList(u8).init(req.arena);
defer temp.deinit();
if (!std.mem.eql(u8, f.stderr, "")) {
var splitAllStr = try string.String.init_with_contents(
req.arena,
f.stderr,
);
const a = try splitAllStr.lines();
if (a.len > 1) {
splitAllStr = a[0];
}
_ = try splitAllStr.replace(".zig", "prog.zig");
_ = try splitAllStr.replace("<stdin>", "prog.zig");
const idx = splitAllStr.find("prog.zig") orelse 0;
defer splitAllStr.deinit();
try std.Uri.Component.percentEncode(temp.writer(), splitAllStr.str()[idx..splitAllStr.len()], isUnreserved);
}

const out = temp.items;
if (isFmt) {
try res.json(.{ .Error = err, .Body = f.stdout orelse "" }, .{});
try res.json(.{ .Error = out, .Body = f.stdout }, .{});
} else {
try res.json(.{ .Errors = err, .Events = .{ .Message = f.stdout orelse "", .Kind = "stdout", .Delay = 0 }, .VetErrors = "" }, .{});
try res.json(.{ .Errors = out, .Events = .{ .Message = f.stdout, .Kind = "stdout", .Delay = 0 }, .VetErrors = "" }, .{});
}
}

fn share(req: *httpz.Request, res: *httpz.Response) !void {
_ = req;
_ = res;
var timer = try std.time.Timer.start();
defer {
const elapsed = timer.lap() / 1000;
std.log.info("{any} {d} {s} from {any} {d}ms", .{ req.method, res.status, req.url.raw, req.address, elapsed });
}
const code = req.body() orelse "";
const id = try snippetId(code);
try put(id, code);
res.body = id;
res.content_type = .TEXT;
}

fn metrics(req: *httpz.Request, res: *httpz.Response) !void {
var timer = try std.time.Timer.start();
defer {
const elapsed = timer.lap() / 1000;
std.log.info("{any} {s} from {any} {d}ms", .{ req.method, req.url.raw, req.address, elapsed });
std.log.info("{any} {d} {s} from {any} {d}ms", .{ req.method, res.status, req.url.raw, req.address, elapsed });
}
res.content_type = .TEXT;
return httpz.writeMetrics(res.writer());
}

const output = struct {
stdout: ?[]const u8,
stderr: ?[]const u8,
stdout: []const u8,
stderr: []const u8,
};

pub fn runFmt(alloc: std.mem.Allocator, code: []const u8) !output {
Expand All @@ -188,7 +232,14 @@ pub fn runFmt(alloc: std.mem.Allocator, code: []const u8) !output {
},
.stdin_input = code,
});
return output{ .stdout = result.stdout, .stderr = result.stderr };
defer result.deinit();
const out = try alloc.dupe(u8, result.stdout orelse "");
var ido = out.len;
if (out.len > 1024) ido = 1024;
const err = try alloc.dupe(u8, result.stderr orelse "");
var ide = err.len;
if (err.len > 1024) ide = 1024;
return output{ .stdout = out[0..ido], .stderr = err[0..ide] };
}

pub fn runCompile(alloc: std.mem.Allocator, code: []const u8) !output {
Expand All @@ -204,38 +255,24 @@ pub fn runCompile(alloc: std.mem.Allocator, code: []const u8) !output {
&.{ "timeout", "5s", "zig", "run", tmp_file.abs_path },
},
});
return output{ .stdout = result.stdout, .stderr = result.stderr };
defer result.deinit();
return output{ .stdout = try alloc.dupe(u8, result.stdout orelse ""), .stderr = try alloc.dupe(u8, result.stderr orelse "") };
}

fn snippetId(body: []u8) ![]const u8 {
// h := sha256.New()
// io.WriteString(h, salt)
// h.Write(s.Body)
// sum := h.Sum(nil)
// b := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
// base64.URLEncoding.Encode(b, sum)
// // Web sites don’t always linkify a trailing underscore, making it seem like
// // the link is broken. If there is an underscore at the end of the substring,
// // extend it until there is not.
// hashLen := 11
// for hashLen <= len(b) && b[hashLen-1] == '_' {
// hashLen++
// }
// return string(b)[:hashLen]
return body;
// var out: [64]u8 = undefined;
// var h = std.crypto.hash.sha2.Sha256.init(.{});
// h.update(salt);
// h.update(body);
// h.final(out[0..]);
// var b: []u8 =undefined;
// std.base64.Base64Encoder.encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8)

// var hashLen = 11;
// for (hashLen <= b.len and b[hashLen-1] == '_') {
// hashLen+=1;
// }
// return b[0..hashLen];
fn snippetId(body: []const u8) ![]const u8 {
var hash: [32]u8 = undefined;
var sha256 = std.crypto.hash.sha2.Sha256.init(.{});
sha256.update(salt);
sha256.update(body);
sha256.final(hash[0..]);
const base64 = std.base64.Base64Encoder.init(std.base64.url_safe_alphabet_chars, null);
var out_buf: [64]u8 = undefined;
const encoded = base64.encode(&out_buf, &hash);
var hashLen: usize = 11;
while (hashLen <= encoded.len and encoded[hashLen - 1] == '_') {
hashLen += 1;
}
return try gpa.dupe(u8, encoded[0..hashLen]);
}

fn initDb() !void {
Expand All @@ -245,25 +282,25 @@ fn initDb() !void {
.mode = s3db.Db.Mode{ .Memory = {} },
.open_flags = .{ .write = true },
});
defer db.deinit();

if (!std.mem.eql(u8, s3endpoint, "")) {
try db.exec("create virtual table if not exists snippets using s3db (s3_endpoint=$s3endpoint{[]const u8}, s3_bucket=$s3bucket{[]const u8}, s3_prefix='snippets', columns='key text primary key, value text')", .{}, .{ .s3endpoint = s3endpoint, .s3bucket = s3bucket });
} else {
try db.exec("create table if not exists snippets (key text primary key, value text)", .{}, .{});
}
}
fn put(id: []const u8, code: []u8) !void {
return try db.exec("insert into snippets(key, value) values($key{[]const u8}, $value{[]const u8})", .{}, .{ id, code });

fn put(id: []const u8, code: []const u8) !void {
return try db.exec("insert or replace into snippets(key, value) values($key{[]const u8}, $value{[]const u8})", .{}, .{ .key = id, .value = code });
}

fn get(id: []const u8) ![]const u8 {
// var stmt = try db.prepare("select id from user where key = $key{[]const u8}");
// defer stmt.deinit();

// const id1 = try stmt.one([]u8, .{}, .{
// .key = id,
// });

// return id1.?;
return id;
const kv = struct {
key: []const u8,
value: []const u8,
};
const code = try db.oneAlloc(kv, std.heap.page_allocator, "select id from user where key = $key{[]const u8}", .{}, .{
.key = id,
});
return if (code) |v| v.value else "";
}

0 comments on commit 60957b4

Please sign in to comment.