Files
SwiftLint/Source/SwiftLintFramework/Configuration/Configuration+RulesMode.swift
T
2025-09-18 10:21:47 +01:00

134 lines
5.7 KiB
Swift

public extension Configuration {
/// Returns the rule for the specified ID, if configured in this configuration.
///
/// - parameter ruleID: The identifier for the rule to look up.
///
/// - returns: The rule for the specified ID, if configured in this configuration.
func configuredRule(forID ruleID: String) -> (any Rule)? {
rules.first { rule in
if type(of: rule).identifier == ruleID {
if let customRules = rule as? CustomRules {
return customRules.configuration.customRuleConfigurations.isNotEmpty
}
return true
}
return false
}
}
/// Represents how a Configuration object can be configured with regards to rules.
enum RulesMode: Equatable {
/// The default rules mode, which will enable all rules that aren't defined as being opt-in
/// (conforming to the `OptInRule` protocol), minus the rules listed in `disabled`, plus the rules listed in
/// `optIn`.
case defaultConfiguration(disabled: Set<String>, optIn: Set<String>)
/// Only enable the rules explicitly listed in the configuration files.
case onlyConfiguration(Set<String>)
/// Only enable the rule(s) explicitly listed on the command line (and their aliases). `--only-rule` can be
/// specified multiple times to enable multiple rules.
case onlyCommandLine(Set<String>)
/// Enable all available rules.
case allCommandLine
internal init(
enableAllRules: Bool,
onlyRule: [String],
onlyRules: [String],
optInRules: [String],
disabledRules: [String],
analyzerRules: [String]
) throws {
func warnAboutDuplicates(in identifiers: [String]) {
if Set(identifiers).count != identifiers.count {
let duplicateRules = identifiers.reduce(into: [String: Int]()) { $0[$1, default: 0] += 1 }
.filter { $0.1 > 1 }
for duplicateRule in duplicateRules {
Issue.listedMultipleTime(ruleID: duplicateRule.0, times: duplicateRule.1).print()
}
}
}
if enableAllRules {
self = .allCommandLine
} else if onlyRule.isNotEmpty {
self = .onlyCommandLine(Set(onlyRule))
} else if onlyRules.isNotEmpty {
if disabledRules.isNotEmpty || optInRules.isNotEmpty {
throw Issue.genericWarning(
"'\(Configuration.Key.disabledRules.rawValue)' or " +
"'\(Configuration.Key.optInRules.rawValue)' cannot be used in combination " +
"with '\(Configuration.Key.onlyRules.rawValue)'"
)
}
warnAboutDuplicates(in: onlyRules + analyzerRules)
self = .onlyConfiguration(Set(onlyRules + analyzerRules))
} else {
warnAboutDuplicates(in: disabledRules)
let effectiveOptInRules: [String]
if optInRules.contains(RuleIdentifier.all.stringRepresentation) {
let allOptInRules = RuleRegistry.shared.list.list.compactMap { ruleID, ruleType in
ruleType is any OptInRule.Type && !(ruleType is any AnalyzerRule.Type) ? ruleID : nil
}
effectiveOptInRules = Array(Set(allOptInRules + optInRules))
} else {
effectiveOptInRules = optInRules
}
let effectiveAnalyzerRules: [String]
if analyzerRules.contains(RuleIdentifier.all.stringRepresentation) {
let allAnalyzerRules = RuleRegistry.shared.list.list.compactMap { ruleID, ruleType in
ruleType is any AnalyzerRule.Type ? ruleID : nil
}
effectiveAnalyzerRules = allAnalyzerRules
} else {
effectiveAnalyzerRules = analyzerRules
}
warnAboutDuplicates(in: effectiveOptInRules + effectiveAnalyzerRules)
self = .defaultConfiguration(
disabled: Set(disabledRules), optIn: Set(effectiveOptInRules + effectiveAnalyzerRules)
)
}
}
internal func applied(aliasResolver: (String) -> String) -> Self {
switch self {
case let .defaultConfiguration(disabled, optIn):
return .defaultConfiguration(
disabled: Set(disabled.map(aliasResolver)),
optIn: Set(optIn.map(aliasResolver))
)
case let .onlyConfiguration(onlyRules):
return .onlyConfiguration(Set(onlyRules.map(aliasResolver)))
case let .onlyCommandLine(onlyRules):
return .onlyCommandLine(Set(onlyRules.map(aliasResolver)))
case .allCommandLine:
return .allCommandLine
}
}
internal func activateCustomRuleIdentifiers(allRulesWrapped: [ConfigurationRuleWrapper]) -> Self {
// In the only mode, if the custom rules rule is enabled, all custom rules are also enabled implicitly
// This method makes the implicitly explicit
switch self {
case let .onlyConfiguration(onlyRules) where onlyRules.contains {
$0 == CustomRules.identifier
}:
let customRulesRule = allRulesWrapped.customRules
return .onlyConfiguration(onlyRules.union(Set(customRulesRule?.customRuleIdentifiers ?? [])))
default:
return self
}
}
}
}