/// Validation and normalization logic for MotorSignal rider handles. class HandleValidationResult { final bool isValid; final String? error; final String? normalizedHandle; const HandleValidationResult._({ required this.isValid, this.error, this.normalizedHandle, }); factory HandleValidationResult.valid(String normalized) => HandleValidationResult._(isValid: true, normalizedHandle: normalized); factory HandleValidationResult.invalid(String error) => HandleValidationResult._(isValid: false, error: error); } class HandleValidator { static const int minLength = 3; static const int maxLength = 20; static final RegExp _formatRegex = RegExp(r'^[a-z][a-z0-9_]{2,19}$'); static const Set reservedHandles = { 'admin', 'administrator', 'motorsignal', 'motorcloud', 'support', 'system', 'mod', 'moderator', 'help', 'official', 'root', 'api', 'bot', 'test', 'dev', 'developer', 'staff', 'security', 'null', 'undefined', 'guest', 'anonymous', 'safety', 'emergency', 'police', 'marshal', 'leader', 'host', }; /// Normalizes a handle: trims whitespace and lowercases. static String normalize(String handle) => handle.trim().toLowerCase(); /// Validates a handle format and checks against reserved names. static HandleValidationResult validate(String rawHandle) { final normalized = normalize(rawHandle); if (normalized.isEmpty) { return HandleValidationResult.invalid('Handle cannot be empty'); } if (normalized.length < minLength) { return HandleValidationResult.invalid( 'Handle must be at least $minLength characters', ); } if (normalized.length > maxLength) { return HandleValidationResult.invalid( 'Handle cannot exceed $maxLength characters', ); } if (!RegExp(r'^[a-z]').hasMatch(normalized)) { return HandleValidationResult.invalid('Handle must start with a letter'); } if (!_formatRegex.hasMatch(normalized)) { return HandleValidationResult.invalid( 'Handle can only contain lowercase letters, numbers, and underscores', ); } if (reservedHandles.contains(normalized)) { return HandleValidationResult.invalid('This handle is reserved'); } return HandleValidationResult.valid(normalized); } }