Skip to content

Commit 22a7d7a

Browse files
authored
Add the ability to set feature flags (#139)
Add the ability to set feature flags ### Motivation For a few upcoming features, we'd like to stage them into the current stable version and try them out, to get feedback and iterate before we tag a new breaking version. Some examples of breaking features that could benefit from this: - multiple request and response content types - improved OpenAPI -> Swift name mapping - new currency types based on swift-http-types - async request and response bodies It would be great to stage these in gradually, and consolidate a few of them into shared breaking versions, to avoid too much churn. ### Modifications This PR adds the concept of feature flags on the generator. They can be set either on the CLI, or in the config file. The idea is that the translator logic will later inspect the list of feature flags, and change behavior accordingly (not present in this PR). Note that I had to already add the `multipleContentTypes` feature flag, as keeping the enum empty broke the build. But that's okay, feature flags should be introduced early in the development process of a feature anyway. ### Result Now adopters can optionally enable pre-release features, and we can get feedback and run experiments before tagging a new breaking version. ### Test Plan Manually tested, with a debugger, that the feature flags propagate through the CLI machinery all the way to the generator pipeline. Reviewed by: simonjbeaumont Builds: ✔︎ pull request validation (5.8) - Build finished. ✔︎ pull request validation (5.9) - Build finished. ✔︎ pull request validation (docc test) - Build finished. ✔︎ pull request validation (integration test) - Build finished. ✔︎ pull request validation (nightly) - Build finished. ✔︎ pull request validation (soundness) - Build finished. #139
1 parent 7bb32a9 commit 22a7d7a

File tree

7 files changed

+75
-8
lines changed

7 files changed

+75
-8
lines changed

Sources/_OpenAPIGeneratorCore/Config.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
/// A single generator pipeline run produces exactly one file, so for
1919
/// generating multiple files, create multiple configuration values, each with
2020
/// a different generator mode.
21-
///
22-
/// TODO: Rename to something more specific, like "GeneratorConfig"
2321
public struct Config {
2422

2523
/// The generator mode to use.
@@ -28,14 +26,22 @@ public struct Config {
2826
/// Additional imports to add to each generated file.
2927
public var additionalImports: [String]
3028

29+
/// Additional pre-release features to enable.
30+
public var featureFlags: FeatureFlags
31+
3132
/// Creates a configuration with the specified generator mode and imports.
3233
/// - Parameters:
3334
/// - mode: The mode to use for generation.
34-
/// - additionalImports: Additional imports to add to each generated
35-
/// file.
36-
public init(mode: GeneratorMode, additionalImports: [String] = []) {
35+
/// - additionalImports: Additional imports to add to each generated file.
36+
/// - featureFlags: Additional pre-release features to enable.
37+
public init(
38+
mode: GeneratorMode,
39+
additionalImports: [String] = [],
40+
featureFlags: FeatureFlags = []
41+
) {
3742
self.mode = mode
3843
self.additionalImports = additionalImports
44+
self.featureFlags = featureFlags
3945
}
4046
}
4147

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator 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 SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
/// A feature that can be explicitly enabled before being released.
16+
///
17+
/// Commonly used to get early feedback on breaking changes, before
18+
/// they are enabled by default, which can only be done in a major version.
19+
///
20+
/// Once a feature is enabled unconditionally in the next major version,
21+
/// the corresponding feature flag should be removed at the same time.
22+
///
23+
/// For example: a breaking feature is being built while version 0.1 is out,
24+
/// and is hidden behind a feature flag. Once ready, the feature is
25+
/// enabled unconditionally on main and the feature flag removed, and version
26+
/// 0.2 is tagged. (This is for pre-1.0 versioning, would be 1.0 and 2.0 after
27+
/// 1.0 is released.)
28+
public enum FeatureFlag: String, Hashable, Equatable, Codable, CaseIterable {
29+
30+
/// Multiple request and response body content types.
31+
///
32+
/// Tracking issues:
33+
/// - https://github.com/apple/swift-openapi-generator/issues/6
34+
/// - https://github.com/apple/swift-openapi-generator/issues/7
35+
case multipleContentTypes
36+
}
37+
38+
/// A set of enabled feature flags.
39+
public typealias FeatureFlags = Set<FeatureFlag>

Sources/swift-openapi-generator/Extensions.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extension URL: ExpressibleByArgument {
2323
}
2424

2525
extension GeneratorMode: ExpressibleByArgument {}
26+
extension FeatureFlag: ExpressibleByArgument {}
2627

2728
extension CaseIterable where Self: RawRepresentable, Self.RawValue == String {
2829

Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ extension _GenerateOptions {
3333
let config = try loadedConfig()
3434
let sortedModes = try resolvedModes(config)
3535
let resolvedAdditionalImports = resolvedAdditionalImports(config)
36+
let resolvedFeatureFlags = resolvedFeatureFlags(config)
3637
let configs: [Config] = sortedModes.map {
3738
.init(
3839
mode: $0,
39-
additionalImports: resolvedAdditionalImports
40+
additionalImports: resolvedAdditionalImports,
41+
featureFlags: resolvedFeatureFlags
4042
)
4143
}
4244
let diagnostics: any DiagnosticCollector
@@ -57,6 +59,7 @@ extension _GenerateOptions {
5759
- OpenAPI document path: \(doc.path)
5860
- Configuration path: \(self.config?.path ?? "<none>")
5961
- Generator modes: \(sortedModes.map(\.rawValue).joined(separator: ", "))
62+
- Feature flags: \(resolvedFeatureFlags.isEmpty ? "<none>" : resolvedFeatureFlags.map(\.rawValue).joined(separator: ", "))
6063
- Output file names: \(sortedModes.map(\.outputFileName).joined(separator: ", "))
6164
- Output directory: \(outputDirectory.path)
6265
- Diagnostics output path: \(diagnosticsOutputPath?.path ?? "<none - logs to stderr>")

Sources/swift-openapi-generator/GenerateOptions.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ struct _GenerateOptions: ParsableArguments {
3131
)
3232
var mode: [GeneratorMode] = []
3333

34-
@Option(help: "Additional imports to add to all generated files.")
34+
@Option(help: "Additional import to add to all generated files.")
3535
var additionalImport: [String] = []
3636

37+
@Option(help: "Pre-release feature to enable. Options: \(FeatureFlag.prettyListing).")
38+
var featureFlag: [FeatureFlag] = []
39+
3740
@Option(
3841
help: "When specified, writes out the diagnostics into a YAML file instead of emitting them to standard error."
3942
)
@@ -77,6 +80,15 @@ extension _GenerateOptions {
7780
return []
7881
}
7982

83+
/// Returns a list of the feature flags requested by the user.
84+
/// - Parameter config: The configuration specified by the user.
85+
func resolvedFeatureFlags(_ config: _UserConfig?) -> FeatureFlags {
86+
if !featureFlag.isEmpty {
87+
return Set(featureFlag)
88+
}
89+
return Set(config?.featureFlags ?? [])
90+
}
91+
8092
/// Returns the configuration requested by the user.
8193
///
8294
/// - Returns: Loaded configuration, if found and parsed successfully.

Sources/swift-openapi-generator/UserConfig.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@ struct _UserConfig: Codable {
2626
/// A list of names of additional imports that are added to every
2727
/// generated Swift file.
2828
var additionalImports: [String]?
29+
30+
/// A set of features to explicitly enable.
31+
var featureFlags: FeatureFlags?
2932
}

Tests/OpenAPIGeneratorReferenceTests/FileBasedReferenceTests.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ struct TestConfig: Encodable {
2020
var docFilePath: String
2121
var mode: GeneratorMode
2222
var additionalImports: [String]?
23+
var featureFlags: FeatureFlags?
2324
var referenceOutputDirectory: String
2425
}
2526

2627
extension TestConfig {
2728
var asConfig: Config {
2829
.init(
2930
mode: mode,
30-
additionalImports: additionalImports ?? []
31+
additionalImports: additionalImports ?? [],
32+
featureFlags: featureFlags ?? []
3133
)
3234
}
3335
}
@@ -134,6 +136,7 @@ class FileBasedReferenceTests: XCTestCase {
134136
docFilePath: "Docs/\(name.fileName)",
135137
mode: mode,
136138
additionalImports: [],
139+
featureFlags: [],
137140
referenceOutputDirectory: "ReferenceSources/\(name.directoryName)"
138141
)
139142
)

0 commit comments

Comments
 (0)