daq

package module
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2024 License: MIT Imports: 6 Imported by: 7

README

daq

DAta Queries: Access nested properties in Go data structures.

Go Reference

Documentation

Overview

Access nested properties in Go data structures with DAta Queries

Concepts

  • A query result is (res any, err error)

  • Use assert functions or fallback functions to easily use query results with an expected type

  • Use As to create an assertion function from a convert function

  • Use Val to create a fallback function from a convert function

  • The convert function for target type T is func(v any) (r T, err error). If conversion of v fails, err != nil and r == nil. Otherwise, r is the conversion result.

  • By convention, convert functions for target type <T> are named To<T>(). Predefined assert functions are named As<T>(), predefined fallback functions <T>Or().

  • Use Get as the general way to perform queries. Read on to find more specific and efficient ways to query:

    Index, Field, Key to step with int, string and any.

    And also SliceAny, DictAny, MapAny to get query results that can be asserted from typical generic Go data.

  • To extend daq to your own types write convert functions and familiarize with Stepper, Slice, Dict and Map.

Convert Functions for Go's numeric types

  • Signed and unsigned integer types are promoted to each other as long as the actual runtime value is in target type's range.

  • Float values are promoted to integer types if the actual float has no fraction, and it is in target type's range.

  • Complex values with imag() == 0 are promoted to integer by promoting the real() part according to the float rules.

Example
var data any
json.Unmarshal([]byte(`{
		"a": 3.1415,
		"bs": [1, 2, 3]
	}`), &data)

prop, _ := Get(data, "a") // Ignore error in example for brevity
fmt.Println(prop)

prop, err := Get(data, "b") // This will fail, no "b"
fmt.Println(prop, err)

x, err := AsFloat64(Get(data, "bs", 1)) // Type assertion (encoding/json uses float64 for numbers)
fmt.Println(x, err)

x, err = AsFloat64(Get(data, "bs", -1)) // Access backwards from end
fmt.Println(x, err)

x = Float64Or(-1)(Get(data, "bs", 3)) // Fallback to -1 if Get fails
fmt.Println(x)
Output:

3.1415
<nil> step 0 [b]: unknown field 'b'
2 <nil>
3 <nil>
-1

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func As added in v0.4.1

func As[T any](conv func(any) (T, error)) func(any, error) (T, error)

As returns the assert function for the given type conversion conv. An assert function takes any value and an error as input. If the error is nil, the assert function performs the type conversion and returns the result. Otherwise, the assert function returns T's zero value and the error. Assert functions are useful to cast query results to expected types T.

Example
data := struct {
	Foo int
	Bar string
}{
	Foo: 4711,
	Bar: "baz",
}
fmt.Println(As(ToString)(Get(data, "Foo")))
fmt.Println(As(ToString)(Get(data, "Bar")))
fmt.Println(As(ToString)(Get(data, "void")))
Output:

cannot return int as string
baz <nil>
 step 0 [void]: unknown field 'void'

func AsBool

func AsBool(v any, err error) (bool, error)
Example
fmt.Println(AsBool(Get(true)))
fmt.Println(BoolOr(false)(Get(4711)))
Output:

true <nil>
false

func AsDuration

func AsDuration(v any, err error) (time.Duration, error)

func AsFloat32

func AsFloat32(v any, err error) (float32, error)

func AsFloat64

func AsFloat64(v any, err error) (float64, error)

func AsInt

func AsInt(v any, err error) (int, error)

func AsInt16

func AsInt16(v any, err error) (int16, error)

func AsInt32

func AsInt32(v any, err error) (int32, error)

func AsInt64

func AsInt64(v any, err error) (int64, error)

func AsInt8

func AsInt8(v any, err error) (int8, error)

func AsString

func AsString(v any, err error) (string, error)
Example
fmt.Println(StringOr("-")(DictAny(testExample).DaQGet("a")))
fmt.Println(Val(ToString, "-")(Get(testExample, "a", 0, "d")))
Output:

-
foo

func AsTime

func AsTime(v any, err error) (time.Time, error)

func AsUint

func AsUint(v any, err error) (uint, error)

func AsUint16

func AsUint16(v any, err error) (uint16, error)

func AsUint32

func AsUint32(v any, err error) (uint32, error)

func AsUint64

func AsUint64(v any, err error) (uint64, error)

func AsUint8

func AsUint8(v any, err error) (uint8, error)

func AsUintPtr

func AsUintPtr(v any, err error) (uintptr, error)

func BoolOr

func BoolOr(fallback bool) func(any, error) bool

func DeepEqual added in v0.5.0

