use core::fmt;
use std::collections::HashMap;
use std::ops::Deref;
use crate::model::{Season, SeasonNumber, SeriesId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Pointer {
series: SeriesId,
number: SeasonNumber,
}
impl Pointer {
fn new(series: SeriesId, number: SeasonNumber) -> Self {
Self { series, number }
}
}
struct SeasonData {
season: Season,
series: SeriesId,
prev: Option<Pointer>,
next: Option<Pointer>,
}
impl SeasonData {
fn into_ref<'a>(&'a self) -> SeasonRef<'a> {
SeasonRef {
season: &self.season,
series: self.series,
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct SeasonRef<'a> {
season: &'a Season,
series: SeriesId,
}
impl<'a> SeasonRef<'a> {
pub(crate) fn series(&self) -> &SeriesId {
&self.series
}
pub(crate) fn into_season(self) -> &'a Season {
self.season
}
}
impl fmt::Debug for SeasonRef<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SeasonRef")
.field("season", &self.season)
.finish_non_exhaustive()
}
}
impl<'a> Deref for SeasonRef<'a> {
type Target = Season;
#[inline]
fn deref(&self) -> &Self::Target {
self.season
}
}
#[derive(Default, Clone, Copy)]
struct SeriesData {
first: Option<Pointer>,
last: Option<Pointer>,
len: usize,
}
#[derive(Default)]
pub(crate) struct Database {
data: HashMap<Pointer, SeasonData>,
by_series: HashMap<SeriesId, SeriesData>,
}
impl Database {
#[tracing::instrument(skip(self, seasons))]
pub(crate) fn insert(&mut self, series: SeriesId, seasons: Vec<Season>) {
let len = seasons.len();
let mut first = None;
let mut prev = None;
let _ = self.remove(&series);
let mut it = seasons.into_iter().peekable();
while let Some(season) = it.next() {
let next = it.peek().map(|s| Pointer::new(series, s.number));
let id = Pointer::new(series, season.number);
let links = SeasonData {
season,
series,
prev,
next,
};
self.data.insert(id, links);
prev = Some(id);
if first.is_none() {
first = Some(id);
}
}
self.by_series.insert(
series,
SeriesData {
first,
last: prev,
len,
},
);
}
pub(crate) fn remove(&mut self, id: &SeriesId) {
let Some(data) = self.by_series.remove(id) else {
return;
};
let mut cur = data.first;
while let Some(id) = cur.take() {
cur = self.data.remove(&id).and_then(|data| data.next);
}
}
pub(crate) fn get(&self, series_id: &SeriesId, season: &SeasonNumber) -> Option<SeasonRef<'_>> {
let data = self.data.get(&Pointer::new(*series_id, *season))?;
Some(data.into_ref())
}
pub(crate) fn by_series(&self, id: &SeriesId) -> Iter<'_> {
let state = self.by_series.get(id).copied().unwrap_or_default();
Iter {
head: state.first,
tail: state.last,
len: state.len,
data: &self.data,
}
}
}
#[derive(Clone)]
pub(crate) struct Iter<'a> {
head: Option<Pointer>,
tail: Option<Pointer>,
len: usize,
data: &'a HashMap<Pointer, SeasonData>,
}
impl Iter<'_> {
pub(crate) fn export(&self) -> Vec<Season> {
let mut data = Vec::with_capacity(self.len);
for e in self.clone() {
data.push(e.season.clone());
}
data
}
}
impl<'a> Iterator for Iter<'a> {
type Item = SeasonRef<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let id = self.head.take()?;
let data = self.data.get(&id)?;
self.head = data.next;
if self.tail == Some(id) {
self.tail = None;
}
self.len = self.len.saturating_sub(1);
Some(data.into_ref())
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
let id = self.tail.take()?;
let data = self.data.get(&id)?;
self.tail = data.prev;
if self.head == Some(id) {
self.head = None;
}
self.len = self.len.saturating_sub(1);
Some(data.into_ref())
}
}
impl ExactSizeIterator for Iter<'_> {
#[inline]
fn len(&self) -> usize {
self.len
}
}