srctree

Gregory Mullen parent d06318ac 4d70ba08
add api/repo/branch skel and tag impl

src/api.zig added: 122, removed: 19, total 103
@@ -1,9 +1,11 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
pub const Routes = @import("routes.zig");
const ROUTE = Routes.ROUTE;
pub const Context = @import("context.zig");
 
const ROUTE = Routes.ROUTE;
 
const Repo = @import("api/repo.zig");
 
const endpoints = [_]Routes.Match{
@@ -15,7 +17,7 @@ const endpoints = [_]Routes.Match{
ROUTE("issue", issue),
ROUTE("network", router),
ROUTE("patch", diff),
ROUTE("repo", Repo.repo),
ROUTE("repo", Repo.router),
ROUTE("user", user),
};
 
 
src/api/repo.zig added: 122, removed: 19, total 103
@@ -1,7 +1,25 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const API = @import("../api.zig");
const Bleach = @import("../bleach.zig");
const Git = @import("../git.zig");
const Routes = @import("../routes.zig");
 
const ROUTE = Routes.ROUTE;
 
const endpoints = [_]Routes.Match{
ROUTE("", repo),
ROUTE("branches", repoBranches),
ROUTE("tags", repoTags),
};
 
pub fn router(ctx: *API.Context) Routes.Error!Routes.Callable {
const uri_api = ctx.uri.next() orelse return repo;
if (!std.mem.eql(u8, uri_api, "repo")) return repo;
 
return Routes.router(ctx, &endpoints);
}
 
pub const Repo = struct {
name: []const u8,
@@ -13,24 +31,34 @@ pub const RepoRequest = struct {
name: []const u8,
};
 
fn openRepo(a: Allocator, raw_name: []const u8) !Git.Repo {
const dname = try a.alloc(u8, raw_name.len);
const rname = Bleach.sanitize(raw_name, dname, .{ .rules = .filename }) catch return error.InvalidName;
if (!std.mem.eql(u8, raw_name, rname)) return error.InvalidName;
 
var cwd = std.fs.cwd();
const filename = try std.fmt.allocPrint(a, "./repos/{s}", .{rname});
defer a.free(filename);
const dir = try cwd.openDir(filename, .{});
var gitrepo = try Git.Repo.init(dir);
try gitrepo.loadData(a);
return gitrepo;
}
 
pub fn repo(ctx: *API.Context) API.Routes.Error!void {
const req = try ctx.reqdata.validate(RepoRequest);
 
const dname = try ctx.alloc.alloc(u8, req.name.len);
const rname = Bleach.sanitize(req.name, dname, .{ .rules = .filename }) catch return error.BadData;
if (!std.mem.eql(u8, req.name, rname)) return error.Abusive;
 
var cwd = std.fs.cwd();
const filename = try std.fmt.allocPrint(ctx.alloc, "./repos/{s}", .{rname});
const dir = cwd.openDir(filename, .{}) catch {
ctx.response.status = .not_found;
return try ctx.sendJSON([0]Repo{});
var gitrepo = openRepo(ctx.alloc, req.name) catch |err| switch (err) {
error.InvalidName => return error.Abusive,
error.FileNotFound => {
ctx.response.status = .not_found;
return try ctx.sendJSON([0]Repo{});
},
else => {
ctx.response.status = .service_unavailable;
return try ctx.sendJSON([0]Repo{});
},
};
var gitrepo = Git.Repo.init(dir) catch {
ctx.response.status = .service_unavailable;
return try ctx.sendJSON([0]Repo{});
};
gitrepo.loadData(ctx.alloc) catch return error.Unknown;
defer gitrepo.raze(ctx.alloc);
 
const head = switch (gitrepo.HEAD(ctx.alloc) catch return error.Unknown) {
@@ -40,8 +68,81 @@ pub fn repo(ctx: *API.Context) API.Routes.Error!void {
};
 
return try ctx.sendJSON([1]Repo{.{
.name = rname,
.name = req.name,
.head = head,
.updated = "undefined",
}});
}
 
pub const RepoBranches = struct {
name: []const u8,
updated: []const u8,
branches: []const []const u8,
};
 
pub fn repoBranches(ctx: *API.Context) API.Routes.Error!void {
const req = try ctx.reqdata.validate(RepoRequest);
 
var gitrepo = openRepo(ctx.alloc, req.name) catch |err| switch (err) {
error.InvalidName => return error.Abusive,
error.FileNotFound => {
ctx.response.status = .not_found;
return try ctx.sendJSON([0]RepoBranches{});
},
else => {
ctx.response.status = .service_unavailable;
return try ctx.sendJSON([0]RepoBranches{});
},
};
defer gitrepo.raze(ctx.alloc);
 
return try ctx.sendJSON([1]RepoBranches{.{
.name = req.name,
.updated = "undefined",
.branches = &.{
"main",
},
}});
}
 
pub const RepoTags = struct {
name: []const u8,
updated: []const u8,
tags: []const []const u8,
};
 
pub fn repoTags(ctx: *API.Context) API.Routes.Error!void {
const req = try ctx.reqdata.validate(RepoRequest);
 
var gitrepo = openRepo(ctx.alloc, req.name) catch |err| switch (err) {
error.InvalidName => return error.Abusive,
error.FileNotFound => {
ctx.response.status = .not_found;
return try ctx.sendJSON([0]RepoTags{});
},
else => {
ctx.response.status = .service_unavailable;
return try ctx.sendJSON([0]RepoTags{});
},
};
defer gitrepo.raze(ctx.alloc);
 
gitrepo.loadTags(ctx.alloc) catch return error.Unknown;
 
const repotags = gitrepo.tags orelse return try ctx.sendJSON([1]RepoTags{.{
.name = req.name,
.updated = "undefined",
.tags = &.{},
}});
 
const tstack = try ctx.alloc.alloc([]const u8, repotags.len);
 
for (repotags, tstack) |tag, *out| {
out.* = tag.name;
}
return try ctx.sendJSON([1]RepoTags{.{
.name = req.name,
.updated = "undefined",
.tags = tstack,
}});
}