From 215d838fa7fb687e1aee6cb3043c740b593be0af Mon Sep 17 00:00:00 2001 From: Virgil Date: Fri, 3 Apr 2026 19:16:22 +0000 Subject: [PATCH] feat(cgo): implement errno mapping helpers Co-Authored-By: Virgil --- call_test.go | 38 ++++++++++++++++++++++++++++++++++++++ call_test_support.go | 12 ++++++++++++ string_conversion.go | 24 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/call_test.go b/call_test.go index 28d386f..1ad9601 100644 --- a/call_test.go +++ b/call_test.go @@ -1,6 +1,8 @@ package cgo import ( + "errors" + "syscall" "testing" ) @@ -49,3 +51,39 @@ func TestCallRejectsUnsupportedInputs(t *testing.T) { _ = Call(callFailureFunction(), 1, 2, 3, 4) }) } + +func TestErrnoMapping(t *testing.T) { + t.Parallel() + + if err := Errno(0); err != nil { + t.Fatalf("expected nil error for zero, got %v", err) + } + + if err := Errno(2); err == nil { + t.Fatal("expected non-nil error for non-zero errno") + } else if !errors.Is(err, syscall.Errno(2)) { + t.Fatalf("expected error type %v, got %T %v", syscall.Errno(2), err, err) + } +} + +func TestWithErrnoReturnsBothResultAndError(t *testing.T) { + t.Parallel() + + result, err := callWithErrnoZero() + if result != 0 { + t.Fatalf("expected result 0, got %d", result) + } + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + + result, err = callWithErrnoFailure() + if result != 2 { + t.Fatalf("expected result 2, got %d", result) + } + if err == nil { + t.Fatal("expected error, got nil") + } else if !errors.Is(err, syscall.Errno(2)) { + t.Fatalf("expected errno error, got %v", err) + } +} diff --git a/call_test_support.go b/call_test_support.go index 648df20..bdb8615 100644 --- a/call_test_support.go +++ b/call_test_support.go @@ -36,3 +36,15 @@ func callFailureFunction() unsafe.Pointer { function := C.call_failure_ptr() return *(*unsafe.Pointer)(unsafe.Pointer(&function)) } + +func callWithErrnoZero() (int, error) { + return WithErrno(func() C.int { + return 0 + }) +} + +func callWithErrnoFailure() (int, error) { + return WithErrno(func() C.int { + return 2 + }) +} diff --git a/string_conversion.go b/string_conversion.go index 90c87f6..401364d 100644 --- a/string_conversion.go +++ b/string_conversion.go @@ -30,6 +30,7 @@ import "C" import ( "fmt" "strconv" + "syscall" "unsafe" ) @@ -196,3 +197,26 @@ func Free(ptr unsafe.Pointer) { } C.free(ptr) } + +// Errno converts a C error number to a Go error. +// +// rc := cgo.Errno(-2) +func Errno(rc C.int) error { + if rc == 0 { + return nil + } + return syscall.Errno(rc) +} + +// WithErrno runs a function that returns C.int and maps the result to Go error. +// +// result, err := cgo.WithErrno(func() C.int { +// return C.my_function() +// }) +func WithErrno(fn func() C.int) (int, error) { + result := fn() + if err := Errno(result); err != nil { + return int(result), err + } + return int(result), nil +} -- 2.45.3