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