func (s State) SetObject(path string, o State) {
	s.Set(path, o)
}
SetObject sets an object property.
package plan
import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"
	"strconv"
	"strings"
	"github.com/chanwit/plandiff"
)
type State map[string]interface{}
var EmptyState = State(nil)
func NewState() State {
	return make(map[string]interface{})
}
func (s State) ToJSON() string {
	json, _ := json.Marshal(s)
	return string(json)
}
func NewStateFromJSON(r io.Reader) (State, error) {
	p := NewState()
	decoder := json.NewDecoder(r)
	err := decoder.Decode(&p)
	return p, err
}
func (s State) toPlan() Plan {
	p := newPlan()
	for id := range s {
		p.graph.AddNode(id)
	}
	for id, dval := range s {
		data := dval.(map[string]interface{})
		depsint := (data["meta"].(map[string]interface{}))["dependsOn"]
		var deps []string
		if depstrs, ok := depsint.([]string); ok {
			deps = depstrs
		} else {
			for _, str := range depsint.([]interface{}) {
				deps = append(deps, str.(string))
			}
		}
		for _, dep := range deps {
			p.graph.AddEdge(dep, id)
		}
		var rtype resourceType
		var vals map[string]interface{}
		for k, v := range data {
			if k != "meta" {
				rtype = resourceTypeByName(k)
				vals = v.(map[string]interface{})
				break
			}
		}
		if rtype.baseType == reflect.ValueOf(Plan{}).Type() {
			pres := State(vals).toPlan()
			p.resources[id] = &pres
			continue
		}
		resref := reflect.New(rtype.baseType).Elem()
		fnameMap := extractActualNamesFromTags(rtype.baseType)
		for fname, fval := range vals {
			if fval != nil {
				resref.FieldByName(lookupActualName(fnameMap, fname)).Set(reflect.ValueOf(fval))
			}
		}
		var res Resource
		if rtype.isPtr {
			res = resref.Addr().Interface().(Resource)
		} else {
			res = resref.Interface().(Resource)
		}
		p.resources[id] = res
	}
	return *p
}
func extractActualNamesFromTags(restype reflect.Type) map[string]string {
	result := make(map[string]string)
	for i := 0; i < restype.NumField(); i++ {
		f := restype.Field(i)
		structsTag := f.Tag.Get("structs")
		if structsTag != "" {
			result[strings.Split(structsTag, ",")[0]] = f.Name
		}
	}
	return result
}
func lookupActualName(fnameMap map[string]string, fname string) string {
	if actualName, ok := fnameMap[fname]; ok {
		return actualName
	}
	return fname
}
func (s State) IsEmpty() bool {
	return len(s) == 0
}
func (s State) Equal(other State) bool {
	return reflect.DeepEqual(s, other)
}
func (s State) Get(path string) (interface{}, error) {
	parts := strings.Split(path, ".")
	m := s
	for i, part := range parts {
		v, found := m[part]
		if !found {
			return nil, fmt.Errorf("invalid path (key not found): %s", strings.Join(parts[:i+1], "."))
		}
		if i == len(parts)-1 {
			if retMap, ok := v.(map[string]interface{}); ok {
				return State(retMap), nil
			}
			return v, nil
		}
		if newMap, ok := v.(map[string]interface{}); ok {
			m = State(newMap)
			continue
		}
		return nil, fmt.Errorf("invalid path (key isn't a map): %s", strings.Join(parts[:i+1], "."))
	}
	return nil, fmt.Errorf("invalid path (eek!): %s", path)
}
func (s State) GetBool(path string) (bool, error) {
	v, err := s.Get(path)
	if err != nil {
		return false, err
	}
	if b, ok := v.(bool); ok {
		return b, nil
	}
	if s, ok := v.(string); ok {
		if b, err := strconv.ParseBool(s); err == nil {
			return b, nil
		}
	}
	return false, fmt.Errorf("cannot convert %q to bool", v)
}
func (s State) Bool(path string) bool {
	b, _ := s.GetBool(path)
	return b
}
func (s State) GetNumber(path string) (float64, error) {
	v, err := s.Get(path)
	if err != nil {
		return 0, err
	}
	if f, ok := v.(float64); ok {
		return f, nil
	}
	if s, ok := v.(string); ok {
		if f, err := strconv.ParseFloat(s, 64); err == nil {
			return f, nil
		}
	}
	return 0, fmt.Errorf("cannot convert %q to float64", v)
}
func (s State) Number(path string) float64 {
	n, _ := s.GetNumber(path)
	return n
}
func (s State) GetString(path string) (string, error) {
	v, err := s.Get(path)
	if err != nil {
		return "", err
	}
	if s, ok := v.(string); ok {
		return s, nil
	}
	return "", fmt.Errorf("cannot convert %q to string", v)
}
func (s State) String(path string) string {
	str, _ := s.GetString(path)
	return str
}
func (s State) GetObject(path string) (State, error) {
	v, err := s.Get(path)
	if err != nil {
		return NewState(), err
	}
	if o, ok := v.(State); ok {
		return o, nil
	}
	return NewState(), fmt.Errorf("cannot convert %q to State", v)
}
func (s State) Object(path string) State {
	v, _ := s.GetObject(path)
	return v
}
func isMap(v interface{}) bool {
	_, ok := v.(map[string]interface{})
	return ok
}
func (s State) Set(path string, v interface{}) {
	parts := strings.Split(path, ".")
	m := s
	for i := 0; i < len(parts)-1; i++ {
		part := parts[i]
		value, ok := m[part]
		if !ok || !isMap(value) {
			p := make(map[string]interface{})
			m[part] = p
			m = p
			continue
		}
		m = m[part].(map[string]interface{})
	}
	if p, ok := v.(State); ok {
		v = map[string]interface{}(p)
	}
	key := parts[len(parts)-1]
	m[key] = v
}
func (s State) SetBool(path string, b bool) {
	s.Set(path, b)
}
func (s State) SetNumber(path string, f float64) {
	s.Set(path, f)
}
func (s State) SetString(path string, str string) {
	s.Set(path, str)
}