1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
9pub struct ContainerLayerId {
10 pub service: String,
12 pub instance_id: String,
14}
15
16impl ContainerLayerId {
17 pub fn new(service: impl Into<String>, instance_id: impl Into<String>) -> Self {
18 Self {
19 service: service.into(),
20 instance_id: instance_id.into(),
21 }
22 }
23
24 #[must_use]
25 pub fn to_key(&self) -> String {
26 format!("{}_{}", self.service, self.instance_id)
27 }
28}
29
30impl std::fmt::Display for ContainerLayerId {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{}/{}", self.service, self.instance_id)
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct LayerSnapshot {
39 pub digest: String,
41 pub size_bytes: u64,
43 pub compressed_size_bytes: u64,
45 pub created_at: chrono::DateTime<chrono::Utc>,
47 pub file_count: u64,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct SyncState {
54 pub container_id: ContainerLayerId,
56 pub local_digest: Option<String>,
58 pub remote_digest: Option<String>,
60 pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
62 pub pending_upload: Option<PendingUpload>,
64}
65
66impl SyncState {
67 #[must_use]
68 pub fn new(container_id: ContainerLayerId) -> Self {
69 Self {
70 container_id,
71 local_digest: None,
72 remote_digest: None,
73 last_sync: None,
74 pending_upload: None,
75 }
76 }
77
78 #[must_use]
79 pub fn needs_upload(&self) -> bool {
80 match (&self.local_digest, &self.remote_digest) {
81 (Some(local), Some(remote)) => local != remote,
82 (Some(_), None) => true,
83 _ => false,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct PendingUpload {
91 pub upload_id: String,
93 pub object_key: String,
95 pub total_parts: u32,
97 pub completed_parts: HashMap<u32, CompletedPart>,
99 pub part_size: u64,
101 pub local_tarball_path: PathBuf,
103 pub started_at: chrono::DateTime<chrono::Utc>,
105 pub digest: String,
107}
108
109impl PendingUpload {
110 #[must_use]
111 pub fn missing_parts(&self) -> Vec<u32> {
112 (1..=self.total_parts)
113 .filter(|p| !self.completed_parts.contains_key(p))
114 .collect()
115 }
116
117 #[must_use]
118 pub fn is_complete(&self) -> bool {
119 self.completed_parts.len() == self.total_parts as usize
120 }
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct CompletedPart {
125 pub part_number: u32,
126 pub e_tag: String,
127}