Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Examples/hello-world-cli-example/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ let package = Package(
.macOS(.v15)
],
dependencies: [
.package(url: "https://github.com/apple/swift-configuration", .upToNextMinor(from: "0.1.0"), traits: [.defaults, "CommandLineArgumentsSupport"])
.package(
url: "https://github.com/apple/swift-configuration",
.upToNextMinor(from: "0.1.0"),
traits: [.defaults, "CommandLineArgumentsSupport"]
)
],
targets: [
.executableTarget(
name: "CLI",
dependencies: [
.product(name: "Configuration", package: "swift-configuration"),
.product(name: "Configuration", package: "swift-configuration")
]
),
)
]
)
12 changes: 11 additions & 1 deletion Sources/Configuration/ConfigKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,23 @@ extension AbsoluteConfigKey {
/// Returns a new absolute configuration key by prepending the given relative key.
/// - Parameter prefix: The relative configuration key to prepend to this key.
/// - Returns: A new absolute configuration key with the prefix prepended.
internal func prepending(_ prefix: ConfigKey) -> AbsoluteConfigKey {
public func prepending(_ prefix: ConfigKey) -> AbsoluteConfigKey {
var prefixedComponents = prefix.components
prefixedComponents.append(contentsOf: self.components)
var mergedContext = prefix.context
mergedContext.merge(self.context) { $1 }
return AbsoluteConfigKey(prefixedComponents, context: mergedContext)
}

/// Returns a new absolute configuration key by appending the given relative key.
/// - Parameter relative: The relative configuration key to append to this key.
/// - Returns: A new absolute configuration key with the relative key appended.
public func appending(_ relative: ConfigKey) -> AbsoluteConfigKey {
var appended = self
appended.components.append(contentsOf: relative.components)
appended.context.merge(relative.context) { $1 }
return appended
}
}

extension AbsoluteConfigKey: ExpressibleByArrayLiteral {
Expand Down
113 changes: 113 additions & 0 deletions Tests/ConfigurationTests/AbsoluteConfigKeyTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftConfiguration open source project
//
// Copyright (c) 2025 Apple Inc. and the SwiftConfiguration project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftConfiguration project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Testing
@testable import Configuration

struct AbsoluteConfigKeyTests {
@Test func prependingSimpleKey() {
let base = AbsoluteConfigKey(["bar", "baz"])
let prefix = ConfigKey(["foo"])
let result = base.prepending(prefix)

#expect(result.components == ["foo", "bar", "baz"])
#expect(result.context.isEmpty)
}

@Test func prependingWithContext() {
let base = AbsoluteConfigKey(["bar"], context: ["env": .string("prod")])
let prefix = ConfigKey(["foo"], context: ["region": .string("us-west")])
let result = base.prepending(prefix)

#expect(result.components == ["foo", "bar"])
#expect(result.context["env"] == .string("prod"))
#expect(result.context["region"] == .string("us-west"))
}

@Test func prependingWithConflictingContext() {
let base = AbsoluteConfigKey(["bar"], context: ["key": .string("base-value")])
let prefix = ConfigKey(["foo"], context: ["key": .string("prefix-value")])
let result = base.prepending(prefix)

#expect(result.components == ["foo", "bar"])
#expect(result.context["key"] == .string("base-value"))
}

@Test func prependingEmptyKey() {
let base = AbsoluteConfigKey(["foo", "bar"])
let prefix = ConfigKey([])
let result = base.prepending(prefix)

#expect(result.components == ["foo", "bar"])
#expect(result.context.isEmpty)
}

@Test func appendingSimpleKey() {
let base = AbsoluteConfigKey(["foo", "bar"])
let suffix = ConfigKey(["baz"])
let result = base.appending(suffix)

#expect(result.components == ["foo", "bar", "baz"])
#expect(result.context.isEmpty)
}

@Test func appendingWithContext() {
let base = AbsoluteConfigKey(["foo"], context: ["env": .string("prod")])
let suffix = ConfigKey(["bar"], context: ["region": .string("us-west")])
let result = base.appending(suffix)

#expect(result.components == ["foo", "bar"])
#expect(result.context["env"] == .string("prod"))
#expect(result.context["region"] == .string("us-west"))
}

@Test func appendingWithConflictingContext() {
let base = AbsoluteConfigKey(["foo"], context: ["key": .string("base-value")])
let suffix = ConfigKey(["bar"], context: ["key": .string("suffix-value")])
let result = base.appending(suffix)

#expect(result.components == ["foo", "bar"])
#expect(result.context["key"] == .string("suffix-value"))
}

@Test func appendingEmptyKey() {
let base = AbsoluteConfigKey(["foo", "bar"])
let suffix = ConfigKey([])
let result = base.appending(suffix)

#expect(result.components == ["foo", "bar"])
#expect(result.context.isEmpty)
}

@Test func appendingMultipleKeys() {
let base = AbsoluteConfigKey(["foo"])
let result = base.appending(ConfigKey(["bar"])).appending(ConfigKey(["baz"]))

#expect(result.components == ["foo", "bar", "baz"])
}

@Test func prependingMultipleKeys() {
let base = AbsoluteConfigKey(["baz"])
let result = base.prepending(ConfigKey(["bar"])).prepending(ConfigKey(["foo"]))

#expect(result.components == ["foo", "bar", "baz"])
}

@Test func prependingAndAppending() {
let base = AbsoluteConfigKey(["middle"])
let result = base.prepending(ConfigKey(["start"])).appending(ConfigKey(["end"]))

#expect(result.components == ["start", "middle", "end"])
}
}
Loading