use byteorder::ByteOrder;
use chrono::{Datelike, Days, Local, NaiveDate};
use semver::Version;
use serde_derive::{Deserialize, Serialize};
use super::CrateVersion;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GlobalStats {
#[serde(rename = "totalDownloads")]
pub total_downloads: i64,
#[serde(rename = "totalCrates")]
pub total_crates: i64,
#[serde(rename = "cratesNewest")]
pub crates_newest: Vec<CrateVersion>,
#[serde(rename = "cratesMostDownloaded")]
pub crates_most_downloaded: Vec<CrateVersion>,
#[serde(rename = "cratesLastUpdated")]
pub crates_last_updated: Vec<CrateVersion>,
}
pub const SERIES_LENGTH: usize = 90;
#[derive(Debug, Clone, Serialize)]
pub struct DownloadStatsForVersion {
pub version: String,
#[serde(skip)]
version_semver: Version,
pub counts: Vec<u32>,
pub total: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct DownloadStats {
pub days: Vec<NaiveDate>,
pub versions: Vec<DownloadStatsForVersion>,
}
impl Default for DownloadStats {
fn default() -> Self {
let today = Local::now().naive_local().date();
let first = today.checked_sub_days(Days::new(SERIES_LENGTH as u64 - 1)).unwrap();
let mut days = Vec::with_capacity(SERIES_LENGTH);
let mut current = first;
for _ in 0..SERIES_LENGTH {
days.push(current);
current = current.succ_opt().unwrap();
}
Self {
days,
versions: Vec::new(),
}
}
}
impl DownloadStats {
pub fn add_version(&mut self, version: String, data: Option<&[u8]>) {
let mut counts = vec![0; SERIES_LENGTH];
let mut total = 0;
if let Some(data) = data {
let today = Local::now().naive_local().date();
let mut index = ((today.ordinal0() + 1) as usize % SERIES_LENGTH) * size_of::<u32>();
for count in &mut counts {
let v = byteorder::NativeEndian::read_u32(&data[index..]);
total += v;
*count = v;
index = (index + size_of::<u32>()) % data.len();
}
}
self.versions.push(DownloadStatsForVersion {
version_semver: version.parse().unwrap(),
version,
counts,
total,
});
}
pub fn finalize(&mut self) {
self.versions.sort_unstable_by(|a, b| b.version_semver.cmp(&a.version_semver));
let other = 4;
if self.versions.len() > other {
self.versions[other].version = String::from("Others");
for i in (other + 1)..self.versions.len() {
self.versions[other].total += self.versions[i].total;
for j in 0..SERIES_LENGTH {
self.versions[other].counts[j] += self.versions[i].counts[j];
}
}
self.versions.truncate(other + 1);
}
}
}