Files
SwiftLint/Source/SwiftLintFramework/Models/LinterCache.swift
T
Marcelo Fabri 94f48f4ebd PR feedback
2017-01-10 13:09:18 -02:00

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
}
}