use std::cmp::min;
use super::{Range, Ranges, SlidingSyncMode};
use crate::{SlidingSyncListLoadingState, sliding_sync::Error};
#[derive(Debug, PartialEq)]
pub enum SlidingSyncListRequestGeneratorKind {
Growing {
batch_size: u32,
maximum_number_of_rooms_to_fetch: Option<u32>,
number_of_fetched_rooms: u32,
fully_loaded: bool,
requested_end: Option<u32>,
},
Paging {
batch_size: u32,
maximum_number_of_rooms_to_fetch: Option<u32>,
number_of_fetched_rooms: u32,
fully_loaded: bool,
requested_end: Option<u32>,
},
Selective,
}
#[derive(Debug)]
pub struct SlidingSyncListRequestGenerator {
ranges: Ranges,
kind: SlidingSyncListRequestGeneratorKind,
}
impl SlidingSyncListRequestGenerator {
pub(super) fn new(sync_mode: SlidingSyncMode) -> Self {
match sync_mode {
SlidingSyncMode::Paging { batch_size, maximum_number_of_rooms_to_fetch } => Self {
ranges: Vec::new(),
kind: SlidingSyncListRequestGeneratorKind::Paging {
batch_size,
maximum_number_of_rooms_to_fetch,
number_of_fetched_rooms: 0,
fully_loaded: false,
requested_end: None,
},
},
SlidingSyncMode::Growing { batch_size, maximum_number_of_rooms_to_fetch } => Self {
ranges: Vec::new(),
kind: SlidingSyncListRequestGeneratorKind::Growing {
batch_size,
maximum_number_of_rooms_to_fetch,
number_of_fetched_rooms: 0,
fully_loaded: false,
requested_end: None,
},
},
SlidingSyncMode::Selective { ranges } => {
Self { ranges, kind: SlidingSyncListRequestGeneratorKind::Selective }
}
}
}
pub fn requested_ranges(&self) -> &[Range] {
&self.ranges
}
pub fn kind(&self) -> &SlidingSyncListRequestGeneratorKind {
&self.kind
}
pub(super) fn generate_next_ranges(
&mut self,
maximum_number_of_rooms: Option<u32>,
) -> Result<Ranges, Error> {
match &mut self.kind {
SlidingSyncListRequestGeneratorKind::Paging { fully_loaded: true, .. }
| SlidingSyncListRequestGeneratorKind::Growing { fully_loaded: true, .. }
| SlidingSyncListRequestGeneratorKind::Selective => {
Ok(self.ranges.clone())
}
SlidingSyncListRequestGeneratorKind::Paging {
number_of_fetched_rooms,
batch_size,
maximum_number_of_rooms_to_fetch,
requested_end,
..
} => {
let range_start = number_of_fetched_rooms;
let range_desired_size = batch_size;
let next_range = create_range(
*range_start,
*range_desired_size,
*maximum_number_of_rooms_to_fetch,
maximum_number_of_rooms,
)?;
*requested_end = Some(*next_range.end());
Ok(vec![next_range])
}
SlidingSyncListRequestGeneratorKind::Growing {
number_of_fetched_rooms,
batch_size,
maximum_number_of_rooms_to_fetch,
requested_end,
..
} => {
let range_start = 0;
let range_desired_size = number_of_fetched_rooms.saturating_add(*batch_size);
let next_range = create_range(
range_start,
range_desired_size,
*maximum_number_of_rooms_to_fetch,
maximum_number_of_rooms,
)?;
*requested_end = Some(*next_range.end());
Ok(vec![next_range])
}
}
}
pub(super) fn handle_response(
&mut self,
list_name: &str,
maximum_number_of_rooms: u32,
) -> Result<SlidingSyncListLoadingState, Error> {
match &mut self.kind {
SlidingSyncListRequestGeneratorKind::Paging {
requested_end,
number_of_fetched_rooms,
fully_loaded,
maximum_number_of_rooms_to_fetch,
..
}
| SlidingSyncListRequestGeneratorKind::Growing {
requested_end,
number_of_fetched_rooms,
fully_loaded,
maximum_number_of_rooms_to_fetch,
..
} => {
let range_end = requested_end.ok_or_else(|| {
Error::RequestGeneratorHasNotBeenInitialized(list_name.to_owned())
})?;
let mut range_maximum = maximum_number_of_rooms;
if let Some(maximum_number_of_rooms_to_fetch) = maximum_number_of_rooms_to_fetch {
range_maximum = min(range_maximum, *maximum_number_of_rooms_to_fetch);
}
range_maximum = range_maximum.saturating_sub(1);
if range_end < range_maximum {
*number_of_fetched_rooms = range_end.saturating_add(1);
*fully_loaded = false;
self.ranges = vec![0..=range_end];
Ok(SlidingSyncListLoadingState::PartiallyLoaded)
}
else {
*number_of_fetched_rooms = range_maximum;
*fully_loaded = true;
self.ranges = vec![0..=range_maximum];
Ok(SlidingSyncListLoadingState::FullyLoaded)
}
}
SlidingSyncListRequestGeneratorKind::Selective => {
Ok(SlidingSyncListLoadingState::FullyLoaded)
}
}
}
pub fn is_fully_loaded(&self) -> bool {
match self.kind {
SlidingSyncListRequestGeneratorKind::Paging { fully_loaded, .. }
| SlidingSyncListRequestGeneratorKind::Growing { fully_loaded, .. } => fully_loaded,
SlidingSyncListRequestGeneratorKind::Selective => true,
}
}
#[cfg(test)]
pub fn is_selective(&self) -> bool {
matches!(self.kind, SlidingSyncListRequestGeneratorKind::Selective)
}
}
fn create_range(
start: u32,
desired_size: u32,
maximum_number_of_rooms_to_fetch: Option<u32>,
maximum_number_of_rooms: Option<u32>,
) -> Result<Range, Error> {
let mut end = start + desired_size;
if let Some(maximum_number_of_rooms_to_fetch) = maximum_number_of_rooms_to_fetch {
end = min(end, maximum_number_of_rooms_to_fetch);
}
if let Some(maximum_number_of_rooms) = maximum_number_of_rooms {
end = min(end, maximum_number_of_rooms);
}
end = end.saturating_sub(1);
if start > end {
return Err(Error::InvalidRange { start, end });
}
Ok(Range::new(start, end))
}
#[cfg(test)]
mod tests {
use std::ops::{Not, RangeInclusive};
use assert_matches::assert_matches;
use super::{
SlidingSyncListRequestGenerator, SlidingSyncListRequestGeneratorKind, create_range,
};
use crate::{SlidingSyncMode, sliding_sync::Error};
#[test]
fn test_create_range_from() {
assert_matches!(create_range(0, 100, None, None), Ok(range) if range == RangeInclusive::new(0, 99));
assert_matches!(create_range(100, 100, None, None), Ok(range) if range == RangeInclusive::new(100, 199));
assert_matches!(create_range(0, 100, Some(50), None), Ok(range) if range == RangeInclusive::new(0, 49));
assert_matches!(create_range(49, 100, Some(50), None), Ok(range) if range == RangeInclusive::new(49, 49));
assert_matches!(
create_range(50, 100, Some(50), None),
Err(Error::InvalidRange { start: 50, end: 49 })
);
assert_matches!(create_range(0, 100, None, Some(50)), Ok(range) if range == RangeInclusive::new(0, 49));
assert_matches!(create_range(49, 100, None, Some(50)), Ok(range) if range == RangeInclusive::new(49, 49));
assert_matches!(
create_range(50, 100, None, Some(50)),
Err(Error::InvalidRange { start: 50, end: 49 })
);
assert_matches!(create_range(0, 100, Some(75), Some(50)), Ok(range) if range == RangeInclusive::new(0, 49));
assert_matches!(create_range(0, 100, Some(50), Some(75)), Ok(range) if range == RangeInclusive::new(0, 49));
}
#[test]
fn test_request_generator_selective_from_sync_mode() {
let sync_mode = SlidingSyncMode::new_selective();
let request_generator = SlidingSyncListRequestGenerator::new(sync_mode.into());
assert!(request_generator.ranges.is_empty());
assert_eq!(request_generator.kind, SlidingSyncListRequestGeneratorKind::Selective);
assert!(request_generator.is_selective());
}
#[test]
fn test_request_generator_paging_from_sync_mode() {
let sync_mode = SlidingSyncMode::new_paging(1).maximum_number_of_rooms_to_fetch(2);
let request_generator = SlidingSyncListRequestGenerator::new(sync_mode.into());
assert!(request_generator.ranges.is_empty());
assert_eq!(
request_generator.kind,
SlidingSyncListRequestGeneratorKind::Paging {
batch_size: 1,
maximum_number_of_rooms_to_fetch: Some(2),
number_of_fetched_rooms: 0,
fully_loaded: false,
requested_end: None,
}
);
assert!(request_generator.is_selective().not());
}
#[test]
fn test_request_generator_growing_from_sync_mode() {
let sync_mode = SlidingSyncMode::new_growing(1).maximum_number_of_rooms_to_fetch(2);
let request_generator = SlidingSyncListRequestGenerator::new(sync_mode.into());
assert!(request_generator.ranges.is_empty());
assert_eq!(
request_generator.kind,
SlidingSyncListRequestGeneratorKind::Growing {
batch_size: 1,
maximum_number_of_rooms_to_fetch: Some(2),
number_of_fetched_rooms: 0,
fully_loaded: false,
requested_end: None,
}
);
assert!(request_generator.is_selective().not());
}
}