1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! A small, dependable client for S3-compatible object stores (AWS S3,
//! Garage, MinIO, Ceph, …): nine verbs, SigV4 signing, retries and integrity
//! checks, plus the `s3pe` command line.
//!
//! Nine verbs, one client, nothing else to learn (plus `copy_s3`,
//! `delete_many_s3`, `list_dir_s3`, `presign_s3`, `sync_up_s3` and
//! `sync_down_s3` since 0.3.0):
//!
//! ```no_run
//! use s3pe::{read_s3, write_s3, list_s3};
//!
//! // endpoint and credentials come from the environment (see below)
//! let manifest = read_s3("s3://my-bucket/data/manifest.json")?;
//! write_s3("s3://my-bucket/reports/figure1.png", std::fs::read("figure1.png")?, None)?;
//! for object in list_s3("s3://my-bucket/data/")? {
//! println!("{} {}", object.size, object.key);
//! }
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! Or with an explicit [`Client`]:
//!
//! ```no_run
//! let s3 = s3pe::Client::builder()
//! .endpoint("https://s3.example.org")
//! .credentials("AKIA…", "…")
//! .bucket("my-bucket")
//! .build()?;
//! let bytes = s3.read("data/manifest.json")?;
//! s3.upload("sales.parquet", "etl/sales.parquet", None)?;
//! # Ok::<(), s3pe::Error>(())
//! ```
//!
//! # What happens in every call
//!
//! 
//!
//! # Configuration
//!
//! Anything not passed to the builder comes from the environment:
//!
//! | Setting | Variables | Default |
//! |---|---|---|
//! | endpoint | `S3PE_ENDPOINT`, `AWS_ENDPOINT_URL` | required |
//! | region | `AWS_REGION`, `AWS_DEFAULT_REGION` | `us-east-1` |
//! | credentials | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` | required |
//! | bucket | `S3PE_BUCKET` | none: use `s3://bucket/key` |
//!
//! URLs are always path-style (`https://host/bucket/key`).
//!
//! # What "dependable" means here
//!
//! - Requests are signed with AWS Signature V4 over the real SHA-256 of the
//! payload (no `aws-chunked`, which some stores, Garage among them, reject).
//! - Connection failures and HTTP 429/500/502/503/504 are retried twice,
//! with a 1 s then 2 s pause. Other errors are reported at once, typed
//! ([`Error::NotFound`], [`Error::AccessDenied`], …).
//! - After an upload the server's ETag is compared with the local MD5;
//! after a download the byte count is compared with `Content-Length`
//! and, when the ETag is an MD5, with the MD5 of what arrived.
//! - Downloads to a file are written to a `.s3pe-part` file and renamed
//! when complete.
//! - Bodies above 64 MiB go up as a multipart upload, 16 MiB parts, four
//! at a time, aborted on failure; the final ETag is checked too.
//! - One line per transfer is logged through the [`log`] facade (target
//! `s3pe`): `↑ s3://bucket/key (12.3 MB, 0.8 s)`.
//!
//! # Command line
//!
//! `cargo install s3pe` also installs the `s3pe` command:
//!
//! ```text
//! s3pe ls s3://my-bucket/data/
//! s3pe get s3://my-bucket/data/manifest.json
//! s3pe put sales.parquet s3://my-bucket/etl/
//! s3pe cat s3://my-bucket/data/manifest.json | jq .version
//! s3pe rm s3://my-bucket/etl/old.parquet s3://my-bucket/etl/older.parquet
//! s3pe cp s3://my-bucket/etl/sales.parquet s3://my-bucket/archive/sales-2026.parquet
//! s3pe presign s3://my-bucket/reports/report.pdf --expires 3600
//! s3pe sync ./out s3://my-bucket/reports/ --last manifest.json
//! ```
//!
//! The same package exists for Python (`pip install s3pe`), R
//! (`install.packages("s3pe")`) and Go (`go get github.com/StrategicProjects/s3pe`).
pub use ;
pub use ;
use Path;
/// Reads an object into memory, with a client built from the environment.
/// Writes bytes to an object, with a client built from the environment.
/// Downloads an object to a file, with a client built from the environment.
/// Uploads a file, with a client built from the environment.
/// Lists the objects under a prefix, with a client built from the environment.
/// Deletes an object, with a client built from the environment.
/// Whether an object exists, with a client built from the environment.
/// Metadata of an object (`None` if absent), with a client built from the environment.
/// The buckets the credentials can see, with a client built from the environment.
/// Copies an object inside the store, with a client built from the environment.
/// Deletes several objects in batches, with a client built from the environment.
/// One level of a prefix (objects and "folders"), with a client built from the environment.
/// A pre-signed URL, with a client built from the environment.
/// Uploads a directory to a prefix, with a client built from the environment.
/// Downloads a prefix to a directory, with a client built from the environment.