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 pub fn to_key(&self) -> String {
25 format!("{}_{}", self.service, self.instance_id)
26 }
27}
28
29impl std::fmt::Display for ContainerLayerId {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "{}/{}", self.service, self.instance_id)
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct LayerSnapshot {
38 pub digest: String,
40 pub size_bytes: u64,
42 pub compressed_size_bytes: u64,
44 pub created_at: chrono::DateTime<chrono::Utc>,
46 pub file_count: u64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct SyncState {
53 pub container_id: ContainerLayerId,
55 pub local_digest: Option<String>,
57 pub remote_digest: Option<String>,
59 pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
61 pub pending_upload: Option<PendingUpload>,
63}
64
65impl SyncState {
66 pub fn new(container_id: ContainerLayerId) -> Self {
67 Self {
68 container_id,
69 local_digest: None,
70 remote_digest: None,
71 last_sync: None,
72 pending_upload: None,
73 }
74 }
75
76 pub fn needs_upload(&self) -> bool {
77 match (&self.local_digest, &self.remote_digest) {
78 (Some(local), Some(remote)) => local != remote,
79 (Some(_), None) => true,
80 _ => false,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct PendingUpload {
88 pub upload_id: String,
90 pub object_key: String,
92 pub total_parts: u32,
94 pub completed_parts: HashMap<u32, CompletedPart>,
96 pub part_size: u64,
98 pub local_tarball_path: PathBuf,
100 pub started_at: chrono::DateTime<chrono::Utc>,
102 pub digest: String,
104}
105
106impl PendingUpload {
107 pub fn missing_parts(&self) -> Vec<u32> {
108 (1..=self.total_parts)
109 .filter(|p| !self.completed_parts.contains_key(p))
110 .collect()
111 }
112
113 pub fn is_complete(&self) -> bool {
114 self.completed_parts.len() == self.total_parts as usize
115 }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct CompletedPart {
120 pub part_number: u32,
121 pub e_tag: String,
122}