1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
7
8use crate::handlers::CodeCommitService;
9use crate::state::CodeCommitState;
10
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13pub struct CodeCommitStateView {
14 #[serde(default)]
16 pub repositories: HashMap<String, RepositoryView>,
17 #[serde(default)]
18 pub branches: HashMap<String, HashMap<String, BranchView>>,
19 #[serde(default)]
20 pub commits: HashMap<String, HashMap<String, CommitView>>,
21 #[serde(default)]
22 pub files: HashMap<String, HashMap<String, HashMap<String, FileEntryView>>>,
23 #[serde(default)]
24 pub pull_requests: HashMap<String, PullRequestView>,
25 #[serde(default)]
26 pub pull_request_counter: u64,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct RepositoryView {
31 pub repository_id: String,
32 pub repository_name: String,
33 pub arn: String,
34 pub description: String,
35 pub clone_url_http: String,
36 pub clone_url_ssh: String,
37 pub creation_date: String,
38 pub last_modified_date: String,
39 pub account_id: String,
40 #[serde(default)]
41 pub default_branch: Option<String>,
42 #[serde(default)]
43 pub tags: HashMap<String, String>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct BranchView {
48 pub branch_name: String,
49 pub commit_id: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct CommitView {
54 pub commit_id: String,
55 pub tree_id: String,
56 pub parent_ids: Vec<String>,
57 pub message: String,
58 pub author_name: String,
59 pub author_email: String,
60 pub date: String,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct FileEntryView {
66 pub file_path: String,
67 pub blob_id: String,
68 pub file_mode: String,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct PullRequestView {
73 pub pull_request_id: String,
74 pub title: String,
75 pub description: String,
76 pub status: String,
77 pub repository_name: String,
78 pub source_reference: String,
79 pub destination_reference: String,
80 pub source_commit: String,
81 pub destination_commit: String,
82 pub creation_date: String,
83 pub last_activity_date: String,
84 pub author_arn: String,
85}
86
87fn parse_datetime(s: &str) -> chrono::DateTime<chrono::Utc> {
88 chrono::DateTime::parse_from_rfc3339(s)
89 .map(|dt| dt.with_timezone(&chrono::Utc))
90 .unwrap_or_else(|_| chrono::Utc::now())
91}
92
93impl From<&CodeCommitState> for CodeCommitStateView {
96 fn from(state: &CodeCommitState) -> Self {
97 let repositories = state
98 .repositories
99 .iter()
100 .map(|(k, r)| {
101 (
102 k.clone(),
103 RepositoryView {
104 repository_id: r.repository_id.clone(),
105 repository_name: r.repository_name.clone(),
106 arn: r.arn.clone(),
107 description: r.description.clone(),
108 clone_url_http: r.clone_url_http.clone(),
109 clone_url_ssh: r.clone_url_ssh.clone(),
110 creation_date: r.creation_date.to_rfc3339(),
111 last_modified_date: r.last_modified_date.to_rfc3339(),
112 account_id: r.account_id.clone(),
113 default_branch: r.default_branch.clone(),
114 tags: r.tags.clone(),
115 },
116 )
117 })
118 .collect();
119
120 let branches = state
121 .branches
122 .iter()
123 .map(|(repo, bmap)| {
124 (
125 repo.clone(),
126 bmap.iter()
127 .map(|(bn, b)| {
128 (
129 bn.clone(),
130 BranchView {
131 branch_name: b.branch_name.clone(),
132 commit_id: b.commit_id.clone(),
133 },
134 )
135 })
136 .collect(),
137 )
138 })
139 .collect();
140
141 let commits = state
142 .commits
143 .iter()
144 .map(|(repo, cmap)| {
145 (
146 repo.clone(),
147 cmap.iter()
148 .map(|(cid, c)| {
149 (
150 cid.clone(),
151 CommitView {
152 commit_id: c.commit_id.clone(),
153 tree_id: c.tree_id.clone(),
154 parent_ids: c.parent_ids.clone(),
155 message: c.message.clone(),
156 author_name: c.author_name.clone(),
157 author_email: c.author_email.clone(),
158 date: c.date.to_rfc3339(),
159 },
160 )
161 })
162 .collect(),
163 )
164 })
165 .collect();
166
167 let files = state
168 .files
169 .iter()
170 .map(|(repo, commit_map)| {
171 (
172 repo.clone(),
173 commit_map
174 .iter()
175 .map(|(cid, fmap)| {
176 (
177 cid.clone(),
178 fmap.iter()
179 .map(|(fp, fe)| {
180 (
181 fp.clone(),
182 FileEntryView {
183 file_path: fe.file_path.clone(),
184 blob_id: fe.blob_id.clone(),
185 file_mode: fe.file_mode.clone(),
186 },
187 )
188 })
189 .collect(),
190 )
191 })
192 .collect(),
193 )
194 })
195 .collect();
196
197 let pull_requests = state
198 .pull_requests
199 .iter()
200 .map(|(id, pr)| {
201 (
202 id.clone(),
203 PullRequestView {
204 pull_request_id: pr.pull_request_id.clone(),
205 title: pr.title.clone(),
206 description: pr.description.clone(),
207 status: pr.status.clone(),
208 repository_name: pr.repository_name.clone(),
209 source_reference: pr.source_reference.clone(),
210 destination_reference: pr.destination_reference.clone(),
211 source_commit: pr.source_commit.clone(),
212 destination_commit: pr.destination_commit.clone(),
213 creation_date: pr.creation_date.to_rfc3339(),
214 last_activity_date: pr.last_activity_date.to_rfc3339(),
215 author_arn: pr.author_arn.clone(),
216 },
217 )
218 })
219 .collect();
220
221 CodeCommitStateView {
222 repositories,
223 branches,
224 commits,
225 files,
226 pull_requests,
227 pull_request_counter: state.pull_request_counter,
228 }
229 }
230}
231
232impl From<CodeCommitStateView> for CodeCommitState {
235 fn from(view: CodeCommitStateView) -> Self {
236 let repositories = view
237 .repositories
238 .into_iter()
239 .map(|(k, r)| {
240 (
241 k,
242 crate::types::Repository {
243 repository_id: r.repository_id,
244 repository_name: r.repository_name,
245 arn: r.arn,
246 description: r.description,
247 clone_url_http: r.clone_url_http,
248 clone_url_ssh: r.clone_url_ssh,
249 creation_date: parse_datetime(&r.creation_date),
250 last_modified_date: parse_datetime(&r.last_modified_date),
251 account_id: r.account_id,
252 default_branch: r.default_branch,
253 tags: r.tags,
254 },
255 )
256 })
257 .collect();
258
259 let branches = view
260 .branches
261 .into_iter()
262 .map(|(repo, bmap)| {
263 (
264 repo,
265 bmap.into_iter()
266 .map(|(bn, b)| {
267 (
268 bn,
269 crate::types::Branch {
270 branch_name: b.branch_name,
271 commit_id: b.commit_id,
272 },
273 )
274 })
275 .collect(),
276 )
277 })
278 .collect();
279
280 let commits = view
281 .commits
282 .into_iter()
283 .map(|(repo, cmap)| {
284 (
285 repo,
286 cmap.into_iter()
287 .map(|(cid, c)| {
288 (
289 cid,
290 crate::types::CommitRecord {
291 commit_id: c.commit_id,
292 tree_id: c.tree_id,
293 parent_ids: c.parent_ids,
294 message: c.message,
295 author_name: c.author_name,
296 author_email: c.author_email,
297 date: parse_datetime(&c.date),
298 },
299 )
300 })
301 .collect(),
302 )
303 })
304 .collect();
305
306 let files = view
307 .files
308 .into_iter()
309 .map(|(repo, commit_map)| {
310 (
311 repo,
312 commit_map
313 .into_iter()
314 .map(|(cid, fmap)| {
315 (
316 cid,
317 fmap.into_iter()
318 .map(|(fp, fe)| {
319 (
320 fp,
321 crate::types::FileEntry {
322 file_path: fe.file_path,
323 blob_id: fe.blob_id,
324 file_mode: fe.file_mode,
325 },
326 )
327 })
328 .collect(),
329 )
330 })
331 .collect(),
332 )
333 })
334 .collect();
335
336 let pull_requests = view
337 .pull_requests
338 .into_iter()
339 .map(|(id, pr)| {
340 (
341 id,
342 crate::types::PullRequestRecord {
343 pull_request_id: pr.pull_request_id,
344 title: pr.title,
345 description: pr.description,
346 status: pr.status,
347 repository_name: pr.repository_name,
348 source_reference: pr.source_reference,
349 destination_reference: pr.destination_reference,
350 source_commit: pr.source_commit,
351 destination_commit: pr.destination_commit,
352 creation_date: parse_datetime(&pr.creation_date),
353 last_activity_date: parse_datetime(&pr.last_activity_date),
354 author_arn: pr.author_arn,
355 },
356 )
357 })
358 .collect();
359
360 CodeCommitState {
361 repositories,
362 branches,
363 commits,
364 files,
365 pull_requests,
366 pull_request_counter: view.pull_request_counter,
367 }
368 }
369}
370
371impl StatefulService for CodeCommitService {
374 type StateView = CodeCommitStateView;
375
376 async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
377 let state = self.state.get(account_id, region);
378 let guard = state.read().await;
379 CodeCommitStateView::from(&*guard)
380 }
381
382 async fn restore(
383 &self,
384 account_id: &str,
385 region: &str,
386 view: Self::StateView,
387 ) -> Result<(), StateViewError> {
388 let state = self.state.get(account_id, region);
389 {
390 let mut guard = state.write().await;
391 *guard = CodeCommitState::from(view);
392 }
393 self.notify_state_changed(account_id, region).await;
394 Ok(())
395 }
396
397 async fn merge(
398 &self,
399 account_id: &str,
400 region: &str,
401 view: Self::StateView,
402 ) -> Result<(), StateViewError> {
403 let state = self.state.get(account_id, region);
404 {
405 let mut guard = state.write().await;
406 let merged = CodeCommitState::from(view);
407 for (k, v) in merged.repositories {
408 guard.repositories.insert(k, v);
409 }
410 for (k, v) in merged.branches {
411 guard.branches.insert(k, v);
412 }
413 for (k, v) in merged.commits {
414 guard.commits.insert(k, v);
415 }
416 for (k, v) in merged.files {
417 guard.files.insert(k, v);
418 }
419 for (k, v) in merged.pull_requests {
420 guard.pull_requests.insert(k, v);
421 }
422 }
423 self.notify_state_changed(account_id, region).await;
424 Ok(())
425 }
426
427 fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
428 &self.notifier
429 }
430}