85 lines
2.1 KiB
Go
85 lines
2.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package state
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
// indexTimeLatest is a special sentinel type that can be specified
|
|
// when querying by IndexTime to use the largest possible time value.
|
|
type indexTimeLatest struct{}
|
|
|
|
// IndexTime indexes a time.Time field of a struct.
|
|
// TODO(mitchellh): test
|
|
type IndexTime struct {
|
|
Field string
|
|
|
|
// Asc if true will index with ascending order. Usually you want to
|
|
// find the most recent data so this is false by default.
|
|
Asc bool
|
|
}
|
|
|
|
func (idx *IndexTime) FromObject(obj interface{}) (bool, []byte, error) {
|
|
v := reflect.Indirect(reflect.ValueOf(obj))
|
|
fv := v.FieldByName(idx.Field)
|
|
if !fv.IsValid() {
|
|
return false, nil,
|
|
fmt.Errorf("field '%s' is invalid %#v ", idx.Field, obj)
|
|
}
|
|
|
|
timeVal, ok := fv.Interface().(time.Time)
|
|
if !ok {
|
|
return false, nil,
|
|
fmt.Errorf("field '%s' is not a time %v ", idx.Field, obj)
|
|
}
|
|
|
|
return true, idx.fromTime(timeVal), nil
|
|
}
|
|
|
|
func (idx *IndexTime) FromArgs(args ...interface{}) ([]byte, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("must provide only a single argument")
|
|
}
|
|
|
|
if _, ok := args[0].(indexTimeLatest); ok {
|
|
if idx.Asc {
|
|
return nil, fmt.Errorf("ascending indexTimeLatest value")
|
|
}
|
|
|
|
var zeroValue [8]byte
|
|
return zeroValue[:], nil
|
|
}
|
|
|
|
arg, ok := args[0].(time.Time)
|
|
if !ok {
|
|
return nil, fmt.Errorf("argument must be a time: %#v", args[0])
|
|
}
|
|
|
|
return idx.fromTime(arg), nil
|
|
}
|
|
|
|
func (idx *IndexTime) fromTime(t time.Time) []byte {
|
|
// If we're ascending, we just use the unix timestamp. This will give us
|
|
// the proper ordering.
|
|
val := t.UnixNano()
|
|
|
|
// If we're descending, we use the time until max int64 which is the
|
|
// maximum size of a Go unix timestamp. This will give us a SMALLER
|
|
// value for NEWER (more recent) times. A descending order.
|
|
if !idx.Asc {
|
|
val = math.MaxInt64 - val
|
|
}
|
|
|
|
// Encoding uint64 is exactly 8 bytes. You can verify this in
|
|
// the encoding/binary source in the Go stdlib.
|
|
var buf [8]byte
|
|
binary.BigEndian.PutUint64(buf[:], uint64(val))
|
|
return buf[:]
|
|
}
|