Skip to content

Commit 6a0e3dd

Browse files
authored
Expose AbsoluteConfigKey prepending/appending (#39)
### Motivation Docs mention: > ```swift > // Only remap the environment variables, not the JSON config > let keyMappedEnvProvider = KeyMappingProvider(upstream: envProvider) { key in > key.prepending(["myapp", "prod"]) > } > ``` but `prepending(_:)`/`appending(_:)` were internal. ### Modifications Switched `internal` to `public`. ### Result `KeyMappingProvider` is easier to use :-) ### Test Plan n/a
1 parent 29823e4 commit 6a0e3dd

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

Examples/hello-world-cli-example/Package.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ let package = Package(
88
.macOS(.v15)
99
],
1010
dependencies: [
11-
.package(url: "https://github.com/apple/swift-configuration", .upToNextMinor(from: "0.1.0"), traits: [.defaults, "CommandLineArgumentsSupport"])
11+
.package(
12+
url: "https://github.com/apple/swift-configuration",
13+
.upToNextMinor(from: "0.1.0"),
14+
traits: [.defaults, "CommandLineArgumentsSupport"]
15+
)
1216
],
1317
targets: [
1418
.executableTarget(
1519
name: "CLI",
1620
dependencies: [
17-
.product(name: "Configuration", package: "swift-configuration"),
21+
.product(name: "Configuration", package: "swift-configuration")
1822
]
19-
),
23+
)
2024
]
2125
)

Sources/Configuration/ConfigKey.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,23 @@ extension AbsoluteConfigKey {
182182
/// Returns a new absolute configuration key by prepending the given relative key.
183183
/// - Parameter prefix: The relative configuration key to prepend to this key.
184184
/// - Returns: A new absolute configuration key with the prefix prepended.
185-
internal func prepending(_ prefix: ConfigKey) -> AbsoluteConfigKey {
185+
public func prepending(_ prefix: ConfigKey) -> AbsoluteConfigKey {
186186
var prefixedComponents = prefix.components
187187
prefixedComponents.append(contentsOf: self.components)
188188
var mergedContext = prefix.context
189189
mergedContext.merge(self.context) { $1 }
190190
return AbsoluteConfigKey(prefixedComponents, context: mergedContext)
191191
}
192+
193+
/// Returns a new absolute configuration key by appending the given relative key.
194+
/// - Parameter relative: The relative configuration key to append to this key.
195+
/// - Returns: A new absolute configuration key with the relative key appended.
196+
public func appending(_ relative: ConfigKey) -> AbsoluteConfigKey {
197+
var appended = self
198+
appended.components.append(contentsOf: relative.components)
199+
appended.context.merge(relative.context) { $1 }
200+
return appended
201+
}
192202
}
193203

194204
extension AbsoluteConfigKey: ExpressibleByArrayLiteral {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftConfiguration open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the SwiftConfiguration project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftConfiguration project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Testing
16+
@testable import Configuration
17+
18+
struct AbsoluteConfigKeyTests {
19+
@Test func prependingSimpleKey() {
20+
let base = AbsoluteConfigKey(["bar", "baz"])
21+
let prefix = ConfigKey(["foo"])
22+
let result = base.prepending(prefix)
23+
24+
#expect(result.components == ["foo", "bar", "baz"])
25+
#expect(result.context.isEmpty)
26+
}
27+
28+
@Test func prependingWithContext() {
29+
let base = AbsoluteConfigKey(["bar"], context: ["env": .string("prod")])
30+
let prefix = ConfigKey(["foo"], context: ["region": .string("us-west")])
31+
let result = base.prepending(prefix)
32+
33+
#expect(result.components == ["foo", "bar"])
34+
#expect(result.context["env"] == .string("prod"))
35+
#expect(result.context["region"] == .string("us-west"))
36+
}
37+
38+
@Test func prependingWithConflictingContext() {
39+
let base = AbsoluteConfigKey(["bar"], context: ["key": .string("base-value")])
40+
let prefix = ConfigKey(["foo"], context: ["key": .string("prefix-value")])
41+
let result = base.prepending(prefix)
42+
43+
#expect(result.components == ["foo", "bar"])
44+
#expect(result.context["key"] == .string("base-value"))
45+
}
46+
47+
@Test func prependingEmptyKey() {
48+
let base = AbsoluteConfigKey(["foo", "bar"])
49+
let prefix = ConfigKey([])
50+
let result = base.prepending(prefix)
51+
52+
#expect(result.components == ["foo", "bar"])
53+
#expect(result.context.isEmpty)
54+
}
55+
56+
@Test func appendingSimpleKey() {
57+
let base = AbsoluteConfigKey(["foo", "bar"])
58+
let suffix = ConfigKey(["baz"])
59+
let result = base.appending(suffix)
60+
61+
#expect(result.components == ["foo", "bar", "baz"])
62+
#expect(result.context.isEmpty)
63+
}
64+
65+
@Test func appendingWithContext() {
66+
let base = AbsoluteConfigKey(["foo"], context: ["env": .string("prod")])
67+
let suffix = ConfigKey(["bar"], context: ["region": .string("us-west")])
68+
let result = base.appending(suffix)
69+
70+
#expect(result.components == ["foo", "bar"])
71+
#expect(result.context["env"] == .string("prod"))
72+
#expect(result.context["region"] == .string("us-west"))
73+
}
74+
75+
@Test func appendingWithConflictingContext() {
76+
let base = AbsoluteConfigKey(["foo"], context: ["key": .string("base-value")])
77+
let suffix = ConfigKey(["bar"], context: ["key": .string("suffix-value")])
78+
let result = base.appending(suffix)
79+
80+
#expect(result.components == ["foo", "bar"])
81+
#expect(result.context["key"] == .string("suffix-value"))
82+
}
83+
84+
@Test func appendingEmptyKey() {
85+
let base = AbsoluteConfigKey(["foo", "bar"])
86+
let suffix = ConfigKey([])
87+
let result = base.appending(suffix)
88+
89+
#expect(result.components == ["foo", "bar"])
90+
#expect(result.context.isEmpty)
91+
}
92+
93+
@Test func appendingMultipleKeys() {
94+
let base = AbsoluteConfigKey(["foo"])
95+
let result = base.appending(ConfigKey(["bar"])).appending(ConfigKey(["baz"]))
96+
97+
#expect(result.components == ["foo", "bar", "baz"])
98+
}
99+
100+
@Test func prependingMultipleKeys() {
101+
let base = AbsoluteConfigKey(["baz"])
102+
let result = base.prepending(ConfigKey(["bar"])).prepending(ConfigKey(["foo"]))
103+
104+
#expect(result.components == ["foo", "bar", "baz"])
105+
}
106+
107+
@Test func prependingAndAppending() {
108+
let base = AbsoluteConfigKey(["middle"])
109+
let result = base.prepending(ConfigKey(["start"])).appending(ConfigKey(["end"]))
110+
111+
#expect(result.components == ["start", "middle", "end"])
112+
}
113+
}

0 commit comments

Comments
 (0)