rusty_cdk_core/secretsmanager/
builder.rs

1use crate::secretsmanager::{GenerateSecretString, Secret, SecretProperties, SecretRef};
2use crate::shared::Id;
3use crate::stack::{Resource, StackBuilder};
4use crate::type_state;
5use crate::wrappers::StringForSecret;
6use serde_json::Value;
7use std::marker::PhantomData;
8
9type_state!(SecretBuilderState, StartState, SelectedSecretTypeState,);
10
11/// Builder for AWS Secrets Manager secrets.
12///
13/// Supports both static secret strings and automatically generated secrets.
14///
15/// # Example
16///
17/// ```rust
18/// use rusty_cdk_core::stack::StackBuilder;
19/// use rusty_cdk_core::secretsmanager::{SecretBuilder, GenerateSecretStringBuilder};
20/// use rusty_cdk_core::wrappers::*;
21/// use rusty_cdk_macros::string_for_secret;
22///
23/// let mut stack_builder = StackBuilder::new();
24///
25/// // Create a secret with a static value
26/// let secret = SecretBuilder::new("my-secret")
27///     .name(string_for_secret!("my-secret-name"))
28///     .description("My database password")
29///     .secret_string("my-password")
30///     .build(&mut stack_builder);
31///
32/// // Create a secret with auto-generated value
33/// let generated_secret_config = GenerateSecretStringBuilder::new()
34///     .password_length(32)
35///     .exclude_punctuation(true)
36///     .build();
37///
38/// let generated_secret = SecretBuilder::new("generated-secret")
39///     .generate_secret_string(generated_secret_config)
40///     .build(&mut stack_builder);
41/// ```
42pub struct SecretBuilder<T: SecretBuilderState> {
43    phantom_data: PhantomData<T>,
44    id: Id,
45    name: Option<String>,
46    description: Option<String>,
47    generate_secret_string: Option<GenerateSecretString>,
48    secret_string: Option<String>,
49}
50
51impl SecretBuilder<StartState> {
52    /// Creates a new Secrets Manager secret builder.
53    ///
54    /// # Arguments
55    /// * `id` - Unique identifier for the secret
56    pub fn new(id: &str) -> Self {
57        SecretBuilder {
58            phantom_data: Default::default(),
59            id: Id(id.to_string()),
60            name: None,
61            description: None,
62            generate_secret_string: None,
63            secret_string: None,
64        }
65    }
66
67    pub fn name(self, name: StringForSecret) -> Self {
68        Self {
69            name: Some(name.0),
70            ..self
71        }
72    }
73
74    pub fn description<T: Into<String>>(self, description: T) -> Self {
75        Self {
76            description: Some(description.into()),
77            ..self
78        }
79    }
80
81    pub fn secret_string<T: Into<String>>(self, value: T) -> SecretBuilder<SelectedSecretTypeState> {
82        SecretBuilder {
83            phantom_data: Default::default(),
84            id: self.id,
85            name: self.name,
86            description: self.description,
87            secret_string: Some(value.into()),
88            generate_secret_string: None,
89        }
90    }
91
92    pub fn generate_secret_string(self, value: GenerateSecretString) -> SecretBuilder<SelectedSecretTypeState> {
93        SecretBuilder {
94            phantom_data: Default::default(),
95            id: self.id,
96            name: self.name,
97            description: self.description,
98            generate_secret_string: Some(value),
99            secret_string: None,
100        }
101    }
102}
103
104impl SecretBuilder<SelectedSecretTypeState> {
105    pub fn build(self, stack_builder: &mut StackBuilder) -> SecretRef {
106        let resource_id = Resource::generate_id("SecretsManagerSecret");
107
108        stack_builder.add_resource(Secret {
109            id: self.id,
110            resource_id: resource_id.to_string(),
111            r#type: "AWS::SecretsManager::Secret".to_string(),
112            properties: SecretProperties {
113                name: self.name,
114                description: self.description,
115                generate_secret_string: self.generate_secret_string,
116                secret_string: self.secret_string,
117            },
118        });
119
120        SecretRef::new(resource_id)
121    }
122}
123
124type_state!(
125    GenerateSecretStringBuilderState,
126    GenerateStringStartState,
127    GenerateStringKeyState,
128    SecretStringTemplateState,
129);
130
131/// Builder for generated secret string configuration.
132///
133/// Configures how AWS Secrets Manager should automatically generate a secret value.
134pub struct GenerateSecretStringBuilder<T: GenerateSecretStringBuilderState> {
135    phantom_data: PhantomData<T>,
136    generate_string_key: Option<String>,
137    secret_string_template: Option<String>,
138    exclude_characters: Option<Vec<char>>,
139    exclude_lowercase: Option<bool>,
140    exclude_numbers: Option<bool>,
141    exclude_punctuation: Option<bool>,
142    exclude_uppercase: Option<bool>,
143    include_space: Option<bool>,
144    password_length: Option<u32>,
145    require_each_included_type: Option<bool>,
146}
147
148impl<T: GenerateSecretStringBuilderState> GenerateSecretStringBuilder<T> {
149    fn build_internal(self) -> GenerateSecretString {
150        GenerateSecretString {
151            exclude_characters: self.exclude_characters.map(|v| v.into_iter().collect()),
152            exclude_lowercase: self.exclude_lowercase,
153            exclude_numbers: self.exclude_numbers,
154            exclude_punctuation: self.exclude_punctuation,
155            exclude_uppercase: self.exclude_uppercase,
156            include_space: self.include_space,
157            password_length: self.password_length,
158            require_each_included_type: self.require_each_included_type,
159            generate_string_key: self.generate_string_key,
160            secret_string_template: self.secret_string_template,
161        }
162    }
163}
164
165impl Default for GenerateSecretStringBuilder<GenerateStringStartState> {
166    fn default() -> Self {
167        Self::new()
168    }
169}
170
171impl GenerateSecretStringBuilder<GenerateStringStartState> {
172    pub fn new() -> Self {
173        Self {
174            phantom_data: Default::default(),
175            exclude_characters: None,
176            exclude_lowercase: None,
177            exclude_numbers: None,
178            exclude_punctuation: None,
179            exclude_uppercase: None,
180            generate_string_key: None,
181            include_space: None,
182            password_length: None,
183            require_each_included_type: None,
184            secret_string_template: None,
185        }
186    }
187
188    pub fn exclude_characters(self, exclude_characters: Vec<char>) -> Self {
189        Self {
190            exclude_characters: Some(exclude_characters),
191            ..self
192        }
193    }
194    pub fn exclude_lowercase(self, exclude_lowercase: bool) -> Self {
195        Self {
196            exclude_lowercase: Some(exclude_lowercase),
197            ..self
198        }
199    }
200
201    pub fn exclude_numbers(self, exclude_numbers: bool) -> Self {
202        Self {
203            exclude_numbers: Some(exclude_numbers),
204            ..self
205        }
206    }
207
208    pub fn exclude_punctuation(self, exclude_punctuation: bool) -> Self {
209        Self {
210            exclude_punctuation: Some(exclude_punctuation),
211            ..self
212        }
213    }
214
215    pub fn exclude_uppercase(self, exclude_uppercase: bool) -> Self {
216        Self {
217            exclude_uppercase: Some(exclude_uppercase),
218            ..self
219        }
220    }
221
222    pub fn include_space(self, include_space: bool) -> Self {
223        Self {
224            include_space: Some(include_space),
225            ..self
226        }
227    }
228
229    pub fn password_length(self, password_length: u32) -> Self {
230        Self {
231            password_length: Some(password_length),
232            ..self
233        }
234    }
235
236    pub fn require_each_included_type(self, require_each_included_type: bool) -> Self {
237        Self {
238            require_each_included_type: Some(require_each_included_type),
239            ..self
240        }
241    }
242
243    pub fn generate_string_key<T: Into<String>>(self, generate_string_key: T) -> GenerateSecretStringBuilder<GenerateStringKeyState> {
244        GenerateSecretStringBuilder {
245            phantom_data: Default::default(),
246            generate_string_key: Some(generate_string_key.into()),
247            exclude_characters: self.exclude_characters,
248            exclude_lowercase: self.exclude_lowercase,
249            exclude_numbers: self.exclude_numbers,
250            exclude_punctuation: self.exclude_punctuation,
251            exclude_uppercase: self.exclude_uppercase,
252            include_space: self.include_space,
253            password_length: self.password_length,
254            require_each_included_type: self.require_each_included_type,
255            secret_string_template: None,
256        }
257    }
258
259    #[must_use]
260    pub fn build(self) -> GenerateSecretString {
261        self.build_internal()
262    }
263}
264
265impl GenerateSecretStringBuilder<GenerateStringKeyState> {
266    pub fn secret_string_template(self, secret_string_template: Value) -> GenerateSecretStringBuilder<SecretStringTemplateState> {
267        GenerateSecretStringBuilder {
268            phantom_data: Default::default(),
269            secret_string_template: Some(secret_string_template.to_string()),
270            generate_string_key: self.generate_string_key,
271            exclude_characters: self.exclude_characters,
272            exclude_lowercase: self.exclude_lowercase,
273            exclude_numbers: self.exclude_numbers,
274            exclude_punctuation: self.exclude_punctuation,
275            exclude_uppercase: self.exclude_uppercase,
276            include_space: self.include_space,
277            password_length: self.password_length,
278            require_each_included_type: self.require_each_included_type,
279        }
280    }
281}
282
283impl GenerateSecretStringBuilder<SecretStringTemplateState> {
284    #[must_use]
285    pub fn build(self) -> GenerateSecretString {
286        self.build_internal()
287    }
288}