@@ -581,44 +581,57 @@ def repl(match):
581581 def reprompt_option (self , name , prompt_text ):
582582 return self .prompt_option (name , prompt_text , force_prompt = True )
583583
584+ def prompt_validated_option (self , name , prompt_text , valid_values , normalizer = None , allow_empty = False ):
585+ """
586+ Prompts for an option with validation and retry support.
587+
588+ :param name: The option name.
589+ :param prompt_text: The prompt to display.
590+ :param valid_values: List of valid values (canonical forms).
591+ :param normalizer: Optional function to normalize input before comparison.
592+ If None, uses case-insensitive comparison.
593+ :param allow_empty: If True, empty input is accepted (returns '').
594+ :return: The validated value.
595+ """
596+ if normalizer is None :
597+ normalizer = lambda v : v .lower ()
598+
599+ def match_value (input_val ):
600+ if allow_empty and (not input_val or input_val .lower () == "none" ):
601+ return ''
602+ for v in valid_values :
603+ if normalizer (v ) == normalizer (input_val ):
604+ return v
605+ return None
606+
607+ valid_display = ', ' .join (valid_values )
608+ if allow_empty :
609+ valid_display += ', or none'
610+
611+ for attempt in range (2 ):
612+ if attempt == 0 :
613+ value = self .prompt_option (name , prompt_text )
614+ else :
615+ value = self .reprompt_option (name , prompt_text )
616+
617+ matched = match_value (value )
618+ if matched is not None :
619+ setattr (self .options , name , matched )
620+ return matched
621+
622+ print (f"Invalid { name .replace ('_' , ' ' )} '{ value } '. Must be one of: { valid_display } ." )
623+
624+ raise ValueError (f"Invalid { name .replace ('_' , ' ' )} '{ value } '. Must be one of: { valid_display } ." )
625+
584626 def prompt_build_type_option (self , name ):
585- value = self .prompt_option (name , "Build type" )
586627 valid_build_types = ["Debug" , "Release" , "RelWithDebInfo" , "MinSizeRel" , "OptimizedDebug" , "DebugFast" ]
587- for t in valid_build_types :
588- if t .lower ().replace ("-" , "" ) == value .lower ().replace ("-" , "" ):
589- if t == "DebugFast" :
590- value = "DebugFast"
591- setattr (self .options , name , t )
592- return value
593- print (f"Invalid build type '{ value } '. Must be one of: { ', ' .join (valid_build_types )} ." )
594- value = self .reprompt_option (name , "Build type" )
595- for t in valid_build_types :
596- if t .lower ().replace ("-" , "" ) == value .lower ().replace ("-" , "" ):
597- if t == "DebugFast" :
598- value = "DebugFast"
599- setattr (self .options , name , t )
600- return value
601- print (f"Invalid build type '{ value } '. Must be one of: { ', ' .join (valid_build_types )} ." )
602- raise ValueError (f"Invalid build type '{ value } '. Must be one of: { ', ' .join (valid_build_types )} ." )
628+ normalizer = lambda v : v .lower ().replace ("-" , "" )
629+ return self .prompt_validated_option (name , "Build type" , valid_build_types , normalizer = normalizer )
603630
604631 def prompt_sanitizer_option (self , name ):
605- value = self .prompt_option (name , "Sanitizer (asan/ubsan/msan/tsan/none)" )
606- if not value :
607- value = ''
608- return value
609632 valid_sanitizers = ["ASan" , "UBSan" , "MSan" , "TSan" ]
610- for t in valid_sanitizers :
611- if t .lower () == value .lower ():
612- setattr (self .options , name , t )
613- return value
614- print (f"Invalid sanitizer '{ value } '. Must be one of: { ', ' .join (valid_sanitizers )} ." )
615- value = self .reprompt_option (name , "Sanitizer (asan/ubsan/msan/tsan/none)" )
616- for t in valid_sanitizers :
617- if t .lower () == value .lower ():
618- setattr (self .options , name , t )
619- return value
620- print (f"Invalid sanitizer '{ value } '. Must be one of: { ', ' .join (valid_sanitizers )} ." )
621- raise ValueError (f"Invalid sanitizer '{ value } '. Must be one of: { ', ' .join (valid_sanitizers )} ." )
633+ return self .prompt_validated_option (
634+ name , "Sanitizer (asan/ubsan/msan/tsan/none)" , valid_sanitizers , allow_empty = True )
622635
623636 def supports_ansi (self ):
624637 return bool (self .ui .color_enabled )
0 commit comments