- Zig 72.9%
- Nix 27.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
test / test (push) Successful in 5m19s
It names Zig 0.17.0's reason for forking packages with .path dependencies, where it named only 0.16.0's. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011iuHq9KGXBSt6R4LduFwfp |
||
| .forgejo/workflows | ||
| api | ||
| LICENSES | ||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| build.zig.zon.nix | ||
| flake.lock | ||
| flake.nix | ||
| package.nix | ||
| README.md | ||
| REUSE.toml | ||
zfj
Zig bindings for the Forgejo API. All 506 operations and 246 models of Forgejo v16.0.3, typed, with the response of every call parsed into the model the API documentation names for it.
Requires Zig 0.17. The last release for Zig 0.16 is v0.1.0, and the
zig-0.16 branch carries it.
The bindings are generated, not written
This repository holds the API description — api/swagger.v1.json, taken from
the Forgejo v16.0.3 release — and a small program that turns it into Zig. The
build runs that program and compiles what it prints; no generated source is
checked in, so the bindings cannot drift from the specification they came from.
Updating to a new Forgejo release is therefore a matter of replacing one file. See Updating the specification.
zig build leaves the generated source at zig-out/api.zig, which is the
reference for what the library actually exposes. zig build docs renders the
same thing as browsable documentation.
Using it
Add the dependency:
zig fetch --save https://git.jcollie.dev/jeff/zfj/archive/main.tar.gz
then wire the module up in build.zig:
const zfj = b.dependency("zfj", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zfj", zfj.module("zfj"));
Operations are grouped the way the Forgejo API documentation groups them, one
client per tag — IssueClient, RepositoryClient, UserClient,
OrganizationClient, AdminClient, NotificationClient, PackageClient,
SettingsClient, MiscellaneousClient, ActivitypubClient:
const std = @import("std");
const zfj = @import("zfj");
pub fn main(init: std.process.Init) !u8 {
const allocator = init.gpa;
var client = zfj.Client.init(allocator, init.io, init.environ_map.get("FORGEJO_TOKEN").?);
defer client.deinit();
client.withBaseUrl("https://codeberg.org/api/v1");
var issues = zfj.IssueClient.init(&client);
var issue = try issues.issueGetIssue(.{
.owner = "forgejo",
.repo = "forgejo",
.index = 42,
});
defer issue.deinit();
std.debug.print("{s}\n", .{issue.value().title.?});
return 0;
}
Every operation is also a plain function taking the client as its first
argument, so the call above can equally be written
zfj.issueGetIssue(&client, .{ ... }). The tag clients are a grouping over
those functions and nothing more.
Three forms of each operation are generated:
| Form | Returns | For |
|---|---|---|
issueGetIssue |
Owned(Issue) |
the usual case: a parsed model, or error.ResponseError |
issueGetIssueResult |
ApiResult(Issue) |
when the failing status code matters |
issueGetIssueRaw |
RawResponse |
when the bytes matter |
Owned(T) owns both the response body and the parsed model; deinit() frees
both, and value() borrows the model out of it. Model fields are optional
and default to null, because the API omits rather than nulls what it has no
value for.
Operations whose only successful response is empty — the deletes, mostly —
return !void.
Authentication
The token passed to Client.init is sent as Authorization: Bearer <token>.
Forgejo accepts that spelling as well as the token <token> its documentation
describes; both are read by the same code path. Pass an empty string to make
requests unauthenticated.
Where this lives
The repository's home is git.jcollie.dev, with a mirror on Tangled at https://tangled.org/jcollie.dev/zfj:
$ git clone https://git.jcollie.dev/jeff/zfj.git
Cloning with Radicle
The repository is also published on the Radicle peer-to-peer network, where a repository is found by its Repository ID rather than by a server name:
rad:z2LTU53rqByAo7pj2BM1vJHeKYJSb
With a local Radicle node running, clone it with:
$ rad clone rad:z2LTU53rqByAo7pj2BM1vJHeKYJSb
That fetches the repository, checks out the default branch, and starts seeding
it so other peers can fetch from you. To follow the repository without checking
out a working copy, use rad seed instead:
$ rad seed rad:z2LTU53rqByAo7pj2BM1vJHeKYJSb
If you already have a clone from the git remote above, you can attach it to the same Radicle repository rather than cloning again:
$ git remote add rad rad://z2LTU53rqByAo7pj2BM1vJHeKYJSb
$ git fetch rad
Updating the specification
$ zig build update-spec
fetches Forgejo's latest release and rewrites api/swagger.v1.json from it.
To take a particular release instead — an older one, or a newer one that is
not yet the latest across all of Forgejo's parallel release branches — name
its tag:
$ zig build update-spec -Dforgejo-version=v16.0.3
Then run zig build test. Nothing else in the repository names a Forgejo
version, so that is the whole of it.
The step is the only thing here that reaches the network, and nothing else
depends on it: an ordinary build never runs it. What it fetches is
templates/swagger/v1_json.tmpl from the release tag, which is the document
an instance renders and serves at /swagger.v1.json. Taking it from the tag
rather than from a running instance is what makes the vendored file say which
Forgejo it describes — an instance's copy is stamped with whatever build
happens to be deployed there.
The template is plain JSON but for two Go placeholders, the instance's version and its sub-URL, and both are required to be present. If a future Forgejo renames one or adds a third, the step fails rather than vendoring a file with a template expression left in the middle of it. What it fetched is also parsed and checked over — that it is JSON, that it is Swagger 2.0, that its version is the release asked for, and that it describes any paths at all — before it is allowed to replace the file in the source tree.
Building with Nix
$ nix build .#zfj
builds the bindings and runs the tests without touching the network, leaving
the generated api.zig in result/. The Zig dependencies come from
build.zig.zon.nix, which zon2nix
generates from build.zig.zon; regenerate it whenever a dependency changes:
$ nix develop -c zon2nix --17 --nix=build.zig.zon.nix build.zig.zon
The same dependencies are exposed on their own as .#zig-deps, for handing to
zig build --system. openapi2zig declares a .path dependency of its own,
which --system cannot build from the store, so package.nix forks it from a
copy inside the build root, as the zon2nix README describes.
License
MIT, and the repository follows the REUSE
specification — reuse lint checks it. api/swagger.v1.json is Forgejo's own,
distributed by them under the MIT license for the purpose of interoperability.