github.com/ngicks/gokugen@v0.0.5/impl/work_registry/param_unmarshaller.go (about)

     1  package workregistry
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"github.com/mitchellh/mapstructure"
     9  	"github.com/ngicks/gokugen"
    10  	"github.com/ngicks/gokugen/cron"
    11  	syncparam "github.com/ngicks/type-param-common/sync-param"
    12  )
    13  
    14  type Unmarshaller = func(naive any) (unmarshalled any, err error)
    15  
    16  // UnmarshalAny is reference Unmarshaller implementation.
    17  // If naive is T it simply returns naive.
    18  // If naive is []byte then unmarshal it as a json string.
    19  // Else, UnmarshalAny treats naive as naively unmarshalled json value
    20  // (e.g. map[any]any, []any or other literal values).
    21  // It copies values from naive to unmarshaled through reflection.
    22  func UnmarshalAny[T any](naive any) (unmarshalled any, err error) {
    23  	if _, ok := naive.(T); ok {
    24  		return naive, nil
    25  	}
    26  
    27  	var val T
    28  
    29  	if bin, ok := naive.([]byte); ok {
    30  		err = json.Unmarshal(bin, &val)
    31  		if err != nil {
    32  			return
    33  		}
    34  		unmarshalled = val
    35  		return
    36  	}
    37  
    38  	err = mapstructure.Decode(naive, &val)
    39  	if err != nil {
    40  		return
    41  	}
    42  	return val, nil
    43  }
    44  
    45  type UnmarshallerRegistry interface {
    46  	Load(key string) (value Unmarshaller, ok bool)
    47  }
    48  
    49  type AnyUnmarshaller struct {
    50  	syncparam.Map[string, Unmarshaller]
    51  }
    52  
    53  // AddType stores UnmarshalAny instantiated with T into the registry.
    54  // AddType is an external function, not a method for *AnyUnmarshaller,
    55  // since Go curently does not allow us to add type parameters to methods individually.
    56  func AddType[T any](key string, registry *AnyUnmarshaller) {
    57  	registry.Store(key, UnmarshalAny[T])
    58  }
    59  
    60  type ParamUnmarshaller struct {
    61  	inner              cron.WorkRegistry
    62  	marshallerRegistry UnmarshallerRegistry
    63  }
    64  
    65  func NewParamUnmarshaller(
    66  	inner cron.WorkRegistry,
    67  	marshallerRegistry UnmarshallerRegistry,
    68  ) *ParamUnmarshaller {
    69  	return &ParamUnmarshaller{
    70  		inner:              inner,
    71  		marshallerRegistry: marshallerRegistry,
    72  	}
    73  }
    74  
    75  func (p *ParamUnmarshaller) Load(key string) (value gokugen.WorkFnWParam, ok bool) {
    76  	work, ok := p.inner.Load(key)
    77  	if !ok {
    78  		return
    79  	}
    80  
    81  	unmarshaller, ok := p.marshallerRegistry.Load(key)
    82  	if !ok {
    83  		return
    84  	}
    85  
    86  	value = func(taskCtx context.Context, scheduled time.Time, param any) (any, error) {
    87  		unmarshaled, err := unmarshaller(param)
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  		return work(taskCtx, scheduled, unmarshaled)
    92  	}
    93  
    94  	return
    95  }