1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! Generic pagination helpers.
//!
//! ScrapeBadger paginates differently per platform (Twitter/Google use opaque
//! cursors, Amazon/Vinted use page numbers, Reddit uses `after` fullnames), so
//! there is no single universal paginator. These building blocks let callers
//! turn any "fetch one page" closure into a flat [`Stream`] of items.
use Future;
use Stream;
use Result;
/// Turn a cursor-based fetch closure into a stream of individual items.
///
/// `fetch` receives the current cursor (`None` on the first call) and returns a
/// page of items plus the next cursor; iteration stops when it yields `None`.
///
/// ```no_run
/// # use scrapebadger::core::pagination::cursor_stream;
/// # async fn demo(client: scrapebadger::Twitter) -> scrapebadger::Result<()> {
/// use futures_util::StreamExt;
/// let stream = cursor_stream(None::<String>, |cursor| {
/// let client = client.clone();
/// async move {
/// let page = client
/// .advanced_search_tweets(scrapebadger::twitter::AdvancedSearchTweetsParams {
/// query: Some("rust".into()),
/// cursor,
/// ..Default::default()
/// })
/// .await?;
/// Ok((page.data.unwrap_or_default(), page.next_cursor))
/// }
/// });
/// futures_util::pin_mut!(stream);
/// while let Some(tweet) = stream.next().await {
/// let _tweet = tweet?;
/// }
/// # Ok(()) }
/// ```
/// Turn a page-number fetch closure into a stream of individual items.
///
/// `fetch` receives the 1-based page number and returns that page's items;
/// iteration stops on the first empty page.