diff --git a/cgo.c b/cgo.c index aa1c712..6700e0c 100644 --- a/cgo.c +++ b/cgo.c @@ -18,4 +18,8 @@ int vm_create_function(ph7_vm *pVm, const char *zName, void *pUserData) { int vm_config_err_report(ph7_vm *pVm) { return ph7_vm_config(pVm, PH7_VM_CONFIG_ERR_REPORT); -} \ No newline at end of file +} + +int vm_output_length(ph7_vm *pVm, unsigned int *pLength) { + return ph7_vm_config(pVm, PH7_VM_OUTPUT_LENGTH, pLength); +} diff --git a/cgo.h b/cgo.h index cef8c52..f0ceffd 100644 --- a/cgo.h +++ b/cgo.h @@ -4,6 +4,7 @@ int engine_config_err_log(ph7 *pEngine, const char **pzPtr, int *pLen); int vm_extract_output(ph7_vm *pVm, const char **pzPtr, int *pLen); int vm_config_err_report(ph7_vm *pVm); +int vm_output_length(ph7_vm *pVm, unsigned int *pLength); extern int write_vm_output(void*, unsigned int, void*); int vm_set_output_callback(ph7_vm *pVm, void *pUserData); diff --git a/ph7.go b/ph7.go index ad00c55..0ae2f8d 100644 --- a/ph7.go +++ b/ph7.go @@ -192,7 +192,26 @@ func (c *Context) ResultString(s string) error { return newError(C.ph7_result_string((*C.ph7_context)(c), cs, C.int(len(s)))) } -// TODO (*Context).ResultStringFormat maybe? Or just use fmt.Sprint*. +type resultWriter Context + +func (rw *resultWriter) Write(p []byte) (int, error) { + cp := C.CBytes(p) + defer C.free(cp) + err := newError(C.ph7_result_string( + (*C.ph7_context)(rw), + (*C.char)(cp), + C.int(len(p)))) + if err != nil { + return 0, err + } + return len(p), nil +} + +func (c *Context) ResultWriter() io.Writer { + return (*resultWriter)(c) +} + +// ph7_result_string_format is redundant when we have fmt.Sprintf and fmt.Fprintf. func (c *Context) ResultValue(value *Value) error { return newError(C.ph7_result_value((*C.ph7_context)(c), (*C.ph7_value)(value))) @@ -200,16 +219,25 @@ func (c *Context) ResultValue(value *Value) error { // TODO (*Context).ResultResource -func (c *Context) Write(p []byte) (int, error) { +type outputWriter Context + +func (ow *outputWriter) Write(p []byte) (int, error) { cp := C.CBytes(p) defer C.free(cp) - err := newError(C.ph7_context_output((*C.ph7_context)(c), (*C.char)(cp), C.int(len(p)))) + err := newError(C.ph7_context_output( + (*C.ph7_context)(ow), + (*C.char)(cp), + C.int(len(p)))) if err != nil { return 0, err } return len(p), nil } +func (c *Context) OutputWriter() io.Writer { + return (*outputWriter)(c) +} + type Severity int const ( @@ -225,7 +253,7 @@ func (c *Context) ThrowError(severity Severity, msg string) error { return newError(C.ph7_context_throw_error((*C.ph7_context)(c), C.int(severity), cmsg)) } -// TODO (*Context).ThrowErrorFormat maybe? +// ph7_context_throw_error_format is redundant when we have fmt.Sprintf. func (c *Context) RandomNum() uint { return uint(C.ph7_context_random_num((*C.ph7_context)(c))) @@ -246,7 +274,10 @@ func (c *Context) RandomString(length int) (string, error) { // TODO (*Context).PushAuxData // TODO (*Context).PeekAuxData // TODO (*Context).PopAuxData -// TODO (*Context).ResultBufLength + +func (c *Context) ResultBufLength() uint { + return uint(C.ph7_context_result_buf_length((*C.ph7_context)(c))) +} func (c *Context) FunctionName() string { return C.GoString(C.ph7_function_name((*C.ph7_context)(c))) @@ -258,6 +289,8 @@ func (c *Context) FunctionName() string { type Value C.ph7_value +// TODO Make *Value a fmt.Formatter, or at least a Stringer. + func (v *Value) ToInt() int { return int(C.ph7_value_to_int((*C.ph7_value)(v))) } @@ -290,16 +323,58 @@ func (v *Value) CompareStrict(v2 *Value) int { return int(C.ph7_value_compare((*C.ph7_value)(v), (*C.ph7_value)(v2), 1)) } -// TODO (*Value).Int -// TODO (*Value).Int64 -// TODO (*Value).Bool -// TODO (*Value).Null -// TODO (*Value).Double -// TODO (*Value).String -// TODO (*Value).StringFormat maybe? -// TODO (*Value).ResetStringCursor +func (v *Value) Int(n int) error { + return newError(C.ph7_value_int((*C.ph7_value)(v), C.int(n))) +} + +func (v *Value) Int64(n int64) error { + return newError(C.ph7_value_int64((*C.ph7_value)(v), C.ph7_int64(n))) +} + +func (v *Value) Bool(b bool) error { + var cb C.int + if b { + cb = 1 + } + return newError(C.ph7_value_bool((*C.ph7_value)(v), cb)) +} + +func (v *Value) Null() error { + return newError(C.ph7_value_null((*C.ph7_value)(v))) +} + +func (v *Value) Double(d float64) error { + return newError(C.ph7_value_double((*C.ph7_value)(v), C.double(d))) +} + +func (v *Value) String(s string) error { + cstr := C.CString(s) + defer C.free(unsafe.Pointer(cstr)) + return newError(C.ph7_value_string( + (*C.ph7_value)(v), + cstr, + C.int(len(s)))) +} + +func (v *Value) Write(p []byte) error { + cp := C.CBytes(p) + defer C.free(cp) + return newError(C.ph7_value_string( + (*C.ph7_value)(v), + (*C.char)(cp), + C.int(len(p)))) +} + +func (v *Value) ResetStringCursor() error { + return newError(C.ph7_value_reset_string_cursor((*C.ph7_value)(v))) +} + // TODO (*Value).Resource -// TODO (*Value).Release + +func (v *Value) Release() error { + return newError(C.ph7_value_release((*C.ph7_value)(v))) +} + // TODO (*Value).ArrayFetch // TODO (*Value).ArrayWalk // TODO (*Value).ArrayAddElem @@ -360,18 +435,36 @@ func (v *Value) IsEmpty() bool { // ph7_lib_init is unnecessary when single-threaded, and possibly always. // TODO Config -// TODO Shutdown -// TODO IsThreadsafe -// TODO Version -// TODO Signature -// TODO Ident -// TODO Copyright + +func Shutdown() error { + return newError(C.ph7_lib_shutdown()) +} + +func IsThreadsafe() bool { + return C.ph7_lib_is_threadsafe() != 0 +} + +func Version() string { + return C.GoString(C.ph7_lib_version()) +} + +func Signature() string { + return C.GoString(C.ph7_lib_signature()) +} + +func Ident() string { + return C.GoString(C.ph7_lib_ident()) +} + +func Copyright() string { + return C.GoString(C.ph7_lib_copyright()) + + "\ngo-ph7 by Brandon Dyck. Do whatever the hell you want with it." +} type VM struct { - ptr *C.ph7_vm - pointerKey unsafe.Pointer - outputWriter io.Writer - // TODO clear outputWriteError when resetting VM + ptr *C.ph7_vm + pointerKey unsafe.Pointer + outputWriter io.Writer outputWriteErr error userFunctionKeys []unsafe.Pointer } @@ -401,6 +494,13 @@ func (vm *VM) SetOutputWriter(w io.Writer) error { // TODO PH7_VM_CONFIG_IMPORT_PATH // TODO PH7_VM_CONFIG_RECURSION_DEPTH + +func (vm *VM) OutputLength() (uint, error) { + var n C.uint + err := newError(C.vm_output_length(vm.ptr, &n)) + return uint(n), err +} + // TODO PH7_VM_CONFIG_CREATE_SUPER // TODO PH7_VM_CONFIG_CREATE_VAR // TODO PH7_VM_CONFIG_HTTP_REQUEST @@ -414,7 +514,6 @@ func (vm *VM) SetOutputWriter(w io.Writer) error { // TODO PH7_VM_CONFIG_EXEC_VALUE // TODO PH7_VM_CONFIG_IO_STREAM // TODO PH7_VM_CONFIG_ARGV_ENTRY -// TODO PH7_VM_CONFIG_EXTRACT_OUTPUT // TODO PH7_VM_CONFIG_ERR_LOG_HANDLER // TODO (*VM).Dump(w io.Writer) @@ -445,6 +544,7 @@ func (vm *VM) Exec() error { } func (vm *VM) Reset() error { + vm.outputWriteErr = nil return newError(C.ph7_vm_reset(vm.ptr)) } diff --git a/ph7_test.go b/ph7_test.go index d29d31d..d574a51 100644 --- a/ph7_test.go +++ b/ph7_test.go @@ -168,7 +168,7 @@ func TestContextWrite(t *testing.T) { var written int f := func(ctx *ph7.Context) error { var err error - written, err = ctx.Write([]byte(s)) + written, err = ctx.OutputWriter().Write([]byte(s)) return err } @@ -204,7 +204,7 @@ func TestFunctionName(t *testing.T) { defer close() f := func(ctx *ph7.Context) error { - _, err := fmt.Fprint(ctx, ctx.FunctionName()) + _, err := fmt.Fprint(ctx.OutputWriter(), ctx.FunctionName()) return err } mustSucceed(t, vm.CreateFunction("doStuff", CtxFunc(f)))