go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/pkg/incrutil/map_2n.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 14 "github.com/wcharczuk/go-incr" 15 "go.charczuk.com/sdk/errutil" 16 "go.charczuk.com/sdk/iter" 17 ) 18 19 // Map2N applies a function to a known input and a variable list of input incrementals and returns 20 // a new incremental of the output type of that function. 21 func Map2N[A, B, C any](scope incr.Scope, fn Map2NFunc[A, B, C]) Map2NIncr[A, B, C] { 22 return incr.WithinScope(scope, &map2NIncr[A, B, C]{ 23 n: incr.NewNode("map_2n"), 24 fn: fn, 25 }) 26 } 27 28 // Map2NFunc is the function that the MapN incremental applies. 29 type Map2NFunc[A, B, C any] func(context.Context, A, *Inputs[B]) (C, error) 30 31 // MapNIncr is a type of incremental that can add inputs over time. 32 type Map2NIncr[A, B, C any] interface { 33 incr.Incr[C] 34 IInputs 35 ISetInput 36 IRemoveInputByID 37 IRawValue 38 ISetRawValue 39 IRawFunction 40 ISetRawFunction 41 } 42 43 var ( 44 _ incr.Incr[string] = (*map2NIncr[int, any, string])(nil) 45 _ Map2NIncr[int, any, string] = (*map2NIncr[int, any, string])(nil) 46 _ incr.INode = (*map2NIncr[int, any, string])(nil) 47 _ incr.IStabilize = (*map2NIncr[int, any, string])(nil) 48 _ fmt.Stringer = (*map2NIncr[int, any, string])(nil) 49 ) 50 51 type map2NIncr[A, B, C any] struct { 52 n *incr.Node 53 input incr.Incr[A] 54 extra []incr.Incr[B] 55 fn Map2NFunc[A, B, C] 56 val C 57 } 58 59 func (mn *map2NIncr[A, B, C]) Parents() (out []incr.INode) { 60 if mn.input != nil { 61 out = append(out, mn.input) 62 } 63 for _, n := range mn.extra { 64 if n != nil { 65 out = append(out, n) 66 } 67 } 68 return 69 } 70 71 func (mn *map2NIncr[A, B, C]) RawValue() any { 72 return mn.val 73 } 74 75 func (mn *map2NIncr[A, B, C]) SetInput(name string, input incr.INode, _ bool) error { 76 switch name { 77 case "input": 78 typed, ok := input.(incr.Incr[A]) 79 if !ok { 80 return fmt.Errorf("set input; invalid input type for input %v: %T", name, input) 81 } 82 mn.input = typed 83 LinkNodes(mn, mn.input) 84 return nil 85 case "extra": 86 typed, ok := input.(incr.Incr[B]) 87 if !ok { 88 return fmt.Errorf("set input; invalid input type for input %v: %T", name, input) 89 } 90 mn.extra = append(mn.extra, typed) 91 LinkNodes(mn, typed) 92 return nil 93 default: 94 return fmt.Errorf("set input; invalid input name: %v", name) 95 } 96 } 97 98 func (mn *map2NIncr[A, B, C]) RemoveInputByID(id incr.Identifier) error { 99 if mn.input != nil && mn.input.Node().ID() == id { 100 UnlinkNodes(mn, mn.input) 101 mn.input = nil 102 } 103 104 var removed []incr.Incr[B] 105 mn.extra, removed = iter.FilterRemoved(mn.extra, func(i incr.Incr[B]) bool { return i.Node().ID() != id }) 106 for _, r := range removed { 107 UnlinkNodes(mn, r) 108 } 109 return nil 110 } 111 112 func (mn *map2NIncr[A, B, C]) Inputs() map[string]incr.INode { 113 output := make(map[string]incr.INode) 114 output["input"] = mn.input 115 for index, input := range mn.extra { 116 output[fmt.Sprintf("extra_%d", index)] = input 117 } 118 return output 119 } 120 121 func (mn *map2NIncr[A, B, C]) RawFunction() any { 122 return mn.fn 123 } 124 125 func (mn *map2NIncr[A, B, C]) SetRawFunction(fn any) error { 126 typed, ok := fn.(Map2NFunc[A, B, C]) 127 if !ok { 128 return fmt.Errorf("invalid function type for node: %T", fn) 129 } 130 mn.fn = typed 131 return nil 132 } 133 134 func (mn *map2NIncr[A, B, C]) Node() *incr.Node { return mn.n } 135 136 func (mn *map2NIncr[A, B, C]) Value() C { return mn.val } 137 138 func (m *map2NIncr[A, B, C]) SetRawValue(value any) error { 139 var typed C 140 if err := CastAny(value, &typed); err != nil { 141 return err 142 } 143 m.val = typed 144 return nil 145 } 146 147 func (mn *map2NIncr[A, B, C]) Stabilize(ctx context.Context) (err error) { 148 var val C 149 val, err = mn.fn(ctx, mn.input.Value(), NewInputs(mn.extra...)) 150 if err != nil { 151 err = errutil.New(fmt.Errorf("%v; %w", mn, err)) 152 return 153 } 154 mn.val = val 155 return nil 156 } 157 158 func (mn *map2NIncr[A, B, C]) String() string { 159 return mn.n.String() 160 }