Ivan FB 965fd68785
feat(codegen): Go bindings support struct/seq/Option params
The Go generator previously emitted a `// SKIPPED` stub for any proc with a
struct, sequence or optional parameter, leaving Echo/Complex/Schedule
uncallable. Now that the native ABI carries those as flat C-POD structs, the Go
side can marshal them: emit an idiomatic Go struct per {.ffi.} type plus a
`toC()` that builds the matching `C.<Type>` (C.CString for strings, a C array
for seqs, present-flags for options, recursively for nested structs) and
returns cleanup funcs run via defer once the call returns. The native path
deep-copies every argument, so releasing the C buffers immediately is safe.

The C bridge already accepted struct-by-value params via the pass-through type
mapping; only the Go-side conversion and the `allSupported` gate needed work.
Bare seq/Option *top-level* params (not wrapped in a struct) remain skipped, as
the native ABI does not expose them either.

The generated package is now self-contained: the native `<lib>.h` is emitted
beside the `.go`, and the cgo directives use ${SRCDIR} so the header and the
staged library resolve without extra env vars. genbindings_go runs gofmt to
finalize column alignment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:37:18 +02:00

453 lines
13 KiB
Go

// Code generated by nim-ffi Go codegen. DO NOT EDIT.
package my_timer
/*
#cgo CFLAGS: -I${SRCDIR}
#cgo LDFLAGS: -L${SRCDIR} -lmy_timer -Wl,-rpath,${SRCDIR}
#include "my_timer.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
extern void my_timerGoEvent(int ret, char* msg, size_t len, void* userData);
typedef struct {
int ret; char* msg; size_t len; int done;
pthread_mutex_t mu; pthread_cond_t cv;
} My_timerResp;
static My_timerResp* my_timerRespNew() {
My_timerResp* r = (My_timerResp*)calloc(1, sizeof(My_timerResp));
pthread_mutex_init(&r->mu, NULL); pthread_cond_init(&r->cv, NULL);
return r;
}
static void my_timerRespFree(My_timerResp* r) {
if (!r) return;
if (r->msg) free(r->msg);
pthread_mutex_destroy(&r->mu); pthread_cond_destroy(&r->cv); free(r);
}
static int my_timerRespRet(My_timerResp* r) { return r->ret; }
static char* my_timerRespMsg(My_timerResp* r) { return r->msg; }
static size_t my_timerRespLen(My_timerResp* r) { return r->len; }
static void my_timerRespCb(int ret, const char* msg, size_t len, void* ud) {
My_timerResp* r = (My_timerResp*)ud;
pthread_mutex_lock(&r->mu);
r->ret = ret;
// Native ABI: (msg, len) is the raw result (RET_OK) or error (RET_ERR).
// Copy it so it survives past the callback.
char* e = (char*)malloc(len + 1); if (e) { memcpy(e, msg, len); e[len] = 0; }
r->msg = e; r->len = len;
r->done = 1; pthread_cond_signal(&r->cv); pthread_mutex_unlock(&r->mu);
}
static void my_timerRespWait(My_timerResp* r) {
pthread_mutex_lock(&r->mu);
while (!r->done) pthread_cond_wait(&r->cv, &r->mu);
pthread_mutex_unlock(&r->mu);
}
static void* my_timerCall_my_timer_create(TimerConfig config, My_timerResp* r) {
void* ctx = my_timer_create(config, my_timerRespCb, r);
my_timerRespWait(r);
return ctx;
}
static int my_timerCall_my_timer_echo(void* ctx, EchoRequest req, My_timerResp* r) {
int rc = my_timer_echo(ctx, my_timerRespCb, r, req);
if (rc == RET_OK) my_timerRespWait(r);
return rc;
}
static int my_timerCall_my_timer_version(void* ctx, My_timerResp* r) {
int rc = my_timer_version(ctx, my_timerRespCb, r);
if (rc == RET_OK) my_timerRespWait(r);
return rc;
}
static int my_timerCall_my_timer_complex(void* ctx, ComplexRequest req, My_timerResp* r) {
int rc = my_timer_complex(ctx, my_timerRespCb, r, req);
if (rc == RET_OK) my_timerRespWait(r);
return rc;
}
static int my_timerCall_my_timer_schedule(void* ctx, JobSpec job, RetryPolicy retry, ScheduleConfig schedule, My_timerResp* r) {
int rc = my_timer_schedule(ctx, my_timerRespCb, r, job, retry, schedule);
if (rc == RET_OK) my_timerRespWait(r);
return rc;
}
static int my_timerCall_my_timer_destroy(void* ctx) { return my_timer_destroy(ctx); }
static uint64_t my_timerRegisterEvents(void* ctx) { return my_timer_add_event_listener(ctx, "", (FFICallBack)my_timerGoEvent, ctx); }
*/
import "C"
import (
"errors"
"sync"
"unsafe"
)
// TimerConfig mirrors the {.ffi.} type of the same name.
type TimerConfig struct {
Name string
}
// toC marshals TimerConfig into its C-POD form, returning cleanup funcs to run after the call.
func (v TimerConfig) toC() (C.TimerConfig, []func()) {
var c C.TimerConfig
var frees []func()
cs_name := C.CString(v.Name)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_name)) })
c.name = cs_name
return c, frees
}
// EchoRequest mirrors the {.ffi.} type of the same name.
type EchoRequest struct {
Message string
DelayMs int64
}
// toC marshals EchoRequest into its C-POD form, returning cleanup funcs to run after the call.
func (v EchoRequest) toC() (C.EchoRequest, []func()) {
var c C.EchoRequest
var frees []func()
cs_message := C.CString(v.Message)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_message)) })
c.message = cs_message
c.delayMs = C.int64_t(v.DelayMs)
return c, frees
}
// EchoResponse mirrors the {.ffi.} type of the same name.
type EchoResponse struct {
Echoed string
TimerName string
}
// toC marshals EchoResponse into its C-POD form, returning cleanup funcs to run after the call.
func (v EchoResponse) toC() (C.EchoResponse, []func()) {
var c C.EchoResponse
var frees []func()
cs_echoed := C.CString(v.Echoed)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_echoed)) })
c.echoed = cs_echoed
cs_timerName := C.CString(v.TimerName)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_timerName)) })
c.timerName = cs_timerName
return c, frees
}
// ComplexRequest mirrors the {.ffi.} type of the same name.
type ComplexRequest struct {
Messages []EchoRequest
Tags []string
Note *string
Retries *int64
}
// toC marshals ComplexRequest into its C-POD form, returning cleanup funcs to run after the call.
func (v ComplexRequest) toC() (C.ComplexRequest, []func()) {
var c C.ComplexRequest
var frees []func()
if n_messages := len(v.Messages); n_messages > 0 {
arr_messages := C.malloc(C.size_t(n_messages) * C.size_t(unsafe.Sizeof(C.EchoRequest{})))
sl_messages := unsafe.Slice((*C.EchoRequest)(arr_messages), n_messages)
for i := 0; i < n_messages; i++ {
cf_messages_e, ff_messages_e := (v.Messages[i]).toC()
frees = append(frees, ff_messages_e...)
sl_messages[i] = cf_messages_e
}
c.messages = (*C.EchoRequest)(arr_messages)
c.messages_len = C.size_t(n_messages)
a_messages := arr_messages
frees = append(frees, func() { C.free(a_messages) })
}
if n_tags := len(v.Tags); n_tags > 0 {
arr_tags := C.malloc(C.size_t(n_tags) * C.size_t(unsafe.Sizeof((*C.char)(nil))))
sl_tags := unsafe.Slice((**C.char)(arr_tags), n_tags)
for i := 0; i < n_tags; i++ {
cs_tags_e := C.CString(v.Tags[i])
frees = append(frees, func() { C.free(unsafe.Pointer(cs_tags_e)) })
sl_tags[i] = cs_tags_e
}
c.tags = (**C.char)(arr_tags)
c.tags_len = C.size_t(n_tags)
a_tags := arr_tags
frees = append(frees, func() { C.free(a_tags) })
}
if v.Note != nil {
c.note_present = 1
cs_note_o := C.CString(*v.Note)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_note_o)) })
c.note = cs_note_o
}
if v.Retries != nil {
c.retries_present = 1
c.retries = C.int64_t(*v.Retries)
}
return c, frees
}
// ComplexResponse mirrors the {.ffi.} type of the same name.
type ComplexResponse struct {
Summary string
ItemCount int64
HasNote bool
}
// toC marshals ComplexResponse into its C-POD form, returning cleanup funcs to run after the call.
func (v ComplexResponse) toC() (C.ComplexResponse, []func()) {
var c C.ComplexResponse
var frees []func()
cs_summary := C.CString(v.Summary)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_summary)) })
c.summary = cs_summary
c.itemCount = C.int64_t(v.ItemCount)
if v.HasNote {
c.hasNote = 1
} else {
c.hasNote = 0
}
return c, frees
}
// EchoEvent mirrors the {.ffi.} type of the same name.
type EchoEvent struct {
Message string
EchoCount int64
}
// toC marshals EchoEvent into its C-POD form, returning cleanup funcs to run after the call.
func (v EchoEvent) toC() (C.EchoEvent, []func()) {
var c C.EchoEvent
var frees []func()
cs_message := C.CString(v.Message)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_message)) })
c.message = cs_message
c.echoCount = C.int64_t(v.EchoCount)
return c, frees
}
// JobSpec mirrors the {.ffi.} type of the same name.
type JobSpec struct {
Name string
Payload []string
Priority int64
}
// toC marshals JobSpec into its C-POD form, returning cleanup funcs to run after the call.
func (v JobSpec) toC() (C.JobSpec, []func()) {
var c C.JobSpec
var frees []func()
cs_name := C.CString(v.Name)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_name)) })
c.name = cs_name
if n_payload := len(v.Payload); n_payload > 0 {
arr_payload := C.malloc(C.size_t(n_payload) * C.size_t(unsafe.Sizeof((*C.char)(nil))))
sl_payload := unsafe.Slice((**C.char)(arr_payload), n_payload)
for i := 0; i < n_payload; i++ {
cs_payload_e := C.CString(v.Payload[i])
frees = append(frees, func() { C.free(unsafe.Pointer(cs_payload_e)) })
sl_payload[i] = cs_payload_e
}
c.payload = (**C.char)(arr_payload)
c.payload_len = C.size_t(n_payload)
a_payload := arr_payload
frees = append(frees, func() { C.free(a_payload) })
}
c.priority = C.int64_t(v.Priority)
return c, frees
}
// RetryPolicy mirrors the {.ffi.} type of the same name.
type RetryPolicy struct {
MaxAttempts int64
BackoffMs int64
RetryOn []string
}
// toC marshals RetryPolicy into its C-POD form, returning cleanup funcs to run after the call.
func (v RetryPolicy) toC() (C.RetryPolicy, []func()) {
var c C.RetryPolicy
var frees []func()
c.maxAttempts = C.int64_t(v.MaxAttempts)
c.backoffMs = C.int64_t(v.BackoffMs)
if n_retryOn := len(v.RetryOn); n_retryOn > 0 {
arr_retryOn := C.malloc(C.size_t(n_retryOn) * C.size_t(unsafe.Sizeof((*C.char)(nil))))
sl_retryOn := unsafe.Slice((**C.char)(arr_retryOn), n_retryOn)
for i := 0; i < n_retryOn; i++ {
cs_retryOn_e := C.CString(v.RetryOn[i])
frees = append(frees, func() { C.free(unsafe.Pointer(cs_retryOn_e)) })
sl_retryOn[i] = cs_retryOn_e
}
c.retryOn = (**C.char)(arr_retryOn)
c.retryOn_len = C.size_t(n_retryOn)
a_retryOn := arr_retryOn
frees = append(frees, func() { C.free(a_retryOn) })
}
return c, frees
}
// ScheduleConfig mirrors the {.ffi.} type of the same name.
type ScheduleConfig struct {
StartAtMs int64
IntervalMs int64
Jitter *int64
}
// toC marshals ScheduleConfig into its C-POD form, returning cleanup funcs to run after the call.
func (v ScheduleConfig) toC() (C.ScheduleConfig, []func()) {
var c C.ScheduleConfig
var frees []func()
c.startAtMs = C.int64_t(v.StartAtMs)
c.intervalMs = C.int64_t(v.IntervalMs)
if v.Jitter != nil {
c.jitter_present = 1
c.jitter = C.int64_t(*v.Jitter)
}
return c, frees
}
// ScheduleResult mirrors the {.ffi.} type of the same name.
type ScheduleResult struct {
JobId string
WillRunCount int64
FirstRunAtMs int64
EffectiveBackoffMs int64
}
// toC marshals ScheduleResult into its C-POD form, returning cleanup funcs to run after the call.
func (v ScheduleResult) toC() (C.ScheduleResult, []func()) {
var c C.ScheduleResult
var frees []func()
cs_jobId := C.CString(v.JobId)
frees = append(frees, func() { C.free(unsafe.Pointer(cs_jobId)) })
c.jobId = cs_jobId
c.willRunCount = C.int64_t(v.WillRunCount)
c.firstRunAtMs = C.int64_t(v.FirstRunAtMs)
c.effectiveBackoffMs = C.int64_t(v.EffectiveBackoffMs)
return c, frees
}
type My_timerNode struct {
ctx unsafe.Pointer
}
// goStr extracts and frees the captured response string.
func respStr(r *C.My_timerResp) string {
return C.GoStringN(C.my_timerRespMsg(r), C.int(C.my_timerRespLen(r)))
}
var (
eventMu sync.Mutex
eventHandler func(string)
)
// SetEventHandler installs the catch-all handler for library-initiated
// events (delivered as raw JSON strings).
func (n *My_timerNode) SetEventHandler(h func(string)) {
eventMu.Lock()
eventHandler = h
eventMu.Unlock()
C.my_timerRegisterEvents(n.ctx)
}
//export my_timerGoEvent
func my_timerGoEvent(ret C.int, msg *C.char, length C.size_t, userData unsafe.Pointer) {
eventMu.Lock()
h := eventHandler
eventMu.Unlock()
if h != nil && ret == C.RET_OK {
h(C.GoStringN(msg, C.int(length)))
}
}
func NewMy_timer(config TimerConfig) (*My_timerNode, error) {
c_config, free_config := config.toC()
defer func() {
for _, f := range free_config {
f()
}
}()
r := C.my_timerRespNew()
defer C.my_timerRespFree(r)
ctx := C.my_timerCall_my_timer_create(c_config, r)
if C.my_timerRespRet(r) != C.RET_OK {
return nil, errors.New(respStr(r))
}
return &My_timerNode{ctx: ctx}, nil
}
func (n *My_timerNode) Echo(req EchoRequest) (string, error) {
c_req, free_req := req.toC()
defer func() {
for _, f := range free_req {
f()
}
}()
r := C.my_timerRespNew()
defer C.my_timerRespFree(r)
C.my_timerCall_my_timer_echo(n.ctx, c_req, r)
if C.my_timerRespRet(r) != C.RET_OK {
return "", errors.New(respStr(r))
}
return respStr(r), nil
}
func (n *My_timerNode) Version() (string, error) {
r := C.my_timerRespNew()
defer C.my_timerRespFree(r)
C.my_timerCall_my_timer_version(n.ctx, r)
if C.my_timerRespRet(r) != C.RET_OK {
return "", errors.New(respStr(r))
}
return respStr(r), nil
}
func (n *My_timerNode) Complex(req ComplexRequest) (string, error) {
c_req, free_req := req.toC()
defer func() {
for _, f := range free_req {
f()
}
}()
r := C.my_timerRespNew()
defer C.my_timerRespFree(r)
C.my_timerCall_my_timer_complex(n.ctx, c_req, r)
if C.my_timerRespRet(r) != C.RET_OK {
return "", errors.New(respStr(r))
}
return respStr(r), nil
}
func (n *My_timerNode) Schedule(job JobSpec, retry RetryPolicy, schedule ScheduleConfig) (string, error) {
c_job, free_job := job.toC()
defer func() {
for _, f := range free_job {
f()
}
}()
c_retry, free_retry := retry.toC()
defer func() {
for _, f := range free_retry {
f()
}
}()
c_schedule, free_schedule := schedule.toC()
defer func() {
for _, f := range free_schedule {
f()
}
}()
r := C.my_timerRespNew()
defer C.my_timerRespFree(r)
C.my_timerCall_my_timer_schedule(n.ctx, c_job, c_retry, c_schedule, r)
if C.my_timerRespRet(r) != C.RET_OK {
return "", errors.New(respStr(r))
}
return respStr(r), nil
}
func (n *My_timerNode) Destroy() error {
if C.my_timerCall_my_timer_destroy(n.ctx) != C.RET_OK {
return errors.New("my_timer destroy failed")
}
return nil
}