github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/pkg/config/state/state_manager.go (about) 1 package state 2 3 import ( 4 "errors" 5 "reflect" 6 7 "github.com/ddev/ddev/pkg/config/state/types" 8 "github.com/mitchellh/mapstructure" 9 ) 10 11 // New returns a new State interface to get and set states based on the 12 // stateManager implementation bellow. The 'storage' parameter defines the 13 // storage to be used to read and write states from and to. 14 func New(storage types.StateStorage) types.State { 15 return &stateManager{ 16 storage: storage, 17 } 18 } 19 20 // stateManager is the default implementation of the State interface. It 21 // includes an in-memory cache of the state to reduce read/write operations 22 // on the storage. 23 type stateManager struct { 24 storage types.StateStorage 25 state types.RawState 26 stateChanged bool 27 stateLoaded bool 28 } 29 30 // Load see interface description. 31 func (m *stateManager) Load() (err error) { 32 m.state, err = m.storage.Read() 33 34 if err == nil { 35 m.stateChanged = false 36 m.stateLoaded = true 37 } 38 39 return 40 } 41 42 // Save see interface description. 43 func (m *stateManager) Save() (err error) { 44 err = m.storage.Write(m.state) 45 46 if err == nil { 47 m.stateChanged = false 48 m.stateLoaded = true 49 } 50 51 return 52 } 53 54 // Loaded see interface description. 55 func (m *stateManager) Loaded() bool { 56 return m.stateLoaded 57 } 58 59 // Changed see interface description. 60 func (m *stateManager) Changed() bool { 61 return m.stateChanged 62 } 63 64 // Get see interface description. 65 func (m *stateManager) Get(key types.StateEntryKey, stateEntry types.StateEntry) (err error) { 66 // Check stateEntry is a pointer. 67 val := reflect.ValueOf(stateEntry) 68 if val.Kind() != reflect.Ptr { 69 return errors.New("stateEntry must be a pointer") 70 } 71 72 // Clear the stateEntry to have a clean state. 73 val.Elem().SetZero() 74 75 // Use mapstructure to convert the raw data to the desired state entry. 76 decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 77 ZeroFields: true, 78 WeaklyTypedInput: true, 79 Result: stateEntry, 80 TagName: m.storage.TagName(), 81 }) 82 if err != nil { 83 return 84 } 85 86 if err = m.ensureLoaded(); err != nil { 87 return 88 } 89 90 return decoder.Decode(m.state[key]) 91 } 92 93 // Set see interface description. 94 func (m *stateManager) Set(key types.StateEntryKey, stateEntry types.StateEntry) (err error) { 95 // Check stateEntry is not a pointer. 96 val := reflect.ValueOf(stateEntry) 97 if val.Kind() == reflect.Ptr { 98 return errors.New("stateEntry must not be a pointer") 99 } 100 101 if err = m.ensureLoaded(); err != nil { 102 return 103 } 104 105 // Update or add state entry. 106 m.state[key] = stateEntry 107 m.stateChanged = true 108 109 return 110 } 111 112 // ensureLoaded loads the state if not already loaded before. 113 func (m *stateManager) ensureLoaded() (err error) { 114 if !m.stateLoaded { 115 err = m.Load() 116 } 117 118 return 119 }