grammers_client/client/iter_buffer.rs
1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::collections::VecDeque;
10
11use grammers_mtsender::InvocationError;
12
13use super::Client;
14
15/// Common parts to most requests that are used for creating iterators.
16///
17/// Client methods will either return a type alias or a wrapper around this.
18pub struct IterBuffer<R, T> {
19 pub(crate) client: Client,
20 pub(crate) limit: Option<usize>,
21 pub(crate) fetched: usize,
22 pub(crate) buffer: VecDeque<T>,
23 pub(crate) last_chunk: bool,
24 pub(crate) total: Option<usize>,
25 pub(crate) request: R,
26}
27
28impl<R, T> IterBuffer<R, T> {
29 /// Create a new `IterBuffer` instance from a handle, capacity and request.
30 pub(crate) fn from_request(client: &Client, capacity: usize, request: R) -> Self {
31 Self {
32 client: client.clone(),
33 limit: None,
34 fetched: 0,
35 buffer: VecDeque::with_capacity(capacity),
36 last_chunk: false,
37 total: None,
38 request,
39 }
40 }
41
42 /// Change how many items will be returned from the iterator.
43 ///
44 /// Using `limit` instead of `take` on the iterator is useful because outgoing requests can
45 /// ask for less items from the server to only fetch what's needed.
46 pub fn limit(mut self, n: usize) -> Self {
47 self.limit = Some(n);
48 self
49 }
50
51 /// Checks whether the limit has been reached and no more items should be fetched.
52 fn limit_reached(&self) -> bool {
53 if let Some(limit) = self.limit {
54 self.fetched >= limit
55 } else {
56 false
57 }
58 }
59
60 /// Return the next result item from the buffer unless more data needs to be fetched.
61 ///
62 /// Data does not need to be fetched if the limit is reached or the buffer is empty and the
63 /// last chunk was reached.
64 pub(crate) fn next_raw(&mut self) -> Option<Result<Option<T>, InvocationError>> {
65 if self.limit_reached() || (self.buffer.is_empty() && self.last_chunk) {
66 Some(Ok(None))
67 } else {
68 self.pop_item().map(|item| Ok(Some(item)))
69 }
70 }
71
72 /// Determines the new "limit" for the request, so that no unnecessary items are fetched from
73 /// the network.
74 pub(crate) fn determine_limit(&self, max: usize) -> i32 {
75 if let Some(limit) = self.limit {
76 if self.fetched < limit {
77 (limit - self.fetched).min(max) as i32
78 } else {
79 1 // 0 would cause Telegram to send a default amount and not actually 0
80 }
81 } else {
82 max as i32
83 }
84 }
85
86 /// Pop a buffered item from the queue, and increment the amount of items fetched (returned).
87 pub(crate) fn pop_item(&mut self) -> Option<T> {
88 if let Some(item) = self.buffer.pop_front() {
89 self.fetched += 1;
90 Some(item)
91 } else {
92 None
93 }
94 }
95}