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  }