grammers_client/types/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 crate::Client;
10pub use grammers_mtsender::InvocationError;
11use std::collections::VecDeque;
12
13/// Common parts to all requests that are used for creating iterators.
14///
15/// End-users should obtain particular instances of this type via client methods.
16pub struct IterBuffer<R, T> {
17 pub(crate) client: Client,
18 pub(crate) limit: Option<usize>,
19 pub(crate) fetched: usize,
20 pub(crate) buffer: VecDeque<T>,
21 pub(crate) last_chunk: bool,
22 pub(crate) total: Option<usize>,
23 pub(crate) request: R,
24}
25
26impl<R, T> IterBuffer<R, T> {
27 /// Create a new `IterBuffer` instance from a handle, capacity and request.
28 pub(crate) fn from_request(client: &Client, capacity: usize, request: R) -> Self {
29 Self {
30 client: client.clone(),
31 limit: None,
32 fetched: 0,
33 buffer: VecDeque::with_capacity(capacity),
34 last_chunk: false,
35 total: None,
36 request,
37 }
38 }
39
40 /// Change how many items will be returned from the iterator.
41 ///
42 /// Using `limit` instead of `take` on the iterator is useful because outgoing requests can
43 /// ask for less items from the server to only fetch what's needed.
44 pub fn limit(mut self, n: usize) -> Self {
45 self.limit = Some(n);
46 self
47 }
48
49 /// Checks whether the limit has been reached and no more items should be fetched.
50 fn limit_reached(&self) -> bool {
51 if let Some(limit) = self.limit {
52 self.fetched >= limit
53 } else {
54 false
55 }
56 }
57
58 /// Return the next result item from the buffer unless more data needs to be fetched.
59 ///
60 /// Data does not need to be fetched if the limit is reached or the buffer is empty and the
61 /// last chunk was reached.
62 pub(crate) fn next_raw(&mut self) -> Option<Result<Option<T>, InvocationError>> {
63 if self.limit_reached() || (self.buffer.is_empty() && self.last_chunk) {
64 Some(Ok(None))
65 } else {
66 self.pop_item().map(|item| Ok(Some(item)))
67 }
68 }
69
70 /// Determines the new "limit" for the request, so that no unnecessary items are fetched from
71 /// the network.
72 pub(crate) fn determine_limit(&self, max: usize) -> i32 {
73 if let Some(limit) = self.limit {
74 if self.fetched < limit {
75 (limit - self.fetched).min(max) as i32
76 } else {
77 1 // 0 would cause Telegram to send a default amount and not actually 0
78 }
79 } else {
80 max as i32
81 }
82 }
83
84 /// Pop a buffered item from the queue, and increment the amount of items fetched (returned).
85 pub(crate) fn pop_item(&mut self) -> Option<T> {
86 if let Some(item) = self.buffer.pop_front() {
87 self.fetched += 1;
88 Some(item)
89 } else {
90 None
91 }
92 }
93}