func DeepEqual(lhs, rhs any) bool

func DeepEqualFunc added in v0.5.0

func DeepEqualFunc(lhs, rhs any, eq func(any, any) bool) bool

func DictOr added in v0.4.1

func DictOr(fallback Dict) func(any, error) Dict

func DurationOr

func DurationOr(fallback time.Duration) func(any, error) time.Duration

func Field

func Field(data any, name string) (res any, err error)

func Float32Or

func Float32Or(fallback float32) func(any, error) float32

func Float64Or

func Float64Or(fallback float64) func(any, error) float64

func Get

func Get(data any, path ...any) (res any, err error)

Get queries an element res from data for a given path of steps.

Example
data := map[string]any{
	"a": []any{
		map[string]any{
			"b": map[any]any{"c": 3},
			"d": "foo",
		},
		false,
	},
}
var i int = IntOr(-1)(Get(data, "a", 0, "b", "c"))
fmt.Println(i)
fmt.Println(Get(data, "a", 0, "b", "d"))
fmt.Println(Get(nil, "a", 0, "b", "d"))
Output:

3
<nil> step 3 [d]: unknown field 'd'
<nil> step 0 [a]: cannot step into <nil>

func Index

func Index(data any, index int) (res any, err error)

func Int16Or

func Int16Or(fallback int16) func(any, error) int16

func Int32Or

func Int32Or(fallback int32) func(any, error) int32

func Int64Or

func Int64Or(fallback int64) func(any, error) int64

func Int8Or

func Int8Or(fallback int8) func(any, error) int8

func IntOr

func IntOr(fallback int) func(any, error) int

func Key added in v0.4.1

func Key(data, key any) (res any, err error)

func MapOr

func MapOr(fallback Map) func(any, error) Map

func SliceOr

func SliceOr(fallback Slice) func(any, error) Slice

func Step

func Step(data, step any) (res any, err error)

func StepEach added in v0.3.0

func StepEach(data any, do func(kind reflect.Kind, key, value any) error) error
Example (Array)
data := [4]any{0, '1', "two", nil}
err := StepEach(data, testPrintStep)
fmt.Println(err)
Output:

kind 'array': key=0 / value=0
kind 'array': key=1 / value=49
kind 'array': key=2 / value="two"
kind 'array': key=3 / value=<nil>
<nil>
Example (Atom)
err := StepEach(5180, testPrintStep)
fmt.Println(err)
Output:

kind 'invalid': key=<nil> / value=5180
<nil>
Example (Map)
data := map[any]any{
	false: "foo",
	7:     nil,
	"bar": 4711,
}
err := StepEach(data, testPrintStep)
fmt.Println(err)
Output:

kind 'map': key=false / value="foo"
kind 'map': key=7 / value=<nil>
kind 'map': key="bar" / value=4711
<nil>
Example (Slice)
data := []any{0, '1', "two", nil}
err := StepEach(data, testPrintStep)
fmt.Println(err)
Output:

kind 'slice': key=0 / value=0
kind 'slice': key=1 / value=49
kind 'slice': key=2 / value="two"
kind 'slice': key=3 / value=<nil>
<nil>
Example (Struct)
type Tstnest struct {
	Foo int
}
data := struct {
	Tstnest
	Bar  string
	Nest Tstnest
}{
	Tstnest: Tstnest{Foo: 4711},
	Bar:     "baz",
	Nest:    Tstnest{Foo: -1},
}
err := StepEach(data, testPrintStep)
fmt.Println(err)
Output:

kind 'struct': key="Foo" / value=4711
kind 'struct': key="Bar" / value="baz"
kind 'struct': key="Nest" / value=daq.Tstnest{Foo:-1}
<nil>

func StringOr

func StringOr(fallback string) func(any, error) string

func TestingDeepEqual added in v0.5.0

func TestingDeepEqual(t testing.TB, lhs, rhs any) (diffCount int)

func TestingDeepEqualFunc added in v0.5.0

func TestingDeepEqualFunc(t testing.TB, lhs, rhs any, eq func(any, any) bool) (diffCount int)

func TimeOr

func TimeOr(fallback time.Time) func(any, error) time.Time

func ToBool added in v0.4.1

func ToBool(v any) (bool, error)

func ToDuration added in v0.4.1

func ToDuration(v any) (time.Duration, error)

func ToFloat32 added in v0.4.1

func ToFloat32(v any) (float32, error)

func ToFloat64 added in v0.4.1

func ToFloat64(v any) (float64, error)

func ToInt added in v0.4.1

func ToInt(v any) (int, error)

func ToInt16 added in v0.4.1

