Skip to content

Commit

Permalink
Zig SDK (#531)
Browse files Browse the repository at this point in the history
* zig sdk

* add Zig to `sdks.md`
  • Loading branch information
jmstevers authored Jan 27, 2025
1 parent 46773b5 commit 44d758a
Show file tree
Hide file tree
Showing 17 changed files with 1,408 additions and 0 deletions.
6 changes: 6 additions & 0 deletions build/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ var Consts = &ConstTemplateData{
Icon: "vscode-icons:file-type-java",
SdkUrl: "https://github.com/starfederation/datastar/tree/main/sdk/java",
},
{
FileExtension: "zig",
Name: "Zig",
Icon: "vscode-icons:file-type-zig",
SdkUrl: "https://github.com/starfederation/datastar/tree/main/sdk/zig",
},
},
DatastarKey: "datastar",
DefaultBools: []*DefaultBool{
Expand Down
83 changes: 83 additions & 0 deletions build/consts_zig.qtpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{%- func zigConsts(data *ConstTemplateData) -%}
// {%s data.DoNotEdit %}

const std = @import("std");

pub const datastar_key = "{%s data.DatastarKey %}";
pub const version = "{%s data.Version %}";

// #region Defaults

// #region Default durations

{%- for _, d := range data.DefaultDurations -%}
/// {%s= d.Description %}
pub const default_{%s d.Name.Snake %} = {%d durationToMs(d.Duration) %};
{%- endfor -%}

// #endregion

// #region Default strings

{%- for _, s := range data.DefaultStrings -%}
/// {%s= s.Description %}
pub const default_{%s s.Name.Snake %} = "{%s s.Value %}";
{%- endfor -%}

// #endregion

// #region Datalines

{%- for _, literal := range data.DatalineLiterals -%}
pub const {%s literal.Snake %}_dataline_literal = "{%s literal.Camel %}";
{%- endfor -%}

// #endregion

// #region Default booleans

{%- for _, b := range data.DefaultBools -%}
/// {%s= b.Description %}
pub const default_{%s b.Name.Snake %} = {%v b.Value %};
{%- endfor -%}

// #endregion

// #region Enums

{%- for _, enum := range data.Enums -%}
/// {%s= enum.Description %}
pub const {%s enum.Name.Pascal %} = enum {
{%- for _, entry := range enum.Values -%}
/// {%s= entry.Description %}
{%s entry.Name.Snake %},
{%- endfor -%}

pub fn format(
self: @This(),
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;

try writer.writeAll(
switch (self) {
{%- for _, entry := range enum.Values -%}
.{%s entry.Name.Snake %} => "{%s entry.Value %}",
{%- endfor -%}
},
);
}
};

{%- if enum.Default != nil -%}
pub const default_{%s enum.Name.Snake %} = {%s enum.Name.Pascal %}.{%s enum.Default.Name.Snake %};
{%- endif -%}

{%- endfor -%}
// #endregion

// #endregion
{%- endfunc -%}
1 change: 1 addition & 0 deletions build/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func writeOutConsts(version string) error {
"sdk/python/src/datastar_py/consts.py": pythonConsts,
"sdk/typescript/src/consts.ts": typescriptConsts,
"sdk/rust/src/consts.rs": rustConsts,
"sdk/zig/src/consts.zig": zigConsts,
}

for path, tmplFn := range templates {
Expand Down
1 change: 1 addition & 0 deletions sdk/zig/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.zig-cache
22 changes: 22 additions & 0 deletions sdk/zig/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Datastar Zig SDK

An implementation of the Datastar SDK in Zig. Currently, this only has framework integration for http.zig.

## Testing

Run `zig build test`.

## Usage

```zig
const datastar = @import("datastar");
// Creates a new `ServerSentEventGenerator`.
var sse = datastar.ServerSentEventGenerator.init(res);
// Merges HTML fragments into the DOM.
sse.mergeFragments("<div id='question'>What do you put in a toaster?</div>", .{});
// Merges signals into the signals.
sse.mergeSignals("{response: '', answer: 'bread'}", .{})
```
68 changes: 68 additions & 0 deletions sdk/zig/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const std = @import("std");

// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});

// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});

// This creates a "module", which represents a collection of source files alongside
// some compilation options, such as optimization mode and linked system libraries.
// Every executable or library we compile will be based on one or more modules.
const lib_mod = b.createModule(.{
// `root_source_file` is the Zig "entry point" of the module. If a module
// only contains e.g. external object files, you can make this `null`.
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});

// Now, we will create a static library based on the module we created above.
// This creates a `std.Build.Step.Compile`, which is the build step responsible
// for actually invoking the compiler.
const lib = b.addStaticLibrary(.{
.name = "zig-sdk",
.root_module = lib_mod,
});

// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib);

// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.target = target,
.optimize = optimize,
.test_runner = b.path("test_runner.zig"),
.root_source_file = b.path("src/root.zig"),
});

const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);

const httpz = b.dependency("httpz", .{
.target = target,
.optimize = optimize,
});

lib.root_module.addImport("httpz", httpz.module("httpz"));
lib_unit_tests.root_module.addImport("httpz", httpz.module("httpz"));
}
48 changes: 48 additions & 0 deletions sdk/zig/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.{
// This is the default name used by packages depending on this one. For
// example, when a user runs `zig fetch --save <url>`, this field is used
// as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = "datastar",

// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",

// This field is optional.
// This is currently advisory only; Zig does not yet do anything
// with this value.
//.minimum_zig_version = "0.11.0",

// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
.httpz = .{
.url = "git+https://github.com/karlseguin/http.zig?ref=master#7000f03ad3b328277c70e84d9d9e58076af0a91a",
.hash = "1220c4c8ea5cf4575ce7effde670cfa52e7773e62a92558361eb2636f68e57d53b90",
},
},

// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}
45 changes: 45 additions & 0 deletions sdk/zig/src/Logger.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// This is a sample middleware

// Generally, using a custom Handler with a dispatch method provides the most
// flexibility and should be your first approach (see the dispatcher.zig example).

// Middleware provide an alternative way to manipulate the request/response and
// is well suited if you need different middleware (or middleware configurations)
// for different routes.

const std = @import("std");
const httpz = @import("httpz");

const Logger = @This();

// Must define an `init` method, which will accept your Config
// Alternatively, you can define a init(config: Config, mc: httpz.MiddlewareConfig)
// here mc will give you access to the server's allocator and arena
pub fn init(_: Config) !Logger {
return .{};
}

// optionally you can define an "deinit" method
// pub fn deinit(self: *Logger) void {

// }

// Must define an `execute` method. `self` doesn't have to be `const`, but
// you're responsible for making your middleware thread-safe.
pub fn execute(_: *const Logger, req: *httpz.Request, res: *httpz.Response, executor: anytype) !void {
std.log.info(
"{} {s} {d}",
.{
req.method,
req.url.path,
res.status,
},
);

// If you don't call executor.next(), there will be no further processing of
// the request and we'll go straight to writing the response.
return executor.next();
}

// Must defined a pub config structure, even if it's empty
pub const Config = struct {};
Loading

0 comments on commit 44d758a

Please sign in to comment.