1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Basics
import Foundation
public struct PackageIndexConfiguration: Equatable {
public var url: URL?
public var searchResultMaxItemsCount: Int
public var cacheDirectory: AbsolutePath
public var cacheTTLInSeconds: Int
public var cacheMaxSizeInMegabytes: Int
// TODO: rdar://87575573 remove feature flag
public internal(set) var enabled = ProcessInfo.processInfo.environment["SWIFTPM_ENABLE_PACKAGE_INDEX"] == "1"
public init(
url: URL? = nil,
searchResultMaxItemsCount: Int? = nil,
disableCache: Bool = false,
cacheDirectory: AbsolutePath? = nil,
cacheTTLInSeconds: Int? = nil,
cacheMaxSizeInMegabytes: Int? = nil
) {
self.url = url
self.searchResultMaxItemsCount = searchResultMaxItemsCount ?? 50
self.cacheDirectory = (try? cacheDirectory.map(resolveSymlinks)) ?? (try? localFileSystem.swiftPMCacheDirectory.appending(components: "package-metadata")) ?? .root
self.cacheTTLInSeconds = disableCache ? -1 : (cacheTTLInSeconds ?? 3600)
self.cacheMaxSizeInMegabytes = cacheMaxSizeInMegabytes ?? 10
}
}
public struct PackageIndexConfigurationStorage {
private let path: AbsolutePath
private let fileSystem: FileSystem
private let encoder: JSONEncoder
private let decoder: JSONDecoder
public init(path: AbsolutePath, fileSystem: FileSystem) {
self.path = path
self.fileSystem = fileSystem
self.encoder = JSONEncoder.makeWithDefaults()
self.decoder = JSONDecoder.makeWithDefaults()
}
public func load() throws -> PackageIndexConfiguration {
guard self.fileSystem.exists(self.path) else {
return .init()
}
let data: Data = try self.fileSystem.readFileContents(self.path)
guard data.count > 0 else {
return .init()
}
let container = try decoder.decode(StorageModel.Container.self, from: data)
return try PackageIndexConfiguration(container.index)
}
public func save(_ configuration: PackageIndexConfiguration) throws {
if !self.fileSystem.exists(self.path.parentDirectory) {
try self.fileSystem.createDirectory(self.path.parentDirectory, recursive: true)
}
let container = StorageModel.Container(configuration)
let buffer = try encoder.encode(container)
try self.fileSystem.writeFileContents(self.path, data: buffer)
}
@discardableResult
public func update(with handler: (inout PackageIndexConfiguration) throws -> Void) throws -> PackageIndexConfiguration {
let configuration = try self.load()
var updatedConfiguration = configuration
try handler(&updatedConfiguration)
if updatedConfiguration != configuration {
try self.save(updatedConfiguration)
}
return updatedConfiguration
}
}
private enum StorageModel {
struct Container: Codable {
var index: Index
init(_ from: PackageIndexConfiguration) {
self.index = .init(
url: from.url?.absoluteString,
searchResultMaxItemsCount: from.searchResultMaxItemsCount,
cacheDirectory: from.cacheDirectory.pathString,
cacheTTLInSeconds: from.cacheTTLInSeconds,
cacheMaxSizeInMegabytes: from.cacheMaxSizeInMegabytes
)
}
}
struct Index: Codable {
let url: String?
let searchResultMaxItemsCount: Int?
let cacheDirectory: String?
let cacheTTLInSeconds: Int?
let cacheMaxSizeInMegabytes: Int?
}
}
// MARK: - Utility
private extension PackageIndexConfiguration {
init(_ from: StorageModel.Index) throws {
let url: URL?
switch from.url {
case .none:
url = nil
case .some(let urlString):
url = URL(string: urlString)
guard url != nil else {
throw SerializationError.invalidURL(urlString)
}
}
let cacheDirectory: AbsolutePath?
switch from.cacheDirectory {
case .none:
cacheDirectory = nil
case .some(let path):
cacheDirectory = try? AbsolutePath(validating: path)
guard cacheDirectory != nil else {
throw SerializationError.invalidPath(path)
}
}
self.init(
url: url,
searchResultMaxItemsCount: from.searchResultMaxItemsCount,
cacheDirectory: cacheDirectory,
cacheTTLInSeconds: from.cacheTTLInSeconds,
cacheMaxSizeInMegabytes: from.cacheMaxSizeInMegabytes
)
}
}
private enum SerializationError: Error {
case invalidURL(String)
case invalidPath(String)
}
|