go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/pkg/incrutil/map_n.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package incrutil 9 10 import ( 11 "context" 12 "fmt" 13 "strconv" 14 15 "github.com/wcharczuk/go-incr" 16 "go.charczuk.com/sdk/errutil" 17 "go.charczuk.com/sdk/iter" 18 ) 19 20 // MapN applies a function to given list of input incrementals and returns 21 // a new incremental of the output type of that function. 22 func MapN[A, B any](scope incr.Scope, fn MapNFunc[A, B], inputs ...incr.Incr[A]) MapNIncr[A, B] { 23 return incr.WithinScope(scope, &mapNIncr[A, B]{ 24 n: incr.NewNode("map_n"), 25 inputs: inputs, 26 fn: fn, 27 }) 28 } 29 30 // MapNFunc is the function that the MapN incremental applies. 31 type MapNFunc[A, B any] func(context.Context, *Inputs[A]) (B, error) 32 33 // MapNIncr is a type of incremental that can add inputs over time. 34 type MapNIncr[A, B any] interface { 35 incr.Incr[B] 36 IInputs 37 IAddInput 38 IRemoveInputByID 39 IRawValue 40 ISetRawValue 41 IRawFunction 42 ISetRawFunction 43 } 44 45 var ( 46 _ incr.Incr[string] = (*mapNIncr[int, string])(nil) 47 _ MapNIncr[int, string] = (*mapNIncr[int, string])(nil) 48 _ incr.INode = (*mapNIncr[int, string])(nil) 49 _ incr.IStabilize = (*mapNIncr[int, string])(nil) 50 _ fmt.Stringer = (*mapNIncr[int, string])(nil) 51 ) 52 53 type mapNIncr[A, B any] struct { 54 n *incr.Node 55 inputs []incr.Incr[A] 56 fn MapNFunc[A, B] 57 val B 58 } 59 60 func (mn *mapNIncr[A, B]) Parents() (out []incr.INode) { 61 for _, n := range mn.inputs { 62 if n != nil { 63 out = append(out, n) 64 } 65 } 66 return 67 } 68 69 func (mn *mapNIncr[A, B]) RawValue() any { 70 return mn.val 71 } 72 73 func (mn *mapNIncr[A, B]) AddInput(i incr.INode) error { 74 typed, ok := i.(incr.Incr[A]) 75 if !ok || typed == nil { 76 return fmt.Errorf("invalid input for node %T", i) 77 } 78 mn.inputs = append(mn.inputs, typed) 79 LinkNodes(mn, i) 80 return nil 81 } 82 83 func (mn *mapNIncr[A, B]) RemoveInputByID(id incr.Identifier) error { 84 var removed []incr.Incr[A] 85 mn.inputs, removed = iter.FilterRemoved(mn.inputs, func(i incr.Incr[A]) bool { return i.Node().ID() != id }) 86 for _, r := range removed { 87 UnlinkNodes(mn, r) 88 } 89 return nil 90 } 91 92 func (mn *mapNIncr[A, B]) Inputs() map[string]incr.INode { 93 output := make(map[string]incr.INode) 94 for index, input := range mn.inputs { 95 output[strconv.Itoa(index)] = input 96 } 97 return output 98 } 99 100 func (mn *mapNIncr[A, B]) RawFunction() any { 101 return mn.fn 102 } 103 104 func (mn *mapNIncr[A, B]) SetRawFunction(fn any) error { 105 typed, ok := fn.(MapNFunc[A, B]) 106 if !ok { 107 return fmt.Errorf("invalid function type for node: %T", fn) 108 } 109 mn.fn = typed 110 return nil 111 } 112 113 func (mn *mapNIncr[A, B]) Node() *incr.Node { return mn.n } 114 115 func (mn *mapNIncr[A, B]) Value() B { return mn.val } 116 117 func (m *mapNIncr[A, B]) SetRawValue(value any) error { 118 var typed B 119 if err := CastAny(value, &typed); err != nil { 120 return err 121 } 122 m.val = typed 123 return nil 124 } 125 126 func (mn *mapNIncr[A, B]) Stabilize(ctx context.Context) (err error) { 127 if mn.fn == nil { 128 err = fmt.Errorf("cannot stabilize; function is unset") 129 return 130 } 131 var val B 132 val, err = mn.fn(ctx, NewInputs(mn.inputs...)) 133 if err != nil { 134 err = errutil.New(fmt.Errorf("%v; %w", mn, err)) 135 return 136 } 137 mn.val = val 138 return nil 139 } 140 141 func (mn *mapNIncr[A, B]) String() string { 142 return mn.n.String() 143 }