1use crate::error::{Error, ErrorResponse};
6use crate::telemetry::SyncTelemetryPing;
7use std::collections::HashMap;
8use std::time::{Duration, SystemTime};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum ServiceStatus {
14 Ok,
16 NetworkError,
18 ServiceError,
20 AuthenticationError,
22 BackedOff,
24 Interrupted,
26 OtherError,
29}
30
31impl ServiceStatus {
32 pub fn from_err(err: &Error) -> ServiceStatus {
35 match err {
36 Error::TokenserverHttpError(status) => {
38 if *status == 401 {
40 ServiceStatus::AuthenticationError
41 } else {
42 ServiceStatus::ServiceError
43 }
44 }
45 Error::BackoffError(_) => ServiceStatus::ServiceError,
47 Error::StorageHttpError(ref e) => match e {
48 ErrorResponse::Unauthorized { .. } => ServiceStatus::AuthenticationError,
49 _ => ServiceStatus::ServiceError,
50 },
51
52 Error::RequestError(_) | Error::UnexpectedStatus(_) | Error::HawkError(_) => {
54 ServiceStatus::NetworkError
55 }
56
57 Error::Interrupted(_) => ServiceStatus::Interrupted,
58 _ => ServiceStatus::OtherError,
59 }
60 }
61}
62
63#[derive(Debug)]
66pub struct SyncResult {
67 pub service_status: ServiceStatus,
69
70 pub declined: Option<Vec<String>>,
72
73 pub result: Result<(), Error>,
75
76 pub engine_results: HashMap<String, Result<(), Error>>,
79
80 pub telemetry: SyncTelemetryPing,
81
82 pub next_sync_after: Option<std::time::SystemTime>,
83}
84
85fn advance_backoff(cur_best: SystemTime, r: &Result<(), Error>) -> SystemTime {
87 if let Err(e) = r {
88 if let Some(time) = e.get_backoff() {
89 return std::cmp::max(time, cur_best);
90 }
91 }
92 cur_best
93}
94
95impl SyncResult {
96 pub(crate) fn set_sync_after(&mut self, backoff_duration: Duration) {
97 let now = SystemTime::now();
98 let toplevel = advance_backoff(now + backoff_duration, &self.result);
99 let sync_after = self.engine_results.values().fold(toplevel, advance_backoff);
100 if sync_after <= now {
101 self.next_sync_after = None;
102 } else {
103 self.next_sync_after = Some(sync_after);
104 }
105 }
106}