380 lines
12 KiB
Go
380 lines
12 KiB
Go
// Copyright 2016 Pulumi, Inc. All rights reserved.
|
|
|
|
package resource
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/pulumi/coconut/pkg/tokens"
|
|
)
|
|
|
|
// PropertyKey is the name of a property.
|
|
type PropertyKey tokens.Name
|
|
|
|
// PropertyMap is a simple map keyed by property name with "JSON-like" values.
|
|
type PropertyMap map[PropertyKey]PropertyValue
|
|
|
|
// PropertyValue is the value of a property, limited to a select few types (see below).
|
|
type PropertyValue struct {
|
|
V interface{}
|
|
}
|
|
|
|
type ReqError struct {
|
|
K PropertyKey
|
|
}
|
|
|
|
func IsReqError(err error) bool {
|
|
_, isreq := err.(*ReqError)
|
|
return isreq
|
|
}
|
|
|
|
func (err *ReqError) Error() string {
|
|
return fmt.Sprintf("required property '%v' is missing", err.K)
|
|
}
|
|
|
|
// BoolOrErr checks that the given property has the type bool, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) BoolOrErr(k PropertyKey, req bool) (*bool, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsBool() {
|
|
return nil, fmt.Errorf("property '%v' is not a bool (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
b := v.BoolValue()
|
|
return &b, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// NumberOrErr checks that the given property has the type float64, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) NumberOrErr(k PropertyKey, req bool) (*float64, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsNumber() {
|
|
return nil, fmt.Errorf("property '%v' is not a number (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
n := v.NumberValue()
|
|
return &n, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// StringOrErr checks that the given property has the type string, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) StringOrErr(k PropertyKey, req bool) (*string, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsString() {
|
|
return nil, fmt.Errorf("property '%v' is not a string (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
s := v.StringValue()
|
|
return &s, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ArrayOrErr checks that the given property has the type array, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) ArrayOrErr(k PropertyKey, req bool) (*[]PropertyValue, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsArray() {
|
|
return nil, fmt.Errorf("property '%v' is not an array (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
a := v.ArrayValue()
|
|
return &a, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ObjectArrayOrErr ensures a property is an array of objects, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) ObjectArrayOrErr(k PropertyKey, req bool) (*[]PropertyMap, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsArray() {
|
|
return nil, fmt.Errorf("property '%v' is not an array (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
a := v.ArrayValue()
|
|
var objs []PropertyMap
|
|
for i, e := range a {
|
|
if e.IsObject() {
|
|
objs = append(objs, e.ObjectValue())
|
|
} else {
|
|
return nil, fmt.Errorf(
|
|
"property '%v' array element %v is not an object (%v)", k, i, reflect.TypeOf(e))
|
|
}
|
|
}
|
|
return &objs, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// StringArrayOrErr ensures a property is an array of strings, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) StringArrayOrErr(k PropertyKey, req bool) (*[]string, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsArray() {
|
|
return nil, fmt.Errorf("property '%v' is not an array (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
a := v.ArrayValue()
|
|
var strs []string
|
|
for i, e := range a {
|
|
if e.IsString() {
|
|
strs = append(strs, e.StringValue())
|
|
} else {
|
|
return nil, fmt.Errorf(
|
|
"property '%v' array element %v is not a string (%v)", k, i, reflect.TypeOf(e))
|
|
}
|
|
}
|
|
return &strs, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ObjectOrErr checks that the given property is an object, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) ObjectOrErr(k PropertyKey, req bool) (*PropertyMap, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsObject() {
|
|
return nil, fmt.Errorf("property '%v' is not an object (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
o := v.ObjectValue()
|
|
return &o, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ResourceOrErr checks that the given property is a resource, issuing an error if not; req indicates if required.
|
|
func (m PropertyMap) ResourceOrErr(k PropertyKey, req bool) (*Moniker, error) {
|
|
if v, has := m[k]; has && !v.IsNull() {
|
|
if !v.IsResource() {
|
|
return nil, fmt.Errorf("property '%v' is not an object (%v)", k, reflect.TypeOf(v.V))
|
|
}
|
|
m := v.ResourceValue()
|
|
return &m, nil
|
|
} else if req {
|
|
return nil, &ReqError{k}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ReqBoolOrErr checks that the given property exists and has the type bool.
|
|
func (m PropertyMap) ReqBoolOrErr(k PropertyKey) (bool, error) {
|
|
b, err := m.BoolOrErr(k, true)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return *b, nil
|
|
}
|
|
|
|
// ReqNumberOrErr checks that the given property exists and has the type float64.
|
|
func (m PropertyMap) ReqNumberOrErr(k PropertyKey) (float64, error) {
|
|
n, err := m.NumberOrErr(k, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return *n, nil
|
|
}
|
|
|
|
// ReqStringOrErr checks that the given property exists and has the type string.
|
|
func (m PropertyMap) ReqStringOrErr(k PropertyKey) (string, error) {
|
|
s, err := m.StringOrErr(k, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return *s, nil
|
|
}
|
|
|
|
// ReqArrayOrErr checks that the given property exists and has the type array.
|
|
func (m PropertyMap) ReqArrayOrErr(k PropertyKey) ([]PropertyValue, error) {
|
|
a, err := m.ArrayOrErr(k, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return *a, nil
|
|
}
|
|
|
|
// ReqObjectArrayOrErr checks that the given property exists and has the type array of objects.
|
|
func (m PropertyMap) ReqObjectArrayOrErr(k PropertyKey) ([]PropertyMap, error) {
|
|
a, err := m.ObjectArrayOrErr(k, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return *a, nil
|
|
}
|
|
|
|
// ReqStringArrayOrErr checks that the given property exists and has the type array of objects.
|
|
func (m PropertyMap) ReqStringArrayOrErr(k PropertyKey) ([]string, error) {
|
|
a, err := m.StringArrayOrErr(k, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return *a, nil
|
|
}
|
|
|
|
// ReqObjectOrErr checks that the given property exists and has the type object.
|
|
func (m PropertyMap) ReqObjectOrErr(k PropertyKey) (PropertyMap, error) {
|
|
o, err := m.ObjectOrErr(k, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return *o, nil
|
|
}
|
|
|
|
// ReqResourceOrErr checks that the given property exists and has the type moniker.
|
|
func (m PropertyMap) ReqResourceOrErr(k PropertyKey) (Moniker, error) {
|
|
r, err := m.ResourceOrErr(k, true)
|
|
if err != nil {
|
|
return Moniker(""), err
|
|
}
|
|
return *r, nil
|
|
}
|
|
|
|
// OptBoolOrErr checks that the given property has the type bool, if it exists.
|
|
func (m PropertyMap) OptBoolOrErr(k PropertyKey) (*bool, error) {
|
|
return m.BoolOrErr(k, false)
|
|
}
|
|
|
|
// OptNumberOrErr checks that the given property has the type float64, if it exists.
|
|
func (m PropertyMap) OptNumberOrErr(k PropertyKey) (*float64, error) {
|
|
return m.NumberOrErr(k, false)
|
|
}
|
|
|
|
// OptStringOrErr checks that the given property has the type string, if it exists.
|
|
func (m PropertyMap) OptStringOrErr(k PropertyKey) (*string, error) {
|
|
return m.StringOrErr(k, false)
|
|
}
|
|
|
|
// OptArrayOrErr checks that the given property has the type array, if it exists.
|
|
func (m PropertyMap) OptArrayOrErr(k PropertyKey) (*[]PropertyValue, error) {
|
|
return m.ArrayOrErr(k, false)
|
|
}
|
|
|
|
// OptObjectArrayOrErr checks that the given property has the type array of objects, if it exists.
|
|
func (m PropertyMap) OptObjectArrayOrErr(k PropertyKey) (*[]PropertyMap, error) {
|
|
return m.ObjectArrayOrErr(k, false)
|
|
}
|
|
|
|
// OptStringArrayOrErr checks that the given property has the type array of objects, if it exists.
|
|
func (m PropertyMap) OptStringArrayOrErr(k PropertyKey) (*[]string, error) {
|
|
return m.StringArrayOrErr(k, false)
|
|
}
|
|
|
|
// OptObjectOrErr checks that the given property has the type object, if it exists.
|
|
func (m PropertyMap) OptObjectOrErr(k PropertyKey) (*PropertyMap, error) {
|
|
return m.ObjectOrErr(k, false)
|
|
}
|
|
|
|
// OptResourceOrErr checks that the given property has the type moniker, if it exists.
|
|
func (m PropertyMap) OptResourceOrErr(k PropertyKey) (*Moniker, error) {
|
|
return m.ResourceOrErr(k, false)
|
|
}
|
|
|
|
// AllResources finds all resource monikers, transitively throughout the property map, and returns them.
|
|
func (props PropertyMap) AllResources() map[Moniker]bool {
|
|
monikers := make(map[Moniker]bool)
|
|
for _, k := range StablePropertyKeys(props) {
|
|
for m, v := range props[k].AllResources() {
|
|
monikers[m] = v
|
|
}
|
|
}
|
|
return monikers
|
|
}
|
|
|
|
// ReplaceResources finds all resources and lets an updater function update them if necessary. This is often used
|
|
// during a "replacement"-style updated, to replace all monikers of a certain value with another.
|
|
func (props PropertyMap) ReplaceResources(updater func(Moniker) Moniker) PropertyMap {
|
|
result := make(PropertyMap)
|
|
for _, k := range StablePropertyKeys(props) {
|
|
result[k] = props[k].ReplaceResources(updater)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func NewPropertyNull() PropertyValue { return PropertyValue{nil} }
|
|
func NewPropertyBool(v bool) PropertyValue { return PropertyValue{v} }
|
|
func NewPropertyNumber(v float64) PropertyValue { return PropertyValue{v} }
|
|
func NewPropertyString(v string) PropertyValue { return PropertyValue{v} }
|
|
func NewPropertyArray(v []PropertyValue) PropertyValue { return PropertyValue{v} }
|
|
func NewPropertyObject(v PropertyMap) PropertyValue { return PropertyValue{v} }
|
|
func NewPropertyResource(v Moniker) PropertyValue { return PropertyValue{v} }
|
|
|
|
func (v PropertyValue) BoolValue() bool { return v.V.(bool) }
|
|
func (v PropertyValue) NumberValue() float64 { return v.V.(float64) }
|
|
func (v PropertyValue) StringValue() string { return v.V.(string) }
|
|
func (v PropertyValue) ArrayValue() []PropertyValue { return v.V.([]PropertyValue) }
|
|
func (v PropertyValue) ObjectValue() PropertyMap { return v.V.(PropertyMap) }
|
|
func (v PropertyValue) ResourceValue() Moniker { return v.V.(Moniker) }
|
|
|
|
func (b PropertyValue) IsNull() bool {
|
|
return b.V == nil
|
|
}
|
|
func (b PropertyValue) IsBool() bool {
|
|
_, is := b.V.(bool)
|
|
return is
|
|
}
|
|
func (b PropertyValue) IsNumber() bool {
|
|
_, is := b.V.(float64)
|
|
return is
|
|
}
|
|
func (b PropertyValue) IsString() bool {
|
|
_, is := b.V.(string)
|
|
return is
|
|
}
|
|
func (b PropertyValue) IsArray() bool {
|
|
_, is := b.V.([]PropertyValue)
|
|
return is
|
|
}
|
|
func (b PropertyValue) IsObject() bool {
|
|
_, is := b.V.(PropertyMap)
|
|
return is
|
|
}
|
|
func (b PropertyValue) IsResource() bool {
|
|
_, is := b.V.(Moniker)
|
|
return is
|
|
}
|
|
|
|
// AllResources finds all resource monikers, transitively throughout the property value, and returns them.
|
|
func (v PropertyValue) AllResources() map[Moniker]bool {
|
|
monikers := make(map[Moniker]bool)
|
|
if v.IsResource() {
|
|
monikers[v.ResourceValue()] = true
|
|
} else if v.IsArray() {
|
|
for _, elem := range v.ArrayValue() {
|
|
for m, v := range elem.AllResources() {
|
|
monikers[m] = v
|
|
}
|
|
}
|
|
} else if v.IsObject() {
|
|
for m, v := range v.ObjectValue().AllResources() {
|
|
monikers[m] = v
|
|
}
|
|
}
|
|
return monikers
|
|
}
|
|
|
|
// ReplaceResources finds all resources and lets an updater function update them if necessary. This is often used
|
|
// during a "replacement"-style updated, to replace all monikers of a certain value with another.
|
|
func (v PropertyValue) ReplaceResources(updater func(Moniker) Moniker) PropertyValue {
|
|
if v.IsResource() {
|
|
m := v.ResourceValue()
|
|
return NewPropertyResource(updater(m))
|
|
} else if v.IsArray() {
|
|
arr := v.ArrayValue()
|
|
elems := make([]PropertyValue, len(arr))
|
|
for i, elem := range arr {
|
|
elems[i] = elem.ReplaceResources(updater)
|
|
}
|
|
return NewPropertyArray(elems)
|
|
} else if v.IsObject() {
|
|
rep := v.ObjectValue().ReplaceResources(updater)
|
|
return NewPropertyObject(rep)
|
|
}
|
|
return v
|
|
}
|