go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/pkg/incrutil/observe.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 ) 17 18 // Observe returns a observe increment. 19 func Observe[A any](graph *incr.Graph) ObserveIncr[A] { 20 return incr.WithinScope(graph, &observeIncr[A]{ 21 n: incr.NewNode("observer"), 22 g: graph, 23 }) 24 } 25 26 // ObserveIncr is a type that implements the observer. 27 type ObserveIncr[A any] interface { 28 incr.Incr[A] 29 fmt.Stringer 30 ISetInput 31 IRemoveInput 32 IInputs 33 IRawValue 34 ISetRawValue 35 incr.IObserver 36 } 37 38 var ( 39 _ incr.Incr[string] = (*observeIncr[string])(nil) 40 _ incr.IStabilize = (*observeIncr[string])(nil) 41 _ ObserveIncr[string] = (*observeIncr[string])(nil) 42 _ incr.INode = (*observeIncr[string])(nil) 43 _ fmt.Stringer = (*observeIncr[string])(nil) 44 ) 45 46 type observeIncr[A any] struct { 47 n *incr.Node 48 g *incr.Graph 49 input incr.Incr[A] 50 value A 51 } 52 53 // Value implements Incr[A]. 54 func (o *observeIncr[A]) Value() (output A) { 55 return o.value 56 } 57 58 func (o *observeIncr[A]) RawValue() (output any) { 59 output = o.value 60 return 61 } 62 63 func (o *observeIncr[A]) SetRawValue(value any) error { 64 var typed A 65 if err := CastAny(value, &typed); err != nil { 66 return err 67 } 68 o.value = typed 69 return nil 70 } 71 72 // Node implements Incr[A]. 73 func (o *observeIncr[A]) Node() *incr.Node { 74 return o.n 75 } 76 77 // String implements fmt.Stringer. 78 func (o *observeIncr[A]) String() string { return o.n.String() } 79 80 // SetInput sets the input for the observer. 81 // 82 // IMPORTANT: it also kicks off "discovery" of all the ancestors 83 // of that node to the observer's graph. 84 // 85 // The observers graph is set when we create the observer. 86 func (o *observeIncr[A]) SetInput(_ string, i incr.INode, isDeserialize bool) error { 87 typed, ok := i.(incr.Incr[A]) 88 if !ok || typed == nil { 89 return fmt.Errorf("set input; invalid untyped input for observer node %T\n%v", i, errutil.GetStackTrace()) 90 } 91 o.input = typed 92 incr.ExpertGraph(o.g).ObserveNode(o, i) 93 return nil 94 } 95 96 func (o *observeIncr[A]) RemoveInput(name string) error { 97 if o.input != nil { 98 UnlinkNodes(o, o.input) 99 o.Unobserve(context.Background()) 100 return nil 101 } 102 return fmt.Errorf("remove input; input unset") 103 } 104 105 func (o *observeIncr[A]) Inputs() map[string]incr.INode { 106 if o.input != nil { 107 return map[string]incr.INode{ 108 "input": o.input, 109 } 110 } 111 return nil 112 } 113 114 func (o *observeIncr[A]) Stabilize(_ context.Context) error { 115 o.value = o.input.Value() 116 return nil 117 } 118 119 func (o *observeIncr[A]) Unobserve(ctx context.Context) { 120 if o.input != nil { 121 g := incr.ExpertGraph(o.g) 122 g.UnobserveNode(o, o.input) 123 o.input = nil 124 } 125 }