Files
SwiftLint/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/NameConfiguration.swift
T
2025-12-07 08:11:08 -05:00

108 lines
4.4 KiB
Swift

import Foundation
import SwiftLintCore
struct NameConfiguration<Parent: Rule>: RuleConfiguration, InlinableOptionType {
typealias Severity = SeverityConfiguration<Parent>
typealias SeverityLevels = SeverityLevelsConfiguration<Parent>
typealias StartWithLowercaseConfiguration = ChildOptionSeverityConfiguration<Parent>
@ConfigurationElement(key: "min_length")
private(set) var minLength = SeverityLevels(warning: 0, error: 0)
@ConfigurationElement(key: "max_length")
private(set) var maxLength = SeverityLevels(warning: 0, error: 0)
@ConfigurationElement(key: "excluded")
private(set) var excludedRegularExpressions = Set<RegularExpression>()
@ConfigurationElement(key: "allowed_symbols")
private(set) var allowedSymbols = Set<String>()
@ConfigurationElement(key: "unallowed_symbols_severity")
private(set) var unallowedSymbolsSeverity = Severity.error
@ConfigurationElement(key: "validates_start_with_lowercase")
private(set) var validatesStartWithLowercase = StartWithLowercaseConfiguration.error
var minLengthThreshold: Int {
max(minLength.warning, minLength.error ?? minLength.warning)
}
var maxLengthThreshold: Int {
min(maxLength.warning, maxLength.error ?? maxLength.warning)
}
var allowedSymbolsAndAlphanumerics: CharacterSet {
CharacterSet(charactersIn: allowedSymbols.joined()).union(.alphanumerics)
}
init(minLengthWarning: Int,
minLengthError: Int,
maxLengthWarning: Int,
maxLengthError: Int,
excluded: [String] = [],
allowedSymbols: [String] = [],
unallowedSymbolsSeverity: Severity = .error,
validatesStartWithLowercase: StartWithLowercaseConfiguration = .error) {
minLength = SeverityLevels(warning: minLengthWarning, error: minLengthError)
maxLength = SeverityLevels(warning: maxLengthWarning, error: maxLengthError)
self.excludedRegularExpressions = Set(excluded.compactMap {
try? RegularExpression(pattern: "^\($0)$")
})
self.allowedSymbols = Set(allowedSymbols)
self.unallowedSymbolsSeverity = unallowedSymbolsSeverity
self.validatesStartWithLowercase = validatesStartWithLowercase
}
mutating func apply(configuration: Any) throws(Issue) {
guard let configurationDict = configuration as? [String: Any] else {
throw .invalidConfiguration(ruleID: Parent.identifier)
}
if let minLengthConfiguration = configurationDict[$minLength.key] {
try minLength.apply(configuration: minLengthConfiguration)
}
if let maxLengthConfiguration = configurationDict[$maxLength.key] {
try maxLength.apply(configuration: maxLengthConfiguration)
}
if let excluded = [String].array(of: configurationDict[$excludedRegularExpressions.key]) {
excludedRegularExpressions = Set(excluded.compactMap {
try? RegularExpression(pattern: "^\($0)$")
})
}
if let allowedSymbols = [String].array(of: configurationDict[$allowedSymbols.key]) {
self.allowedSymbols = Set(allowedSymbols)
}
if let unallowedSymbolsSeverity = configurationDict[$unallowedSymbolsSeverity.key] {
try self.unallowedSymbolsSeverity.apply(configuration: unallowedSymbolsSeverity)
}
if let validatesStartWithLowercase = configurationDict[$validatesStartWithLowercase.key] as? String {
try self.validatesStartWithLowercase.apply(configuration: validatesStartWithLowercase)
}
}
}
extension NameConfiguration {
func severity(forLength length: Int) -> ViolationSeverity? {
if let minError = minLength.error, length < minError {
return .error
}
if let maxError = maxLength.error, length > maxError {
return .error
}
if length < minLength.warning || length > maxLength.warning {
return .warning
}
return nil
}
func containsOnlyAllowedCharacters(name: String) -> Bool {
allowedSymbolsAndAlphanumerics.isSuperset(of: CharacterSet(charactersIn: name))
}
}
// MARK: - `exclude` option extensions
extension NameConfiguration {
func shouldExclude(name: String) -> Bool {
excludedRegularExpressions.contains {
!$0.regex.matches(in: name, options: [], range: NSRange(name.startIndex..., in: name)).isEmpty
}
}
}