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