package script import ( "log" tengo "github.com/d5/tengo/v2" "github.com/d5/tengo/v2/token" "git.autistici.org/ai3/tools/iprep/ext" ) // Tengo object type that wraps a read-only map[string]int64, without // requiring us to go through a map[string]interface{}. type intMap map[string]int64 func (m intMap) String() string { return "<intMap>" } func (m intMap) TypeName() string { return "int-map" } func (m intMap) Copy() tengo.Object { return m } func (m intMap) IsFalsy() bool { return len(m) == 0 } func (m intMap) Equals(o tengo.Object) bool { return false } func (m intMap) BinaryOp(op token.Token, rhs tengo.Object) (tengo.Object, error) { return nil, tengo.ErrInvalidOperator } func (m intMap) IndexGet(index tengo.Object) (tengo.Object, error) { indexStr, ok := index.(*tengo.String) if !ok { return nil, tengo.ErrInvalidIndexType } // value, ok := m[indexStr.Value] // if !ok { // return tengo.UndefinedValue, nil // } value := m[indexStr.Value] return &tengo.Int{Value: value}, nil } func (m intMap) IndexSet(index, value tengo.Object) error { return tengo.ErrNotIndexAssignable } func (m intMap) Iterate() tengo.Iterator { return newIntMapIterator(m) } func (m intMap) Call(args ...tengo.Object) (ret tengo.Object, err error) { return } func (m intMap) CanCall() bool { return false } func (m intMap) CanIterate() bool { return false } // We need to make a copy of the map to iterate on it with this API. // Note that in the iterator, 'idx' maps to the *next* item. type intMapIterator struct { arr []keyIntPair idx int } type keyIntPair struct { key string value int64 } func newIntMapIterator(m intMap) *intMapIterator { arr := make([]keyIntPair, 0, len(m)) for k, v := range m { arr = append(arr, keyIntPair{key: k, value: v}) } return &intMapIterator{arr: arr} } func (i *intMapIterator) String() string { return "<intMapIterator>" } func (i *intMapIterator) TypeName() string { return "int-map-iterator" } func (i *intMapIterator) Copy() tengo.Object { return i } func (i *intMapIterator) IsFalsy() bool { return len(i.arr) == 0 } func (i *intMapIterator) Equals(o tengo.Object) bool { return false } func (i *intMapIterator) BinaryOp(op token.Token, rhs tengo.Object) (tengo.Object, error) { return nil, tengo.ErrInvalidOperator } func (i *intMapIterator) Next() bool { i.idx++ return i.idx <= len(i.arr) } func (i *intMapIterator) Key() tengo.Object { return &tengo.String{Value: i.arr[i.idx-1].key} } func (i *intMapIterator) Value() tengo.Object { return &tengo.Int{Value: i.arr[i.idx-1].value} } func (*intMapIterator) IndexGet(index tengo.Object) (tengo.Object, error) { return nil, tengo.ErrNotIndexable } func (*intMapIterator) IndexSet(index, value tengo.Object) error { return tengo.ErrNotIndexAssignable } func (*intMapIterator) Iterate() tengo.Iterator { return nil } func (*intMapIterator) Call(args ...tengo.Object) (ret tengo.Object, err error) { return } func (*intMapIterator) CanCall() bool { return false } func (*intMapIterator) CanIterate() bool { return false } // A Tengo callable to look up data in external sources. type extLookupFunc map[string]ext.ExternalSource func (f extLookupFunc) String() string { return "<extLookupFunc>" } func (f extLookupFunc) TypeName() string { return "ext-lookup-func" } func (f extLookupFunc) Copy() tengo.Object { return f } func (f extLookupFunc) IsFalsy() bool { return len(f) == 0 } func (f extLookupFunc) Equals(o tengo.Object) bool { return false } func (f extLookupFunc) BinaryOp(op token.Token, rhs tengo.Object) (tengo.Object, error) { return nil, tengo.ErrInvalidOperator } func (f extLookupFunc) Call(args ...tengo.Object) (ret tengo.Object, err error) { // Expected arguments: source name, IP. if len(args) != 2 { return nil, tengo.ErrWrongNumArguments } name, ok := tengo.ToString(args[0]) if !ok { return nil, tengo.ErrInvalidArgumentType{ Name: "source_name", Expected: "string", Found: args[0].TypeName(), } } ip, ok := tengo.ToString(args[1]) if !ok { return nil, tengo.ErrInvalidArgumentType{ Name: "ip", Expected: "string", Found: args[1].TypeName(), } } // Invoke the external source lookup method. if f == nil { return nil, tengo.ErrInvalidIndexValueType } src, ok := f[name] if !ok { return nil, tengo.ErrInvalidIndexValueType } result, err := src.LookupIP(ip) if err != nil { log.Printf("external source lookup error: ip=%s: %v", ip, err) return tengo.UndefinedValue, nil } return result, nil } func (extLookupFunc) IndexGet(index tengo.Object) (tengo.Object, error) { return nil, tengo.ErrNotIndexable } func (extLookupFunc) IndexSet(index, value tengo.Object) error { return tengo.ErrNotIndexAssignable } func (extLookupFunc) Iterate() tengo.Iterator { return nil } func (extLookupFunc) CanCall() bool { return true } func (extLookupFunc) CanIterate() bool { return false }