v2/pkg/genruntime/property_bag.go (56 lines of code) (raw):

/* * Copyright (c) Microsoft Corporation. * Licensed under the MIT license. */ package genruntime import ( "bytes" "encoding/json" "github.com/rotisserie/eris" ) // PropertyBag is an unordered set of stashed information that used for properties not directly supported by storage // resources, allowing for full fidelity round trip conversions type PropertyBag map[string]string // Background: // We store items in the bag as serialized JSON, which we can then deserialize in a just-in-time fashion once we know // the type of the instance we're going to populate. Unlike other platforms, Go doesn't embed type information as it // serializes to JSON or YAML, which means that deserialization requires a type hint that's not available when our // containing resource is hydrated. We only have the required type available when we are doing the conversion to a // related type. // This comment kept separate from the definition above so that it doesn't get copied into the generated YAML files. // PropertyBag returns a new property bag // originals is a (potentially empty) sequence of existing property bags who's content will be copied into the new // property bag. In the case of key overlaps, values from bags later in the parameter list overwrite the earlier value. func NewPropertyBag(originals ...PropertyBag) PropertyBag { result := make(PropertyBag) for _, orig := range originals { for k, v := range orig { result[k] = v } } return result } // Contains returns true if the specified name is present in the bag; false otherwise func (bag PropertyBag) Contains(name string) bool { _, found := bag[name] return found } // Add is used to add a value into the bag; exact formatting depends on the type. // Any existing value will be overwritten. // property is the name of the item to put into the bag // value is the instance to be stashed away for later func (bag PropertyBag) Add(property string, value interface{}) error { switch v := value.(type) { case string: bag[property] = v default: // Default to treating as a JSON blob j, err := json.Marshal(v) if err != nil { return eris.Wrapf(err, "adding %s as JSON", property) } bag[property] = string(j) } return nil } // Pull removes a value from the bag, using it to populate the destination // property is the name of the item to remove and return // destination should be a pointer to where the value is to be placed // If the item is present and successfully deserialized, returns no error (nil); otherwise returns an error. // If an error happens deserializing an item from the bag, it is still removed from the bag. func (bag PropertyBag) Pull(property string, destination interface{}) error { value, found := bag[property] if !found { // Property not found in the bag return eris.Errorf("property bag does not contain %q", property) } // Property found, remove the value delete(bag, property) switch d := destination.(type) { case *string: *d = value default: data := []byte(value) decoder := json.NewDecoder(bytes.NewReader(data)) decoder.DisallowUnknownFields() err := decoder.Decode(destination) if err != nil { return eris.Wrapf(err, "pulling %q from PropertyBag", property) } } return nil } // Remove ensures the property bag doesn't contain a value for the specified name // property is the name of the item to remove // It is not an error to try and remove an item that's not present func (bag PropertyBag) Remove(property string) { delete(bag, property) }