22//!
33//! This module defines the structure and validation for environment variables
44//! that will be rendered into the .env file for Docker Compose.
5+ //!
6+ //! The context is organized by service to mirror the structure of the .env template:
7+ //! - Tracker service configuration
8+ //! - `MySQL` service configuration (optional)
59
610use serde:: Serialize ;
711
8- /// Context for rendering the .env template
12+ /// Configuration for the Tracker service
913///
10- /// Contains all variables needed for the Docker Compose environment configuration .
14+ /// Contains environment variables for the Torrust Tracker container .
1115#[ derive( Serialize , Debug , Clone ) ]
12- pub struct EnvContext {
16+ pub struct TrackerServiceConfig {
1317 /// The admin token for the Torrust Tracker HTTP API
14- tracker_api_admin_token : String ,
18+ pub api_admin_token : String ,
1519 /// Database driver type ("sqlite3" or "mysql")
1620 /// Controls which config template the container entrypoint uses
17- database_driver : String ,
18- /// `MySQL` root password (only used when `MySQL` driver is configured)
19- #[ serde( skip_serializing_if = "Option::is_none" ) ]
20- mysql_root_password : Option < String > ,
21- /// `MySQL` database name (only used when `MySQL` driver is configured)
22- #[ serde( skip_serializing_if = "Option::is_none" ) ]
23- mysql_database : Option < String > ,
24- /// `MySQL` user (only used when `MySQL` driver is configured)
25- #[ serde( skip_serializing_if = "Option::is_none" ) ]
26- mysql_user : Option < String > ,
27- /// `MySQL` password (only used when `MySQL` driver is configured)
21+ pub database_driver : String ,
22+ }
23+
24+ /// Configuration for the `MySQL` service
25+ ///
26+ /// Contains environment variables for the `MySQL` container.
27+ /// Only included when `MySQL` driver is configured.
28+ #[ derive( Serialize , Debug , Clone ) ]
29+ pub struct MySqlServiceConfig {
30+ /// `MySQL` root password
31+ pub root_password : String ,
32+ /// `MySQL` database name
33+ pub database : String ,
34+ /// `MySQL` user
35+ pub user : String ,
36+ /// `MySQL` password
37+ pub password : String ,
38+ }
39+
40+ /// Context for rendering the .env template
41+ ///
42+ /// Contains all variables needed for the Docker Compose environment configuration,
43+ /// organized by service to mirror the template structure.
44+ #[ derive( Serialize , Debug , Clone ) ]
45+ pub struct EnvContext {
46+ /// Tracker service configuration
47+ pub tracker : TrackerServiceConfig ,
48+ /// `MySQL` service configuration (only present when `MySQL` driver is configured)
2849 #[ serde( skip_serializing_if = "Option::is_none" ) ]
29- mysql_password : Option < String > ,
50+ pub mysql : Option < MySqlServiceConfig > ,
3051}
3152
3253impl EnvContext {
@@ -42,19 +63,21 @@ impl EnvContext {
4263 /// use torrust_tracker_deployer_lib::infrastructure::templating::docker_compose::template::wrappers::env::EnvContext;
4364 ///
4465 /// let context = EnvContext::new("MySecretToken123".to_string());
45- /// assert_eq!(context.tracker_api_admin_token(), "MySecretToken123");
66+ /// assert_eq!(context.tracker.api_admin_token, "MySecretToken123");
67+ /// assert_eq!(context.tracker.database_driver, "sqlite3");
68+ /// assert!(context.mysql.is_none());
4669 /// ```
4770 #[ must_use]
4871 pub fn new ( tracker_api_admin_token : String ) -> Self {
4972 Self {
50- tracker_api_admin_token,
51- database_driver : "sqlite3" . to_string ( ) ,
52- mysql_root_password : None ,
53- mysql_database : None ,
54- mysql_user : None ,
55- mysql_password : None ,
73+ tracker : TrackerServiceConfig {
74+ api_admin_token : tracker_api_admin_token,
75+ database_driver : "sqlite3" . to_string ( ) ,
76+ } ,
77+ mysql : None ,
5678 }
5779 }
80+
5881 /// Creates a new `EnvContext` with `MySQL` credentials
5982 ///
6083 /// # Arguments
@@ -64,6 +87,22 @@ impl EnvContext {
6487 /// * `mysql_database` - `MySQL` database name
6588 /// * `mysql_user` - `MySQL` user
6689 /// * `mysql_password` - `MySQL` password
90+ ///
91+ /// # Examples
92+ ///
93+ /// ```rust
94+ /// use torrust_tracker_deployer_lib::infrastructure::templating::docker_compose::template::wrappers::env::EnvContext;
95+ ///
96+ /// let context = EnvContext::new_with_mysql(
97+ /// "MySecretToken123".to_string(),
98+ /// "root_pass".to_string(),
99+ /// "tracker_db".to_string(),
100+ /// "tracker_user".to_string(),
101+ /// "user_pass".to_string(),
102+ /// );
103+ /// assert_eq!(context.tracker.database_driver, "mysql");
104+ /// assert!(context.mysql.is_some());
105+ /// ```
67106 #[ must_use]
68107 pub fn new_with_mysql (
69108 tracker_api_admin_token : String ,
@@ -73,43 +112,53 @@ impl EnvContext {
73112 mysql_password : String ,
74113 ) -> Self {
75114 Self {
76- tracker_api_admin_token,
77- database_driver : "mysql" . to_string ( ) ,
78- mysql_root_password : Some ( mysql_root_password) ,
79- mysql_database : Some ( mysql_database) ,
80- mysql_user : Some ( mysql_user) ,
81- mysql_password : Some ( mysql_password) ,
115+ tracker : TrackerServiceConfig {
116+ api_admin_token : tracker_api_admin_token,
117+ database_driver : "mysql" . to_string ( ) ,
118+ } ,
119+ mysql : Some ( MySqlServiceConfig {
120+ root_password : mysql_root_password,
121+ database : mysql_database,
122+ user : mysql_user,
123+ password : mysql_password,
124+ } ) ,
82125 }
83126 }
84127
85128 /// Get the tracker API admin token
86129 #[ must_use]
87130 pub fn tracker_api_admin_token ( & self ) -> & str {
88- & self . tracker_api_admin_token
131+ & self . tracker . api_admin_token
132+ }
133+
134+ /// Get the database driver type
135+ #[ must_use]
136+ pub fn database_driver ( & self ) -> & str {
137+ & self . tracker . database_driver
89138 }
90139
91140 /// Get the `MySQL` root password (if configured)
92141 #[ must_use]
93142 pub fn mysql_root_password ( & self ) -> Option < & str > {
94- self . mysql_root_password . as_deref ( )
143+ self . mysql . as_ref ( ) . map ( |m| m . root_password . as_str ( ) )
95144 }
96145
97146 /// Get the `MySQL` database name (if configured)
98147 #[ must_use]
99148 pub fn mysql_database ( & self ) -> Option < & str > {
100- self . mysql_database . as_deref ( )
149+ self . mysql . as_ref ( ) . map ( |m| m . database . as_str ( ) )
101150 }
102151
103152 /// Get the `MySQL` user (if configured)
104153 #[ must_use]
105154 pub fn mysql_user ( & self ) -> Option < & str > {
106- self . mysql_user . as_deref ( )
155+ self . mysql . as_ref ( ) . map ( |m| m . user . as_str ( ) )
107156 }
108157
109158 /// Get the `MySQL` password (if configured)
110159 #[ must_use]
111160 pub fn mysql_password ( & self ) -> Option < & str > {
112- self . mysql_password . as_deref ( )
161+ self . mysql . as_ref ( ) . map ( |m| m . password . as_str ( ) )
113162 }
114163}
115164
@@ -122,7 +171,30 @@ mod tests {
122171 let token = "TestToken123" . to_string ( ) ;
123172 let context = EnvContext :: new ( token. clone ( ) ) ;
124173
125- assert_eq ! ( context. tracker_api_admin_token( ) , "TestToken123" ) ;
174+ assert_eq ! ( context. tracker. api_admin_token, "TestToken123" ) ;
175+ assert_eq ! ( context. tracker. database_driver, "sqlite3" ) ;
176+ assert ! ( context. mysql. is_none( ) ) ;
177+ }
178+
179+ #[ test]
180+ fn it_should_create_context_with_mysql_configuration ( ) {
181+ let context = EnvContext :: new_with_mysql (
182+ "AdminToken456" . to_string ( ) ,
183+ "root_pass" . to_string ( ) ,
184+ "tracker_db" . to_string ( ) ,
185+ "tracker_user" . to_string ( ) ,
186+ "user_pass" . to_string ( ) ,
187+ ) ;
188+
189+ assert_eq ! ( context. tracker. api_admin_token, "AdminToken456" ) ;
190+ assert_eq ! ( context. tracker. database_driver, "mysql" ) ;
191+ assert ! ( context. mysql. is_some( ) ) ;
192+
193+ let mysql_config = context. mysql . as_ref ( ) . unwrap ( ) ;
194+ assert_eq ! ( mysql_config. root_password, "root_pass" ) ;
195+ assert_eq ! ( mysql_config. database, "tracker_db" ) ;
196+ assert_eq ! ( mysql_config. user, "tracker_user" ) ;
197+ assert_eq ! ( mysql_config. password, "user_pass" ) ;
126198 }
127199
128200 #[ test]
@@ -131,7 +203,51 @@ mod tests {
131203
132204 // Verify it can be serialized (needed for Tera template rendering)
133205 let serialized = serde_json:: to_string ( & context) . unwrap ( ) ;
134- assert ! ( serialized. contains( "tracker_api_admin_token" ) ) ;
206+ assert ! ( serialized. contains( "tracker" ) ) ;
207+ assert ! ( serialized. contains( "api_admin_token" ) ) ;
135208 assert ! ( serialized. contains( "AdminToken456" ) ) ;
136209 }
210+
211+ #[ test]
212+ fn it_should_serialize_mysql_config_when_present ( ) {
213+ let context = EnvContext :: new_with_mysql (
214+ "Token123" . to_string ( ) ,
215+ "root" . to_string ( ) ,
216+ "db" . to_string ( ) ,
217+ "user" . to_string ( ) ,
218+ "pass" . to_string ( ) ,
219+ ) ;
220+
221+ let serialized = serde_json:: to_string ( & context) . unwrap ( ) ;
222+ assert ! ( serialized. contains( "mysql" ) ) ;
223+ assert ! ( serialized. contains( "root_password" ) ) ;
224+ }
225+
226+ #[ test]
227+ fn it_should_not_serialize_mysql_config_when_absent ( ) {
228+ let context = EnvContext :: new ( "Token123" . to_string ( ) ) ;
229+
230+ let serialized = serde_json:: to_string ( & context) . unwrap ( ) ;
231+ // MySQL section should not be present when None
232+ assert ! ( !serialized. contains( "mysql" ) ) ;
233+ }
234+
235+ #[ test]
236+ fn it_should_provide_backward_compatible_getters ( ) {
237+ let context = EnvContext :: new_with_mysql (
238+ "Token123" . to_string ( ) ,
239+ "root_pass" . to_string ( ) ,
240+ "tracker_db" . to_string ( ) ,
241+ "tracker_user" . to_string ( ) ,
242+ "user_pass" . to_string ( ) ,
243+ ) ;
244+
245+ // Backward compatible getter methods
246+ assert_eq ! ( context. tracker_api_admin_token( ) , "Token123" ) ;
247+ assert_eq ! ( context. database_driver( ) , "mysql" ) ;
248+ assert_eq ! ( context. mysql_root_password( ) , Some ( "root_pass" ) ) ;
249+ assert_eq ! ( context. mysql_database( ) , Some ( "tracker_db" ) ) ;
250+ assert_eq ! ( context. mysql_user( ) , Some ( "tracker_user" ) ) ;
251+ assert_eq ! ( context. mysql_password( ) , Some ( "user_pass" ) ) ;
252+ }
137253}
0 commit comments