1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
use std::pin::Pin;
use futures::{future::FusedFuture, Future};
use mullvad_types::{device::Device, wireguard::WireguardData};
use super::{Error, PrivateAccountAndDevice, ResponseTx};
pub(crate) struct CurrentApiCall {
current_call: Option<Call>,
}
impl CurrentApiCall {
pub fn new() -> Self {
Self { current_call: None }
}
pub fn clear(&mut self) {
self.current_call = None;
}
pub fn set_login(&mut self, login: ApiCall<PrivateAccountAndDevice>, tx: ResponseTx<()>) {
self.current_call = Some(Call::Login(login, Some(tx)));
}
pub fn set_oneshot_rotation(&mut self, rotation: ApiCall<WireguardData>) {
self.current_call = Some(Call::OneshotKeyRotation(rotation));
}
pub fn set_timed_rotation(&mut self, rotation: ApiCall<WireguardData>) {
self.current_call = Some(Call::TimerKeyRotation(rotation));
}
pub fn set_validation(&mut self, validation: ApiCall<Device>) {
self.current_call = Some(Call::Validation(validation));
}
pub fn is_validating(&self) -> bool {
matches!(
&self.current_call,
Some(Call::Validation(_)) | Some(Call::OneshotKeyRotation(_))
)
}
pub fn is_running_timed_totation(&self) -> bool {
matches!(&self.current_call, Some(Call::TimerKeyRotation(_)))
}
pub fn is_idle(&self) -> bool {
self.current_call.is_none()
}
pub fn is_logging_in(&self) -> bool {
use Call::*;
matches!(&self.current_call, Some(Login(..)))
}
}
impl Future for CurrentApiCall {
type Output = ApiResult;
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
match self.current_call.as_mut() {
Some(call) => {
let result = Pin::new(call).poll(cx);
if result.is_ready() {
self.current_call = None;
}
result
}
None => panic!("Polled an unfinished future"),
}
}
}
impl FusedFuture for CurrentApiCall {
fn is_terminated(&self) -> bool {
self.current_call.is_none()
}
}
type ApiCall<T> = Pin<Box<dyn Future<Output = Result<T, Error>> + Send>>;
enum Call {
Login(ApiCall<PrivateAccountAndDevice>, Option<ResponseTx<()>>),
TimerKeyRotation(ApiCall<WireguardData>),
OneshotKeyRotation(ApiCall<WireguardData>),
Validation(ApiCall<Device>),
}
impl futures::Future for Call {
type Output = ApiResult;
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
use Call::*;
match &mut *self {
Login(call, tx) => {
if let std::task::Poll::Ready(response) = Pin::new(call).poll(cx) {
std::task::Poll::Ready(ApiResult::Login(response, tx.take().unwrap()))
} else {
std::task::Poll::Pending
}
}
TimerKeyRotation(call) | OneshotKeyRotation(call) => {
Pin::new(call).poll(cx).map(ApiResult::Rotation)
}
Validation(call) => Pin::new(call).poll(cx).map(ApiResult::Validation),
}
}
}
pub(crate) enum ApiResult {
Login(Result<PrivateAccountAndDevice, Error>, ResponseTx<()>),
Rotation(Result<WireguardData, Error>),
Validation(Result<Device, Error>),
}
|