Example #1
0
// MagicApplier wraps the function type recieved applying any magic for the
// expected types, returning a SubApplier to call the functions as needed.
func MagicApplier(handle interface{}) SubApplier {
	switch handle.(type) {
	case func(context.Context, interface{}, interface{}) (interface{}, error):
		return handle.(func(context.Context, interface{}, interface{}) (interface{}, error))
	case func(interface{}, interface{}) (interface{}, error):
		hl := handle.(func(interface{}, interface{}) (interface{}, error))
		return func(_ context.Context, d1 interface{}, d2 interface{}) (interface{}, error) {
			return hl(d1, d2)
		}
	default:
		if !reflection.IsFuncType(handle) {
			return nil
		}

		tm, _ := reflection.FuncValue(handle)
		args, _ := reflection.GetFuncArgumentsType(handle)

		dLen := len(args)
		if dLen < 2 {
			return nil
		}

		var useContext bool
		var useOne bool

		var d1 reflect.Type
		var d2 reflect.Type

		var d1Zero reflect.Value
		var d2Zero reflect.Value

		if dLen == 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			if useContext {
				d1 = args[1]
				d1Zero = reflect.Zero(d1)
				useOne = true
			} else {
				d1 = args[0]
				d1Zero = reflect.Zero(d1)

				d2 = args[1]
				d2Zero = reflect.Zero(d2)
			}
		}

		if dLen > 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			if !useContext {
				return nil
			}

			d1 = args[0]
			d1Zero = reflect.Zero(d1)

			d2 = args[1]
			d2Zero = reflect.Zero(d2)
		}

		return func(ctx context.Context, dl interface{}, rl interface{}) (interface{}, error) {
			var fnArgs []reflect.Value
			var resArgs []reflect.Value

			if useContext {
				fnArgs = append(fnArgs, reflect.ValueOf(ctx))
			}

			var dv1 reflect.Value
			var dv2 reflect.Value

			if dl != nil {
				dv1 = reflect.ValueOf(dl)
			} else {
				dv1 = d1Zero
			}

			if rl != nil {
				dv2 = reflect.ValueOf(rl)
			} else {
				dv2 = d2Zero
			}

			if !useOne {
				can, convert := reflection.CanSetFor(d1, dv1)
				if !can {
					return nil, ErrInvalidType
				}

				can2, convert2 := reflection.CanSetFor(d2, dv2)
				if !can2 {
					return nil, ErrInvalidType
				}

				if convert {
					dv1 = dv1.Convert(d1)
				}

				if convert2 {
					dv2 = dv2.Convert(d2)
				}

				fnArgs = append(fnArgs, dv1, dv2)
				resArgs = tm.Call(fnArgs)
				resLen := len(resArgs)

				// If it has no return, by logic, this is a secondary lift, hence
				// return the first argument.
				if resLen < 1 {
					return dl, nil
				}

				if resLen < 2 {
					rOnly := resArgs[0]
					if erErr, ok := rOnly.Interface().(error); ok {
						return nil, erErr
					}

					return rOnly.Interface(), nil
				}

				rData := resArgs[0].Interface()
				rErr := resArgs[1].Interface()

				if erErr, ok := rErr.(error); ok {
					return rData, erErr
				}

				return rData, nil
			}

			var useFirst bool

			can, convert := reflection.CanSetFor(d1, dv1)
			if can {
				useFirst = true

				if convert {
					dv1 = dv1.Convert(d1)
				}

				if useFirst {
					fnArgs = append(fnArgs, dv1)
				}
			}

			if !useFirst {
				can, convert = reflection.CanSetFor(d2, dv2)
				if !can {
					return nil, errors.New("Invalid Type Recieved")
				}

				if can && convert {
					dv2 = dv2.Convert(d2)
				}

				fnArgs = append(fnArgs, dv2)
			}

			resArgs = tm.Call(fnArgs)
			resLen := len(resArgs)

			// If it has no return, by logic, this is a secondary lift, hence
			// return the first argument.
			if resLen < 1 {
				return dl, nil
			}

			if resLen < 2 {
				rOnly := resArgs[0]
				if erErr, ok := rOnly.Interface().(error); ok {
					return nil, erErr
				}

				return rOnly.Interface(), nil
			}

			rData := resArgs[0].Interface()
			rErr := resArgs[1].Interface()

			if erErr, ok := rErr.(error); ok {
				return rData, erErr
			}

			return rData, nil
		}
	}
}
Example #2
0
// Wrap returns a new Handler wrapping the provided value as needed if
// it matches its DataHandler, ErrorHandler, Handler or magic function type.
// MagicFunction type is a function which follows this type form:
// func(context.Context, error, <CustomType>).
func Wrap(node interface{}) Handler {
	var hl Handler

	switch mh := node.(type) {
	case Handler:
		hl = node.(Handler)
	case func():
		hl = func(ctx context.Context, err error, d interface{}) (interface{}, error) {
			node.(func())()

			if err != nil {
				return nil, err
			}

			return d, err
		}
	case func(context.Context, error, interface{}) (interface{}, error):
		hl = node.(func(context.Context, error, interface{}) (interface{}, error))
	case func(context.Context, interface{}):
		hl = wrapDataWithNoReturn(node.(func(context.Context, interface{})))
	case func(context.Context, interface{}) interface{}:
		hl = wrapDataWithReturn(node.(func(context.Context, interface{}) interface{}))
	case func(context.Context, interface{}) (interface{}, error):
		hl = wrapData(node.(func(context.Context, interface{}) (interface{}, error)))
	case func(context.Context, error) (interface{}, error):
		hl = wrapError(node.(func(context.Context, error) (interface{}, error)))
	case func(context.Context, error):
		hl = func(ctx context.Context, err error, d interface{}) (interface{}, error) {
			if err != nil {
				node.(func(context.Context, error))(ctx, err)
			}

			return d, err
		}
	case func(context.Context, error) error:
		hl = func(ctx context.Context, err error, d interface{}) (interface{}, error) {
			if err != nil {
				(node.(func(context.Context, error)))(ctx, err)
			}

			return d, err
		}
	case func(interface{}) (interface{}, error):
		hl = func(ctx context.Context, err error, d interface{}) (interface{}, error) {
			if err != nil {
				return nil, err
			}

			return mh(d)
		}

	case func(interface{}) interface{}:
		hl = wrapDataOnly(node.(func(interface{}) interface{}))
	case func(interface{}):
		hl = wrapJustData(node.(func(interface{})))
	case func(error):
		hl = wrapJustError(node.(func(error)))
	case func(error) error:
		hl = wrapErrorReturn(node.(func(error) error))
	case func() interface{}:
		hl = wrapNoData(node.(func() interface{}))
	case func(interface{}) error:
		hl = wrapErrorOnly(node.(func(interface{}) error))
	default:
		if !reflection.IsFuncType(node) {
			return nil
		}

		tm, _ := reflection.FuncValue(node)
		args, _ := reflection.GetFuncArgumentsType(node)

		dLen := len(args)

		var data reflect.Type
		var dZero reflect.Value

		var useContext bool
		var useErr bool
		var useData bool
		var isCustom bool

		// Check if this first item is a context.Context type.
		if dLen < 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			useErr, _ = reflection.CanSetForType(errorType, args[0])

			if !useErr {
				data = args[0]
				dZero = reflect.Zero(data)
				useData = true
				isCustom = true
			}
		}

		if dLen == 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			useErr, _ = reflection.CanSetForType(errorType, args[1])

			if !useErr {
				data = args[1]
				dZero = reflect.Zero(data)
				useData = true
				isCustom = true
			}
		}

		if dLen > 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			useErr, _ = reflection.CanSetForType(errorType, args[1])

			data = args[2]
			dZero = reflect.Zero(data)
			useData = true

			if !useContext || !useData || !useErr {
				return nil
			}
		}

		if !useData && !useErr {
			return nil
		}

		hl = func(ctx context.Context, err error, val interface{}) (interface{}, error) {
			var fnArgs []reflect.Value
			var resArgs []reflect.Value

			var mctx reflect.Value

			me := dZeroError
			md := dZero

			if useContext {
				mctx = reflect.ValueOf(ctx)
			}

			if err != nil {
				me = reflect.ValueOf(err)
			}

			// Flag to skip function if data does not match.
			breakOfData := true

			if val != nil && useData {
				ok, convertData := reflection.CanSetForType(data, reflect.TypeOf(val))
				if ok {
					breakOfData = false
					md = reflect.ValueOf(val)

					if convertData {
						md = md.Convert(data)
					}
				}
			}

			if !useContext && !useData && !useErr {
				resArgs = tm.Call(nil)
			} else {
				if isCustom && !useErr && err != nil {
					return nil, err
				}

				if !useContext && !useData && useErr && err != nil {
					return nil, err
				}

				if useContext && !useData && useErr && err != nil {
					return nil, err
				}

				// Call the function if it only cares about the error
				if useContext && useErr && me != dZeroError && !useData {
					fnArgs = []reflect.Value{mctx, me}
				}

				// If data does not match then skip this fall.
				if breakOfData && len(fnArgs) < 1 {
					return nil, ErrInvalidType
				}

				if !breakOfData {
					if useContext && useErr && useData {
						fnArgs = []reflect.Value{mctx, me, md}
					}

					if useContext && !useErr && useData {
						fnArgs = []reflect.Value{mctx, md}
					}

					if !useContext && useData && useErr {
						fnArgs = []reflect.Value{me, md}
					}

					if !useContext && useData && !useErr {
						fnArgs = []reflect.Value{md}
					}
				}

				resArgs = tm.Call(fnArgs)
			}

			resLen := len(resArgs)
			if resLen > 0 {

				if resLen < 2 {
					rOnly := resArgs[0]

					if erErr, ok := rOnly.Interface().(error); ok {
						return nil, erErr
					}

					return rOnly.Interface(), nil
				}

				rData := resArgs[0].Interface()
				rErr := resArgs[1].Interface()

				if erErr, ok := rErr.(error); ok {
					return rData, erErr
				}

				return rData, nil
			}

			return dZero, nil
		}
	}

	return hl
}
Example #3
0
// WrapStreamHandler wraps a handler returning a StreamHandler.
func WrapStreamHandler(h interface{}) StreamHandler {
	switch h.(type) {
	case func(context.Context, error, interface{}) (interface{}, error):
		return func(ctx context.Context, data interface{}, end bool) interface{} {
			if ed, ok := data.(error); ok {
				res, err := (h.(func(context.Context, error, interface{}) (interface{}, error)))(ctx, ed, nil)
				if err != nil {
					return err
				}

				return res
			}

			res, err := (h.(func(context.Context, error, interface{}) (interface{}, error)))(ctx, nil, data)
			if err != nil {
				return err
			}

			return res
		}

	case func(context.Context, interface{}, bool) interface{}:
		return h.(func(context.Context, interface{}, bool) interface{})

	default:
		if !reflection.IsFuncType(h) {
			return nil
		}

		tm, _ := reflection.FuncValue(h)
		args, _ := reflection.GetFuncArgumentsType(h)

		dLen := len(args)

		var data reflect.Type
		var dZero reflect.Value

		var useContext bool
		var useBool bool
		var useData bool
		// var isCustom bool

		// Check if this first item is a context.Context type.
		if dLen < 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			useBool, _ = reflection.CanSetForType(boolType, args[0])

			if !useBool {
				data = args[0]
				dZero = reflect.Zero(data)
				useData = true
				// isCustom = true
			}
		}

		if dLen == 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			useBool, _ = reflection.CanSetForType(boolType, args[1])

			if !useBool {
				data = args[1]
				dZero = reflect.Zero(data)
				useData = true
				// isCustom = true
			}
		}

		if dLen > 2 {
			useContext, _ = reflection.CanSetForType(ctxType, args[0])
			useBool, _ = reflection.CanSetForType(boolType, args[2])

			data = args[1]
			dZero = reflect.Zero(data)
			useData = true
		}

		return func(ctx context.Context, val interface{}, done bool) interface{} {
			var fnArgs []reflect.Value
			var resArgs []reflect.Value

			var mctx reflect.Value

			me := reflect.ValueOf(done)
			md := dZero

			if useContext {
				mctx = reflect.ValueOf(ctx)
			}

			// Flag to skip function if data does not match.
			breakOfData := true

			if val != nil && useData {
				ok, convertData := reflection.CanSetForType(data, reflect.TypeOf(val))
				if ok {
					breakOfData = false
					md = reflect.ValueOf(val)

					if convertData {
						md = md.Convert(data)
					}
				}
			}

			if !useContext && !useData && !useBool {
				resArgs = tm.Call(nil)
			} else {

				// If data does not match then skip this fall.
				if breakOfData && len(fnArgs) < 1 {
					return nil
				}

				if !breakOfData {
					if useContext && useBool && useData {
						fnArgs = []reflect.Value{mctx, md, me}
					}

					if useContext && !useBool && useData {
						fnArgs = []reflect.Value{mctx, md}
					}

					if !useContext && useData && useBool {
						fnArgs = []reflect.Value{md, me}
					}

					if !useContext && useData && !useBool {
						fnArgs = []reflect.Value{md}
					}
				}

				resArgs = tm.Call(fnArgs)
			}

			resLen := len(resArgs)
			if resLen < 1 {
				return nil
			}

			return resArgs[0].Interface()
		}
	}
}