@@ -170,12 +170,12 @@ func Example_ordering() {
170170 // The string to search for in the downloaded files
171171 needle := []byte ("26" )
172172
173- // Start with a stream of numbers from 0 to 999
174- fileIDs := streamNumbers ( ctx , 0 , 1000 )
175-
176- // Generate a stream of URLs from http://example.com/file-0.txt to http://example.com/file-999.txt
177- urls := rill . OrderedMap ( fileIDs , 1 , func ( id int ) ( string , error ) {
178- return fmt . Sprintf ( "https://example.com/file-%d.txt" , id ), nil
173+ // Generate a stream of URLs from https://example.com/file-0.txt
174+ // to https://example.com/file-999.txt
175+ urls := rill . Generate ( func ( send func ( string ), sendErr func ( error )) {
176+ for i := 0 ; i < 1000 && ctx . Err () == nil ; i ++ {
177+ send ( fmt . Sprintf ( "https://example.com/file-%d.txt" , i ))
178+ }
179179 })
180180
181181 // Download and process the files
@@ -267,24 +267,21 @@ func Example_flatMap() {
267267}
268268
269269// StreamUsers is a reusable streaming wrapper around the mockapi.ListUsers function.
270- // It iterates through all listing pages and returns a stream of users.
270+ // It iterates through all listing pages and uses [Generate] to simplify sending users and errors to the resulting stream .
271271// This function is useful both on its own and as part of larger pipelines.
272272func StreamUsers (ctx context.Context , query * mockapi.UserQuery ) <- chan rill.Try [* mockapi.User ] {
273- res := make (chan rill.Try [* mockapi.User ])
274-
275- if query == nil {
276- query = & mockapi.UserQuery {}
277- }
278-
279- go func () {
280- defer close (res )
273+ return rill .Generate (func (send func (* mockapi.User ), sendErr func (error )) {
274+ var currentQuery mockapi.UserQuery
275+ if query != nil {
276+ currentQuery = * query
277+ }
281278
282279 for page := 0 ; ; page ++ {
283- query .Page = page
280+ currentQuery .Page = page
284281
285- users , err := mockapi .ListUsers (ctx , query )
282+ users , err := mockapi .ListUsers (ctx , & currentQuery )
286283 if err != nil {
287- res <- rill . Wrap [ * mockapi. User ]( nil , err )
284+ sendErr ( err )
288285 return
289286 }
290287
@@ -293,12 +290,10 @@ func StreamUsers(ctx context.Context, query *mockapi.UserQuery) <-chan rill.Try[
293290 }
294291
295292 for _ , user := range users {
296- res <- rill . Wrap (user , nil )
293+ send (user )
297294 }
298295 }
299- }()
300-
301- return res
296+ })
302297}
303298
304299// This example demonstrates how to gracefully stop a pipeline on the first error.
@@ -308,7 +303,7 @@ func Example_context() {
308303 ctx := context .Background ()
309304
310305 // ID 999 doesn't exist, so fetching will stop after hitting it.
311- err := CheckAllUsersExist (ctx , 3 , []int {1 , 2 , 3 , 4 , 5 , 999 , 7 , 8 , 9 , 10 })
306+ err := CheckAllUsersExist (ctx , 3 , []int {1 , 2 , 3 , 4 , 5 , 999 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 })
312307 fmt .Printf ("Check result: %v\n " , err )
313308}
314309
@@ -319,7 +314,15 @@ func CheckAllUsersExist(ctx context.Context, concurrency int, ids []int) error {
319314 defer cancel ()
320315
321316 // Convert the slice into a stream
322- idsStream := rill .FromSlice (ids , nil )
317+ // Use Generate instead of FromSlice to make the first stage context-aware
318+ idsStream := rill .Generate (func (send func (int ), sendErr func (error )) {
319+ for _ , id := range ids {
320+ if ctx .Err () != nil {
321+ return
322+ }
323+ send (id )
324+ }
325+ })
323326
324327 // Fetch users concurrently.
325328 users := rill .Map (idsStream , concurrency , func (id int ) (* mockapi.User , error ) {
@@ -615,6 +618,33 @@ func ExampleForEach_ordered() {
615618 fmt .Println ("Error:" , err )
616619}
617620
621+ // Generate a stream of URLs from https://example.com/file-0.txt to https://example.com/file-9.txt
622+ func ExampleGenerate () {
623+ urls := rill .Generate (func (send func (string ), sendErr func (error )) {
624+ for i := 0 ; i < 10 ; i ++ {
625+ send (fmt .Sprintf ("https://example.com/file-%d.txt" , i ))
626+ }
627+ })
628+
629+ printStream (urls )
630+ }
631+
632+ // Generate an infinite stream of natural numbers (1, 2, 3, ...).
633+ // New numbers are sent to the stream every 500ms until the context is canceled
634+ func ExampleGenerate_context () {
635+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
636+ defer cancel ()
637+
638+ numbers := rill .Generate (func (send func (int ), sendErr func (error )) {
639+ for i := 1 ; ctx .Err () == nil ; i ++ {
640+ send (i )
641+ time .Sleep (500 * time .Millisecond )
642+ }
643+ })
644+
645+ printStream (numbers )
646+ }
647+
618648func ExampleMap () {
619649 // Convert a slice of numbers into a stream
620650 numbers := rill .FromSlice ([]int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }, nil )
@@ -746,22 +776,6 @@ func square(x int) int {
746776 return x * x
747777}
748778
749- // helper function that creates a stream of numbers [start, end) and respects the context
750- func streamNumbers (ctx context.Context , start , end int ) <- chan rill.Try [int ] {
751- out := make (chan rill.Try [int ])
752- go func () {
753- defer close (out )
754- for i := start ; i < end ; i ++ {
755- select {
756- case <- ctx .Done ():
757- return
758- case out <- rill.Try [int ]{Value : i }:
759- }
760- }
761- }()
762- return out
763- }
764-
765779// printStream prints all items from a stream (one per line) and an error if any.
766780func printStream [A any ](stream <- chan rill.Try [A ]) {
767781 fmt .Println ("Result:" )
0 commit comments