Skip to content

Commit 690e964

Browse files
committed
Add RegistryConfiguration
Add RegistryConfigurationTests
1 parent b8f0af0 commit 690e964

File tree

3 files changed

+228
-0
lines changed

3 files changed

+228
-0
lines changed

Sources/PackageRegistry/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_library(PackageRegistry
10+
RegistryConfiguration.swift
1011
RegistryManager.swift)
1112
target_link_libraries(PackageRegistry PUBLIC
1213
TSCBasic
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Foundation
12+
13+
public struct RegistryConfiguration: Hashable {
14+
public typealias Scope = String
15+
16+
public struct Registry: Hashable {
17+
public var url: Foundation.URL
18+
}
19+
20+
public enum Version: Int {
21+
case v1 = 1
22+
}
23+
24+
public static let version: Version = .v1
25+
26+
public var defaultRegistry: Registry?
27+
public var scopedRegistries: [Scope: Registry]
28+
29+
public init() {
30+
self.defaultRegistry = nil
31+
self.scopedRegistries = [:]
32+
}
33+
}
34+
35+
// MARK: - Codable
36+
37+
extension RegistryConfiguration: Codable {
38+
private enum CodingKeys: String, CodingKey {
39+
case registries
40+
case version
41+
}
42+
43+
private struct ScopeCodingKey: CodingKey, Hashable {
44+
static let `default` = ScopeCodingKey(stringValue: "[default]")
45+
46+
var stringValue: String
47+
var intValue: Int? { nil }
48+
49+
init(stringValue: String) {
50+
self.stringValue = stringValue
51+
}
52+
53+
init?(intValue: Int) {
54+
return nil
55+
}
56+
}
57+
58+
public init(from decoder: Decoder) throws {
59+
let container = try decoder.container(keyedBy: CodingKeys.self)
60+
61+
let version = try container.decode(Version.RawValue.self, forKey: .version)
62+
switch Version(rawValue: version) {
63+
case .v1:
64+
let nestedContainer = try container.nestedContainer(keyedBy: ScopeCodingKey.self, forKey: .registries)
65+
66+
self.defaultRegistry = try nestedContainer.decodeIfPresent(Registry.self, forKey: .default)
67+
68+
var scopedRegistries: [Scope: Registry] = [:]
69+
for key in nestedContainer.allKeys where key != .default {
70+
scopedRegistries[key.stringValue] = try nestedContainer.decode(Registry.self, forKey: key)
71+
}
72+
self.scopedRegistries = scopedRegistries
73+
case nil:
74+
throw DecodingError.dataCorruptedError(forKey: .version, in: container, debugDescription: "invalid version: \(version)")
75+
}
76+
}
77+
78+
public func encode(to encoder: Encoder) throws {
79+
var container = encoder.container(keyedBy: CodingKeys.self)
80+
81+
try container.encode(Self.version, forKey: .version)
82+
83+
var nestedContainer = container.nestedContainer(keyedBy: ScopeCodingKey.self, forKey: .registries)
84+
85+
try nestedContainer.encodeIfPresent(defaultRegistry, forKey: .default)
86+
87+
for (scope, registry) in scopedRegistries {
88+
let key = ScopeCodingKey(stringValue: scope)
89+
try nestedContainer.encode(registry, forKey: key)
90+
}
91+
}
92+
}
93+
94+
extension RegistryConfiguration.Version: Codable {}
95+
extension RegistryConfiguration.Registry: Codable {}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import XCTest
12+
@testable import PackageRegistry
13+
14+
private let defaultRegistryBaseURL = URL(string: "https://packages.example.com/")!
15+
private let customRegistryBaseURL = URL(string: "https://custom.packages.example.com/")!
16+
17+
final class RegistryConfigurationTests: XCTestCase {
18+
let encoder = JSONEncoder()
19+
let decoder = JSONDecoder()
20+
21+
func testEmptyConfiguration() throws {
22+
let configuration = RegistryConfiguration()
23+
XCTAssertNil(configuration.defaultRegistry)
24+
XCTAssertEqual(configuration.scopedRegistries, [:])
25+
}
26+
27+
func testRoundTripCodingForEmptyConfiguration() throws {
28+
let configuration = RegistryConfiguration()
29+
30+
let encoded = try encoder.encode(configuration)
31+
let decoded = try decoder.decode(RegistryConfiguration.self, from: encoded)
32+
33+
XCTAssertEqual(configuration, decoded)
34+
}
35+
36+
func testRoundTripCodingForExampleConfiguration() throws {
37+
var configuration = RegistryConfiguration()
38+
39+
configuration.defaultRegistry = .init(url: defaultRegistryBaseURL)
40+
configuration.scopedRegistries["foo"] = .init(url: customRegistryBaseURL)
41+
configuration.scopedRegistries["bar"] = .init(url: customRegistryBaseURL)
42+
43+
let encoded = try encoder.encode(configuration)
44+
let decoded = try decoder.decode(RegistryConfiguration.self, from: encoded)
45+
46+
XCTAssertEqual(configuration, decoded)
47+
}
48+
49+
func testDecodeEmptyConfiguration() throws {
50+
let data = #"""
51+
{
52+
"registries": {},
53+
"version": 1
54+
}
55+
"""#.data(using: .utf8)!
56+
57+
let configuration = try decoder.decode(RegistryConfiguration.self, from: data)
58+
XCTAssertNil(configuration.defaultRegistry)
59+
XCTAssertEqual(configuration.scopedRegistries, [:])
60+
}
61+
62+
func testDecodeExampleConfiguration() throws {
63+
let data = #"""
64+
{
65+
"registries": {
66+
"[default]": {
67+
"url": "\#(defaultRegistryBaseURL)"
68+
},
69+
"foo": {
70+
"url": "\#(customRegistryBaseURL)"
71+
},
72+
"bar": {
73+
"url": "\#(customRegistryBaseURL)"
74+
},
75+
},
76+
"version": 1
77+
}
78+
"""#.data(using: .utf8)!
79+
80+
let configuration = try decoder.decode(RegistryConfiguration.self, from: data)
81+
XCTAssertEqual(configuration.defaultRegistry?.url, defaultRegistryBaseURL)
82+
XCTAssertEqual(configuration.scopedRegistries["foo"]?.url, customRegistryBaseURL)
83+
XCTAssertEqual(configuration.scopedRegistries["bar"]?.url, customRegistryBaseURL)
84+
}
85+
86+
func testDecodeConfigurationWithInvalidRegistryKey() throws {
87+
let data = #"""
88+
{
89+
"registries": {
90+
0: "\#(customRegistryBaseURL)"
91+
},
92+
"version": 1
93+
}
94+
"""#.data(using: .utf8)!
95+
96+
XCTAssertThrowsError(try decoder.decode(RegistryConfiguration.self, from: data))
97+
}
98+
99+
func testDecodeConfigurationWithInvalidRegistryValue() throws {
100+
let data = #"""
101+
{
102+
"registries": {
103+
"[default]": "\#(customRegistryBaseURL)"
104+
},
105+
"version": 1
106+
}
107+
"""#.data(using: .utf8)!
108+
109+
XCTAssertThrowsError(try decoder.decode(RegistryConfiguration.self, from: data))
110+
}
111+
112+
func testDecodeConfigurationWithMissingVersion() throws {
113+
let data = #"""
114+
{
115+
"registries": {}
116+
}
117+
"""#.data(using: .utf8)!
118+
119+
XCTAssertThrowsError(try decoder.decode(RegistryConfiguration.self, from: data))
120+
}
121+
122+
func testDecodeConfigurationWithInvalidVersion() throws {
123+
let data = #"""
124+
{
125+
"registries": {},
126+
"version": 999
127+
}
128+
"""#.data(using: .utf8)!
129+
130+
XCTAssertThrowsError(try decoder.decode(RegistryConfiguration.self, from: data))
131+
}
132+
}

0 commit comments

Comments
 (0)