@@ -57,7 +57,7 @@ pub struct Recording {
5757 recording_file : String ,
5858 recording_assets_file : Option < String > ,
5959 id : Option < RecordingId > ,
60- variables : RwLock < HashMap < String , Value > > ,
60+ variables : RwLock < HashMap < String , String > > ,
6161 rand : OnceLock < Mutex < ChaCha20Rng > > ,
6262}
6363
@@ -325,24 +325,24 @@ impl Recording {
325325 let key = key. as_ref ( ) ;
326326 if self . test_mode == TestMode :: Playback {
327327 let variables = self . variables . read ( ) . map_err ( read_lock_error) . ok ( ) ?;
328- return variables. get ( key) . map ( Into :: into ) ;
328+ return variables. get ( key) . cloned ( ) ;
329329 }
330330
331- let value = self . env ( key) ;
331+ // Get the environment variable or, if unset (None), the optional VarOptions::default_value.
332+ let options = options. unwrap_or_default ( ) ;
333+ let ( value, sanitized) = options. apply ( self . env ( key) ) ;
334+
332335 if self . test_mode == TestMode :: Live {
333336 return value;
334337 }
335338
336- match value {
337- None => None ,
338- Some ( v) if v. is_empty ( ) => None ,
339- Some ( v) => {
340- let v = Some ( v) ;
341- let mut variables = self . variables . write ( ) . map_err ( write_lock_error) . ok ( ) ?;
342- variables. insert ( key. into ( ) , Value :: from ( v. as_ref ( ) , options) ) ;
343- v
344- }
339+ // Do not record unset (None) environment variables.
340+ if let Some ( sanitized) = sanitized {
341+ let mut variables = self . variables . write ( ) . map_err ( write_lock_error) . ok ( ) ?;
342+ variables. insert ( key. into ( ) , sanitized) ;
345343 }
344+
345+ value
346346 }
347347}
348348
@@ -403,7 +403,7 @@ impl Recording {
403403 env:: var_os ( self . service_directory . clone ( ) + "_" + key. as_ref ( ) )
404404 . or_else ( || env:: var_os ( key. as_ref ( ) ) )
405405 . or_else ( || env:: var_os ( String :: from ( AZURE_PREFIX ) + key. as_ref ( ) ) )
406- . and_then ( |v| v . into_string ( ) . ok ( ) )
406+ . and_then ( |value| value . into_string ( ) . ok ( ) )
407407 }
408408
409409 fn rng ( & self ) -> & Mutex < ChaCha20Rng > {
@@ -416,9 +416,8 @@ impl Recording {
416416 . read ( )
417417 . map_err ( read_lock_error)
418418 . unwrap_or_else ( |err| panic ! ( "{err}" ) ) ;
419- let seed: String = variables
419+ let seed = variables
420420 . get ( RANDOM_SEED_NAME )
421- . map ( Into :: into)
422421 . unwrap_or_else ( || panic ! ( "random seed variable not set" ) ) ;
423422 let seed = base64:: decode ( seed)
424423 . unwrap_or_else ( |err| panic ! ( "failed to decode random seed: {err}" ) ) ;
@@ -438,7 +437,7 @@ impl Recording {
438437 . write ( )
439438 . map_err ( write_lock_error)
440439 . unwrap_or_else ( |err| panic ! ( "{err}" ) ) ;
441- variables. insert ( RANDOM_SEED_NAME . to_string ( ) , Value :: from ( Some ( seed) , None ) ) ;
440+ variables. insert ( RANDOM_SEED_NAME . to_string ( ) , seed) ;
442441
443442 rng. into ( )
444443 }
@@ -478,7 +477,7 @@ impl Recording {
478477 TestMode :: Playback => {
479478 let result = client. playback_start ( payload. try_into ( ) ?, None ) . await ?;
480479 let mut variables = self . variables . write ( ) . map_err ( write_lock_error) ?;
481- variables. extend ( result. variables . into_iter ( ) . map ( | ( k , v ) | ( k , v . into ( ) ) ) ) ;
480+ variables. extend ( result. variables . into_iter ( ) ) ;
482481
483482 result. recording_id
484483 }
@@ -516,7 +515,9 @@ impl Recording {
516515 let variables = self . variables . read ( ) . map_err ( read_lock_error) ?;
517516 VariablePayload {
518517 variables : HashMap :: from_iter (
519- variables. iter ( ) . map ( |( k, v) | ( k. clone ( ) , v. into ( ) ) ) ,
518+ variables
519+ . iter ( )
520+ . map ( |( k, value) | ( k. clone ( ) , value. clone ( ) ) ) ,
520521 ) ,
521522 }
522523 } ;
@@ -586,6 +587,9 @@ impl Drop for SkipGuard<'_> {
586587/// Options for getting variables from a [`Recording`].
587588#[ derive( Clone , Debug ) ]
588589pub struct VarOptions {
590+ /// The value to return if not already recorded.
591+ pub default_value : Option < Cow < ' static , str > > ,
592+
589593 /// Whether to sanitize the variable value with [`VarOptions::sanitize_value`].
590594 pub sanitize : bool ,
591595
@@ -595,59 +599,137 @@ pub struct VarOptions {
595599 pub sanitize_value : Cow < ' static , str > ,
596600}
597601
602+ impl VarOptions {
603+ /// Returns a tuple of the `value` or [`VarOptions::default_value`], and the sanitized value.
604+ ///
605+ /// The `value` is only replaced with the `VarOptions::default_value` if `None`. This is returned as the first tuple field.
606+ ///
607+ /// The [`VarOptions::sanitize_value`] is only `Some` if [`VarOptions::sanitize`] is `true`. This is returned as the second tuple field.
608+ fn apply < S : Into < String > > ( self , value : Option < S > ) -> ( Option < String > , Option < String > ) {
609+ let value = value. map_or_else (
610+ || self . default_value . as_deref ( ) . map ( ToString :: to_string) ,
611+ |value| Some ( value. into ( ) ) ,
612+ ) ;
613+ let sanitized = match value. as_deref ( ) {
614+ None => None ,
615+ Some ( _) if self . sanitize => Some ( self . sanitize_value . to_string ( ) ) ,
616+ Some ( v) => Some ( v. to_string ( ) ) ,
617+ } ;
618+ ( value, sanitized)
619+ }
620+ }
621+
598622impl Default for VarOptions {
599623 fn default ( ) -> Self {
600624 Self {
625+ default_value : None ,
601626 sanitize : false ,
602627 sanitize_value : Cow :: Borrowed ( crate :: DEFAULT_SANITIZED_VALUE ) ,
603628 }
604629 }
605630}
606631
607- #[ derive( Debug ) ]
608- struct Value {
609- value : String ,
610- sanitized : Option < Cow < ' static , str > > ,
611- }
632+ #[ test]
633+ fn test_var_options_apply ( ) {
634+ let ( value, ..) = VarOptions :: default ( ) . apply ( None :: < String > ) ;
635+ assert_eq ! ( value, None ) ;
612636
613- impl Value {
614- fn from < S > ( value : Option < S > , options : Option < VarOptions > ) -> Self
615- where
616- S : Into < String > ,
617- {
618- Self {
619- value : value. map_or_else ( String :: new, Into :: into) ,
620- sanitized : match options {
621- Some ( options) if options. sanitize => Some ( options. sanitize_value . clone ( ) ) ,
622- _ => None ,
623- } ,
624- }
637+ let ( value, ..) = VarOptions :: default ( ) . apply ( Some ( "" . to_string ( ) ) ) ;
638+ assert_eq ! ( value, Some ( String :: new( ) ) ) ;
639+
640+ let ( value, ..) = VarOptions :: default ( ) . apply ( Some ( "test" . to_string ( ) ) ) ;
641+ assert_eq ! ( value, Some ( "test" . into( ) ) ) ;
642+
643+ let ( value, ..) = VarOptions {
644+ default_value : None ,
645+ ..Default :: default ( )
625646 }
626- }
647+ . apply ( None :: < String > ) ;
648+ assert_eq ! ( value, None ) ;
627649
628- impl From < & str > for Value {
629- fn from ( value : & str ) -> Self {
630- Self {
631- value : value. into ( ) ,
632- sanitized : None ,
633- }
650+ let ( value, ..) = VarOptions {
651+ default_value : Some ( "" . into ( ) ) ,
652+ ..Default :: default ( )
634653 }
635- }
654+ . apply ( None :: < String > ) ;
655+ assert_eq ! ( value, Some ( "" . into( ) ) ) ;
636656
637- impl From < String > for Value {
638- fn from ( value : String ) -> Self {
639- Self {
640- value,
641- sanitized : None ,
642- }
657+ let ( value, ..) = VarOptions {
658+ default_value : Some ( "test" . into ( ) ) ,
659+ ..Default :: default ( )
660+ }
661+ . apply ( None :: < String > ) ;
662+ assert_eq ! ( value, Some ( "test" . into( ) ) ) ;
663+
664+ let ( value, ..) = VarOptions {
665+ default_value : Some ( "default" . into ( ) ) ,
666+ ..Default :: default ( )
667+ }
668+ . apply ( Some ( "" . to_string ( ) ) ) ;
669+ assert_eq ! ( value, Some ( "" . into( ) ) ) ;
670+
671+ let ( value, ..) = VarOptions {
672+ default_value : Some ( "default" . into ( ) ) ,
673+ ..Default :: default ( )
643674 }
675+ . apply ( Some ( "test" . to_string ( ) ) ) ;
676+ assert_eq ! ( value, Some ( "test" . into( ) ) ) ;
644677}
645678
646- impl From < & Value > for String {
647- fn from ( value : & Value ) -> Self {
648- value
649- . sanitized
650- . as_ref ( )
651- . map_or_else ( || value. value . clone ( ) , |v| v. to_string ( ) )
679+ #[ test]
680+ fn test_var_options_apply_sanitized ( ) {
681+ let ( value, sanitized) = VarOptions :: default ( ) . apply ( None :: < String > ) ;
682+ assert_eq ! ( value, None ) ;
683+ assert_eq ! ( sanitized, None ) ;
684+
685+ let ( value, sanitized) = VarOptions {
686+ sanitize : true ,
687+ ..Default :: default ( )
688+ }
689+ . apply ( None :: < String > ) ;
690+ assert_eq ! ( value, None ) ;
691+ assert_eq ! ( sanitized, None ) ;
692+
693+ let ( value, sanitized) = VarOptions {
694+ sanitize : true ,
695+ ..Default :: default ( )
696+ }
697+ . apply ( Some ( "" . to_string ( ) ) ) ;
698+ assert_eq ! ( value, Some ( "" . to_string( ) ) ) ;
699+ assert_eq ! ( sanitized, Some ( "Sanitized" . into( ) ) ) ;
700+
701+ let ( value, sanitized) = VarOptions {
702+ sanitize : true ,
703+ ..Default :: default ( )
704+ }
705+ . apply ( Some ( "test" . to_string ( ) ) ) ;
706+ assert_eq ! ( value, Some ( "test" . to_string( ) ) ) ;
707+ assert_eq ! ( sanitized, Some ( "Sanitized" . into( ) ) ) ;
708+
709+ let ( value, sanitized) = VarOptions {
710+ sanitize : true ,
711+ sanitize_value : "*****" . into ( ) ,
712+ ..Default :: default ( )
713+ }
714+ . apply ( None :: < String > ) ;
715+ assert_eq ! ( value, None ) ;
716+ assert_eq ! ( sanitized, None ) ;
717+
718+ let ( value, sanitized) = VarOptions {
719+ sanitize : true ,
720+ sanitize_value : "*****" . into ( ) ,
721+ ..Default :: default ( )
722+ }
723+ . apply ( Some ( "" . to_string ( ) ) ) ;
724+ assert_eq ! ( value, Some ( "" . to_string( ) ) ) ;
725+ assert_eq ! ( sanitized, Some ( "*****" . into( ) ) ) ;
726+
727+ let ( value, sanitized) = VarOptions {
728+ sanitize : true ,
729+ sanitize_value : "*****" . into ( ) ,
730+ ..Default :: default ( )
652731 }
732+ . apply ( Some ( "test" . to_string ( ) ) ) ;
733+ assert_eq ! ( value, Some ( "test" . to_string( ) ) ) ;
734+ assert_eq ! ( sanitized, Some ( "*****" . into( ) ) ) ;
653735}
0 commit comments