103 lines
2.8 KiB
Dart
103 lines
2.8 KiB
Dart
import '../social/handle_validator.dart';
|
|
|
|
/// Result of a Club slug validation check.
|
|
class ClubSlugValidationResult {
|
|
final bool isValid;
|
|
final String? error;
|
|
final String? normalizedSlug;
|
|
|
|
const ClubSlugValidationResult._({
|
|
required this.isValid,
|
|
this.error,
|
|
this.normalizedSlug,
|
|
});
|
|
|
|
factory ClubSlugValidationResult.valid(String normalized) =>
|
|
ClubSlugValidationResult._(isValid: true, normalizedSlug: normalized);
|
|
|
|
factory ClubSlugValidationResult.invalid(String error) =>
|
|
ClubSlugValidationResult._(isValid: false, error: error);
|
|
}
|
|
|
|
/// Normalization and validation logic for MotorSignal Club slugs.
|
|
class ClubSlugValidator {
|
|
static const int minLength = 3;
|
|
static const int maxLength = 30;
|
|
|
|
static final RegExp _formatRegex = RegExp(r'^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$');
|
|
|
|
/// Reserved words specific to clubs, chapters, and platform organizations.
|
|
static const Set<String> clubReservedWords = {
|
|
'club',
|
|
'clubs',
|
|
'motorcycle',
|
|
'motorcycles',
|
|
'mc',
|
|
'chapter',
|
|
'chapters',
|
|
'national',
|
|
'international',
|
|
'official',
|
|
'rally',
|
|
'groupride',
|
|
'grouprides',
|
|
'event',
|
|
'events',
|
|
'all',
|
|
'global',
|
|
'feed',
|
|
'roster',
|
|
'members',
|
|
'settings',
|
|
'admin',
|
|
};
|
|
|
|
/// Normalizes a raw slug: trims whitespace and lowercases.
|
|
static String normalize(String rawSlug) => rawSlug.trim().toLowerCase();
|
|
|
|
/// Validates a candidate slug format and checks against reserved words.
|
|
static ClubSlugValidationResult validate(String rawSlug) {
|
|
final normalized = normalize(rawSlug);
|
|
|
|
if (normalized.isEmpty) {
|
|
return ClubSlugValidationResult.invalid('Club slug cannot be empty');
|
|
}
|
|
|
|
if (normalized.length < minLength) {
|
|
return ClubSlugValidationResult.invalid(
|
|
'Club slug must be at least $minLength characters',
|
|
);
|
|
}
|
|
|
|
if (normalized.length > maxLength) {
|
|
return ClubSlugValidationResult.invalid(
|
|
'Club slug cannot exceed $maxLength characters',
|
|
);
|
|
}
|
|
|
|
if (normalized.contains('--')) {
|
|
return ClubSlugValidationResult.invalid(
|
|
'Club slug cannot contain consecutive hyphens',
|
|
);
|
|
}
|
|
|
|
if (!_formatRegex.hasMatch(normalized)) {
|
|
return ClubSlugValidationResult.invalid(
|
|
'Club slug must start and end with a letter or number, and contain only lowercase letters, numbers, and hyphens',
|
|
);
|
|
}
|
|
|
|
// Check against standard platform handles (admin, support, motorsignal, etc.)
|
|
if (HandleValidator.reservedHandles.contains(normalized)) {
|
|
return ClubSlugValidationResult.invalid('This club slug is reserved');
|
|
}
|
|
|
|
// Check against club-specific reserved words
|
|
if (clubReservedWords.contains(normalized)) {
|
|
return ClubSlugValidationResult.invalid('This club slug is reserved');
|
|
}
|
|
|
|
return ClubSlugValidationResult.valid(normalized);
|
|
}
|
|
}
|