[ create a new paste ] login | about

Link: http://codepad.org/SnEYRoVa    [ raw code | fork ]

Plain Text, pasted on Jan 8:
/*
Error handling routines

Conventional golang, for comparison:
foo,err := op()
if(err != nil){
	log.PanicLn("Oops")
}

Usage:
foo := try(a(op()), ERROR, "Oops").(footype)
foo := try(a(op()), log.Panicln, "Oops").(footype)
foo := try(a(op())).(footype) // just log a warning
*/

package main

import (
	"fmt"
	"log"
	"path"
	"reflect"
	"runtime"
)

const (
	ERROR = iota
	WARN
	INFO
	DEBUG
)
const (
	ERRORLEVEL = INFO
)

// array_unwrap() -- pull a single item out of a length 1 array
// otherwise return whatever was passed in.
func array_unwrap(param interface{}) interface{} {
	result := param
	if reflect.ValueOf(param).Kind() == reflect.Slice {
		// cast to array needs a new variable
		a := param.([]interface{})
		if len(a) == 1 {
			// Unwrap the single parameter
			result = a[0]
		}
	}
	return result
}

// a() -- convert multiple-value to an array
// Ernest Micklei's wrapper to solve the problem of:
// 	multiple-value foo.Bar() in single-value context
// http://ernestmicklei.com/2013/11/25/from-multiple-to-single-value-context-in-go/
// it is not clear how this works because passing to an interface{}
// is what broke in the first place.
func a(args ...interface{}) []interface{} {
	return args
}

// try() -- function for wrapping error handling of (obj,err) functions
// The inner function must be wrapped by a(). It's a golang syntax issue.
// The first parameter will be a []interface{} of the (obj,err) returned
// by the wrapped function.
// The second parameter may be any of:
//  * a string to be reported as an error message
//  * a function that takes an error message string
//  * one of the enums ERROR, WARN, INFO
// The third parameter is an optional error message.
// Because the function returns an interface, the caller must cast the
// result to the desired type.
func try(params ...interface{}) interface{} {

	//p0 := params[0].([]interface{}) // params[0] is (obj, err)
	// obj := p0[0]	// Value of undetermined type
	//err := p0[1]	// nil or error

	var obj interface{} // defined later
	var err error

	errlevels := []string{"Error", "Warn", "Info", "Debug"}
	level := WARN            // Index to errlevels
	extra_msg := ""          // extra error message
	f_handler := log.Println // Function to handle the error

	// Handle params[0]

	// Check for an a() wrapped error as a single-value
	p0 := array_unwrap(params[0])
	// log.Println("Kind ", reflect.ValueOf(p0).Kind())

	// General case, params[0] is (obj, err)
	// new declaration needed to treat p[0] as array
	if reflect.ValueOf(p0).Kind() == reflect.Slice {
		pa := p0.([]interface{}) // p0 is an array
		// Save obj and err
		obj = pa[0]
		if pa[1] != nil {
			err = pa[1].(error)
		}
	} else {
		switch p0.(type) {
		case nil:
			_ = 0 // no op
		case error:
			// support functions that only return an error
			obj = nil
			err = p0.(error)
		default:
			// Not a slice. p0 is the object to be returned.
			obj = p0

			// if the call was wrong, the program will crash in
			// the type handling.
		}
	}

	// params[1] may be an optional errorlevel constant
	if len(params) > 1 {
		switch params[1].(type) {

		// params[1] may be a custom function to handle the error.
		// every arg type needs its own case
		case func(...interface{}):
			f_handler = params[1].(func(...interface{}))
		case func(string):
			f_tmp := params[1].(func(string))
			f_handler = func(p2 ...interface{}) { f_tmp(p2[0].(string)) }
		case func(error):
			f_tmp := params[1].(func(error))
			f_handler = func(p2 ...interface{}) { f_tmp(p2[0].(error)) }

		// params[1] may be a custom error message
		case string:
			extra_msg = params[1].(string)

		// params[1] may be WARN, ERROR, etc
		default:
			// Assume int in default case
			// Let it crash if there's a problem
			level = params[1].(int)
			if level == ERROR {
				f_handler = log.Panicln
			}
		}
	}

	// Get optional third parameter
	if len(params) > 2 {
		switch params[2].(type) {
		case string:
			extra_msg = params[2].(string)
		}
	}

	// Log an error if there was one.
	if err != nil && level <= ERRORLEVEL {
		err_msg := err.(error).Error()

		// Handle the extra message if it exists.
		if len(extra_msg) > 0 {
			err_msg = fmt.Sprintf("%s:%s", err_msg, extra_msg)
		}

		// Get stack info
		pc, file, line, ok := runtime.Caller(1)

		if !ok {
			// seriously bad shit
			log.Panicln("runtime.Caller() failed")
		}

		caller := runtime.FuncForPC(pc)
		stack_msg := fmt.Sprintf("%s:%d:%s", path.Base(file), line, caller.Name())

		// Log the error.
		f_handler(fmt.Sprintf("%s:%s:%s", stack_msg, errlevels[level], err_msg))
	}

	return obj
}



Create a new paste based on this one


Comments: