1use crate::{
10 common::{App, CheckOptions, CheckTiming, ProtocolState, UpdateCheckSchedule},
11 installer::Plan,
12 request_builder::RequestParams,
13 time::{ComplexTime, TimeSource},
14};
15use futures::future::BoxFuture;
16
17#[cfg(test)]
18mod mock;
19#[cfg(test)]
20pub use mock::MockPolicyEngine;
21mod stub;
22pub use stub::StubPolicy;
23pub use stub::StubPolicyEngine;
24use typed_builder::TypedBuilder;
25
26#[derive(Clone, Debug, TypedBuilder)]
28pub struct PolicyData {
29 #[builder(setter(into))]
31 pub current_time: ComplexTime,
32}
33
34#[derive(Clone, Debug, PartialEq)]
36pub enum CheckDecision {
37 Ok(RequestParams),
39 OkUpdateDeferred(RequestParams),
41
42 TooSoon,
44 ThrottledByPolicy,
45 DeniedByPolicy,
46}
47
48#[cfg(test)]
49impl Default for CheckDecision {
50 fn default() -> Self {
51 CheckDecision::Ok(RequestParams::default())
52 }
53}
54
55#[derive(Clone, Debug, PartialEq)]
57pub enum UpdateDecision {
58 Ok,
60 DeferredByPolicy,
62 DeniedByPolicy,
64}
65
66#[cfg(test)]
67#[allow(clippy::derivable_impls)]
70impl Default for UpdateDecision {
71 fn default() -> Self {
72 UpdateDecision::Ok
73 }
74}
75
76pub trait Policy {
78 type ComputeNextUpdateTimePolicyData;
79 type UpdateCheckAllowedPolicyData;
80 type UpdateCanStartPolicyData;
81 type RebootPolicyData;
82 type InstallPlan: Plan;
83
84 fn compute_next_update_time(
86 policy_data: &Self::ComputeNextUpdateTimePolicyData,
87 apps: &[App],
88 scheduling: &UpdateCheckSchedule,
89 protocol_state: &ProtocolState,
90 ) -> CheckTiming;
91
92 fn update_check_allowed(
98 policy_data: &Self::UpdateCheckAllowedPolicyData,
99 apps: &[App],
100 scheduling: &UpdateCheckSchedule,
101 protocol_state: &ProtocolState,
102 check_options: &CheckOptions,
103 ) -> CheckDecision;
104
105 fn update_can_start(
108 policy_data: &Self::UpdateCanStartPolicyData,
109 proposed_install_plan: &Self::InstallPlan,
110 ) -> UpdateDecision;
111
112 fn reboot_allowed(policy_data: &Self::RebootPolicyData, check_options: &CheckOptions) -> bool;
114
115 fn reboot_needed(install_plan: &Self::InstallPlan) -> bool;
117}
118
119pub trait PolicyEngine {
120 type TimeSource: TimeSource + Clone;
121 type InstallResult;
122 type InstallPlan: Plan;
123
124 fn time_source(&self) -> &Self::TimeSource;
126
127 fn compute_next_update_time<'a>(
129 &'a mut self,
130 apps: &'a [App],
131 scheduling: &'a UpdateCheckSchedule,
132 protocol_state: &'a ProtocolState,
133 ) -> BoxFuture<'a, CheckTiming>;
134
135 fn update_check_allowed<'a>(
140 &'a mut self,
141 apps: &'a [App],
142 scheduling: &'a UpdateCheckSchedule,
143 protocol_state: &'a ProtocolState,
144 check_options: &'a CheckOptions,
145 ) -> BoxFuture<'a, CheckDecision>;
146
147 fn update_can_start<'a>(
150 &'a mut self,
151 proposed_install_plan: &'a Self::InstallPlan,
152 ) -> BoxFuture<'a, UpdateDecision>;
153
154 fn reboot_allowed<'a>(
156 &'a mut self,
157 check_options: &'a CheckOptions,
158 install_result: &'a Self::InstallResult,
159 ) -> BoxFuture<'a, bool>;
160
161 fn reboot_needed<'a>(&'a mut self, install_plan: &'a Self::InstallPlan) -> BoxFuture<'a, bool>;
163}
164
165#[cfg(test)]
166mod test {
167 use super::*;
168 use crate::time::MockTimeSource;
169
170 #[test]
171 pub fn test_policy_data_builder_with_system_time() {
172 let current_time = MockTimeSource::new_from_now().now();
173 let policy_data = PolicyData::builder().current_time(current_time).build();
174 assert_eq!(policy_data.current_time, current_time);
175 }
176
177 #[test]
178 pub fn test_policy_data_builder_with_clock() {
179 let source = MockTimeSource::new_from_now();
180 let current_time = source.now();
181 let policy_data = PolicyData::builder().current_time(source.now()).build();
182 assert_eq!(policy_data.current_time, current_time);
183 }
184}