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  }