@@ -5,6 +5,7 @@ package fuego
5
5
import (
6
6
"fmt"
7
7
8
+ "github.com/google/go-cmp/cmp"
8
9
"go.uber.org/zap"
9
10
)
10
11
@@ -386,6 +387,256 @@ func (s Stream[T]) NoneMatch(p Predicate[T]) bool {
386
387
return ! s .AnyMatch (p )
387
388
}
388
389
390
+ // Drop the first 'n' elements of this stream and returns a new stream.
391
+ //
392
+ // This function streams continuously until the in-stream is closed at
393
+ // which point the out-stream will be closed too.
394
+ func (s Stream [T ]) Drop (n uint64 ) Stream [T ] {
395
+ return s .DropWhile (func () func (e T ) bool {
396
+ count := uint64 (0 )
397
+ return func (e T ) bool {
398
+ count ++
399
+ return count <= n
400
+ }
401
+ }())
402
+ }
403
+
404
+ // DropWhile drops the first elements of this stream while the predicate
405
+ // is satisfied and returns a new stream.
406
+ //
407
+ // This function streams continuously until the in-stream is closed at
408
+ // which point the out-stream will be closed too.
409
+ func (s Stream [T ]) DropWhile (p Predicate [T ]) Stream [T ] {
410
+ outstream := make (chan T , cap (s .stream ))
411
+
412
+ go func () {
413
+ defer close (outstream )
414
+
415
+ if s .stream == nil {
416
+ return
417
+ }
418
+
419
+ // drop elements as required
420
+ for val := range s .stream {
421
+ if p (val ) {
422
+ continue
423
+ }
424
+ outstream <- val
425
+
426
+ break
427
+ }
428
+
429
+ // flush the remainder to outstream
430
+ for val := range s .stream {
431
+ outstream <- val
432
+ }
433
+ }()
434
+
435
+ return NewConcurrentStream (outstream , s .concurrency )
436
+ }
437
+
438
+ // DropUntil drops the first elements of this stream until the predicate
439
+ // is satisfied and returns a new stream.
440
+ //
441
+ // This function streams continuously until the in-stream is closed at
442
+ // which point the out-stream will be closed too.
443
+ func (s Stream [T ]) DropUntil (p Predicate [T ]) Stream [T ] {
444
+ return s .DropWhile (p .Negate ())
445
+ }
446
+
447
+ // Last returns the last Entry in this stream.
448
+ //
449
+ // This function streams continuously until the in-stream is closed at
450
+ // which point the out-stream will be closed too.
451
+ func (s Stream [T ]) Last () T {
452
+ return s .LastN (1 )[0 ]
453
+ }
454
+
455
+ // LastN returns a slice of the last n elements in this stream.
456
+ //
457
+ // This function streams continuously until the in-stream is closed at
458
+ // which point the out-stream will be closed too.
459
+ func (s Stream [T ]) LastN (n uint64 ) []T {
460
+ const flushTriggerDefault = uint64 (100 )
461
+
462
+ if s .stream == nil {
463
+ panic (PanicMissingChannel )
464
+ }
465
+
466
+ if n < 1 {
467
+ panic (PanicNoSuchElement )
468
+ }
469
+
470
+ val , ok := <- s .stream
471
+ if ! ok {
472
+ panic (PanicNoSuchElement )
473
+ }
474
+
475
+ result := []T {val }
476
+
477
+ count := uint64 (len (result ))
478
+ flushTrigger := flushTriggerDefault
479
+
480
+ if n > flushTrigger {
481
+ flushTrigger = n
482
+ }
483
+
484
+ for val = range s .stream {
485
+ result = append (result , val )
486
+ if count ++ ; count > flushTrigger {
487
+ // this is simply to reduce the number of
488
+ // slice resizing operations
489
+ result = result [uint64 (len (result ))- n :]
490
+ count = 0
491
+ }
492
+ }
493
+
494
+ if uint64 (len (result )) > n {
495
+ return result [uint64 (len (result ))- n :]
496
+ }
497
+
498
+ return result
499
+ }
500
+
501
+ // Head returns the first Entry in this stream.
502
+ //
503
+ // This function only consumes at most one element from the stream.
504
+ func (s Stream [T ]) Head () T {
505
+ head := s .HeadN (1 )
506
+ if len (head ) != 1 {
507
+ panic (PanicNoSuchElement )
508
+ }
509
+
510
+ return head [0 ]
511
+ }
512
+
513
+ // HeadN returns a slice of the first n elements in this stream.
514
+ //
515
+ // This function only consumes at most 'n' elements from the stream.
516
+ func (s Stream [T ]) HeadN (n uint64 ) []T {
517
+ return Collect (
518
+ s .Take (n ),
519
+ NewCollector (
520
+ func () []T { return []T {} },
521
+ func (e1 []T , e2 T ) []T { return append (e1 , e2 ) },
522
+ IdentityFinisher [[]T ],
523
+ ))
524
+ }
525
+
526
+ // Take returns a stream of the first 'n' elements of this stream.
527
+ //
528
+ // This function streams continuously until the 'n' elements are picked
529
+ // or the in-stream is closed at which point the out-stream
530
+ // will be closed too.
531
+ func (s Stream [T ]) Take (n uint64 ) Stream [T ] {
532
+ counterIsLessThanOrEqualTo := func (maxCount uint64 ) Predicate [T ] {
533
+ counter := uint64 (0 )
534
+
535
+ return func (t T ) bool {
536
+ counter ++
537
+ return counter <= maxCount
538
+ }
539
+ }
540
+
541
+ return s .TakeWhile (counterIsLessThanOrEqualTo (n ))
542
+ }
543
+
544
+ // Limit is a synonym for Take.
545
+ func (s Stream [T ]) Limit (n uint64 ) Stream [T ] {
546
+ return s .Take (n )
547
+ }
548
+
549
+ // TakeWhile returns a stream of the first elements of this
550
+ // stream while the predicate is satisfied.
551
+ //
552
+ // This function streams continuously until the in-stream is closed at
553
+ // which point the out-stream will be closed too.
554
+ func (s Stream [T ]) TakeWhile (p Predicate [T ]) Stream [T ] {
555
+ if s .stream == nil {
556
+ panic (PanicMissingChannel )
557
+ }
558
+
559
+ outstream := make (chan T , cap (s .stream ))
560
+
561
+ go func () {
562
+ defer close (outstream )
563
+
564
+ for val := range s .stream {
565
+ if ! p (val ) {
566
+ return
567
+ }
568
+ outstream <- val
569
+ }
570
+ }()
571
+
572
+ return NewConcurrentStream (outstream , s .concurrency )
573
+ }
574
+
575
+ // TakeUntil returns a stream of the first elements
576
+ // of this stream until the predicate is satisfied.
577
+ //
578
+ // This function streams continuously until the in-stream is closed at
579
+ // which point the out-stream will be closed too.
580
+ func (s Stream [T ]) TakeUntil (p Predicate [T ]) Stream [T ] {
581
+ return s .TakeWhile (p .Negate ())
582
+ }
583
+
584
+ // StartsWith returns true when this stream starts
585
+ // with the elements in the supplied slice.
586
+ //
587
+ // This function only consume as much data from the stream as
588
+ // is necessary to prove (or disprove) it starts with the supplied
589
+ // slice data.
590
+ func (s Stream [T ]) StartsWith (slice []T ) bool {
591
+ startElements := s .HeadN (uint64 (len (slice )))
592
+ if len (slice ) == 0 || len (startElements ) != len (slice ) {
593
+ return false
594
+ }
595
+
596
+ for idx , el := range slice {
597
+ if ! cmp .Equal (el , startElements [idx ]) {
598
+ return false
599
+ }
600
+ }
601
+
602
+ return true
603
+ }
604
+
605
+ // EndsWith returns true when this stream ends
606
+ // with the supplied elements.
607
+ //
608
+ // This is a potentially expensive method since it has
609
+ // to consume all the elements in the Stream.
610
+ //
611
+ // This function streams continuously until the in-stream is closed at
612
+ // which point the out-stream will be closed too.
613
+ func (s Stream [T ]) EndsWith (slice []T ) bool {
614
+ if len (slice ) == 0 {
615
+ return false
616
+ }
617
+
618
+ endElements := func () []T {
619
+ defer func () {
620
+ // TODO: this doesn't look great... Need to re-write LastN like HeadN as a collect of TakeRight (to be implemented)
621
+ _ = recover ()
622
+ }()
623
+
624
+ return s .LastN (uint64 (len (slice )))
625
+ }()
626
+
627
+ if len (endElements ) != len (slice ) {
628
+ return false
629
+ }
630
+
631
+ for idx , el := range slice {
632
+ if ! cmp .Equal (el , endElements [idx ]) {
633
+ return false
634
+ }
635
+ }
636
+
637
+ return true
638
+ }
639
+
389
640
// ForEach executes the given consumer function for each entry in this stream.
390
641
//
391
642
// This is a continuous terminal operation. It will only complete if the producer closes the stream.
0 commit comments