package common import ( "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" "sync" ) type ( LazyFactory func() (core.Value, error) LazyValue struct { sync.Mutex factory LazyFactory ready bool value core.Value err error } ) func NewLazyValue(factory LazyFactory) *LazyValue { lz := new(LazyValue) lz.ready = false lz.factory = factory lz.value = values.None return lz } /* * Indicates whether the value is ready. * @returns (Boolean) - Boolean value indicating whether the value is ready. */ func (lv *LazyValue) Ready() bool { lv.Lock() defer lv.Unlock() return lv.ready } /* * Returns an underlying value. * Not thread safe. Should not mutated. * @returns (Value) - Underlying value if successfully loaded, otherwise error */ func (lv *LazyValue) Read() (core.Value, error) { lv.Lock() defer lv.Unlock() if !lv.ready { lv.load() } return lv.value, lv.err } /* * Safely mutates an underlying value. * Loads a value if it's not ready. * Thread safe. */ func (lv *LazyValue) Write(writer func(v core.Value, err error)) { lv.Lock() defer lv.Unlock() if !lv.ready { lv.load() } writer(lv.value, lv.err) } /* * Resets the storage. * Next call of Read will trigger the factory function again. */ func (lv *LazyValue) Reset() { lv.Lock() defer lv.Unlock() lv.ready = false lv.value = values.None lv.err = nil } func (lv *LazyValue) load() { val, err := lv.factory() if err == nil { lv.value = val lv.err = nil } else { lv.value = values.None lv.err = err } lv.ready = true }