| 1 | // Copyright (c) 2019-2026 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module s3 |
| 5 | |
| 6 | import net.http |
| 7 | |
| 8 | // ACL is the canned Access Control List applied to a stored object or bucket. |
| 9 | // Values match the S3 wire protocol. |
| 10 | pub enum Acl { |
| 11 | unset |
| 12 | private |
| 13 | public_read |
| 14 | public_read_write |
| 15 | aws_exec_read |
| 16 | authenticated_read |
| 17 | bucket_owner_read |
| 18 | bucket_owner_full_control |
| 19 | log_delivery_write |
| 20 | } |
| 21 | |
| 22 | // to_header_value renders the ACL as the canonical S3 string used in headers |
| 23 | // and presigned query parameters. `.unset` returns an empty string so callers |
| 24 | // can skip the header. |
| 25 | pub fn (a Acl) to_header_value() string { |
| 26 | return match a { |
| 27 | .unset { '' } |
| 28 | .private { 'private' } |
| 29 | .public_read { 'public-read' } |
| 30 | .public_read_write { 'public-read-write' } |
| 31 | .aws_exec_read { 'aws-exec-read' } |
| 32 | .authenticated_read { 'authenticated-read' } |
| 33 | .bucket_owner_read { 'bucket-owner-read' } |
| 34 | .bucket_owner_full_control { 'bucket-owner-full-control' } |
| 35 | .log_delivery_write { 'log-delivery-write' } |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // parse_acl returns the matching Acl from a wire string. |
| 40 | // Returns `.unset` when the input is empty or unknown so the caller can ignore it. |
| 41 | pub fn parse_acl(s string) Acl { |
| 42 | return match s { |
| 43 | 'private' { Acl.private } |
| 44 | 'public-read' { Acl.public_read } |
| 45 | 'public-read-write' { Acl.public_read_write } |
| 46 | 'aws-exec-read' { Acl.aws_exec_read } |
| 47 | 'authenticated-read' { Acl.authenticated_read } |
| 48 | 'bucket-owner-read' { Acl.bucket_owner_read } |
| 49 | 'bucket-owner-full-control' { Acl.bucket_owner_full_control } |
| 50 | 'log-delivery-write' { Acl.log_delivery_write } |
| 51 | else { Acl.unset } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // StorageClass enumerates the standard S3 storage tiers. `.unset` means the |
| 56 | // server default (typically STANDARD) and produces no `x-amz-storage-class` |
| 57 | // header. Providers vary on which tiers they actually honour. |
| 58 | pub enum StorageClass { |
| 59 | unset |
| 60 | standard |
| 61 | deep_archive |
| 62 | express_onezone |
| 63 | glacier |
| 64 | glacier_ir |
| 65 | intelligent_tiering |
| 66 | onezone_ia |
| 67 | outposts |
| 68 | reduced_redundancy |
| 69 | snow |
| 70 | standard_ia |
| 71 | } |
| 72 | |
| 73 | // to_header_value renders the storage class as the on-wire string. |
| 74 | pub fn (sc StorageClass) to_header_value() string { |
| 75 | return match sc { |
| 76 | .unset { '' } |
| 77 | .standard { 'STANDARD' } |
| 78 | .deep_archive { 'DEEP_ARCHIVE' } |
| 79 | .express_onezone { 'EXPRESS_ONEZONE' } |
| 80 | .glacier { 'GLACIER' } |
| 81 | .glacier_ir { 'GLACIER_IR' } |
| 82 | .intelligent_tiering { 'INTELLIGENT_TIERING' } |
| 83 | .onezone_ia { 'ONEZONE_IA' } |
| 84 | .outposts { 'OUTPOSTS' } |
| 85 | .reduced_redundancy { 'REDUCED_REDUNDANCY' } |
| 86 | .snow { 'SNOW' } |
| 87 | .standard_ia { 'STANDARD_IA' } |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | // Stat is the result of HEAD-ing an object. |
| 92 | pub struct Stat { |
| 93 | pub: |
| 94 | size i64 // Content-Length in bytes |
| 95 | last_modified string // RFC 1123 date as returned by S3 |
| 96 | etag string // unquoted ETag (server returns it wrapped in quotes; we strip them) |
| 97 | content_type string // MIME type |
| 98 | } |
| 99 | |
| 100 | // Owner identifies an S3 object owner — only populated when `fetch_owner` is true. |
| 101 | pub struct Owner { |
| 102 | pub: |
| 103 | id string |
| 104 | display_name string |
| 105 | } |
| 106 | |
| 107 | // ObjectInfo is one entry from a ListObjectsV2 response. |
| 108 | pub struct ObjectInfo { |
| 109 | pub: |
| 110 | key string |
| 111 | last_modified string |
| 112 | etag string |
| 113 | size i64 |
| 114 | storage_class string |
| 115 | owner ?Owner |
| 116 | } |
| 117 | |
| 118 | // CommonPrefix represents a "directory-like" prefix in a list result when a |
| 119 | // delimiter is provided. |
| 120 | pub struct CommonPrefix { |
| 121 | pub: |
| 122 | prefix string |
| 123 | } |
| 124 | |
| 125 | // ListResult aggregates a ListObjectsV2 response. |
| 126 | // NextContinuationToken should be passed back as `continuation_token` to fetch the next page. |
| 127 | pub struct ListResult { |
| 128 | pub: |
| 129 | name string |
| 130 | prefix string |
| 131 | delimiter string |
| 132 | start_after string |
| 133 | max_keys int |
| 134 | key_count int |
| 135 | is_truncated bool |
| 136 | continuation_token string |
| 137 | next_continuation_token string |
| 138 | objects []ObjectInfo |
| 139 | common_prefixes []CommonPrefix |
| 140 | } |
| 141 | |
| 142 | // ListOptions configures a ListObjectsV2 call. `bucket` overrides the client's |
| 143 | // default; leave empty to use the client's bound bucket. |
| 144 | @[params] |
| 145 | pub struct ListOptions { |
| 146 | pub: |
| 147 | bucket string |
| 148 | prefix string |
| 149 | continuation_token string |
| 150 | delimiter string |
| 151 | max_keys int // 0 means default (server picks 1000) |
| 152 | start_after string |
| 153 | encoding_type string // 'url' or empty |
| 154 | fetch_owner bool |
| 155 | } |
| 156 | |
| 157 | // PresignOptions controls presigned URL generation. |
| 158 | @[params] |
| 159 | pub struct PresignOptions { |
| 160 | pub: |
| 161 | bucket string // overrides the client's default bucket for this call |
| 162 | method http.Method = .get |
| 163 | expires_in int = 86400 // seconds, 1..604800 |
| 164 | acl Acl |
| 165 | storage_class StorageClass |
| 166 | content_type string |
| 167 | content_disposition string |
| 168 | request_payer bool |
| 169 | } |
| 170 | |