summaryrefslogtreecommitdiff
path: root/vendor/github.com/lann/builder/builder.go
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
committermo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
commit20ef0d92694465ac86b550df139e8366a0a2b4fa (patch)
tree3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/lann/builder/builder.go
parent44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff)
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/lann/builder/builder.go')
-rw-r--r--vendor/github.com/lann/builder/builder.go225
1 files changed, 225 insertions, 0 deletions
diff --git a/vendor/github.com/lann/builder/builder.go b/vendor/github.com/lann/builder/builder.go
new file mode 100644
index 0000000..ff62140
--- /dev/null
+++ b/vendor/github.com/lann/builder/builder.go
@@ -0,0 +1,225 @@
+// Package builder provides a method for writing fluent immutable builders.
+package builder
+
+import (
+ "github.com/lann/ps"
+ "go/ast"
+ "reflect"
+)
+
+// Builder stores a set of named values.
+//
+// New types can be declared with underlying type Builder and used with the
+// functions in this package. See example.
+//
+// Instances of Builder should be treated as immutable. It is up to the
+// implementor to ensure mutable values set on a Builder are not mutated while
+// the Builder is in use.
+type Builder struct {
+ builderMap ps.Map
+}
+
+var (
+ EmptyBuilder = Builder{ps.NewMap()}
+ emptyBuilderValue = reflect.ValueOf(EmptyBuilder)
+)
+
+func getBuilderMap(builder interface{}) ps.Map {
+ b := convert(builder, Builder{}).(Builder)
+
+ if b.builderMap == nil {
+ return ps.NewMap()
+ }
+
+ return b.builderMap
+}
+
+// Set returns a copy of the given builder with a new value set for the given
+// name.
+//
+// Set (and all other functions taking a builder in this package) will panic if
+// the given builder's underlying type is not Builder.
+func Set(builder interface{}, name string, v interface{}) interface{} {
+ b := Builder{getBuilderMap(builder).Set(name, v)}
+ return convert(b, builder)
+}
+
+// Delete returns a copy of the given builder with the given named value unset.
+func Delete(builder interface{}, name string) interface{} {
+ b := Builder{getBuilderMap(builder).Delete(name)}
+ return convert(b, builder)
+}
+
+// Append returns a copy of the given builder with new value(s) appended to the
+// named list. If the value was previously unset or set with Set (even to a e.g.
+// slice values), the new value(s) will be appended to an empty list.
+func Append(builder interface{}, name string, vs ...interface{}) interface{} {
+ return Extend(builder, name, vs)
+}
+
+// Extend behaves like Append, except it takes a single slice or array value
+// which will be concatenated to the named list.
+//
+// Unlike a variadic call to Append - which requires a []interface{} value -
+// Extend accepts slices or arrays of any type.
+//
+// Extend will panic if the given value is not a slice, array, or nil.
+func Extend(builder interface{}, name string, vs interface{}) interface{} {
+ if vs == nil {
+ return builder
+ }
+
+ maybeList, ok := getBuilderMap(builder).Lookup(name)
+
+ var list ps.List
+ if ok {
+ list, ok = maybeList.(ps.List)
+ }
+ if !ok {
+ list = ps.NewList()
+ }
+
+ forEach(vs, func(v interface{}) {
+ list = list.Cons(v)
+ })
+
+ return Set(builder, name, list)
+}
+
+func listToSlice(list ps.List, arrayType reflect.Type) reflect.Value {
+ size := list.Size()
+ slice := reflect.MakeSlice(arrayType, size, size)
+ for i := size - 1; i >= 0; i-- {
+ val := reflect.ValueOf(list.Head())
+ slice.Index(i).Set(val)
+ list = list.Tail()
+ }
+ return slice
+}
+
+var anyArrayType = reflect.TypeOf([]interface{}{})
+
+// Get retrieves a single named value from the given builder.
+// If the value has not been set, it returns (nil, false). Otherwise, it will
+// return (value, true).
+//
+// If the named value was last set with Append or Extend, the returned value
+// will be a slice. If the given Builder has been registered with Register or
+// RegisterType and the given name is an exported field of the registered
+// struct, the returned slice will have the same type as that field. Otherwise
+// the slice will have type []interface{}. It will panic if the given name is a
+// registered struct's exported field and the value set on the Builder is not
+// assignable to the field.
+func Get(builder interface{}, name string) (interface{}, bool) {
+ val, ok := getBuilderMap(builder).Lookup(name)
+ if !ok {
+ return nil, false
+ }
+
+ list, isList := val.(ps.List)
+ if isList {
+ arrayType := anyArrayType
+
+ if ast.IsExported(name) {
+ structType := getBuilderStructType(reflect.TypeOf(builder))
+ if structType != nil {
+ field, ok := (*structType).FieldByName(name)
+ if ok {
+ arrayType = field.Type
+ }
+ }
+ }
+
+ val = listToSlice(list, arrayType).Interface()
+ }
+
+ return val, true
+}
+
+// GetMap returns a map[string]interface{} of the values set in the given
+// builder.
+//
+// See notes on Get regarding returned slices.
+func GetMap(builder interface{}) map[string]interface{} {
+ m := getBuilderMap(builder)
+ structType := getBuilderStructType(reflect.TypeOf(builder))
+
+ ret := make(map[string]interface{}, m.Size())
+
+ m.ForEach(func(name string, val ps.Any) {
+ list, isList := val.(ps.List)
+ if isList {
+ arrayType := anyArrayType
+
+ if structType != nil {
+ field, ok := (*structType).FieldByName(name)
+ if ok {
+ arrayType = field.Type
+ }
+ }
+
+ val = listToSlice(list, arrayType).Interface()
+ }
+
+ ret[name] = val
+ })
+
+ return ret
+}
+
+// GetStruct builds a new struct from the given registered builder.
+// It will return nil if the given builder's type has not been registered with
+// Register or RegisterValue.
+//
+// All values set on the builder with names that start with an uppercase letter
+// (i.e. which would be exported if they were identifiers) are assigned to the
+// corresponding exported fields of the struct.
+//
+// GetStruct will panic if any of these "exported" values are not assignable to
+// their corresponding struct fields.
+func GetStruct(builder interface{}) interface{} {
+ structVal := newBuilderStruct(reflect.TypeOf(builder))
+ if structVal == nil {
+ return nil
+ }
+ return scanStruct(builder, structVal)
+}
+
+// GetStructLike builds a new struct from the given builder with the same type
+// as the given struct.
+//
+// All values set on the builder with names that start with an uppercase letter
+// (i.e. which would be exported if they were identifiers) are assigned to the
+// corresponding exported fields of the struct.
+//
+// ScanStruct will panic if any of these "exported" values are not assignable to
+// their corresponding struct fields.
+func GetStructLike(builder interface{}, strct interface{}) interface{} {
+ structVal := reflect.New(reflect.TypeOf(strct)).Elem()
+ return scanStruct(builder, &structVal)
+}
+
+func scanStruct(builder interface{}, structVal *reflect.Value) interface{} {
+ getBuilderMap(builder).ForEach(func(name string, val ps.Any) {
+ if ast.IsExported(name) {
+ field := structVal.FieldByName(name)
+
+ var value reflect.Value
+ switch v := val.(type) {
+ case nil:
+ switch field.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ value = reflect.Zero(field.Type())
+ }
+ // nil is not valid for this Type; Set will panic
+ case ps.List:
+ value = listToSlice(v, field.Type())
+ default:
+ value = reflect.ValueOf(val)
+ }
+ field.Set(value)
+ }
+ })
+
+ return structVal.Interface()
+}