2018-02-15 17:14:05 +08:00
|
|
|
package vals
|
2018-01-25 08:26:46 +08:00
|
|
|
|
2018-01-25 09:40:15 +08:00
|
|
|
import (
|
|
|
|
"errors"
|
2018-01-28 01:26:22 +08:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2018-01-25 09:40:15 +08:00
|
|
|
"unicode/utf8"
|
2018-01-28 01:26:22 +08:00
|
|
|
|
|
|
|
"github.com/xiaq/persistent/vector"
|
2018-01-25 09:40:15 +08:00
|
|
|
)
|
2018-01-25 08:26:46 +08:00
|
|
|
|
2018-01-30 02:58:23 +08:00
|
|
|
// Getter wraps the Get method.
|
|
|
|
type Getter interface {
|
|
|
|
// Get retrieves the value corresponding to the specified key in the
|
|
|
|
// container. It returns the value (if any), and whether it actually
|
|
|
|
// exists.
|
|
|
|
Get(k interface{}) (v interface{}, ok bool)
|
|
|
|
}
|
|
|
|
|
2018-01-25 08:26:46 +08:00
|
|
|
// Indexer wraps the Index method.
|
|
|
|
type Indexer interface {
|
|
|
|
// Index retrieves one value from the receiver at the specified index.
|
2018-01-30 01:39:41 +08:00
|
|
|
Index(idx interface{}) (interface{}, error)
|
2018-01-25 08:26:46 +08:00
|
|
|
}
|
|
|
|
|
2018-01-28 01:26:22 +08:00
|
|
|
var (
|
|
|
|
errIndexMustBeString = errors.New("index must be string")
|
|
|
|
errNotIndexable = errors.New("not indexable")
|
|
|
|
errBadIndex = errors.New("bad index")
|
|
|
|
errIndexOutOfRange = errors.New("index out of range")
|
|
|
|
)
|
2018-01-25 08:26:46 +08:00
|
|
|
|
2018-01-29 22:36:43 +08:00
|
|
|
type noSuchKeyError struct {
|
2018-01-30 01:39:41 +08:00
|
|
|
key interface{}
|
2018-01-29 22:36:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NoSuchKey returns an error indicating that a key is not found in a map-like
|
|
|
|
// value.
|
2018-01-30 01:39:41 +08:00
|
|
|
func NoSuchKey(k interface{}) error {
|
2018-01-29 22:36:43 +08:00
|
|
|
return noSuchKeyError{k}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (err noSuchKeyError) Error() string {
|
|
|
|
return "no such key: " + Repr(err.key, NoPretty)
|
|
|
|
}
|
|
|
|
|
2018-01-28 01:26:22 +08:00
|
|
|
// Index indexes a value with the given key. It is implemented for the builtin
|
2018-01-30 02:58:23 +08:00
|
|
|
// type string, and types satisfying the listIndexable, Getter or Indexer
|
2018-01-29 22:36:43 +08:00
|
|
|
// interface. For other types, it returns a nil value and a non-nil error.
|
2018-01-30 01:39:41 +08:00
|
|
|
func Index(a, k interface{}) (interface{}, error) {
|
2018-01-25 08:26:46 +08:00
|
|
|
switch a := a.(type) {
|
2018-02-02 12:42:41 +08:00
|
|
|
case Indexer:
|
|
|
|
return a.Index(k)
|
2018-01-25 09:40:15 +08:00
|
|
|
case string:
|
2018-01-28 01:26:22 +08:00
|
|
|
return indexString(a, k)
|
|
|
|
case listIndexable:
|
|
|
|
return indexList(a, k)
|
2018-01-30 02:58:23 +08:00
|
|
|
case Getter:
|
2018-01-29 22:36:43 +08:00
|
|
|
v, ok := a.Get(k)
|
|
|
|
if !ok {
|
|
|
|
return nil, NoSuchKey(k)
|
|
|
|
}
|
|
|
|
return v, nil
|
2018-01-25 08:26:46 +08:00
|
|
|
default:
|
|
|
|
return nil, errNotIndexable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MustIndex indexes i with k and returns the value. If the operation
|
|
|
|
// resulted in an error, it panics. It is useful when the caller knows that the
|
|
|
|
// key must be present.
|
2018-01-30 01:39:41 +08:00
|
|
|
func MustIndex(i Indexer, k interface{}) interface{} {
|
2018-01-25 08:26:46 +08:00
|
|
|
v, err := i.Index(k)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
2018-01-25 09:40:15 +08:00
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func indexString(s string, index interface{}) (string, error) {
|
2018-01-28 01:26:22 +08:00
|
|
|
i, j, err := convertStringIndex(index, s)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return s[i:j], nil
|
|
|
|
}
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func convertStringIndex(rawIndex interface{}, s string) (int, int, error) {
|
2018-01-28 01:26:22 +08:00
|
|
|
index, err := ConvertListIndex(rawIndex, len(s))
|
2018-01-25 09:40:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return 0, 0, err
|
|
|
|
}
|
2018-01-28 01:26:22 +08:00
|
|
|
r, size := utf8.DecodeRuneInString(s[index.Lower:])
|
2018-01-25 09:40:15 +08:00
|
|
|
if r == utf8.RuneError {
|
2018-01-28 01:26:22 +08:00
|
|
|
return 0, 0, errBadIndex
|
|
|
|
}
|
|
|
|
if index.Slice {
|
|
|
|
if r, _ := utf8.DecodeLastRuneInString(s[:index.Upper]); r == utf8.RuneError {
|
|
|
|
return 0, 0, errBadIndex
|
|
|
|
}
|
|
|
|
return index.Lower, index.Upper, nil
|
|
|
|
}
|
|
|
|
return index.Lower, index.Lower + size, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type listIndexable interface {
|
|
|
|
Lener
|
|
|
|
Nth(int) interface{}
|
|
|
|
SubVector(int, int) vector.Vector
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ listIndexable = vector.Vector(nil)
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func indexList(l listIndexable, rawIndex interface{}) (interface{}, error) {
|
2018-01-28 01:26:22 +08:00
|
|
|
index, err := ConvertListIndex(rawIndex, l.Len())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if index.Slice {
|
|
|
|
return l.SubVector(index.Lower, index.Upper), nil
|
|
|
|
}
|
|
|
|
return l.Nth(index.Lower), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListIndex represents a (converted) list index.
|
|
|
|
type ListIndex struct {
|
|
|
|
Slice bool
|
|
|
|
Lower int
|
|
|
|
Upper int
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConvertListIndex parses a list index, check whether it is valid, and returns
|
|
|
|
// the converted structure.
|
2018-01-30 01:39:41 +08:00
|
|
|
func ConvertListIndex(rawIndex interface{}, n int) (*ListIndex, error) {
|
2018-01-28 01:26:22 +08:00
|
|
|
s, ok := rawIndex.(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, errIndexMustBeString
|
|
|
|
}
|
|
|
|
slice, i, j, err := parseListIndex(s, n)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if i < 0 {
|
|
|
|
i += n
|
|
|
|
}
|
|
|
|
if j < 0 {
|
|
|
|
j += n
|
2018-01-25 09:40:15 +08:00
|
|
|
}
|
2018-02-04 10:26:24 +08:00
|
|
|
if slice {
|
|
|
|
if !(0 <= i && i <= j && j <= n) {
|
|
|
|
return nil, errIndexOutOfRange
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if !(0 <= i && i < n) {
|
|
|
|
return nil, errIndexOutOfRange
|
|
|
|
}
|
2018-01-28 01:26:22 +08:00
|
|
|
}
|
|
|
|
return &ListIndex{slice, i, j}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListIndex = Number |
|
|
|
|
// Number ':' Number
|
|
|
|
func parseListIndex(s string, n int) (slice bool, i int, j int, err error) {
|
|
|
|
colon := strings.IndexRune(s, ':')
|
|
|
|
if colon == -1 {
|
|
|
|
// A single number
|
|
|
|
i, err := atoi(s)
|
|
|
|
if err != nil {
|
|
|
|
return false, 0, 0, err
|
|
|
|
}
|
|
|
|
return false, i, 0, nil
|
|
|
|
}
|
|
|
|
if s[:colon] == "" {
|
|
|
|
i = 0
|
|
|
|
} else {
|
|
|
|
i, err = atoi(s[:colon])
|
|
|
|
if err != nil {
|
|
|
|
return false, 0, 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if s[colon+1:] == "" {
|
|
|
|
j = n
|
|
|
|
} else {
|
|
|
|
j, err = atoi(s[colon+1:])
|
|
|
|
if err != nil {
|
|
|
|
return false, 0, 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Two numbers
|
|
|
|
return true, i, j, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// atoi is a wrapper around strconv.Atoi, converting strconv.ErrRange to
|
|
|
|
// errIndexOutOfRange.
|
|
|
|
func atoi(a string) (int, error) {
|
|
|
|
i, err := strconv.Atoi(a)
|
|
|
|
if err != nil {
|
|
|
|
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
|
|
|
return 0, errIndexOutOfRange
|
2018-01-25 09:40:15 +08:00
|
|
|
}
|
2018-01-28 01:26:22 +08:00
|
|
|
return 0, errBadIndex
|
2018-01-25 09:40:15 +08:00
|
|
|
}
|
2018-01-28 01:26:22 +08:00
|
|
|
return i, nil
|
2018-01-25 09:40:15 +08:00
|
|
|
}
|