func ToInt16(v any) (int16, error)

func ToInt32 added in v0.4.1

func ToInt32(v any) (int32, error)

func ToInt64 added in v0.4.1

func ToInt64(v any) (int64, error)

func ToInt8 added in v0.4.1

func ToInt8(v any) (int8, error)

func ToString added in v0.4.1

func ToString(v any) (string, error)

func ToTime added in v0.4.1

func ToTime(v any) (time.Time, error)
Example
t := time.Date(2023, 12, 29, 2, 4, 0, 0, time.UTC)
fmt.Println(ToTime(t))
fmt.Println(ToTime(&t))
fmt.Println(ToTime((*time.Time)(nil)))
Output:

2023-12-29 02:04:00 +0000 UTC <nil>
2023-12-29 02:04:00 +0000 UTC <nil>
0001-01-01 00:00:00 +0000 UTC <nil>

func ToUint added in v0.4.1

func ToUint(v any) (uint, error)

func ToUint16 added in v0.4.1

func ToUint16(v any) (uint16, error)

func ToUint32 added in v0.4.1

func ToUint32(v any) (uint32, error)

func ToUint64 added in v0.4.1

func ToUint64(v any) (uint64, error)

func ToUint8 added in v0.4.1

func ToUint8(v any) (uint8, error)

func ToUintPtr added in v0.4.1

func ToUintPtr(v any) (uintptr, error)

func Uint16Or

func Uint16Or(fallback uint16) func(any, error) uint16

func Uint32Or

func Uint32Or(fallback uint32) func(any, error) uint32

func Uint64Or

func Uint64Or(fallback uint64) func(any, error) uint64

func Uint8Or

func Uint8Or(fallback uint8) func(any, error) uint8

func UintOr

func UintOr(fallback uint) func(any, error) uint

func UintPtrOr

func UintPtrOr(fallback uintptr) func(any, error) uintptr

func Val

func Val[T any](conv func(any) (T, error), fallback T) func(v any, err error) T

Val returns a fallback function for the given type conversion conv. A fallback function always returns a value of expected type T. It returns the asserted value of As in case of success. Otherwise it returns the fallback.

Example
data := struct {
	Foo int
	Bar string
}{
	Foo: 4711,
	Bar: "baz",
}
fmt.Println(Val(ToString, "fallback1")(Get(data, "Foo")))
fmt.Println(Val(ToString, "fallback2")(Get(data, "Bar")))
fmt.Println(Val(ToString, "fallback3")(Get(data, "void")))
Output:

fallback1
baz
fallback3

Types

type Alt

type Alt struct {
	S  any
	Or any
}
Example
data := []string{"foo", "bar", "baz"}
fmt.Println(Step(data, "bar"))
fmt.Println(Step(data, Alt{S: "bar", Or: 4711}))
Output:

<nil> key lookup: cannot return []string as daq.Map
4711 <nil>

func (Alt) Step

func (a Alt) Step(data any) (any, error)

type CollectionTypeError added in v0.5.0

type CollectionTypeError struct {
	// contains filtered or unexported fields
}

func (CollectionTypeError) Error added in v0.5.0

func (e CollectionTypeError) Error() string

func (CollectionTypeError) Is added in v0.5.0

func (e CollectionTypeError) Is(err error) bool

type Delta added in v0.5.0

type Delta struct {
	AtomEqual   func(lhs, rhs any) bool
	OnNotEqual  func(path []any, left, right any) error
	OnLeftOnly  func(path []any, val any) error
	OnRightOnly func(path []any, val any) error
}

func (*Delta) Compare added in v0.5.0

func (cmpr *Delta) Compare(l, r any) error

type Dict added in v0.4.1

type Dict interface {
	DaQGet(name string) (res any, err error)
	DaQEachKey(do func(key string) error) error
}

Dict is queried with a string as name.

func AsDict added in v0.4.1

func AsDict(v any, err error) (Dict, error)

func ToDict added in v0.4.1

func ToDict(v any) (Dict, error)

type DictAny added in v0.4.1

type DictAny map[string]any

func (DictAny) DaQEachKey added in v0.4.1

func (d DictAny) DaQEachKey(do func(key string) error) error

func (DictAny) DaQGet added in v0.4.1

func (d DictAny) DaQGet(n string) (any, error)

type FirstIdxOf added in v0.5.0

type FirstIdxOf []int
Example
data := []string{"foo", "bar", "baz"}
fmt.Println(Step(data, FirstIdxOf{99, 1})) // more efficient
fmt.Println(Step(data, FirstOf{99, 1}))    // more general
Output:

