Skip to content

Commit cc2944b

Browse files
authored
[Docs] Editing pass to prep for 1.0 (#97)
### Motivation 1.0 docs should be high quality. ### Modifications Minor edits from an editing pass. ### Result Slightly nicer docs. ### Test Plan N/A
1 parent 7e0b0c9 commit cc2944b

25 files changed

+204
-138
lines changed

Sources/Configuration/AccessReporter.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ public protocol AccessReporter: Sendable {
4646
public struct AccessEvent: Sendable {
4747

4848
/// Metadata describing the configuration access operation.
49+
///
50+
/// Contains information about the type of access, the key accessed, value type,
51+
/// source location, and timestamp.
4952
public struct Metadata: Sendable {
5053

51-
/// The source code location where the configuration access occurred.
54+
/// The source code location where a configuration access occurred.
55+
///
56+
/// Captures the file identifier and line number for debugging and auditing purposes.
5257
public struct SourceLocation: Sendable, CustomStringConvertible {
5358

5459
/// The identifier of the source file where the access occurred.
@@ -73,6 +78,9 @@ public struct AccessEvent: Sendable {
7378
}
7479

7580
/// The type of configuration access operation.
81+
///
82+
/// Indicates whether the access was a synchronous get, asynchronous fetch,
83+
/// or an async watch operation.
7684
@frozen public enum AccessKind: String, Sendable {
7785

7886
/// A synchronous get operation that returns the current value.
@@ -126,6 +134,9 @@ public struct AccessEvent: Sendable {
126134
}
127135

128136
/// The result of a configuration lookup from a specific provider.
137+
///
138+
/// Contains the provider's name and the outcome of querying that provider,
139+
/// which can be either a successful lookup result or an error.
129140
public struct ProviderResult: Sendable {
130141

131142
/// The name of the configuration provider that processed the lookup.
@@ -186,6 +197,17 @@ public struct AccessEvent: Sendable {
186197
/// Use this reporter to send configuration access events to multiple destinations
187198
/// simultaneously. Each upstream reporter receives a copy of every event in the
188199
/// order they were provided during initialization.
200+
///
201+
/// ```swift
202+
/// let fileLogger = try FileAccessLogger(filePath: "/tmp/config.log")
203+
/// let accessLogger = AccessLogger(logger: logger)
204+
/// let broadcaster = BroadcastingAccessReporter(upstreams: [fileLogger, accessLogger])
205+
///
206+
/// let config = ConfigReader(
207+
/// provider: EnvironmentVariablesProvider(),
208+
/// accessReporter: broadcaster
209+
/// )
210+
/// ```
189211
@available(Configuration 1.0, *)
190212
public struct BroadcastingAccessReporter: Sendable {
191213

Sources/Configuration/AccessReporters/AccessLogger.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ import Synchronization
4747
/// ## Log format
4848
///
4949
/// Each access event generates a structured log entry with metadata including:
50-
/// - `kind`: The type of access operation (get, fetch, watch)
51-
/// - `key`: The configuration key that was accessed
52-
/// - `location`: The source code location where the access occurred
53-
/// - `value`: The resolved configuration value (redacted for secrets)
54-
/// - `counter`: An incrementing counter for tracking access frequency
55-
/// - Provider-specific information for each provider in the hierarchy
50+
/// - `kind`: The type of access operation (get, fetch, watch).
51+
/// - `key`: The configuration key that was accessed.
52+
/// - `location`: The source code location where the access occurred.
53+
/// - `value`: The resolved configuration value (redacted for secrets).
54+
/// - `counter`: An incrementing counter for tracking access frequency.
55+
/// - Provider-specific information for each provider in the hierarchy.
5656
@available(Configuration 1.0, *)
5757
public final class AccessLogger: Sendable {
5858

Sources/Configuration/AccessReporters/FileAccessLogger.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ import Synchronization
5959
/// ```
6060
///
6161
/// The log entries include:
62-
/// - Status emoji (✅ success, 🟡 default/nil, ❌ error)
63-
/// - Configuration key that was accessed
64-
/// - Resolved value (redacted for secrets)
65-
/// - Provider that supplied the value or error information
66-
/// - Access metadata (operation type, value type, source location, timestamp)
62+
/// - Status emoji (✅ success, 🟡 default/nil, ❌ error).
63+
/// - Configuration key that was accessed.
64+
/// - Resolved value (redacted for secrets).
65+
/// - Provider that supplied the value or error information.
66+
/// - Access metadata (operation type, value type, source location, timestamp).
6767
@available(Configuration 1.0, *)
6868
public final class FileAccessLogger: Sendable {
6969

Sources/Configuration/ConfigKey.swift

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,24 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
/// A configuration key that represents a relative path to a configuration value.
15+
/// A configuration key representing a relative path to a configuration value.
1616
///
17-
/// Configuration keys consist of an array of string components that form a hierarchical
18-
/// path, similar to file system paths or JSON object keys. For example, the key
19-
/// `["http", "timeout"]` represents the value `timeout` nested underneath `http`.
17+
/// Configuration keys consist of hierarchical string components forming paths similar to
18+
/// file system paths or JSON object keys. For example, `["http", "timeout"]` represents
19+
/// the `timeout` value nested under `http`.
2020
///
21-
/// Keys can include additional context information that some providers use to
22-
/// refine value lookups or provide more specific results.
21+
/// Keys support additional context information that providers can use to refine lookups
22+
/// or provide specialized behavior.
23+
///
24+
/// ## Usage
25+
///
26+
/// Create keys using string literals, arrays, or the initializers:
27+
///
28+
/// ```swift
29+
/// let key1: ConfigKey = "database.connection.timeout"
30+
/// let key2 = ConfigKey(["api", "endpoints", "primary"])
31+
/// let key3 = ConfigKey("server.port", context: ["environment": .string("production")])
32+
/// ```
2333
@available(Configuration 1.0, *)
2434
public struct ConfigKey: Sendable {
2535

@@ -46,7 +56,7 @@ public struct ConfigKey: Sendable {
4656

4757
/// Creates a new configuration key.
4858
/// - Parameters:
49-
/// - string: The string represenation of the key path, for example `"http.timeout"`.
59+
/// - string: The string representation of the key path, for example `"http.timeout"`.
5060
/// - context: Additional context information for the key.
5161
public init(_ string: String, context: [String: ConfigContextValue] = [:]) {
5262
self = DotSeparatorKeyDecoder.decode(string, context: context)

Sources/Configuration/ConfigProvider.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ public protocol ConfigSnapshot: Sendable {
161161
}
162162

163163
/// The result of looking up a configuration value in a provider.
164+
///
165+
/// Providers return this result from value lookup methods, containing both the
166+
/// encoded key used for the lookup and the value found:
167+
///
168+
/// ```swift
169+
/// let result = try provider.value(forKey: key, type: .string)
170+
/// if let value = result.value {
171+
/// print("Found: \(value)")
172+
/// }
173+
/// ```
164174
@available(Configuration 1.0, *)
165175
public struct LookupResult: Sendable, Equatable, Hashable {
166176

@@ -435,9 +445,14 @@ public struct LookupResult: Sendable, Equatable, Hashable {
435445

436446
/// A configuration value that wraps content with metadata.
437447
///
438-
/// Configuration values include the actual content and a flag indicating whether
439-
/// the value contains sensitive information. Secret values are protected from
440-
/// accidental disclosure in logs and debug output.
448+
/// Configuration values pair raw content with a flag indicating whether the value
449+
/// contains sensitive information. Secret values are protected from accidental
450+
/// disclosure in logs and debug output:
451+
///
452+
/// ```swift
453+
/// let apiKey = ConfigValue(.string("sk-abc123"), isSecret: true)
454+
/// print(apiKey) // Prints: [string: <REDACTED>]
455+
/// ```
441456
@available(Configuration 1.0, *)
442457
public struct ConfigValue: Sendable, Equatable, Hashable {
443458

Sources/Configuration/ConfigSnapshotReader.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import Synchronization
1616

1717
/// A container type for reading config values from snapshots.
1818
///
19-
/// A config snapshot reader provides read-only access to config values stored in an underlying snapshot.
20-
/// Unlike ``ConfigReader``, which can access live, changing config values from providers, a snapshot reader
21-
/// works with a fixed, immutable snapshot of the configuration data.
19+
/// A config snapshot reader provides read-only access to config values stored in an underlying
20+
/// ``ConfigSnapshot``. Unlike a config reader, which can access live, changing config values
21+
/// from providers, a snapshot reader works with a fixed, immutable snapshot of the configuration data.
2222
///
2323
/// ## Usage
2424
///
25-
/// Get a ``ConfigSnapshotReader`` from a ``ConfigReader`` by using ``ConfigReader/snapshot()``
26-
/// to retrieve a snapshot. All values in the snapshot are guaranteed to be from the same point in time:
25+
/// Get a snapshot reader from a config reader by using the ``ConfigReader/snapshot()`` method. All values in the
26+
/// snapshot are guaranteed to be from the same point in time:
2727
/// ```swift
2828
/// // Get a snapshot from a ConfigReader
2929
/// let config = ConfigReader(provider: EnvironmentVariablesProvider())
@@ -37,7 +37,7 @@ import Synchronization
3737
/// let identity = MyIdentity(cert: cert, privateKey: privateKey)
3838
/// ```
3939
///
40-
/// Or you can watch for snapshot updates using the ``ConfigReader/watchSnapshot(fileID:line:updatesHandler:)``:
40+
/// Or you can watch for snapshot updates using the ``ConfigReader/watchSnapshot(fileID:line:updatesHandler:)`` method:
4141
///
4242
/// ```swift
4343
/// try await config.watchSnapshot { snapshots in
@@ -211,7 +211,7 @@ public struct ConfigSnapshotReader: Sendable {
211211
/// let timeout = httpConfig.int(forKey: "timeout") // Reads from "client.http.timeout" in the snapshot
212212
/// ```
213213
///
214-
/// - Parameters configKey: The key to append to the current key prefix.
214+
/// - Parameter configKey: The key to append to the current key prefix.
215215
/// - Returns: A reader for accessing scoped values.
216216
public func scoped(to configKey: ConfigKey)
217217
-> ConfigSnapshotReader

Sources/Configuration/Documentation.docc/Documentation.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ For example, to read the timeout configuration value for an HTTP client, check o
120120
```swift
121121
// Environment variables consulted first, then JSON.
122122
let primaryProvider = EnvironmentVariablesProvider()
123-
let secondaryProvider = try await JSONProvider(
123+
let secondaryProvider = try await FileProvider<JSONSnapshot>(
124124
filePath: "/etc/config.json"
125125
)
126126
let config = ConfigReader(providers: [
@@ -246,8 +246,7 @@ You can also implement a custom ``ConfigProvider``.
246246
In addition to using providers individually, you can create fallback behavior using an array of providers.
247247
The first provider that returns a non-nil value wins.
248248

249-
The following example illustrates a hierarchy of provides, with environmental variables overrides winning
250-
over command line arguments, a file at `/etc/config.json`, and in-memory defaults:
249+
The following example shows a provider hierarchy where environment variables take precedence over command line arguments, a JSON file, and in-memory defaults:
251250

252251
```swift
253252
// Create a hierarchy of providers with fallback behavior.

Sources/Configuration/Documentation.docc/Guides/Best-practices.md

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@ Follow these principles to make your code easily configurable and composable wit
44

55
## Overview
66

7-
When designing configuration for your Swift libraries and applications, following established patterns helps create
8-
a consistent and maintainable experience for developers. These best practices ensure your configuration integrates
9-
well with the broader Swift ecosystem.
7+
When designing configuration for Swift libraries and applications, follow these patterns to create consistent, maintainable code that integrates well with the Swift ecosystem.
108

119
### Document configuration keys
1210

13-
Include comprehensive documentation about what configuration keys your library reads. For each key, document:
11+
Include thorough documentation about what configuration keys your library reads. For each key, document:
1412

15-
- The key name and its hierarchical structure
16-
- The expected data type
17-
- Whether the key is required or optional
18-
- Default values when applicable
19-
- Valid value ranges or constraints
20-
- Usage examples
13+
- The key name and its hierarchical structure.
14+
- The expected data type.
15+
- Whether the key is required or optional.
16+
- Default values when applicable.
17+
- Valid value ranges or constraints.
18+
- Usage examples.
2119

2220
```swift
2321
public struct HTTPClientConfiguration {
@@ -38,8 +36,7 @@ public struct HTTPClientConfiguration {
3836

3937
### Use sensible defaults
4038

41-
Provide reasonable default values whenever possible to make your library work without extensive configuration.
42-
This reduces the barrier to adoption and ensures your library works out of the box for common use cases.
39+
Provide reasonable default values to make your library work without extensive configuration.
4340

4441
```swift
4542
// Good: Provides sensible defaults
@@ -114,8 +111,7 @@ For more details, check out <doc:Choosing-reader-methods>.
114111

115112
### Validate configuration values
116113

117-
Consider validating configuration values and throwing meaningful errors if they're invalid. This helps
118-
developers catch configuration issues early.
114+
Validate configuration values and throw meaningful errors for invalid input to catch configuration issues early.
119115

120116
```swift
121117
public init(config: ConfigReader) throws {

Sources/Configuration/Documentation.docc/Guides/Choosing-access-patterns.md

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ Learn how to select the right method for reading configuration values based on y
44

55
## Overview
66

7-
Swift Configuration provides three access patterns for retrieving configuration values, each optimized
8-
for different use cases and performance requirements.
7+
Swift Configuration provides three access patterns for retrieving configuration values, each optimized for different use cases and performance requirements.
98

109
The three access patterns are:
1110

@@ -44,13 +43,11 @@ Use the "get" pattern when:
4443

4544
- Returns the currently cached value from the provider.
4645
- No network or I/O operations occur during the call.
47-
- Values may become stale if the underlying data source changes and the provider is either non-reloading, or
48-
has a long reload interval.
46+
- Values may become stale if the underlying data source changes and the provider is either non-reloading, or has a long reload interval.
4947

5048
### Fetch: Asynchronous fresh access
5149

52-
The "fetch" pattern asynchronously retrieves the most current value from the authoritative data source. This ensures
53-
you always get up-to-date configuration, even if it requires network calls or file system access.
50+
The "fetch" pattern asynchronously retrieves the most current value from the authoritative data source, ensuring you always get up-to-date configuration.
5451

5552
```swift
5653
let config = ConfigReader(provider: remoteConfigProvider)
@@ -74,13 +71,12 @@ let dbConnectionString = try await config.fetchRequiredString(
7471

7572
#### When to use
7673

77-
Use the `fetch` pattern when:
74+
Use the "fetch" pattern when:
7875

7976
- **Freshness is critical**: You need the latest configuration values.
8077
- **Remote providers**: Using configuration services, databases, or external APIs that perform evaluation remotely.
8178
- **Infrequent access**: Reading configuration occasionally, not in hot paths.
82-
- **Setup operations**: Configuring long-lived resources like database connections where one-time overhead isn't
83-
a concern, and the improved freshness is important.
79+
- **Setup operations**: Configuring long-lived resources like database connections where one-time overhead isn't a concern, and the improved freshness is important.
8480
- **Administrative operations**: Fetching current settings for management interfaces.
8581

8682
#### Behavior characteristics
@@ -115,8 +111,8 @@ try await config.watchInt(forKey: "http.timeout", default: 30) { updates in
115111
Use the "watch" pattern when:
116112

117113
- **Dynamic configuration**: Values change during application runtime.
118-
- **Hot reloading**: Need to update behavior without restarting the service.
119-
- **Feature toggles**: Enabling/disabling features based on configuration changes.
114+
- **Hot reloading**: You need to update behavior without restarting the service.
115+
- **Feature toggles**: Enabling or disabling features based on configuration changes.
120116
- **Resource management**: Adjusting timeouts, limits, or thresholds dynamically.
121117
- **A/B testing**: Updating experimental parameters in real-time.
122118

@@ -149,8 +145,8 @@ let context: [String: ConfigContextValue] = [
149145
let dbConfig = try await config.fetchRequiredString(
150146
forKey: ConfigKey(
151147
"database.connection_string",
152-
context: context,
153-
)
148+
context: context
149+
),
154150
isSecret: true
155151
)
156152

@@ -171,16 +167,19 @@ try await config.watchInt(
171167
### Summary of performance considerations
172168

173169
#### Get pattern performance
170+
174171
- **Fastest**: No async overhead, immediate return.
175172
- **Memory usage**: Minimal, uses cached values.
176173
- **Best for**: Request handling, hot code paths, startup configuration.
177174

178-
#### Fetch pattern performance
175+
#### Fetch pattern performance
176+
179177
- **Moderate**: Async overhead plus data source access time.
180178
- **Network dependent**: Performance varies with provider implementation.
181179
- **Best for**: Infrequent access, setup operations, administrative tasks.
182180

183181
#### Watch pattern performance
182+
184183
- **Background monitoring**: Continuous resource usage for monitoring.
185184
- **Event-driven**: Efficient updates only when values change.
186185
- **Best for**: Long-running services, dynamic configuration, feature toggles.
@@ -228,18 +227,10 @@ try await config.watchRequiredInt(forKey: "port") { updates in
228227

229228
### Best practices
230229

231-
1. **Choose based on use case**: Use "get" for performance-critical paths, "fetch" for freshness, and
232-
"watch" for hot reloading.
233-
234-
2. **Handle errors appropriately**: Design error handling strategies that match your application's
235-
resilience requirements.
236-
237-
3. **Use context judiciously**: Provide context when you need environment-specific or conditional
238-
configuration values.
239-
240-
4. **Monitor configuration access**: Use ``AccessReporter`` to understand your application's
241-
configuration dependencies.
242-
230+
1. **Choose based on use case**: Use "get" for performance-critical paths, "fetch" for freshness, and "watch" for hot reloading.
231+
2. **Handle errors appropriately**: Design error handling strategies that match your application's resilience requirements.
232+
3. **Use context judiciously**: Provide context when you need environment-specific or conditional configuration values.
233+
4. **Monitor configuration access**: Use ``AccessReporter`` to understand your application's configuration dependencies.
243234
5. **Cache wisely**: For frequently accessed values, prefer "get" over repeated "fetch" calls.
244235

245236
For more guidance on selecting the right reader methods for your needs, see <doc:Choosing-reader-methods>.

0 commit comments

Comments
 (0)