Skip to content

Commit

Permalink
build(bindgen): check for corresponding .zig file (#15896)
Browse files Browse the repository at this point in the history
Co-authored-by: Don Isaac <[email protected]>
  • Loading branch information
DonIsaac and Don Isaac authored Dec 27, 2024
1 parent e4bb309 commit 2f39423
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 24 deletions.
101 changes: 77 additions & 24 deletions src/codegen/bindgen-lib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// This is the public API for `bind.ts` files
// It is aliased as `import {} from 'bindgen'`
/**
* This is the public API for `bind.ts` files
* It is aliased as `import {} from 'bindgen'`
* @see https://bun.sh/docs/project/bindgen
*/

import {
isType,
dictionaryImpl,
Expand Down Expand Up @@ -161,41 +165,57 @@ export namespace t {
/**
* The DOMString type corresponds to strings.
*
* **Note**: A DOMString value might include unmatched surrogate code points.
* @note A DOMString value might include unmatched surrogate code points.
* Use USVString if this is not desirable.
*
* https://webidl.spec.whatwg.org/#idl-DOMString
* @see https://webidl.spec.whatwg.org/#idl-DOMString
*/
export const DOMString = builtinType<string>()("DOMString");
/*
* The USVString type corresponds to scalar value strings. Depending on the
/**
* The {@link USVString} type corresponds to scalar value strings. Depending on the
* context, these can be treated as sequences of code units or scalar values.
*
* Specifications should only use USVString for APIs that perform text
* processing and need a string of scalar values to operate on. Most APIs that
* use strings should instead be using DOMString, which does not make any
* use strings should instead be using {@link DOMString}, which does not make any
* interpretations of the code units in the string. When in doubt, use
* DOMString
* {@link DOMString}
*
* https://webidl.spec.whatwg.org/#idl-USVString
* @see https://webidl.spec.whatwg.org/#idl-USVString
*/
export const USVString = builtinType<string>()("USVString");
/**
* The ByteString type corresponds to byte sequences.
*
* WARNING: Specifications should only use ByteString for interfacing with protocols
* that use bytes and strings interchangeably, such as HTTP. In general,
* strings should be represented with DOMString values, even if it is expected
* that values of the string will always be in ASCII or some 8-bit character
* encoding. Sequences or frozen arrays with octet or byte elements,
* Uint8Array, or Int8Array should be used for holding 8-bit data rather than
* ByteString.
* WARNING: Specifications should only use ByteString for interfacing with
* protocols that use bytes and strings interchangeably, such as HTTP. In
* general, strings should be represented with {@link DOMString} values, even
* if it is expected that values of the string will always be in ASCII or some
* 8-bit character encoding. Sequences or frozen arrays with octet or byte
* elements, {@link Uint8Array}, or {@link Int8Array} should be used for
* holding 8-bit data rather than `ByteString`.
*
* https://webidl.spec.whatwg.org/#idl-ByteString
*/
export const ByteString = builtinType<string>()("ByteString");
/**
* DOMString but encoded as `[]const u8`
*
* ```ts
* // foo.bind.ts
* import { fn, t } from "bindgen";
*
* export const foo = fn({
* args: { bar: t.UTF8String },
* })
* ```
*
* ```zig
* // foo.zig
* pub fn foo(bar: []const u8) void {
* // ...
* }
* ```
*/
export const UTF8String = builtinType<string>()("UTF8String");

Expand All @@ -217,7 +237,7 @@ export namespace t {

/**
* Reference a type by string name instead of by object reference. This is
* required in some siutations like `Request` which can take an existing
* required in some siutations like {@link Request} which can take an existing
* request object in as itself.
*/
export function ref<T>(name: string): Type<T> {
Expand Down Expand Up @@ -275,13 +295,46 @@ export namespace t {
}
}

export type FuncOptions = FuncMetadata &
(
| {
variants: FuncVariant[];
}
| FuncVariant
);
interface FuncOptionsWithVariant extends FuncMetadata {
/**
* Declare a function with multiple overloads. Each overload gets its own
* native function named "name`n`" where `n` is the 1-based index of the
* overload.
*
* ## Example
* ```ts
* // foo.bind.ts
* import { fn } from "bindgen";
*
* export const foo = fn({
* variants: [
* {
* args: { a: t.i32 },
* ret: t.i32,
* },
* {
* args: { a: t.i32, b: t.i32 },
* ret: t.boolean,
* }
* ]
* });
* ```
*
* ```zig
* // foo.zig
* pub fn foo1(a: i32) i32 {
* return a;
* }
*
* pub fn foo2(a: i32, b: i32) bool {
* return a == b;
* }
* ```
*/
variants: FuncVariant[];
}
type FuncWithoutOverloads = FuncMetadata & FuncVariant;
type FuncOptions = FuncOptionsWithVariant | FuncWithoutOverloads;

export interface FuncMetadata {
/**
Expand Down
9 changes: 9 additions & 0 deletions src/codegen/bindgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Generated bindings are available in `bun.generated.<basename>.*` in Zig,
// or `Generated::<basename>::*` in C++ from including `Generated<basename>.h`.
import * as path from "node:path";
import fs from "node:fs";
import {
CodeWriter,
TypeImpl,
Expand Down Expand Up @@ -1076,7 +1077,15 @@ const unsortedFiles = readdirRecursiveWithExclusionsAndExtensionsSync(src, ["nod
// Sort for deterministic output
for (const fileName of [...unsortedFiles].sort()) {
const zigFile = path.relative(src, fileName.replace(/\.bind\.ts$/, ".zig"));
const zigFilePath = path.join(src, zigFile);
let file = files.get(zigFile);
if (!fs.existsSync(zigFilePath)) {
// It would be nice if this would generate the file with the correct boilerplate
const bindName = path.basename(fileName);
throw new Error(
`${bindName} is missing a corresponding Zig file at ${zigFile}. Please create it and make sure it matches signatures in ${bindName}.`,
);
}
if (!file) {
file = { functions: [], typedefs: [] };
files.set(zigFile, file);
Expand Down

0 comments on commit 2f39423

Please sign in to comment.