@@ -15,6 +15,7 @@ use nym_sdk::{
1515use std:: os:: fd:: RawFd ;
1616use std:: { path:: PathBuf , sync:: Arc , time:: Duration } ;
1717use tokio_util:: sync:: CancellationToken ;
18+ use typed_builder:: TypedBuilder ;
1819
1920use crate :: error:: RegistrationClientError ;
2021
@@ -27,11 +28,13 @@ pub struct NymNodeWithKeys {
2728 pub keys : Arc < KeyPair > ,
2829}
2930
31+ #[ derive( TypedBuilder ) ]
3032pub struct BuilderConfig {
3133 pub entry_node : NymNodeWithKeys ,
3234 pub exit_node : NymNodeWithKeys ,
3335 pub data_path : Option < PathBuf > ,
3436 pub mixnet_client_config : MixnetClientConfig ,
37+ #[ builder( default = MIXNET_CLIENT_STARTUP_TIMEOUT ) ]
3538 pub mixnet_client_startup_timeout : Duration ,
3639 pub two_hops : bool ,
3740 pub user_agent : UserAgent ,
@@ -58,22 +61,6 @@ pub struct MixnetClientConfig {
5861}
5962
6063impl BuilderConfig {
61- /// Creates a builder for BuilderConfig
62- ///
63- /// This is the preferred way to construct a BuilderConfig.
64- ///
65- /// # Example
66- /// ```ignore
67- /// let config = BuilderConfig::builder()
68- /// .entry_node(entry)
69- /// .exit_node(exit)
70- /// .user_agent(agent)
71- /// .build()?;
72- /// ```
73- pub fn builder ( ) -> BuilderConfigBuilder {
74- BuilderConfigBuilder :: default ( )
75- }
76-
7764 pub fn mixnet_client_debug_config ( & self ) -> DebugConfig {
7865 if self . two_hops {
7966 two_hop_debug_config ( & self . mixnet_client_config )
@@ -225,168 +212,6 @@ fn true_to_disabled(val: bool) -> &'static str {
225212 if val { "disabled" } else { "enabled" }
226213}
227214
228- /// Error type for BuilderConfig validation
229- #[ derive( Debug , Clone , thiserror:: Error ) ]
230- #[ allow( clippy:: enum_variant_names) ]
231- pub enum BuilderConfigError {
232- #[ error( "entry_node is required" ) ]
233- MissingEntryNode ,
234- #[ error( "exit_node is required" ) ]
235- MissingExitNode ,
236- #[ error( "mixnet_client_config is required" ) ]
237- MissingMixnetClientConfig ,
238- #[ error( "user_agent is required" ) ]
239- MissingUserAgent ,
240- #[ error( "custom_topology_provider is required" ) ]
241- MissingTopologyProvider ,
242- #[ error( "network_env is required" ) ]
243- MissingNetworkEnv ,
244- #[ error( "cancel_token is required" ) ]
245- MissingCancelToken ,
246- #[ cfg( unix) ]
247- #[ error( "connection_fd_callback is required" ) ]
248- MissingConnectionFdCallback ,
249- }
250-
251- /// Builder for `BuilderConfig`
252- ///
253- /// This provides a more convenient way to construct a `BuilderConfig` compared to the
254- /// `new()` constructor with many arguments.
255- #[ derive( Default ) ]
256- pub struct BuilderConfigBuilder {
257- entry_node : Option < NymNodeWithKeys > ,
258- exit_node : Option < NymNodeWithKeys > ,
259- data_path : Option < PathBuf > ,
260- mixnet_client_config : Option < MixnetClientConfig > ,
261- mixnet_client_startup_timeout : Duration ,
262- two_hops : bool ,
263- user_agent : Option < UserAgent > ,
264- custom_topology_provider : Option < Box < dyn TopologyProvider + Send + Sync > > ,
265- network_env : Option < NymNetworkDetails > ,
266- cancel_token : Option < CancellationToken > ,
267- #[ cfg( unix) ]
268- connection_fd_callback : Option < Arc < dyn Fn ( RawFd ) + Send + Sync > > ,
269- }
270-
271- impl BuilderConfigBuilder {
272- pub fn new ( ) -> Self {
273- Self {
274- mixnet_client_startup_timeout : MIXNET_CLIENT_STARTUP_TIMEOUT ,
275- ..Default :: default ( )
276- }
277- }
278-
279- #[ must_use]
280- pub fn entry_node ( mut self , entry_node : NymNodeWithKeys ) -> Self {
281- self . entry_node = Some ( entry_node) ;
282- self
283- }
284-
285- #[ must_use]
286- pub fn exit_node ( mut self , exit_node : NymNodeWithKeys ) -> Self {
287- self . exit_node = Some ( exit_node) ;
288- self
289- }
290-
291- #[ must_use]
292- pub fn data_path ( mut self , data_path : Option < PathBuf > ) -> Self {
293- self . data_path = data_path;
294- self
295- }
296-
297- #[ must_use]
298- pub fn mixnet_client_config ( mut self , mixnet_client_config : MixnetClientConfig ) -> Self {
299- self . mixnet_client_config = Some ( mixnet_client_config) ;
300- self
301- }
302-
303- #[ must_use]
304- pub fn mixnet_client_startup_timeout (
305- mut self ,
306- mixnet_client_startup_timeout : Duration ,
307- ) -> Self {
308- self . mixnet_client_startup_timeout = mixnet_client_startup_timeout;
309- self
310- }
311-
312- #[ must_use]
313- pub fn two_hops ( mut self , two_hops : bool ) -> Self {
314- self . two_hops = two_hops;
315- self
316- }
317-
318- #[ must_use]
319- pub fn user_agent ( mut self , user_agent : UserAgent ) -> Self {
320- self . user_agent = Some ( user_agent) ;
321- self
322- }
323-
324- #[ must_use]
325- pub fn custom_topology_provider (
326- mut self ,
327- custom_topology_provider : Box < dyn TopologyProvider + Send + Sync > ,
328- ) -> Self {
329- self . custom_topology_provider = Some ( custom_topology_provider) ;
330- self
331- }
332-
333- #[ must_use]
334- pub fn network_env ( mut self , network_env : NymNetworkDetails ) -> Self {
335- self . network_env = Some ( network_env) ;
336- self
337- }
338-
339- #[ must_use]
340- pub fn cancel_token ( mut self , cancel_token : CancellationToken ) -> Self {
341- self . cancel_token = Some ( cancel_token) ;
342- self
343- }
344-
345- #[ cfg( unix) ]
346- #[ must_use]
347- pub fn connection_fd_callback (
348- mut self ,
349- connection_fd_callback : Arc < dyn Fn ( RawFd ) + Send + Sync > ,
350- ) -> Self {
351- self . connection_fd_callback = Some ( connection_fd_callback) ;
352- self
353- }
354-
355- /// Builds the `BuilderConfig`.
356- ///
357- /// Returns an error if any required field is missing.
358- pub fn build ( self ) -> Result < BuilderConfig , BuilderConfigError > {
359- Ok ( BuilderConfig {
360- entry_node : self
361- . entry_node
362- . ok_or ( BuilderConfigError :: MissingEntryNode ) ?,
363- exit_node : self . exit_node . ok_or ( BuilderConfigError :: MissingExitNode ) ?,
364- data_path : self . data_path ,
365- mixnet_client_config : self
366- . mixnet_client_config
367- . ok_or ( BuilderConfigError :: MissingMixnetClientConfig ) ?,
368- mixnet_client_startup_timeout : self . mixnet_client_startup_timeout ,
369- two_hops : self . two_hops ,
370- user_agent : self
371- . user_agent
372- . ok_or ( BuilderConfigError :: MissingUserAgent ) ?,
373- custom_topology_provider : self
374- . custom_topology_provider
375- . ok_or ( BuilderConfigError :: MissingTopologyProvider ) ?,
376- network_env : self
377- . network_env
378- . ok_or ( BuilderConfigError :: MissingNetworkEnv ) ?,
379- cancel_token : self
380- . cancel_token
381- . ok_or ( BuilderConfigError :: MissingCancelToken ) ?,
382- #[ cfg( unix) ]
383- connection_fd_callback : self
384- . connection_fd_callback
385- . ok_or ( BuilderConfigError :: MissingConnectionFdCallback ) ?,
386- } )
387- }
388- }
389-
390215#[ cfg( test) ]
391216mod tests {
392217 use super :: * ;
@@ -399,54 +224,4 @@ mod tests {
399224 assert_eq ! ( config. min_mixnode_performance, None ) ;
400225 assert_eq ! ( config. min_gateway_performance, None ) ;
401226 }
402-
403- #[ test]
404- fn test_builder_config_builder_fails_without_required_fields ( ) {
405- // Building without any fields should fail with specific error
406- let result = BuilderConfig :: builder ( ) . build ( ) ;
407- assert ! ( result. is_err( ) ) ;
408- match result {
409- Err ( BuilderConfigError :: MissingEntryNode ) => ( ) , // Expected
410- Err ( e) => panic ! ( "Expected MissingEntryNode, got: {}" , e) ,
411- Ok ( _) => panic ! ( "Expected error, got Ok" ) ,
412- }
413- }
414-
415- #[ test]
416- fn test_builder_config_builder_validates_all_required_fields ( ) {
417- // Test that each required field is validated
418- let result = BuilderConfig :: builder ( ) . build ( ) ;
419- assert ! ( result. is_err( ) ) ;
420-
421- // Short-circuits at first missing field, so we just verify it's one of the expected errors
422- #[ allow( unreachable_patterns) ] // All variants are covered, but keeping catch-all for safety
423- match result {
424- Err ( BuilderConfigError :: MissingEntryNode )
425- | Err ( BuilderConfigError :: MissingExitNode )
426- | Err ( BuilderConfigError :: MissingMixnetClientConfig )
427- | Err ( BuilderConfigError :: MissingUserAgent )
428- | Err ( BuilderConfigError :: MissingTopologyProvider )
429- | Err ( BuilderConfigError :: MissingNetworkEnv )
430- | Err ( BuilderConfigError :: MissingCancelToken ) => ( ) ,
431- #[ cfg( unix) ]
432- Err ( BuilderConfigError :: MissingConnectionFdCallback ) => ( ) ,
433- Err ( e) => panic ! ( "Unexpected error: {}" , e) ,
434- Ok ( _) => panic ! ( "Expected validation error, got Ok" ) ,
435- }
436- }
437-
438- #[ test]
439- fn test_builder_config_builder_method_chaining ( ) {
440- // Test that builder methods chain properly and return Self
441- let builder = BuilderConfig :: builder ( ) ;
442-
443- // Verify the builder returns itself for chaining
444- let builder = builder. two_hops ( true ) ;
445- let builder = builder. two_hops ( false ) ;
446- let builder = builder. data_path ( None ) ;
447-
448- // Builder should still fail because required fields are missing
449- let result = builder. build ( ) ;
450- assert ! ( result. is_err( ) ) ;
451- }
452227}
0 commit comments