Skip to main content

fakecloud_s3/
state.rs

1use bytes::Bytes;
2use chrono::{DateTime, Utc};
3use parking_lot::RwLock;
4use std::collections::{BTreeMap, HashMap};
5use std::sync::Arc;
6
7/// An ACL grant entry.
8#[derive(Debug, Clone)]
9pub struct AclGrant {
10    pub grantee_type: String, // "CanonicalUser" or "Group"
11    pub grantee_id: Option<String>,
12    pub grantee_display_name: Option<String>,
13    pub grantee_uri: Option<String>,
14    pub permission: String, // READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL
15}
16
17#[derive(Debug, Clone)]
18pub struct S3Object {
19    pub key: String,
20    pub data: Bytes,
21    pub content_type: String,
22    pub etag: String,
23    pub size: u64,
24    pub last_modified: DateTime<Utc>,
25    pub metadata: HashMap<String, String>,
26    pub storage_class: String,
27    pub tags: HashMap<String, String>,
28    pub acl_grants: Vec<AclGrant>,
29    pub acl_owner_id: Option<String>,
30    /// If created from multipart upload, the number of parts.
31    pub parts_count: Option<u32>,
32    /// Per-part sizes for multipart objects (part_number, size).
33    pub part_sizes: Option<Vec<(u32, u64)>>,
34    /// Server-side encryption algorithm.
35    pub sse_algorithm: Option<String>,
36    /// KMS key ID for SSE-KMS.
37    pub sse_kms_key_id: Option<String>,
38    /// Whether bucket key is enabled.
39    pub bucket_key_enabled: Option<bool>,
40    pub version_id: Option<String>,
41    pub is_delete_marker: bool,
42    pub content_encoding: Option<String>,
43    pub website_redirect_location: Option<String>,
44    /// Glacier restore: ongoing request status.
45    pub restore_ongoing: Option<bool>,
46    /// Glacier restore: expiry date string.
47    pub restore_expiry: Option<String>,
48    /// Checksum algorithm (CRC32, SHA1, SHA256).
49    pub checksum_algorithm: Option<String>,
50    /// Base64-encoded checksum value.
51    pub checksum_value: Option<String>,
52    /// Object lock mode (GOVERNANCE or COMPLIANCE).
53    pub lock_mode: Option<String>,
54    /// Object lock retain-until date (ISO 8601).
55    pub lock_retain_until: Option<DateTime<Utc>>,
56    /// Legal hold status (ON or OFF).
57    pub lock_legal_hold: Option<String>,
58}
59
60/// A part uploaded via the multipart upload API.
61#[derive(Debug, Clone)]
62pub struct UploadPart {
63    pub part_number: u32,
64    pub data: Bytes,
65    pub etag: String,
66    pub size: u64,
67    pub last_modified: DateTime<Utc>,
68}
69
70/// An in-progress multipart upload.
71#[derive(Debug, Clone)]
72pub struct MultipartUpload {
73    pub upload_id: String,
74    pub key: String,
75    pub initiated: DateTime<Utc>,
76    /// Parts keyed by part number.
77    pub parts: BTreeMap<u32, UploadPart>,
78    /// Metadata provided at CreateMultipartUpload time.
79    pub metadata: HashMap<String, String>,
80    pub content_type: String,
81    pub storage_class: String,
82    pub sse_algorithm: Option<String>,
83    pub sse_kms_key_id: Option<String>,
84    pub tagging: Option<String>,
85    pub acl_grants: Vec<AclGrant>,
86    pub checksum_algorithm: Option<String>,
87}
88
89#[derive(Debug, Clone)]
90pub struct S3Bucket {
91    pub name: String,
92    pub creation_date: DateTime<Utc>,
93    pub region: String,
94    /// Objects keyed by their full key path.
95    pub objects: BTreeMap<String, S3Object>,
96    pub tags: HashMap<String, String>,
97    pub acl_grants: Vec<AclGrant>,
98    pub acl_owner_id: String,
99    /// In-progress multipart uploads keyed by upload ID.
100    pub multipart_uploads: HashMap<String, MultipartUpload>,
101    /// Versioning status: None = never enabled, Some("Enabled"), Some("Suspended").
102    pub versioning: Option<String>,
103    /// Object versions keyed by key, each value is a list of versions.
104    pub object_versions: HashMap<String, Vec<S3Object>>,
105    /// Bucket ACL (canned or XML).
106    pub acl: Option<String>,
107    pub encryption_config: Option<String>,
108    pub lifecycle_config: Option<String>,
109    pub policy: Option<String>,
110    pub cors_config: Option<String>,
111    pub notification_config: Option<String>,
112    pub logging_config: Option<String>,
113    pub website_config: Option<String>,
114    pub accelerate_status: Option<String>,
115    pub public_access_block: Option<String>,
116    pub object_lock_config: Option<String>,
117    pub replication_config: Option<String>,
118    pub ownership_controls: Option<String>,
119    pub inventory_configs: HashMap<String, String>,
120    /// Whether EventBridge notifications are enabled for this bucket.
121    pub eventbridge_enabled: bool,
122}
123
124impl S3Bucket {
125    pub fn new(name: &str, region: &str, owner_id: &str) -> Self {
126        Self {
127            name: name.to_string(),
128            creation_date: Utc::now(),
129            region: region.to_string(),
130            objects: BTreeMap::new(),
131            tags: HashMap::new(),
132            acl_grants: vec![AclGrant {
133                grantee_type: "CanonicalUser".to_string(),
134                grantee_id: Some(owner_id.to_string()),
135                grantee_display_name: Some(owner_id.to_string()),
136                grantee_uri: None,
137                permission: "FULL_CONTROL".to_string(),
138            }],
139            acl_owner_id: owner_id.to_string(),
140            multipart_uploads: HashMap::new(),
141            versioning: None,
142            object_versions: HashMap::new(),
143            acl: None,
144            encryption_config: None,
145            lifecycle_config: None,
146            policy: None,
147            cors_config: None,
148            notification_config: None,
149            logging_config: None,
150            website_config: None,
151            accelerate_status: None,
152            public_access_block: None,
153            object_lock_config: None,
154            replication_config: None,
155            ownership_controls: None,
156            inventory_configs: HashMap::new(),
157            eventbridge_enabled: false,
158        }
159    }
160}
161
162/// A recorded S3 notification event for introspection.
163#[derive(Debug, Clone)]
164pub struct S3NotificationEvent {
165    pub bucket: String,
166    pub key: String,
167    pub event_type: String,
168    pub timestamp: DateTime<Utc>,
169}
170
171pub struct S3State {
172    pub account_id: String,
173    pub region: String,
174    pub buckets: HashMap<String, S3Bucket>,
175    pub notification_events: Vec<S3NotificationEvent>,
176}
177
178impl S3State {
179    pub fn new(account_id: &str, region: &str) -> Self {
180        Self {
181            account_id: account_id.to_string(),
182            region: region.to_string(),
183            buckets: HashMap::new(),
184            notification_events: Vec::new(),
185        }
186    }
187
188    pub fn reset(&mut self) {
189        self.buckets.clear();
190        self.notification_events.clear();
191    }
192}
193
194pub type SharedS3State = Arc<RwLock<S3State>>;