use crate::api::kasl_server::{DayResult, DayUpload, KaslServer, UploadError};
use crate::db::server_outbox::ServerOutbox;
use crate::libs::day_upload::build_day_upload;
use anyhow::Result;
use chrono::NaiveDate;
pub const BATCH_SIZE: usize = 30;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Delivered {
Accepted {
date: NaiveDate,
pauses: usize,
tasks: usize,
deleted_tasks: u64,
},
Refused { date: NaiveDate, reason: String },
Deferred { date: NaiveDate, reason: String },
}
impl Delivered {
pub fn date(&self) -> NaiveDate {
match self {
Delivered::Accepted { date, .. } | Delivered::Refused { date, .. } | Delivered::Deferred { date, .. } => *date,
}
}
}
pub async fn deliver(client: &KaslServer, token: &str, outbox: &mut ServerOutbox, dates: &[NaiveDate]) -> Result<Vec<Delivered>> {
let mut outcomes = Vec::new();
let mut sendable: Vec<(NaiveDate, DayUpload)> = Vec::new();
for &date in dates {
match build_day_upload(date) {
Ok(None) => {
outbox.remove(date)?;
}
Ok(Some(day)) => sendable.push((date, day)),
Err(error) => {
let reason = error.to_string();
outbox.enqueue(date, &reason)?;
outcomes.push(Delivered::Deferred { date, reason });
}
}
}
for chunk in sendable.chunks(BATCH_SIZE) {
let days: Vec<DayUpload> = chunk.iter().map(|(_, day)| day.clone()).collect();
let dates: Vec<NaiveDate> = chunk.iter().map(|(date, _)| *date).collect();
match client.upload_batch(token, &days).await {
Ok(result) => {
let mut answered: Vec<NaiveDate> = Vec::with_capacity(result.results.len());
for day_result in &result.results {
let outcome = record(outbox, day_result)?;
answered.push(outcome.date());
outcomes.push(outcome);
}
for date in dates.iter().filter(|date| !answered.contains(date)) {
let reason = "the server did not report on this day".to_string();
outbox.enqueue(*date, &reason)?;
outcomes.push(Delivered::Deferred { date: *date, reason });
}
}
Err(error) => {
let retryable = error.is_retryable();
let reason = error.to_string();
for date in dates {
outcomes.push(settle(outbox, date, &reason, retryable)?);
}
}
}
}
Ok(outcomes)
}
fn record(outbox: &mut ServerOutbox, result: &DayResult) -> Result<Delivered> {
match result {
DayResult::Accepted { day } => {
outbox.remove(day.date)?;
Ok(Delivered::Accepted {
date: day.date,
pauses: day.pauses,
tasks: day.tasks,
deleted_tasks: day.deleted_tasks,
})
}
DayResult::Rejected { date, error } => {
outbox.remove(*date)?;
Ok(Delivered::Refused {
date: *date,
reason: error.clone(),
})
}
}
}
fn settle(outbox: &mut ServerOutbox, date: NaiveDate, reason: &str, retryable: bool) -> Result<Delivered> {
if retryable {
outbox.enqueue(date, reason)?;
Ok(Delivered::Deferred {
date,
reason: reason.to_string(),
})
} else {
outbox.remove(date)?;
Ok(Delivered::Refused {
date,
reason: reason.to_string(),
})
}
}
pub fn record_single(outbox: &mut ServerOutbox, date: NaiveDate, error: &UploadError) -> Result<Delivered> {
settle(outbox, date, &error.to_string(), error.is_retryable())
}