bar <nil>
bar <nil>

func (FirstIdxOf) Step added in v0.5.0

func (idxs FirstIdxOf) Step(data any) (res any, err error)

type FirstKeyOf added in v0.5.0

type FirstKeyOf []any
Example
data := map[any]any{
	"foo": 1,
	"bar": 2,
	"baz": 3,
}
fmt.Println(Step(data, FirstKeyOf{"quux", "bar"})) // more efficient
fmt.Println(Step(data, FirstOf{"quux", "bar"}))    // more general
Output:

2 <nil>
2 <nil>

func (FirstKeyOf) Step added in v0.5.0

func (ns FirstKeyOf) Step(data any) (res any, err error)

type FirstNameOf

type FirstNameOf []string
Example
data := map[string]int{
	"foo": 1,
	"bar": 2,
	"baz": 3,
}
fmt.Println(Step(data, FirstNameOf{"quux", "bar"})) // more efficient
fmt.Println(Step(data, FirstOf{"quux", "bar"}))     // more general
Output:

2 <nil>
2 <nil>

func (FirstNameOf) Step

func (ns FirstNameOf) Step(data any) (res any, err error)

type FirstOf

type FirstOf []any

func (FirstOf) Step

func (keys FirstOf) Step(data any) (res any, err error)

type IndexOutOfRange added in v0.4.1

type IndexOutOfRange struct {
	I, Min, Max int
}

func (IndexOutOfRange) Error added in v0.4.1

func (e IndexOutOfRange) Error() string

type Map

type Map interface {
	DaQGet(key any) (res any, err error)
	DaQEachKey(do func(key any) error) error
}

Map is queried with any comparable value as key.

func AsMap

func AsMap(v any, err error) (Map, error)

func ToMap added in v0.4.1

func ToMap(v any) (Map, error)

type MapAny added in v0.4.1

type MapAny map[any]any

func (MapAny) DaQEachKey added in v0.4.1

func (m MapAny) DaQEachKey(do func(key any) error) error

func (MapAny) DaQGet added in v0.4.1

func (m MapAny) DaQGet(key any) (any, error)

type Slice

type Slice interface {
	DaQGet(index int) (res any, err error)
	DaQLen() int
}

Slice is queried with an int index. Negative indices access from the end with -1 being the last element and -DaQLen() the first.

func AsSlice

func AsSlice(v any, err error) (Slice, error)
Example
s := SliceOr(nil)(DictAny(testExample).DaQGet("a"))
for i := 0; i < s.DaQLen(); i++ {
	fmt.Println(must.Ret(s.DaQGet(i)))
}
Output:

map[b:map[c:3] d:foo]
false

func ToSlice added in v0.4.1

func ToSlice(v any) (Slice, error)

type SliceAny added in v0.4.1

type SliceAny []any

func (SliceAny) DaQGet added in v0.4.1

func (s SliceAny) DaQGet(i int) (any, error)

func (SliceAny) DaQLen added in v0.4.1

func (s SliceAny) DaQLen() int

type StepError

type StepError struct {
	N    int
	Step any
	Err  error
}

func (StepError) Error

func (e StepError) Error() string

func (StepError) Unwrap

func (e StepError) Unwrap() error

type Stepper

type Stepper interface {
	Step(data any) (any, error)
}

Stepper allows to define how Step does the step from one data object to an element.

type Time

type Time string
Example
data := struct {
	Time string
}{
	Time: "2023-12-29T22:04:00Z",
}
// Asserting time.Time needs parsing, so we use Time() to create a parsing assertion
fmt.Println(Val(Time(time.RFC3339).To, time.Time{})(Get(data, "Time")))
fmt.Println(Val(Time(time.RFC3339).To, time.Time{})(Get(data, "void")))
Output:

2023-12-29 22:04:00 +0000 UTC
0001-01-01 00:00:00 +0000 UTC

func (Time) To added in v0.4.1

func (t Time) To(v any) (time.Time, error)

Convert function producing time.Time that also uses time.Parse() with layout t when v is string or fmt.Stringer. Otherwise it converts like ToTime.

type Track

type Track []any

func GetTrack

func GetTrack(data any, path ...any) (t Track, err error)

func (Track) Step added in v0.5.0

func (t Track) Step(i int) any

type UnknownField added in v0.4.1

type UnknownField string

func (UnknownField) Error added in v0.4.1

func (e UnknownField) Error() string

type UnknownKey added in v0.4.1

type UnknownKey struct {
	// contains filtered or unexported fields
}

func (UnknownKey) Error added in v0.4.1

func (e UnknownKey) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL