Skip to main content

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}