github.com/searKing/golang/go@v1.2.117/go/generator/generator.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package generator
     6  
     7  import (
     8  	"context"
     9  )
    10  
    11  type Yield func(msg any) (ok bool)
    12  
    13  // Generator behaves like Generator in python or ES6
    14  // Generator function contains one or more yield statement.
    15  // Generator functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop.
    16  // see https://wiki.python.org/moin/Generators
    17  // see https://www.programiz.com/python-programming/generator
    18  // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
    19  type Generator struct {
    20  	// Used by Next, to notify or deliver what is generated, as next in python or ES6
    21  	C <-chan any
    22  
    23  	r runtimeGenerator
    24  }
    25  
    26  // Stop prevents the Generator from firing, with the channel drained.
    27  // Stop ensures the channel is empty after a call to Stop.
    28  // It returns true if the call stops the generator, false if the generator has already
    29  // expired or been stopped.
    30  // Stop does not close the channel, to prevent a read from the channel succeeding
    31  // incorrectly.
    32  //
    33  // This cannot be done concurrent to other receives from the Generator's
    34  // channel.
    35  //
    36  // For a generator created with GeneratorFuncWithSupplier(supplierC, f), if t.Stop returns false, then the generator
    37  // has already expired and the function f has been started in its own goroutine;
    38  // Stop does not wait for f to complete before returning.
    39  // If the caller needs to know whether f is completed, it must coordinate
    40  // with f explicitly.
    41  func (g *Generator) Stop() bool {
    42  	if g.r.f == nil || g.r.ctx == nil || g.r.cancel == nil {
    43  		panic("generator: Stop called on uninitialized Generator")
    44  	}
    45  	return g.r.stop()
    46  }
    47  
    48  func (g *Generator) StoppedC() context.Context {
    49  	if g.r.f == nil || g.r.ctx == nil || g.r.cancel == nil {
    50  		panic("generator: StoppedC called on uninitialized Generator")
    51  	}
    52  	return g.r.ctx
    53  }
    54  
    55  func (g *Generator) Stopped() bool {
    56  	if g.r.f == nil || g.r.ctx == nil || g.r.cancel == nil {
    57  		panic("generator: Stopped called on uninitialized Generator")
    58  	}
    59  	return g.r.stopped()
    60  }
    61  
    62  // Next behaves like an iterator, i.e. it can be used in a for loop.
    63  // It's a grammar sugar for chan
    64  func (g *Generator) Next() (msg any, ok bool) {
    65  	msg, ok = <-g.C
    66  	return
    67  }
    68  
    69  // Yield is a grammar sugar for data src of generator
    70  // ok returns true if msg sent; false if consume canceled
    71  // If a function contains at least one yield statement (it may contain other yield or return statements),
    72  // it becomes a generator function. Both yield and return will return some value from a function.
    73  // The difference is that, while a return statement terminates a function entirely,
    74  // yield statement pauses the function saving all its states and later continues from there on successive calls.
    75  func (g *Generator) Yield(supplierC chan<- any) Yield {
    76  	return func(msg any) (ok bool) {
    77  		select {
    78  		case <-g.StoppedC().Done():
    79  			return false
    80  		case supplierC <- msg:
    81  			return true
    82  		}
    83  	}
    84  }
    85  
    86  // Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).
    87  
    88  // GeneratorFunc returns an object (iterator) which we can iterate over (one value at a time).
    89  // It returns a Generator that can be used to cancel the call using its Stop method.
    90  // Iterate will be stopped when f is return or Stop is called.
    91  func GeneratorFunc(f func(yield Yield)) *Generator {
    92  	supplierC := make(chan any)
    93  	g := GeneratorWithSupplier(supplierC)
    94  	supplierF := func(args ...any) {
    95  		yield := g.Yield(supplierC)
    96  		f(yield)
    97  		close(supplierC)
    98  	}
    99  	go supplierF()
   100  	return g
   101  }
   102  
   103  // GeneratorWithSupplier is like GeneratorFunc.
   104  // But it's data src is from supplierC.
   105  // Iterate will be stopped when supplierC is closed or Stop is called.
   106  func GeneratorWithSupplier(supplierC <-chan any) *Generator {
   107  	c := make(chan any)
   108  
   109  	ctx, cancel := context.WithCancel(context.Background())
   110  	g := &Generator{
   111  		C: c,
   112  		r: runtimeGenerator{
   113  			f:         sendChan,
   114  			arg:       c,
   115  			ctx:       ctx,
   116  			cancel:    cancel,
   117  			supplierC: supplierC,
   118  			consumerC: c,
   119  		},
   120  	}
   121  	g.r.start()
   122  	return g
   123  }
   124  
   125  // Deprecated: Use GeneratorFunc wrapped by closure instead.
   126  func GeneratorVariadicFunc(f func(yield Yield, args ...any)) func(args ...any) *Generator {
   127  	supplierC := make(chan any)
   128  	g := GeneratorWithSupplier(supplierC)
   129  	supplierF := func(args ...any) {
   130  		yield := g.Yield(supplierC)
   131  		f(yield, args...)
   132  		close(supplierC)
   133  	}
   134  	return func(args ...any) *Generator {
   135  		go supplierF(args...)
   136  		return g
   137  	}
   138  }
   139  
   140  // GeneratorFuncWithSupplier waits for the supplierC to supply and then calls f
   141  // in its own goroutine every time. It returns a Generator that can
   142  // be used to cancel the call using its Stop method.
   143  // Consume will be stopped when supplierC is closed.
   144  func GeneratorFuncWithSupplier(supplierC <-chan any, f func(msg any)) *Generator {
   145  	ctx, cancel := context.WithCancel(context.Background())
   146  	g := &Generator{
   147  		r: runtimeGenerator{
   148  			f:         goFunc,
   149  			arg:       f,
   150  			ctx:       ctx,
   151  			cancel:    cancel,
   152  			supplierC: supplierC,
   153  		},
   154  	}
   155  	g.r.start()
   156  	return g
   157  
   158  }
   159  
   160  func sendChan(ctx context.Context, c any, msg any) {
   161  	// Blocking send of msg on c.
   162  	select {
   163  	case <-ctx.Done():
   164  		return
   165  	case c.(chan any) <- msg:
   166  	}
   167  	return
   168  }
   169  
   170  // arg -> func
   171  func goFunc(ctx context.Context, f any, msg any) {
   172  	go f.(func(any))(msg)
   173  }