toolcraft_s3_kit/
config.rs1use std::{env, sync::Arc};
2
3use crate::{
4 bucket_client::BucketClient,
5 client::S3Client,
6 error::{Error, Result},
7};
8
9#[derive(Debug, Clone)]
18pub struct S3BucketConfig {
19 pub endpoint: String,
20 pub access_key: String,
21 pub secret_key: String,
22 pub bucket: String,
23 pub region: Option<String>,
24}
25
26impl S3BucketConfig {
27 pub fn new(
28 endpoint: impl Into<String>,
29 access_key: impl Into<String>,
30 secret_key: impl Into<String>,
31 bucket: impl Into<String>,
32 region: Option<String>,
33 ) -> Self {
34 Self {
35 endpoint: endpoint.into(),
36 access_key: access_key.into(),
37 secret_key: secret_key.into(),
38 bucket: bucket.into(),
39 region,
40 }
41 }
42
43 pub fn from_env() -> Result<Self> {
45 let endpoint = read_required(&["TOOLCRAFT_S3_ENDPOINT", "S3_ENDPOINT"])?;
46 let access_key = read_required(&["TOOLCRAFT_S3_ACCESS_KEY", "S3_ACCESS_KEY"])?;
47 let secret_key = read_required(&["TOOLCRAFT_S3_SECRET_KEY", "S3_SECRET_KEY"])?;
48 let bucket = read_required(&["TOOLCRAFT_S3_BUCKET", "S3_BUCKET"])?;
49 let region = read_optional(&["TOOLCRAFT_S3_REGION", "S3_REGION"]);
50
51 Ok(Self {
52 endpoint,
53 access_key,
54 secret_key,
55 bucket,
56 region,
57 })
58 }
59
60 pub fn build_s3_client(&self) -> Result<S3Client> {
61 S3Client::new(
62 &self.endpoint,
63 &self.access_key,
64 &self.secret_key,
65 self.region.as_deref(),
66 )
67 }
68
69 pub fn build_bucket_client(&self) -> Result<BucketClient> {
70 let s3_client = Arc::new(self.build_s3_client()?);
71 Ok(BucketClient::new(s3_client, self.bucket.clone()))
72 }
73}
74
75fn read_required(names: &[&str]) -> Result<String> {
76 for name in names {
77 if let Ok(value) = env::var(name) {
78 let trimmed = value.trim();
79 if !trimmed.is_empty() {
80 return Ok(trimmed.to_string());
81 }
82 }
83 }
84
85 let joined = names.join(" / ");
86 Err(Error::Message(
87 format!("missing required environment variable: {joined}").into(),
88 ))
89}
90
91fn read_optional(names: &[&str]) -> Option<String> {
92 for name in names {
93 if let Ok(value) = env::var(name) {
94 let trimmed = value.trim();
95 if !trimmed.is_empty() {
96 return Some(trimmed.to_string());
97 }
98 }
99 }
100 None
101}