github.com/viant/toolbox@v0.34.5/context.go (about)

     1  package toolbox
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  )
     7  
     8  //Context represents type safe map.
     9  type Context interface {
    10  
    11  	//GetRequired returns a value for a target type of error if it does not exist
    12  	GetRequired(targetType interface{}) (interface{}, error)
    13  
    14  	//GetOptional  returns a value for a target type
    15  	GetOptional(targetType interface{}) interface{}
    16  
    17  	//GetOptional into sets requested context value into target, returns true if value was found
    18  	GetInto(targetType interface{}, target interface{}) bool
    19  
    20  	//Put puts target type value to the context, or error if value exists,  is nil or incompatible with target type
    21  	Put(targetType interface{}, value interface{}) error
    22  
    23  	//Replace repaces value in the context
    24  	Replace(targetType interface{}, value interface{}) error
    25  
    26  	//Remove removes value from the context
    27  	Remove(targetType interface{}) interface{}
    28  
    29  	//Contains chekcs if a value of a terget type is in contet
    30  	Contains(targetType interface{}) bool
    31  
    32  	//Clone create a shallow copy of a context
    33  	Clone() Context
    34  }
    35  
    36  type contextImpl struct {
    37  	context map[string]interface{}
    38  }
    39  
    40  func (c *contextImpl) getReflectType(targetType interface{}) reflect.Type {
    41  	var reflectType reflect.Type
    42  	var ok bool
    43  	reflectType, ok = targetType.(reflect.Type)
    44  	if !ok {
    45  		reflectType = reflect.TypeOf(targetType)
    46  	}
    47  	return reflectType
    48  }
    49  
    50  func (c *contextImpl) getKey(targetType interface{}) string {
    51  	var reflectType = c.getReflectType(targetType)
    52  	return reflectType.String()
    53  }
    54  
    55  func (c *contextImpl) GetRequired(targetType interface{}) (interface{}, error) {
    56  	if !c.Contains(targetType) {
    57  		key := c.getKey(targetType)
    58  		return nil, fmt.Errorf("failed to lookup key:" + key)
    59  	}
    60  	return c.GetOptional(targetType), nil
    61  }
    62  
    63  func (c *contextImpl) GetOptional(targetType interface{}) interface{} {
    64  	key := c.getKey(targetType)
    65  	if result, ok := c.context[key]; ok {
    66  		return result
    67  	}
    68  	return nil
    69  }
    70  
    71  func (c *contextImpl) GetInto(targetType, target interface{}) bool {
    72  	key := c.getKey(targetType)
    73  	if result, ok := c.context[key]; ok {
    74  		reflect.ValueOf(target).Elem().Set(reflect.ValueOf(result))
    75  		return true
    76  	}
    77  	return false
    78  }
    79  
    80  func (c *contextImpl) Put(targetType interface{}, value interface{}) error {
    81  	if c.Contains(targetType) {
    82  		key := c.getKey(targetType)
    83  		return fmt.Errorf("failed to put key - already exist: " + key)
    84  	}
    85  	return c.Replace(targetType, value)
    86  }
    87  
    88  func (c *contextImpl) Replace(targetType interface{}, value interface{}) error {
    89  	key := c.getKey(targetType)
    90  	targetReflectType := c.getReflectType(targetType)
    91  	valueReflectType := reflect.TypeOf(value)
    92  	if valueReflectType == targetReflectType {
    93  		c.context[key] = value
    94  		return nil
    95  	}
    96  
    97  	if targetReflectType.Kind() == reflect.Ptr {
    98  		converted := reflect.ValueOf(value).Elem().Convert(targetReflectType.Elem())
    99  		convertedPointer := reflect.New(targetReflectType.Elem())
   100  		convertedPointer.Elem().Set(converted)
   101  		value = convertedPointer.Interface()
   102  
   103  	} else {
   104  		if !valueReflectType.AssignableTo(targetReflectType) {
   105  			return fmt.Errorf("value of type %v is not assignable to %v", valueReflectType, targetReflectType)
   106  		}
   107  		value = reflect.ValueOf(value).Convert(targetReflectType).Interface()
   108  	}
   109  	c.context[key] = value
   110  	return nil
   111  }
   112  
   113  func (c *contextImpl) Remove(targetType interface{}) interface{} {
   114  	key := c.getKey(targetType)
   115  	result := c.GetOptional(targetType)
   116  	delete(c.context, key)
   117  	return result
   118  }
   119  
   120  func (c *contextImpl) Contains(targetType interface{}) bool {
   121  	key := c.getKey(targetType)
   122  	if _, ok := c.context[key]; ok {
   123  		return true
   124  	}
   125  	return false
   126  }
   127  
   128  func (c *contextImpl) Clone() Context {
   129  	var result = &contextImpl{context: make(map[string]interface{})}
   130  	for k, v := range c.context {
   131  		result.context[k] = v
   132  	}
   133  	return result
   134  }
   135  
   136  //NewContext creates a new context
   137  func NewContext() Context {
   138  	var result Context = &contextImpl{context: make(map[string]interface{})}
   139  	return result
   140  }