Files
SwiftLint/Source/SwiftLintFramework/Rules/RuleConfigurations/RegexConfiguration.swift
T
Ryan Demo 51544bb4cc Add excluded_match_kinds custom rule config parameter (#3336)
This allows custom rules to define an `excluded_match_kinds` array instead of listing out all but one or a few of the `SyntaxKind`s in `match_kinds`. Rules that include both `match_kinds` and `excluded_match_kinds` will be invalid, since there's not an obvious way to resolve the two without an arbitrary priority between them.
2020-09-10 21:08:41 -04:00

107 lines
4.0 KiB
Swift

import Foundation
import SourceKittenFramework
public struct RegexConfiguration: RuleConfiguration, Hashable, CacheDescriptionProvider {
public let identifier: String
public var name: String?
public var message = "Regex matched."
public var regex: NSRegularExpression!
public var included: NSRegularExpression?
public var excluded: NSRegularExpression?
public var excludedMatchKinds = Set<SyntaxKind>()
public var severityConfiguration = SeverityConfiguration(.warning)
public var captureGroup: Int = 0
public var severity: ViolationSeverity {
return severityConfiguration.severity
}
public var consoleDescription: String {
return "\(severity.rawValue): \(regex.pattern)"
}
internal var cacheDescription: String {
let jsonObject: [String] = [
identifier,
name ?? "",
message,
regex.pattern,
included?.pattern ?? "",
excluded?.pattern ?? "",
SyntaxKind.allKinds.subtracting(excludedMatchKinds)
.map({ $0.rawValue }).sorted(by: <).joined(separator: ","),
severityConfiguration.consoleDescription
]
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject),
let jsonString = String(data: jsonData, encoding: .utf8) {
return jsonString
}
queuedFatalError("Could not serialize regex configuration for cache")
}
public var description: RuleDescription {
return RuleDescription(identifier: identifier, name: name ?? identifier,
description: "", kind: .style)
}
public init(identifier: String) {
self.identifier = identifier
}
public mutating func apply(configuration: Any) throws {
guard let configurationDict = configuration as? [String: Any],
let regexString = configurationDict["regex"] as? String else {
throw ConfigurationError.unknownConfiguration
}
regex = try .cached(pattern: regexString)
if let includedString = configurationDict["included"] as? String {
included = try .cached(pattern: includedString)
}
if let excludedString = configurationDict["excluded"] as? String {
excluded = try .cached(pattern: excludedString)
}
if let name = configurationDict["name"] as? String {
self.name = name
}
if let message = configurationDict["message"] as? String {
self.message = message
}
if let severityString = configurationDict["severity"] as? String {
try severityConfiguration.apply(configuration: severityString)
}
if let captureGroup = configurationDict["capture_group"] as? Int {
guard (0 ... regex.numberOfCaptureGroups).contains(captureGroup) else {
throw ConfigurationError.unknownConfiguration
}
self.captureGroup = captureGroup
}
self.excludedMatchKinds = try self.excludedMatchKinds(from: configurationDict)
}
public func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
private func excludedMatchKinds(from configurationDict: [String: Any]) throws -> Set<SyntaxKind> {
let matchKinds = [String].array(of: configurationDict["match_kinds"])
let excludedMatchKinds = [String].array(of: configurationDict["excluded_match_kinds"])
switch (matchKinds, excludedMatchKinds) {
case (.some(let matchKinds), nil):
let includedKinds = Set(try matchKinds.map({ try SyntaxKind(shortName: $0) }))
return SyntaxKind.allKinds.subtracting(includedKinds)
case (nil, .some(let excludedMatchKinds)):
return Set(try excludedMatchKinds.map({ try SyntaxKind(shortName: $0) }))
case (nil, nil):
return .init()
case (.some, .some):
throw ConfigurationError.ambiguousMatchKindParameters
}
}
}