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  }