1pub use escouade_plugin_protocol::forge::*;
6
7use crate::tr;
8use anyhow::Result;
9use serde_json::Value;
10use std::future::Future;
11use std::pin::Pin;
12use std::time::Duration;
13
14pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
16
17pub fn instant(v: &Value) -> Option<u64> {
19 let t = chrono::DateTime::parse_from_rfc3339(v.as_str()?)
20 .ok()?
21 .timestamp();
22 u64::try_from(t).ok().filter(|t| *t > 0)
24}
25
26#[derive(Debug)]
28pub struct Unsupported(pub &'static str);
29
30impl std::fmt::Display for Unsupported {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 let what = self.0;
33 f.write_str(&tr!(
34 "Cet hébergeur ne sait pas faire : {what}",
35 "This code host can’t do it: {what}"
36 ))
37 }
38}
39
40impl std::error::Error for Unsupported {}
41
42pub fn unsupported<'a, T: Send + 'a>(what: &'static str) -> BoxFuture<'a, Result<T>> {
43 Box::pin(async move { Err(Unsupported(what).into()) })
44}
45
46pub trait Forge: Send + Sync {
49 fn me(&self) -> BoxFuture<'_, Result<Identity>>;
51
52 fn create_pr<'a>(
53 &'a self,
54 _repo: &'a RepoRef,
55 _pr: &'a PullRequestDraft,
56 ) -> BoxFuture<'a, Result<PullRequest>> {
57 unsupported("create_pr")
58 }
59
60 fn pull_request<'a>(
61 &'a self,
62 _repo: &'a RepoRef,
63 _number: u64,
64 ) -> BoxFuture<'a, Result<PullRequest>> {
65 unsupported("pull_request")
66 }
67
68 fn pull_requests_of_branch<'a>(
70 &'a self,
71 _repo: &'a RepoRef,
72 _branch: &'a str,
73 ) -> BoxFuture<'a, Result<Vec<PullRequest>>> {
74 unsupported("pull_requests_of_branch")
75 }
76
77 fn parent<'a>(&'a self, _repo: &'a RepoRef) -> BoxFuture<'a, Result<Option<RepoRef>>> {
79 Box::pin(async { Ok(None) })
80 }
81
82 fn open_pull_requests<'a>(
85 &'a self,
86 _repo: &'a RepoRef,
87 ) -> BoxFuture<'a, Result<Vec<PullRequest>>> {
88 unsupported("open_pull_requests")
89 }
90
91 fn pull_requests<'a>(
93 &'a self,
94 _repo: &'a RepoRef,
95 _numbers: &'a [u64],
96 ) -> BoxFuture<'a, Result<Vec<PullRequest>>> {
97 unsupported("pull_requests")
98 }
99
100 fn merge<'a>(
101 &'a self,
102 _repo: &'a RepoRef,
103 _number: u64,
104 _strategy: MergeStrategy,
105 ) -> BoxFuture<'a, Result<()>> {
106 unsupported("merge")
107 }
108
109 fn merge_options<'a>(
112 &'a self,
113 _repo: &'a RepoRef,
114 _number: u64,
115 ) -> BoxFuture<'a, Result<MergeOptions>> {
116 unsupported("merge_options")
117 }
118
119 fn auto_merge<'a>(
121 &'a self,
122 _repo: &'a RepoRef,
123 _number: u64,
124 _strategy: MergeStrategy,
125 ) -> BoxFuture<'a, Result<()>> {
126 unsupported("auto_merge")
127 }
128
129 fn checks<'a>(&'a self, _repo: &'a RepoRef, _number: u64) -> BoxFuture<'a, Result<Vec<Check>>> {
130 unsupported("checks")
131 }
132
133 fn job_log<'a>(&'a self, _repo: &'a RepoRef, _job: &'a str) -> BoxFuture<'a, Result<String>> {
134 unsupported("job_log")
135 }
136
137 fn reviews<'a>(
138 &'a self,
139 _repo: &'a RepoRef,
140 _number: u64,
141 ) -> BoxFuture<'a, Result<Vec<Review>>> {
142 unsupported("reviews")
143 }
144
145 fn comments<'a>(
146 &'a self,
147 _repo: &'a RepoRef,
148 _number: u64,
149 ) -> BoxFuture<'a, Result<Vec<ReviewComment>>> {
150 unsupported("comments")
151 }
152
153 fn comment<'a>(
154 &'a self,
155 _repo: &'a RepoRef,
156 _number: u64,
157 _body: &'a str,
158 ) -> BoxFuture<'a, Result<()>> {
159 unsupported("comment")
160 }
161
162 fn rerun_failed<'a>(
164 &'a self,
165 _repo: &'a RepoRef,
166 _number: u64,
167 ) -> BoxFuture<'a, Result<usize>> {
168 unsupported("rerun_failed")
169 }
170
171 fn branch_checks<'a>(
174 &'a self,
175 _repo: &'a RepoRef,
176 _branch: &'a str,
177 ) -> BoxFuture<'a, Result<Vec<Check>>> {
178 unsupported("branch_checks")
179 }
180
181 fn rerun_failed_of_branch<'a>(
183 &'a self,
184 _repo: &'a RepoRef,
185 _branch: &'a str,
186 ) -> BoxFuture<'a, Result<usize>> {
187 unsupported("rerun_failed_of_branch")
188 }
189
190 fn review<'a>(
192 &'a self,
193 _repo: &'a RepoRef,
194 _number: u64,
195 _review: &'a ReviewDraft,
196 ) -> BoxFuture<'a, Result<()>> {
197 unsupported("review")
198 }
199
200 fn reply<'a>(
202 &'a self,
203 _repo: &'a RepoRef,
204 _number: u64,
205 _comment: &'a str,
206 _body: &'a str,
207 ) -> BoxFuture<'a, Result<()>> {
208 unsupported("reply")
209 }
210
211 fn resolve<'a>(
213 &'a self,
214 _repo: &'a RepoRef,
215 _number: u64,
216 _comment: &'a str,
217 ) -> BoxFuture<'a, Result<()>> {
218 unsupported("resolve")
219 }
220
221 fn rate_limit(&self) -> Option<RateLimit> {
223 None
224 }
225
226 fn deployments<'a>(
227 &'a self,
228 _repo: &'a RepoRef,
229 _git_ref: &'a str,
230 ) -> BoxFuture<'a, Result<Vec<Deployment>>> {
231 unsupported("deployments")
232 }
233
234 fn environments<'a>(&'a self, _repo: &'a RepoRef) -> BoxFuture<'a, Result<Vec<Deployment>>> {
238 unsupported("environments")
239 }
240}
241
242#[derive(Debug, Clone, Copy)]
244pub struct Pace {
245 pub slow_below: f64,
247 pub max_wait: Duration,
249}
250
251impl Default for Pace {
252 fn default() -> Self {
253 Self {
254 slow_below: 0.1,
255 max_wait: Duration::from_secs(30),
256 }
257 }
258}
259
260impl Pace {
261 pub fn delay(&self, rl: &RateLimit, now: u64) -> Duration {
265 let left = rl.reset.saturating_sub(now);
266 let wait = if rl.remaining == 0 {
267 left.saturating_add(1) as f64
268 } else if rl.limit == 0 || f64::from(rl.remaining) / f64::from(rl.limit) >= self.slow_below
269 {
270 return Duration::ZERO;
271 } else {
272 left as f64 / f64::from(rl.remaining + 1)
273 };
274 Duration::from_secs_f64(wait).min(self.max_wait)
275 }
276}