go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/pkg/incrutil/map.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 ) 16 17 // MapN applies a function to given list of input incrementals and returns 18 // a new incremental of the output type of that function. 19 func Map[A, B any](scope incr.Scope, fn MapFunc[A, B]) MapIncr[A, B] { 20 return incr.WithinScope(scope, &mapIncr[A, B]{ 21 n: incr.NewNode("map"), 22 fn: fn, 23 }) 24 } 25 26 // MapNFunc is the function that the ApplyN incremental applies. 27 type MapFunc[A, B any] func(context.Context, A) (B, error) 28 29 // MapNIncr is a type of incremental that can add inputs over time. 30 type MapIncr[A, B any] interface { 31 incr.Incr[B] 32 ISetInput 33 IRemoveInput 34 IInputs 35 IRawValue 36 ISetRawValue 37 IRawFunction 38 ISetRawFunction 39 AddInput(incr.Incr[A]) 40 } 41 42 var ( 43 _ incr.Incr[string] = (*mapIncr[int, string])(nil) 44 _ MapIncr[int, string] = (*mapIncr[int, string])(nil) 45 _ incr.INode = (*mapIncr[int, string])(nil) 46 _ incr.IStabilize = (*mapIncr[int, string])(nil) 47 _ fmt.Stringer = (*mapIncr[int, string])(nil) 48 ) 49 50 type mapIncr[A, B any] struct { 51 n *incr.Node 52 input incr.Incr[A] 53 fn MapFunc[A, B] 54 val B 55 } 56 57 func (m *mapIncr[A, B]) Parents() []incr.INode { 58 if m.input != nil { 59 return []incr.INode{m.input} 60 } 61 return nil 62 } 63 64 func (m *mapIncr[A, B]) RawValue() any { 65 return m.val 66 } 67 68 func (m *mapIncr[A, B]) SetRawValue(value any) error { 69 var typed B 70 if err := CastAny(value, &typed); err != nil { 71 return err 72 } 73 m.val = typed 74 return nil 75 } 76 77 func (m *mapIncr[A, B]) AddInput(i incr.Incr[A]) { 78 m.input = i 79 LinkNodes(m, i) 80 } 81 82 func (m *mapIncr[A, B]) SetInput(name string, i incr.INode, _ bool) error { 83 typed, ok := i.(incr.Incr[A]) 84 if !ok || typed == nil { 85 return fmt.Errorf("invalid untyped input for map node %T", i) 86 } 87 m.input = typed 88 LinkNodes(m, i) 89 return nil 90 } 91 92 func (m *mapIncr[A, B]) RemoveInput(name string) error { 93 if m.input != nil { 94 UnlinkNodes(m, m.input) 95 m.input = nil 96 return nil 97 } 98 return fmt.Errorf("remove input; input is unset") 99 } 100 101 func (m *mapIncr[A, B]) Inputs() map[string]incr.INode { 102 if m.input != nil { 103 return map[string]incr.INode{ 104 "input": m.input, 105 } 106 } 107 return nil 108 } 109 110 func (m *mapIncr[A, B]) RawFunction() any { 111 return m.fn 112 } 113 114 func (m *mapIncr[A, B]) SetRawFunction(fn any) error { 115 typed, ok := fn.(MapFunc[A, B]) 116 if !ok { 117 return fmt.Errorf("invalid function type for node: %T", fn) 118 } 119 m.fn = typed 120 return nil 121 } 122 123 func (m *mapIncr[A, B]) Node() *incr.Node { return m.n } 124 125 func (m *mapIncr[A, B]) Value() B { return m.val } 126 127 func (m *mapIncr[A, B]) Stabilize(ctx context.Context) (err error) { 128 if m.input != nil { 129 var val B 130 val, err = m.fn(ctx, m.input.Value()) 131 if err != nil { 132 err = fmt.Errorf("%v; %w", m, err) 133 return 134 } 135 m.val = val 136 } 137 return nil 138 } 139 140 func (m *mapIncr[A, B]) String() string { 141 return m.n.String() 142 }