Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
25 changes: 11 additions & 14 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ traits.insert(

let package = Package(
name: "swift-configuration",
platforms: [
.macOS(.v15), .iOS(.v18), .macCatalyst(.v18), .tvOS(.v18), .watchOS(.v11), .visionOS(.v2),
],
products: [
.library(name: "Configuration", targets: ["Configuration"]),
.library(name: "ConfigurationTesting", targets: ["ConfigurationTesting"]),
Expand Down Expand Up @@ -170,21 +167,21 @@ let package = Package(
)

for target in package.targets {
if target.type != .plugin {
var settings = target.swiftSettings ?? []
var settings = target.swiftSettings ?? []

// https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md
// Require `any` for existential types.
settings.append(.enableUpcomingFeature("ExistentialAny"))

// https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md
// Require `any` for existential types.
settings.append(.enableUpcomingFeature("ExistentialAny"))
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
settings.append(.enableUpcomingFeature("MemberImportVisibility"))

// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
settings.append(.enableUpcomingFeature("MemberImportVisibility"))
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md
settings.append(.enableUpcomingFeature("InternalImportsByDefault"))

// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md
settings.append(.enableUpcomingFeature("InternalImportsByDefault"))
settings.append(.enableExperimentalFeature("AvailabilityMacro=Configuration 1.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"))

target.swiftSettings = settings
}
target.swiftSettings = settings
}

if addDoccPlugin {
Expand Down
4 changes: 4 additions & 0 deletions Sources/Configuration/AccessReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Synchronization
/// Access reporters track when configuration values are read, fetched, or watched,
/// to provide visibility into configuration usage patterns. This is useful for
/// debugging, auditing, and understanding configuration dependencies.
@available(Configuration 1.0, *)
public protocol AccessReporter: Sendable {

/// Processes a configuration access event.
Expand All @@ -41,6 +42,7 @@ public protocol AccessReporter: Sendable {
/// Access events are generated whenever configuration values are accessed through
/// ``ConfigReader`` and ``ConfigSnapshotReader`` methods. They contain metadata about
/// the access, results from individual providers, and the final outcome of the operation.
@available(Configuration 1.0, *)
public struct AccessEvent: Sendable {

/// Metadata describing the configuration access operation.
Expand Down Expand Up @@ -184,6 +186,7 @@ public struct AccessEvent: Sendable {
/// Use this reporter to send configuration access events to multiple destinations
/// simultaneously. Each upstream reporter receives a copy of every event in the
/// order they were provided during initialization.
@available(Configuration 1.0, *)
public struct BroadcastingAccessReporter: Sendable {

/// The reporters that receive forwarded events.
Expand All @@ -198,6 +201,7 @@ public struct BroadcastingAccessReporter: Sendable {
}
}

@available(Configuration 1.0, *)
extension BroadcastingAccessReporter: AccessReporter {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public func report(_ event: AccessEvent) {
Expand Down
6 changes: 6 additions & 0 deletions Sources/Configuration/AccessReporters/AccessLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import Synchronization
/// - `value`: The resolved configuration value (redacted for secrets)
/// - `counter`: An incrementing counter for tracking access frequency
/// - Provider-specific information for each provider in the hierarchy
@available(Configuration 1.0, *)
public final class AccessLogger: Sendable {

/// The logger used to emit configuration access events.
Expand Down Expand Up @@ -94,6 +95,7 @@ public final class AccessLogger: Sendable {
}
}

@available(Configuration 1.0, *)
extension AccessLogger: AccessReporter {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public func report(_ event: AccessEvent) {
Expand All @@ -109,6 +111,7 @@ extension AccessLogger: AccessReporter {
}
}

@available(Configuration 1.0, *)
extension AccessEvent.Metadata {
/// Add log metadata.
/// - Parameter metadata: The metadata to which to add values.
Expand All @@ -119,6 +122,7 @@ extension AccessEvent.Metadata {
}
}

@available(Configuration 1.0, *)
extension AccessEvent.ProviderResult {
/// Add log metadata.
/// - Parameters:
Expand All @@ -136,6 +140,7 @@ extension AccessEvent.ProviderResult {
}
}

@available(Configuration 1.0, *)
extension AccessEvent {
/// Add log metadata.
/// - Parameter metadata: The metadata to which to add values.
Expand All @@ -151,6 +156,7 @@ extension AccessEvent {
}
}

@available(Configuration 1.0, *)
extension Result<ConfigValue?, any Error> {
/// Add log metadata.
/// - Parameter metadata: The metadata to which to add values.
Expand Down
5 changes: 5 additions & 0 deletions Sources/Configuration/AccessReporters/FileAccessLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import Synchronization
/// - Resolved value (redacted for secrets)
/// - Provider that supplied the value or error information
/// - Access metadata (operation type, value type, source location, timestamp)
@available(Configuration 1.0, *)
public final class FileAccessLogger: Sendable {

/// The file descriptor used for writing access events to the log file.
Expand Down Expand Up @@ -199,6 +200,7 @@ public final class FileAccessLogger: Sendable {
}
}

@available(Configuration 1.0, *)
extension FileAccessLogger: AccessReporter {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public func report(_ event: AccessEvent) {
Expand All @@ -213,6 +215,7 @@ extension FileAccessLogger: AccessReporter {
}
}

@available(Configuration 1.0, *)
extension FileAccessLogger {
/// Renders a string summary for the event.
/// - Parameter event: The event to render.
Expand All @@ -222,6 +225,7 @@ extension FileAccessLogger {
}
}

@available(Configuration 1.0, *)
extension AccessEvent {
/// Returns a human-readable single line string summarizing the access event.
/// - Parameter dateFormatStyle: The format style used for rendering dates.
Expand Down Expand Up @@ -280,6 +284,7 @@ extension AccessEvent {
}
}

@available(Configuration 1.0, *)
extension ConfigContent {
/// Returns a string representation of the config value, formatting complex types appropriately.
///
Expand Down
15 changes: 15 additions & 0 deletions Sources/Configuration/ConfigProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
///
/// **Dynamic providers**: Implement `watch` methods to emit real-time updates from
/// polling, file system monitoring, or other change detection mechanisms.
@available(Configuration 1.0, *)
public protocol ConfigProvider: Sendable {

/// The human-readable name of the configuration provider.
Expand Down Expand Up @@ -137,6 +138,7 @@ public protocol ConfigProvider: Sendable {
/// Snapshots enable consistent reads of multiple related configuration keys by
/// capturing the provider's state at a specific moment. This prevents the underlying
/// data from changing between individual key lookups.
@available(Configuration 1.0, *)
public protocol ConfigSnapshotProtocol: Sendable {

/// The human-readable name of the configuration provider that created this snapshot.
Expand All @@ -159,6 +161,7 @@ public protocol ConfigSnapshotProtocol: Sendable {
}

/// The result of looking up a configuration value in a provider.
@available(Configuration 1.0, *)
public struct LookupResult: Sendable, Equatable, Hashable {

/// The provider-specific encoding of the configuration key.
Expand All @@ -181,6 +184,7 @@ public struct LookupResult: Sendable, Equatable, Hashable {
}

/// The supported configuration value types.
@available(Configuration 1.0, *)
@frozen public enum ConfigType: String, Sendable, Equatable, Hashable {

/// A string value.
Expand Down Expand Up @@ -215,6 +219,7 @@ public struct LookupResult: Sendable, Equatable, Hashable {
}

/// The raw content of a configuration value.
@available(Configuration 1.0, *)
@frozen public enum ConfigContent: Sendable, Equatable, Hashable {

/// A string value.
Expand Down Expand Up @@ -433,6 +438,7 @@ public struct LookupResult: Sendable, Equatable, Hashable {
/// Configuration values include the actual content and a flag indicating whether
/// the value contains sensitive information. Secret values are protected from
/// accidental disclosure in logs and debug output.
@available(Configuration 1.0, *)
public struct ConfigValue: Sendable, Equatable, Hashable {

/// The configuration content.
Expand All @@ -451,6 +457,7 @@ public struct ConfigValue: Sendable, Equatable, Hashable {
}
}

@available(Configuration 1.0, *)
extension ConfigValue: CustomStringConvertible {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public var description: String {
Expand All @@ -462,55 +469,63 @@ extension ConfigValue: CustomStringConvertible {
}
}

@available(Configuration 1.0, *)
extension ConfigValue: ExpressibleByStringLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(stringLiteral value: String) {
self = .init(.string(value), isSecret: false)
}
}

@available(Configuration 1.0, *)
extension ConfigContent: ExpressibleByStringLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(stringLiteral value: String) {
self = .string(value)
}
}

@available(Configuration 1.0, *)
extension ConfigValue: ExpressibleByIntegerLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(integerLiteral value: Int) {
self = .init(.int(value), isSecret: false)
}
}

@available(Configuration 1.0, *)
extension ConfigContent: ExpressibleByIntegerLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(integerLiteral value: Int) {
self = .int(value)
}
}

@available(Configuration 1.0, *)
extension ConfigValue: ExpressibleByFloatLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(floatLiteral value: Double) {
self = .init(.double(value), isSecret: false)
}
}

@available(Configuration 1.0, *)
extension ConfigContent: ExpressibleByFloatLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(floatLiteral value: Double) {
self = .double(value)
}
}

@available(Configuration 1.0, *)
extension ConfigValue: ExpressibleByBooleanLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(booleanLiteral value: Bool) {
self = .init(.bool(value), isSecret: false)
}
}

@available(Configuration 1.0, *)
extension ConfigContent: ExpressibleByBooleanLiteral {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public init(booleanLiteral value: Bool) {
Expand Down
3 changes: 3 additions & 0 deletions Sources/Configuration/ConfigProviderHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//

@available(Configuration 1.0, *)
extension ConfigProvider {

/// Implements `watchValue` by getting the current value and emitting it immediately.
Expand Down Expand Up @@ -116,6 +117,7 @@ extension ConfigProvider {
/// - work: A closure that performs the value lookup and returns the result.
/// - Returns: A lookup result containing the encoded key and the value from the closure.
/// - Throws: Rethrows any errors thrown by the provided closure.
@available(Configuration 1.0, *)
package func withConfigValueLookup<Failure: Error>(
encodedKey: String,
work: () throws(Failure) -> ConfigValue?
Expand Down Expand Up @@ -147,6 +149,7 @@ package func withConfigValueLookup<Failure: Error>(
/// - work: An async closure that performs the value lookup and returns the result.
/// - Returns: A lookup result containing the encoded key and the value from the closure.
/// - Throws: Rethrows any errors thrown by the provided closure.
@available(Configuration 1.0, *)
package func withConfigValueLookup<Failure: Error>(
encodedKey: String,
work: () async throws(Failure) -> ConfigValue?
Expand Down
10 changes: 10 additions & 0 deletions Sources/Configuration/ConfigReader+internalHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import FoundationEssentials
import Foundation
#endif

@available(Configuration 1.0, *)
extension ConfigValue {
/// Returns a copy of the config value marked as secret.
///
Expand All @@ -29,6 +30,7 @@ extension ConfigValue {
}
}

@available(Configuration 1.0, *)
extension Result<ConfigValue?, any Error> {
/// Returns a copy of the result with the value marked as secret.
///
Expand All @@ -38,6 +40,7 @@ extension Result<ConfigValue?, any Error> {
}
}

@available(Configuration 1.0, *)
extension LookupResult {
/// Returns a copy of the config value marked as secret.
///
Expand All @@ -49,6 +52,7 @@ extension LookupResult {
}
}

@available(Configuration 1.0, *)
extension Result<LookupResult, any Error> {
/// Returns a copy of the result with the value marked as secret.
///
Expand All @@ -58,6 +62,7 @@ extension Result<LookupResult, any Error> {
}
}

@available(Configuration 1.0, *)
extension AccessEvent.ProviderResult {
/// Returns a copy of the result marked as secret.
///
Expand All @@ -80,6 +85,7 @@ extension AccessEvent.ProviderResult {
/// - isSecret: Whether to mark the values as secret.
/// - tuple: A tuple containing provider results and a configuration value result.
/// - Returns: The tuple with values marked as secret if the flag is true, otherwise unchanged.
@available(Configuration 1.0, *)
private func mergingIsSecret(
_ isSecret: Bool,
_ tuple: ([AccessEvent.ProviderResult], Result<ConfigValue?, any Error>)
Expand Down Expand Up @@ -121,6 +127,7 @@ private func mergingIsSecret(
/// - fileID: Source file identifier for access event metadata.
/// - line: Source line number for access event metadata.
/// - Returns: The converted configuration value, or `nil` if not found or conversion fails.
@available(Configuration 1.0, *)
internal func valueFromReader<Value>(
forKey key: ConfigKey,
type: ConfigType,
Expand Down Expand Up @@ -193,6 +200,7 @@ internal func valueFromReader<Value>(
/// - fileID: Source file identifier used for event reporting.
/// - line: Source line number used for event reporting.
/// - Returns: The configuration value converted to the requested type.
@available(Configuration 1.0, *)
internal func valueFromReader<Value>(
forKey key: ConfigKey,
type: ConfigType,
Expand Down Expand Up @@ -269,6 +277,7 @@ internal func valueFromReader<Value>(
/// - Throws: `ConfigError.missingRequiredConfigValue` if the configuration value is not found, or any error thrown
/// by the `unwrap` closure if conversion fails.
/// - Returns: The configuration value converted to the requested type.
@available(Configuration 1.0, *)
internal func requiredValueFromReader<Value>(
forKey key: ConfigKey,
type: ConfigType,
Expand Down Expand Up @@ -324,6 +333,7 @@ internal func requiredValueFromReader<Value>(
return try finalResult.get().0
}

@available(Configuration 1.0, *)
extension ConfigReader {

/// Gets a config value synchronously.
Expand Down
1 change: 1 addition & 0 deletions Sources/Configuration/ConfigReader+methods.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// # #
// #############################################################################

@available(Configuration 1.0, *)
extension ConfigReader {

// MARK: - Get
Expand Down
Loading
Loading