aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2026-05-01 18:33:16 +0200
committerAnhgelus Morhtuuzh <william@herges.fr>2026-05-01 18:33:21 +0200
commit3e00c224007db19cdc7869435967c5decea570a5 (patch)
tree2e08f1a91e5930c1cc6509707581c83b8508c128
parenta9e8d0e9c929bec830b086e473ef1362e1f873d9 (diff)
feat(lib): introduce document notion in C ABI
-rw-r--r--build.zig58
-rw-r--r--examples/main.c22
-rw-r--r--include/typdown.h5
-rw-r--r--src/eval/math.zig2
-rw-r--r--src/root.zig30
5 files changed, 81 insertions, 36 deletions
diff --git a/build.zig b/build.zig
index 0af4927..cf4493c 100644
--- a/build.zig
+++ b/build.zig
@@ -8,6 +8,10 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
+ const short = b.option(bool, "short", "skip long tests") orelse false;
+ const options = b.addOptions();
+ options.addOption(bool, "short", short);
+
const install = b.getInstallStep();
// build typst module
@@ -17,44 +21,58 @@ pub fn build(b: *std.Build) void {
build_typst.setCwd(b.path(TYPST));
if (optimize != .Debug) build_typst.addArg("--release");
+ const typst = b.addTranslateC(.{
+ .root_source_file = b.path(TYPST ++ "/typdown_typst.h"),
+ .link_libc = true,
+ .target = target,
+ .optimize = optimize,
+ });
+
const mod = b.addModule("typdown", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
+ .imports = &.{
+ .{ .name = "typst", .module = typst.createModule() },
+ },
});
if (!target.result.isWasiLibC()) mod.link_libc = true;
if (optimize != .Debug) mod.strip = true;
+ mod.addOptions("config", options);
// find typst module
- mod.addIncludePath(b.path(TYPST));
+ mod.linkSystemLibrary("typdown_typst", .{ .preferred_link_mode = .static });
mod.addLibraryPath(if (optimize == .Debug) b.path(TYPST_DEBUG) else b.path(TYPST_RELEASE));
const lib = b.addLibrary(.{
.name = "typdown",
- .linkage = .static,
+ .linkage = .dynamic,
.root_module = mod,
.use_llvm = true, // zig internal backend crashes during linking (for 0.15.2)
});
- // link typst module
- lib.linkSystemLibrary("typdown_typst");
const installed_lib = b.addInstallArtifact(lib, .{});
installed_lib.step.dependOn(&build_typst.step);
// when emitting headers will be fixed
//installed_lib.emitted_h = lib.getEmittedH();
- const example = b.addExecutable(.{
- .name = "example",
- .root_module = b.createModule(.{
- .target = target,
- .optimize = optimize,
- .link_libc = true,
- }),
+ const example_mod = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
});
- example.root_module.addCSourceFile(.{
+ example_mod.addCSourceFile(.{
.file = b.path("examples/main.c"),
});
- example.root_module.linkLibrary(lib);
- example.root_module.addIncludePath(b.path("include"));
+ example_mod.linkLibrary(lib);
+ example_mod.addIncludePath(b.path("include"));
+ example_mod.linkSystemLibrary("typdown_typst", .{ .preferred_link_mode = .static });
+ example_mod.addLibraryPath(if (optimize == .Debug) b.path(TYPST_DEBUG) else b.path(TYPST_RELEASE));
+
+ const example = b.addExecutable(.{
+ .name = "example",
+ .root_module = example_mod,
+ });
+ example.step.dependOn(install);
install.dependOn(&installed_lib.step);
@@ -68,24 +86,18 @@ pub fn build(b: *std.Build) void {
install.dependOn(&fmt.step);
const test_step = b.step("test", "Run tests");
- const mod_tests = b.addTest(.{
+ const exe_tests = b.addTest(.{
.root_module = mod,
.use_llvm = true, // zig internal backend crashes during linking (for 0.15.2)
});
+ exe_tests.step.dependOn(install);
- const options = b.addOptions();
- const short = b.option(bool, "short", "skip long tests") orelse false;
- options.addOption(bool, "short", short);
- mod_tests.root_module.addOptions("config", options);
-
- const run_mod_tests = b.addRunArtifact(mod_tests);
+ const run_mod_tests = b.addRunArtifact(exe_tests);
generateSVG(b, &run_mod_tests.step) catch |err| run_mod_tests.step.addError("{}\n", .{err}) catch unreachable;
- run_mod_tests.step.dependOn(install);
test_step.dependOn(&run_mod_tests.step);
const examples_step = b.step("examples", "Run examples");
const example_run = b.addRunArtifact(example);
- example_run.step.dependOn(install);
examples_step.dependOn(&example_run.step);
const check = b.step("check", "Check if foo compiles");
diff --git a/examples/main.c b/examples/main.c
index 5dd3a4b..18393c6 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -1,15 +1,25 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
-#include "typdown.h"
+#include <typdown.h>
void foo(char *v) {
uint8_t code;
- char *res = typdown_parse(v, &code);
- if (code == 0) {
- printf("%s\n", res);
- free(res);
- } else printf("cannot parse '%s', error: %s (%d)\n", v, typdown_getErrorString(code), code);
+ void *doc = typdown_parse(v, &code);
+ if (code != 0) {
+ printf("cannot parse '%s', error: %s (%d)\n", v, typdown_getErrorString(code), code);
+ typdown_free(doc);
+ return;
+ }
+ char *res = typdown_renderHTML(doc, &code);
+ if (code != 0) {
+ printf("cannot render '%s', error: %s (%d)\n", v, typdown_getErrorString(code), code);
+ typdown_free(doc);
+ return;
+ }
+ printf("%s\n", res);
+ free(res);
+ typdown_free(doc);
}
int main() {
diff --git a/include/typdown.h b/include/typdown.h
index 51821a0..8de37d8 100644
--- a/include/typdown.h
+++ b/include/typdown.h
@@ -2,4 +2,7 @@
#include <stdint.h>
char * typdown_getErrorString(uint8_t);
-char * typdown_parse(char *, uint8_t *);
+
+void * typdown_parse(char *, uint8_t *);
+void typdown_free(void *);
+char * typdown_renderHTML(void *, uint8_t *);
diff --git a/src/eval/math.zig b/src/eval/math.zig
index fa95918..071287e 100644
--- a/src/eval/math.zig
+++ b/src/eval/math.zig
@@ -1,5 +1,5 @@
const std = @import("std");
-const typst = @cImport(@cInclude("typdown_typst.h"));
+const typst = @import("typst");
const Allocator = std.mem.Allocator;
const HTML = Element.HTML;
const Element = @import("Element.zig");
diff --git a/src/root.zig b/src/root.zig
index f7899a7..3a7c134 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -54,16 +54,28 @@ var default_alloc: std.mem.Allocator =
/// Parse the content.
/// Code is a pointer to an u8 populated with an error code > 0.
///
-/// Returns a not null strings and set the code to 0 if everything is fine.
+/// Returns a non-null void pointer containing the document and set the code to 0 if everything is fine.
/// Else, it returns null and set an error code above 0.
/// Use typdown_getErrorString to retrieve the string linked with the error code.
/// Use parse if you are in Zig.
-export fn typdown_parse(content: [*:0]const u8, code: *u8) ?[*:0]const u8 {
- const doc = parse(default_alloc, std.mem.span(content)) catch |err| {
+///
+/// You must free the document with typdown_free.
+export fn typdown_parse(content: [*:0]const u8, code: *u8) ?*anyopaque {
+ return parse(default_alloc, std.mem.span(content)) catch |err| {
code.* = getErrorCode(err);
return null;
};
- defer doc.deinit();
+}
+
+/// Free the document.
+export fn typdown_free(document: *anyopaque) void {
+ const doc: *Document = @ptrCast(@alignCast(document));
+ doc.deinit();
+}
+
+/// Render an HTML from the document.
+export fn typdown_renderHTML(document: *anyopaque, code: *u8) ?[*:0]const u8 {
+ const doc: *Document = @ptrCast(@alignCast(document));
const res = doc.renderHTML(default_alloc) catch |err| {
code.* = getErrorCode(err);
return null;
@@ -88,7 +100,15 @@ fn doTest(content: [*:0]const u8, exp: []const u8, comptime exp_code: u8) !void
const expect = std.testing.expect;
var code: u8 = undefined;
- const raw = typdown_parse(content, &code) orelse {
+ const doc = typdown_parse(content, &code) orelse {
+ expect(code == exp_code) catch |err| {
+ std.debug.print("{}\n", .{code});
+ return err;
+ };
+ return;
+ };
+ defer typdown_free(doc);
+ const raw = typdown_renderHTML(doc, &code) orelse {
expect(code == exp_code) catch |err| {
std.debug.print("{}\n", .{code});
return err;