1mod aws_sig_v4;
2pub mod bucket;
4pub mod object;
5pub extern crate zjhttpc;
6
7use std::collections::HashMap;
8use std::fs;
9use std::path::PathBuf;
10
11use anyhow_ext::Context;
12use anyhow_ext::Result;
13use anyhow_ext::anyhow;
14use derive_builder::Builder;
15use serde::Deserialize;
16use serde::Serialize;
17use std::path::Path;
18use zjhttpc::client::ZJHttpClient;
19use zjhttpc::requestx::Request;
20use zjhttpc::response::Response;
21use zjhttpc::url::Url;
22
23#[derive(Default, Debug)]
24pub struct S3Client {
25 pub endpoint: String,
26 pub bucket: String,
27 pub access_key: String,
28 pub secret_key: String,
29 pub httpc: ZJHttpClient,
30}
31
32#[derive(Serialize, Deserialize, Debug)]
33pub struct S3Config {
34 pub endpoint: String,
35 pub bucket: String,
36 pub access_key: String,
37 pub secret_key: String,
38 pub trust_cert_path: Option<String>,
39}
40
41impl S3Client {
42 pub fn new(
43 endpoint: String,
44 bucket: String,
45 access_key: String,
46 secret_key: String,
47 trust_cert_path: Option<String>,
48 ) -> Self {
49 let mut httpc = ZJHttpClient::new();
59 if let Some(cert_path) = trust_cert_path {
60 httpc.global_trust_store_pem =
61 Some(zjhttpc::misc::TrustStorePem::Path(PathBuf::from(cert_path)));
62 }
63 return S3Client {
64 endpoint,
65 bucket,
66 access_key,
67 secret_key,
68 httpc,
70 };
71 }
72 pub fn from_toml_config<P>(path: P) -> Result<Self>
73 where
74 P: AsRef<Path>,
75 {
76 let txt = fs::read_to_string(path)?;
77 let c: S3Config = toml::from_str(&txt)?;
78 return Ok(Self::new(
79 c.endpoint,
80 c.bucket,
81 c.access_key,
82 c.secret_key,
83 c.trust_cert_path,
84 ));
85 }
86 pub async fn send(
87 &self,
88 path: Option<&str>,
89 method: &'static str,
90 queries: Option<&impl Serialize>,
91 headers: Option<HashMap<String, String>>,
92 body: Option<S3Body>,
93 ) -> Result<Response> {
94 let mut url = format!("{}/{}", self.endpoint, self.bucket);
95 if let Some(p) = path {
96 url.push_str("/");
97 url.push_str(p);
98 }
99 let url = Url::parse(&url)?;
100 let mut req = Request::new(method, url).dot()?;
101 if let Some(headers) = headers {
102 req = req.set_headers_nondup(headers);
103 }
104 if let Some(queries) = queries {
105 req = req.set_queries_serde(queries).dot()?;
106 }
107 req = crate::aws_sig_v4::auth(&self.access_key, &self.secret_key, req, None, body).await.dot()?;
108 let resp = self
109 .httpc
110 .send(&mut req)
111 .await
112 .map_err(|err| anyhow!(err.to_string()))?;
113 Ok(resp)
114 }
115
116 }
153
154pub enum S3Body {
155 Bytes(Vec<u8>),
156 Path(PathBuf)
157}
158
159#[derive(Deserialize, Debug)]
160#[serde(rename_all = "PascalCase")]
161pub struct S3Error {
162 code: String,
163 message: String,
164 resource: Option<String>,
165 request_id: Option<String>,
166}
167
168#[cfg(test)]
169mod test {
170 use crate::{
171 bucket::{ListBucketParams, ListBucketParamsBuilder}, S3Client
172 };
173 use anyhow_ext::Result;
174 use async_std::task;
175 use tracing::info;
176
177 #[test]
178 fn test_s3_client() {
179 }
182}