| // Copyright 2016 Google LLC. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package iterator_test | |
| import ( | |
| "bytes" | |
| "context" | |
| "fmt" | |
| "html/template" | |
| "log" | |
| "math" | |
| "net/http" | |
| "sort" | |
| "strconv" | |
| "google.golang.org/api/iterator" | |
| ) | |
| var ( | |
| client *Client | |
| ctx = context.Background() | |
| ) | |
| var pageTemplate = template.Must(template.New("").Parse(` | |
| <table> | |
| {{range .Entries}} | |
| <tr><td>{{.}}</td></tr> | |
| {{end}} | |
| </table> | |
| {{with .Next}} | |
| <a href="/entries?pageToken={{.}}">Next Page</a> | |
| {{end}} | |
| `)) | |
| func Example() { | |
| it := Primes(19) | |
| for { | |
| item, err := it.Next() | |
| if err == iterator.Done { | |
| break | |
| } | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| fmt.Printf("%d ", item) | |
| } | |
| // Output: | |
| // 2 3 5 7 11 13 17 19 | |
| } | |
| // This example demonstrates how to use Pager to support | |
| // pagination on a web site. | |
| func Example_webHandler() { | |
| // Assuming some response writer and request per https://golang.org/pkg/net/http/#Handler. | |
| var w http.ResponseWriter | |
| var r *http.Request | |
| const pageSize = 25 | |
| it := client.Items(ctx) | |
| var items []int | |
| pageToken, err := iterator.NewPager(it, pageSize, r.URL.Query().Get("pageToken")).NextPage(&items) | |
| if err != nil { | |
| http.Error(w, fmt.Sprintf("getting next page: %v", err), http.StatusInternalServerError) | |
| } | |
| data := struct { | |
| Items []int | |
| Next string | |
| }{ | |
| items, | |
| pageToken, | |
| } | |
| var buf bytes.Buffer | |
| // pageTemplate is a global html/template.Template that is only parsed once, rather than for | |
| // every invocation. | |
| if err := pageTemplate.Execute(&buf, data); err != nil { | |
| http.Error(w, fmt.Sprintf("executing page template: %v", err), http.StatusInternalServerError) | |
| } | |
| w.Header().Set("Content-Type", "text/html; charset=utf-8") | |
| if _, err := buf.WriteTo(w); err != nil { | |
| log.Printf("writing response: %v", err) | |
| } | |
| } | |
| // This example demonstrates how to use a Pager to page through an iterator in a loop. | |
| func Example_pageLoop() { | |
| // Find all primes up to 42, in pages of size 5. | |
| const max = 42 | |
| const pageSize = 5 | |
| p := iterator.NewPager(Primes(max), pageSize, "" /* start from the beginning */) | |
| for page := 0; ; page++ { | |
| var items []int | |
| pageToken, err := p.NextPage(&items) | |
| if err != nil { | |
| log.Fatalf("Iterator paging failed: %v", err) | |
| } | |
| fmt.Printf("Page %d: %v\n", page, items) | |
| if pageToken == "" { | |
| break | |
| } | |
| } | |
| // Output: | |
| // Page 0: [2 3 5 7 11] | |
| // Page 1: [13 17 19 23 29] | |
| // Page 2: [31 37 41] | |
| } | |
| // The example demonstrates how to use a Pager to request a page from a given token. | |
| func Example_pageToken() { | |
| const pageSize = 5 | |
| const pageToken = "1337" | |
| p := iterator.NewPager(Primes(0), pageSize, pageToken) | |
| var items []int | |
| nextPage, err := p.NextPage(&items) | |
| if err != nil { | |
| log.Fatalf("Iterator paging failed: %v", err) | |
| } | |
| fmt.Printf("Primes: %v\nToken: %q\n", items, nextPage) | |
| // Output: | |
| // Primes: [1361 1367 1373 1381 1399] | |
| // Token: "1400" | |
| } | |
| // This example demonstrates how to get exactly the items in the buffer, without | |
| // triggering an extra RPC. | |
| func Example_serverPages() { | |
| // The iterator returned by Primes has a default page size of 20, which means | |
| // it will return all the primes in the range [2, 21). | |
| it := Primes(0) | |
| var items []int | |
| for { | |
| item, err := it.Next() | |
| if err == iterator.Done { | |
| break | |
| } | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| items = append(items, item) | |
| if it.PageInfo().Remaining() == 0 { | |
| break | |
| } | |
| } | |
| fmt.Println(items) | |
| // Output: | |
| // [2 3 5 7 11 13 17 19] | |
| } | |
| // Primes returns a iterator which returns a sequence of prime numbers. | |
| // If non-zero, max specifies the maximum number which could possibly be | |
| // returned. | |
| func Primes(max int) *SieveIterator { | |
| it := &SieveIterator{pos: 2, max: max} | |
| it.pageInfo, it.nextFunc = iterator.NewPageInfo( | |
| it.fetch, | |
| func() int { return len(it.items) }, | |
| func() interface{} { b := it.items; it.items = nil; return b }) | |
| return it | |
| } | |
| // SieveIterator is an iterator that returns primes using the sieve of | |
| // Eratosthenes. It is a demonstration of how an iterator might work. | |
| // Internally, it uses "page size" as the number of ints to consider, | |
| // and "page token" as the first number to consider (defaults to 2). | |
| type SieveIterator struct { | |
| pageInfo *iterator.PageInfo | |
| nextFunc func() error | |
| max int // The largest number to consider. | |
| p []int // Primes in the range [2, pos). | |
| pos int // Next number to consider when generating p. | |
| items []int | |
| } | |
| // PageInfo returns a PageInfo, which supports pagination. | |
| func (it *SieveIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } | |
| func (it *SieveIterator) fetch(pageSize int, pageToken string) (string, error) { | |
| start := 2 | |
| if pageToken != "" { | |
| s, err := strconv.Atoi(pageToken) | |
| if err != nil || s < 2 { | |
| return "", fmt.Errorf("invalid token %q", pageToken) | |
| } | |
| start = s | |
| } | |
| if pageSize == 0 { | |
| pageSize = 20 // Default page size. | |
| } | |
| // Make sure sufficient primes have been calculated. | |
| it.calc(start + pageSize) | |
| // Find the subslice of primes which match this page. | |
| // Note that PageInfo requires that fetch does not remove any existing items, | |
| // so we cannot assume that items is empty at this call. | |
| items := it.p[sort.SearchInts(it.p, start):] | |
| items = items[:sort.SearchInts(items, start+pageSize)] | |
| it.items = append(it.items, items...) | |
| if it.max > 0 && start+pageSize > it.max { | |
| return "", nil // No more possible numbers to return. | |
| } | |
| return strconv.Itoa(start + pageSize), nil | |
| } | |
| // calc populates p with all primes up to, but not including, max. | |
| func (it *SieveIterator) calc(max int) { | |
| if it.max > 0 && max > it.max+1 { // it.max is an inclusive bounds, max is exclusive. | |
| max = it.max + 1 | |
| } | |
| outer: | |
| for x := it.pos; x < max; x++ { | |
| sqrt := int(math.Sqrt(float64(x))) | |
| inner: | |
| for _, p := range it.p { | |
| switch { | |
| case x%p == 0: | |
| // Not a prime. | |
| continue outer | |
| case p > sqrt: | |
| // Only need to check up to sqrt. | |
| break inner | |
| } | |
| } | |
| it.p = append(it.p, x) | |
| } | |
| it.pos = max | |
| } | |
| func (it *SieveIterator) Next() (int, error) { | |
| if err := it.nextFunc(); err != nil { | |
| return 0, err | |
| } | |
| item := it.items[0] | |
| it.items = it.items[1:] | |
| return item, nil | |
| } |