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
//! A small, dependable S3 client, made for `s3.pe.gov.br` (the Garage
//! object store of the State of Pernambuco, Brazil) and usable with any
//! S3-style store.
//!
//! Nine verbs, one client, nothing else to learn:
//!
//! ```no_run
//! use s3pe::{read_s3, write_s3, list_s3};
//!
//! // credentials and endpoint come from the environment (see below)
//! let manifest = read_s3("s3://iaempe/dados/metadata.json")?;
//! write_s3("s3://iaempe/nota/figura1.png", std::fs::read("figura1.png")?, None)?;
//! for object in list_s3("s3://iaempe/dados/")? {
//! 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.pe.gov.br")
//! .credentials("GK…", "…")
//! .bucket("iaempe")
//! .build()?;
//! let bytes = s3.read("dados/metadata.json")?;
//! s3.upload("painel.parquet", "etl/painel.parquet", None)?;
//! # Ok::<(), s3pe::Error>(())
//! ```
//!
//! # Configuration
//!
//! Anything not passed to the builder comes from the environment:
//!
//! | Setting | Variables | Default |
//! |---|---|---|
//! | endpoint | `S3PE_ENDPOINT`, `AWS_ENDPOINT_URL` | `https://s3.pe.gov.br` |
//! | 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 Garage rejects).
//! - 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.
//! - 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://iaempe/dados/
//! s3pe get s3://iaempe/dados/metadata.json
//! s3pe put painel.parquet s3://iaempe/etl/
//! s3pe cat s3://iaempe/dados/metadata.json | jq .ultimo_mes
//! s3pe rm s3://iaempe/etl/old.parquet
//! ```
//!
//! 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.