package hook import ( "fmt" "log" "reflect" "sort" "strings" "tunnel/pkg/server/env" "tunnel/pkg/server/opts" "tunnel/pkg/server/queue" ) type Type struct { Name string Desc string Func bool Param []opts.Param data interface{} } var hooks = map[string]*Type{} type Pipe struct { priv interface{} Hook H Send Func Recv Func } type Hooker interface { New(env env.Env) (interface{}, error) } type H interface { New(env env.Env) (*Pipe, error) } type Sender interface { Send(rq, wq queue.Q) error } type Recver interface { Recv(rq, wq queue.Q) error } type Func func(rq, wq queue.Q) error func (f Func) Send(rq, wq queue.Q) error { return f(rq, wq) } func (f Func) New(env env.Env) (interface{}, error) { return f, nil } type wrapper struct { hook Hooker name string reverse bool } func (w *wrapper) String() string { return fmt.Sprintf("hook:%s", w.name) } func (w *wrapper) New(env env.Env) (*Pipe, error) { it, err := w.hook.New(env) if err != nil { return nil, err } pipe := &Pipe{priv: it, Hook: w} if s, ok := it.(Sender); ok { pipe.Send = s.Send } if r, ok := it.(Recver); ok { pipe.Recv = r.Recv } if w.reverse { pipe.Send, pipe.Recv = pipe.Recv, pipe.Send } return pipe, nil } func (p *Pipe) Close() { if c, ok := p.priv.(interface{ Close() }); ok { c.Close() } } func initHook(i interface{}, opts opts.Opts) (Hooker, error) { if f, ok := i.(Func); ok { return f, nil } if p, ok := i.(pipeHolder); ok { return p, nil } h := reflect.New(reflect.TypeOf(i)).Interface() if err := opts.Configure(h); err != nil { return nil, err } return h.(Hooker), nil } func New(desc string) (H, error) { name, opts := opts.Parse(desc) reverse := false if len(name) > 0 && strings.Contains("-/", name[:1]) { name, reverse = name[1:], true } if hookType, ok := hooks[name]; !ok { return nil, fmt.Errorf("unknown hook '%s'", name) } else if h, err := initHook(hookType.data, opts); err != nil { return nil, fmt.Errorf("%s: %w", name, err) } else { w := &wrapper{ hook: h, name: name, reverse: reverse, } return w, nil } } func register(name string, desc string, i interface{}) { var param []opts.Param switch t := reflect.TypeOf(i); t.Kind() { case reflect.Struct: if _, ok := reflect.New(t).Interface().(Hooker); !ok { log.Panicf("uncompatible hook type '%s'", t) } param = opts.Parametrize(t) case reflect.Func: if _, ok := i.(Func); !ok { log.Panicf("uncompatible func type '%s'", t) } default: log.Panicf("non-struct and non-func type '%s'", t) } if _, ok := hooks[name]; ok { log.Panicf("duplicate hook name '%s'", name) } hooks[name] = &Type{ Name: name, Desc: desc, Param: param, data: i, } } func registerFunc(name string, desc string, f Func) { register(name, desc, f) } type pipeHolder struct { i interface{} } func (p pipeHolder) New(env.Env) (interface{}, error) { return p.i, nil } func registerPipe(name string, desc string, i interface{}) { register(name, desc, pipeHolder{i}) } func GetList() []string { var list []string for k := range hooks { list = append(list, k) } sort.Strings(list) return list } func GetType(name string) *Type { return hooks[name] }