gotype/unfold_user.go (234 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package gotype import ( "errors" "fmt" "reflect" "unsafe" structform "github.com/elastic/go-structform" stunsafe "github.com/elastic/go-structform/internal/unsafe" ) // Expander supports the creation of an UnfoldState for handling the unfolding // into the current value. type Expander interface { Expand() UnfoldState } // UnfoldCtx provides access to the shared unfolding stack. It is used with // UnfoldState, so to implement very custom parsing. type UnfoldCtx interface { // Done signals the context that the current state is finished. // The current state will be removed from the stack and processing continues // with the current state. Done() // Cont replaces the current state with the new state. All unfolding // will continue with the new state Cont(st UnfoldState) // Push adds a new parsing state on top of the state stack. Unfolding // will continue with the new state. Push(st UnfoldState) } // UnfoldState defines a custom user defined unfolder. // When unfolding a stack is used to keep state. The UnfoldCtx provides // methods to manipluate the stack of active unfolders. // The current UnfoldState will be used as long as it has not been remove or replaced // using one of the UnfoldCtx control methods. type UnfoldState interface { // primitives OnNil(ctx UnfoldCtx) error OnBool(ctx UnfoldCtx, b bool) error OnString(ctx UnfoldCtx, str string) error OnInt(ctx UnfoldCtx, i int64) error OnUint(ctx UnfoldCtx, u uint64) error OnFloat(ctx UnfoldCtx, f float64) error // array types OnArrayStart(ctx UnfoldCtx, length int, bt structform.BaseType) error OnArrayFinished(ctx UnfoldCtx) error // object types OnObjectStart(ctx UnfoldCtx, length int, bt structform.BaseType) error OnObjectFinished(ctx UnfoldCtx) error OnKey(ctx UnfoldCtx, key string) error } // BaseUnfoldState implements UnfoldState, but returns an error for every // callback possible. // One case embedd BaseUnfoldState in a custom struct, so to reduce the number // of methods to implement. type BaseUnfoldState struct{} type stateUnfolder struct { unfolder UnfoldState } type unfoldUserStateInit struct { fn unfoldStateInitFn } type unfoldStateInitFn func(unsafe.Pointer) UnfoldState type unfoldExpanderInit struct{} func makeUserUnfolder(fn reflect.Value) (target reflect.Type, unfolder reflUnfolder, err error) { t := fn.Type() if fn.Kind() != reflect.Func { return nil, nil, errors.New("function type required") } switch { case t.NumIn() == 2 && t.NumOut() == 1: unfolder, err = makeUserPrimitiveUnfolder(fn) case t.NumIn() == 1 && t.NumOut() == 1: unfolder, err = makeUserStateUnfolder(fn) case t.NumIn() == 1 && t.NumOut() == 2: unfolder, err = makeUserProcessingUnfolder(fn) default: return nil, nil, fmt.Errorf("invalid number of arguments in unfolder: %v", fn) } return t.In(0), unfolder, err } func makeUserPrimitiveUnfolder(fn reflect.Value) (reflUnfolder, error) { t := fn.Type() if fn.Kind() != reflect.Func { return nil, errors.New("function type required") } if t.NumIn() != 2 { return nil, fmt.Errorf("function '%v' must accept 2 arguments", fn) } if t.NumOut() != 1 || (t.NumOut() > 0 && t.Out(0) != tError) { return nil, fmt.Errorf("function '%v' does not return errors", fn) } ta0 := t.In(0) if ta0.Kind() != reflect.Ptr { return nil, fmt.Errorf("first argument in function '%v' must be a pointer", t.Name()) } constr := lookupUserPrimitiveConstructor(t.In(1)) if constr == nil { return nil, fmt.Errorf("%v is no supported primitive type", t.In(1)) } unfolder := constr(fn) return liftGoUnfolder(unfolder), nil } func makeUserProcessingUnfolder(fn reflect.Value) (reflUnfolder, error) { if err := checkProcessingUnfolder(fn); err != nil { return nil, err } return liftGoUnfolder(&unfolderUserProcessingInit{ fnInit: *((*userProcessingInitFn)(stunsafe.UnsafeFnPtr(fn))), }), nil } func checkProcessingUnfolder(fn reflect.Value) error { if fn.Kind() != reflect.Func { return fmt.Errorf("processing unfolder '%v' is no function", fn) } t := fn.Type() // check input if t.NumIn() != 1 { return fmt.Errorf("processing unfolder '%v' must accept one target argument", fn) } in := t.In(0) if in.Kind() != reflect.Ptr { return fmt.Errorf("processing unfolder '%v' target argument must be a pointer", fn) } // check returns if t.NumOut() != 2 { return fmt.Errorf("processing unfolder '%v' must return 2 values", fn) } if t.Out(0) != tInterface { return fmt.Errorf("processing unfolder '%v' must return interface{} as first value", fn) } proc := t.Out(1) if proc.Kind() != reflect.Func { return fmt.Errorf("processing unfolder '%v' second return must be a function", fn) } // check processing function input if proc.NumIn() != 2 { return fmt.Errorf("processing function of '%v' must accept 2 arguments", fn) } if proc.In(0) != in { return fmt.Errorf("processing function of '%v' must accept the target type '%v'", fn, in) } if proc.In(1) != tInterface { return fmt.Errorf("processing function of '%v' must accept interface{} as second argument", fn) } // check processing function output if proc.NumOut() != 1 { return fmt.Errorf("processing function of '%v' must return exactly one value", fn) } if proc.Out(0) != tError { return fmt.Errorf("processing function of '%v' must return an error value", fn) } return nil } func makeUserStateUnfolder(fn reflect.Value) (reflUnfolder, error) { if err := checkUserStateUnfolder(fn); err != nil { return nil, err } return liftGoUnfolder(&unfoldUserStateInit{ fn: *((*unfoldStateInitFn)(stunsafe.UnsafeFnPtr(fn))), }), nil } func checkUserStateUnfolder(fn reflect.Value) error { if fn.Kind() != reflect.Func { return fmt.Errorf("state unfolder '%v' is no function", fn) } t := fn.Type() // check input if t.NumIn() != 1 { return fmt.Errorf("state unfolder '%v' must accept one target argument", fn) } in := t.In(0) if in.Kind() != reflect.Ptr { return fmt.Errorf("state unfolder '%v' target argument must be a pointer", fn) } if t.NumOut() != 1 || (t.NumOut() > 0 && t.Out(0) != tUnfoldState) { return fmt.Errorf("function '%v' does not return UnfoldState type", fn) } return nil } var _unfoldExpanderInit = &unfoldExpanderInit{} func newExpanderInit() reflUnfolder { return _unfoldExpanderInit } func (*unfoldExpanderInit) initState(ctx *unfoldCtx, val reflect.Value) { st := val.Interface().(Expander).Expand() ctx.Push(st) } func (ctx *unfoldCtx) Done() { ctx.unfolder.pop() } func (ctx *unfoldCtx) Cont(st UnfoldState) { ctx.unfolder.pop() ctx.unfolder.push(&stateUnfolder{st}) } func (ctx *unfoldCtx) Push(st UnfoldState) { ctx.unfolder.push(&stateUnfolder{st}) } func (u *unfoldUserStateInit) initState(ctx *unfoldCtx, ptr unsafe.Pointer) { st := u.fn(ptr) ctx.Push(st) } func (u *stateUnfolder) OnNil(ctx *unfoldCtx) error { return u.unfolder.OnNil(ctx) } func (u *stateUnfolder) OnBool(ctx *unfoldCtx, v bool) error { return u.unfolder.OnBool(ctx, v) } func (u *stateUnfolder) OnString(ctx *unfoldCtx, v string) error { return u.unfolder.OnString(ctx, v) } func (u *stateUnfolder) OnStringRef(ctx *unfoldCtx, v []byte) error { return u.unfolder.OnString(ctx, string(v)) } func (u *stateUnfolder) OnInt8(ctx *unfoldCtx, v int8) error { return u.unfolder.OnInt(ctx, int64(v)) } func (u *stateUnfolder) OnInt16(ctx *unfoldCtx, v int16) error { return u.unfolder.OnInt(ctx, int64(v)) } func (u *stateUnfolder) OnInt32(ctx *unfoldCtx, v int32) error { return u.unfolder.OnInt(ctx, int64(v)) } func (u *stateUnfolder) OnInt64(ctx *unfoldCtx, v int64) error { return u.unfolder.OnInt(ctx, int64(v)) } func (u *stateUnfolder) OnInt(ctx *unfoldCtx, v int) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnByte(ctx *unfoldCtx, v byte) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnUint8(ctx *unfoldCtx, v uint8) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnUint16(ctx *unfoldCtx, v uint16) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnUint32(ctx *unfoldCtx, v uint32) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnUint64(ctx *unfoldCtx, v uint64) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnUint(ctx *unfoldCtx, v uint) error { return u.unfolder.OnUint(ctx, uint64(v)) } func (u *stateUnfolder) OnFloat32(ctx *unfoldCtx, v float32) error { return u.unfolder.OnFloat(ctx, float64(v)) } func (u *stateUnfolder) OnFloat64(ctx *unfoldCtx, v float64) error { return u.unfolder.OnFloat(ctx, float64(v)) } func (u *stateUnfolder) OnArrayStart(ctx *unfoldCtx, N int, bt structform.BaseType) error { return u.unfolder.OnArrayStart(ctx, N, bt) } func (u *stateUnfolder) OnArrayFinished(ctx *unfoldCtx) error { return u.unfolder.OnArrayFinished(ctx) } func (u *stateUnfolder) OnChildArrayDone(ctx *unfoldCtx) error { return nil } func (u *stateUnfolder) OnObjectStart(ctx *unfoldCtx, N int, bt structform.BaseType) error { return u.unfolder.OnObjectStart(ctx, N, bt) } func (u *stateUnfolder) OnObjectFinished(ctx *unfoldCtx) error { return u.unfolder.OnObjectFinished(ctx) } func (u *stateUnfolder) OnKey(ctx *unfoldCtx, v string) error { return u.unfolder.OnKey(ctx, v) } func (u *stateUnfolder) OnKeyRef(ctx *unfoldCtx, v []byte) error { return u.unfolder.OnKey(ctx, string(v)) } func (u *stateUnfolder) OnChildObjectDone(ctx *unfoldCtx) error { return nil } func (*BaseUnfoldState) OnNil(ctx UnfoldCtx) error { return errUnexpectedNil } func (*BaseUnfoldState) OnBool(ctx UnfoldCtx, b bool) error { return errUnexpectedBool } func (*BaseUnfoldState) OnString(ctx UnfoldCtx, str string) error { return errUnexpectedString } func (*BaseUnfoldState) OnInt(ctx UnfoldCtx, i int64) error { return errUnexpectedNum } func (*BaseUnfoldState) OnUint(ctx UnfoldCtx, u uint64) error { return errUnexpectedNum } func (*BaseUnfoldState) OnFloat(ctx UnfoldCtx, f float64) error { return errUnexpectedNum } func (*BaseUnfoldState) OnArrayStart(ctx UnfoldCtx, length int, bt structform.BaseType) error { return errUnexpectedArrayStart } func (*BaseUnfoldState) OnArrayFinished(ctx UnfoldCtx) error { return errUnexpectedArrayEnd } func (*BaseUnfoldState) OnObjectStart(ctx UnfoldCtx, length int, bt structform.BaseType) error { return errUnexpectedObjectStart } func (*BaseUnfoldState) OnObjectFinished(ctx UnfoldCtx) error { return errUnexpectedObjectEnd } func (*BaseUnfoldState) OnKey(ctx UnfoldCtx, key string) error { return errUnexpectedString }