mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
120 lines
4.0 KiB
Swift
120 lines
4.0 KiB
Swift
//
|
|
// LinterCache.swift
|
|
// SwiftLint
|
|
//
|
|
// Created by Marcelo Fabri on 12/27/16.
|
|
// Copyright © 2016 Realm. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import SourceKittenFramework
|
|
|
|
public enum LinterCacheError: Error {
|
|
case invalidFormat
|
|
case differentVersion
|
|
case differentConfiguration
|
|
}
|
|
|
|
public final class LinterCache {
|
|
private var cache: [String: Any]
|
|
private let lock = NSLock()
|
|
|
|
public init(currentVersion: Version = .current, configurationHash: Int? = nil) {
|
|
cache = [
|
|
"version": currentVersion.value,
|
|
"files": [:]
|
|
]
|
|
cache["configuration_hash"] = configurationHash
|
|
}
|
|
|
|
public init(cache: Any, currentVersion: Version = .current, configurationHash: Int? = nil) throws {
|
|
guard let dictionary = cache as? [String: Any] else {
|
|
throw LinterCacheError.invalidFormat
|
|
}
|
|
|
|
guard let version = dictionary["version"] as? String, version == currentVersion.value else {
|
|
throw LinterCacheError.differentVersion
|
|
}
|
|
|
|
if dictionary["configuration_hash"] as? Int != configurationHash {
|
|
throw LinterCacheError.differentConfiguration
|
|
}
|
|
|
|
self.cache = dictionary
|
|
}
|
|
|
|
public convenience init(contentsOf url: URL, currentVersion: Version = .current,
|
|
configurationHash: Int? = nil) throws {
|
|
let data = try Data(contentsOf: url)
|
|
let json = try JSONSerialization.jsonObject(with: data, options: [])
|
|
try self.init(cache: json, currentVersion: currentVersion,
|
|
configurationHash: configurationHash)
|
|
}
|
|
|
|
public func cache(violations: [StyleViolation], forFile file: String, fileHash: Int) {
|
|
lock.lock()
|
|
var filesCache = (cache["files"] as? [String: Any]) ?? [:]
|
|
filesCache[file] = [
|
|
"violations": violations.map(dictionary(for:)),
|
|
"hash": fileHash
|
|
]
|
|
cache["files"] = filesCache
|
|
lock.unlock()
|
|
}
|
|
|
|
public func violations(forFile file: String, hash: Int) -> [StyleViolation]? {
|
|
lock.lock()
|
|
|
|
guard let filesCache = cache["files"] as? [String: Any],
|
|
let entry = filesCache[file] as? [String: Any],
|
|
let cacheHash = entry["hash"] as? Int,
|
|
cacheHash == hash,
|
|
let violations = entry["violations"] as? [[String: Any]] else {
|
|
lock.unlock()
|
|
return nil
|
|
}
|
|
|
|
lock.unlock()
|
|
return violations.flatMap { StyleViolation.from(cache: $0, file: file) }
|
|
}
|
|
|
|
public func save(to url: URL) throws {
|
|
lock.lock()
|
|
let json = toJSON(cache)
|
|
lock.unlock()
|
|
try json.write(to: url, atomically: true, encoding: .utf8)
|
|
}
|
|
|
|
private func dictionary(for violation: StyleViolation) -> [String: Any] {
|
|
return [
|
|
"line": violation.location.line ?? NSNull() as Any,
|
|
"character": violation.location.character ?? NSNull() as Any,
|
|
"severity": violation.severity.rawValue,
|
|
"type": violation.ruleDescription.name,
|
|
"rule_id": violation.ruleDescription.identifier,
|
|
"reason": violation.reason
|
|
]
|
|
}
|
|
}
|
|
|
|
extension StyleViolation {
|
|
fileprivate static func from(cache: [String: Any], file: String) -> StyleViolation? {
|
|
guard let severity = (cache["severity"] as? String).flatMap({ ViolationSeverity(rawValue: $0) }),
|
|
let name = cache["type"] as? String,
|
|
let ruleId = cache["rule_id"] as? String,
|
|
let reason = cache["reason"] as? String else {
|
|
return nil
|
|
}
|
|
|
|
let line = cache["line"] as? Int
|
|
let character = cache["character"] as? Int
|
|
|
|
let ruleDescription = RuleDescription(identifier: ruleId, name: name, description: reason)
|
|
let location = Location(file: file, line: line, character: character)
|
|
let violation = StyleViolation(ruleDescription: ruleDescription, severity: severity,
|
|
location: location, reason: reason)
|
|
|
|
return violation
|
|
}
|
|
}
|