Skip to content

Commit

Permalink
Merge pull request #634 from allevato/inline-config
Browse files Browse the repository at this point in the history
Allow JSON configuration text to be passed directly on the command line.
  • Loading branch information
allevato authored Sep 20, 2023
2 parents 2050b92 + 85b6ba6 commit 0e5e7b9
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 17 deletions.
7 changes: 6 additions & 1 deletion Sources/SwiftFormat/API/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,14 @@ public struct Configuration: Codable, Equatable {
/// ```
public var multiElementCollectionTrailingCommas: Bool

/// Constructs a Configuration by loading it from a configuration file.
/// Creates a new `Configuration` by loading it from a configuration file.
public init(contentsOf url: URL) throws {
let data = try Data(contentsOf: url)
try self.init(data: data)
}

/// Creates a new `Configuration` by decoding it from the UTF-8 representation in the given data.
public init(data: Data) throws {
self = try JSONDecoder().decode(Configuration.self, from: data)
}

Expand Down
41 changes: 27 additions & 14 deletions Sources/swift-format/Frontend/Frontend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class Frontend {
/// Processes source content from standard input.
private func processStandardInput() {
guard let configuration = configuration(
at: lintFormatOptions.configurationPath.map(URL.init(fileURLWithPath:)),
fromPathOrString: lintFormatOptions.configuration,
orInferredFromSwiftFileAt: nil)
else {
// Already diagnosed in the called method.
Expand Down Expand Up @@ -150,7 +150,7 @@ class Frontend {

guard
let configuration = configuration(
at: lintFormatOptions.configurationPath.map(URL.init(fileURLWithPath:)),
fromPathOrString: lintFormatOptions.configuration,
orInferredFromSwiftFileAt: url)
else {
// Already diagnosed in the called method.
Expand All @@ -161,31 +161,44 @@ class Frontend {
}

/// Returns the configuration that applies to the given `.swift` source file, when an explicit
/// configuration path is also perhaps provided. Checks for unrecognized rules within the configuration.
/// configuration path is also perhaps provided.
///
/// This method also checks for unrecognized rules within the configuration.
///
/// - Parameters:
/// - configurationFilePath: The path to a configuration file that will be loaded, or `nil` to
/// try to infer it from `swiftFilePath`.
/// - pathOrString: A string containing either the path to a configuration file that will be
/// loaded, JSON configuration data directly, or `nil` to try to infer it from
/// `swiftFilePath`.
/// - swiftFilePath: The path to a `.swift` file, which will be used to infer the path to the
/// configuration file if `configurationFilePath` is nil.
///
/// - Returns: If successful, the returned configuration is the one loaded from
/// `configurationFilePath` if it was provided, or by searching in paths inferred by
/// `swiftFilePath` if one exists, or the default configuration otherwise. If an error occurred
/// when reading the configuration, a diagnostic is emitted and `nil` is returned.
/// if neither `configurationFilePath` nor `swiftFilePath` were provided, a default `Configuration()` will be returned.
/// - Returns: If successful, the returned configuration is the one loaded from `pathOrString` if
/// it was provided, or by searching in paths inferred by `swiftFilePath` if one exists, or the
/// default configuration otherwise. If an error occurred when reading the configuration, a
/// diagnostic is emitted and `nil` is returned. If neither `pathOrString` nor `swiftFilePath`
/// were provided, a default `Configuration()` will be returned.
private func configuration(
at configurationFileURL: URL?,
fromPathOrString pathOrString: String?,
orInferredFromSwiftFileAt swiftFileURL: URL?
) -> Configuration? {
// If an explicit configuration file path was given, try to load it and fail if it cannot be
// loaded. (Do not try to fall back to a path inferred from the source file path.)
if let configurationFileURL = configurationFileURL {
if let pathOrString = pathOrString {
// If an explicit configuration file path was given, try to load it and fail if it cannot be
// loaded. (Do not try to fall back to a path inferred from the source file path.)
let configurationFileURL = URL(fileURLWithPath: pathOrString)
do {
let configuration = try configurationLoader.configuration(at: configurationFileURL)
self.checkForUnrecognizedRules(in: configuration)
return configuration
} catch {
// If we failed to load this from the path, try interpreting the string as configuration
// data itself because the user might have written something like `--configuration '{...}'`,
let data = pathOrString.data(using: .utf8)!
if let configuration = try? Configuration(data: data) {
return configuration
}

// Fail if the configuration flag was neither a valid file path nor valid configuration
// data.
diagnosticsEngine.emitError("Unable to read configuration: \(error.localizedDescription)")
return nil
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/swift-format/Subcommands/LintFormatOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ struct LintFormatOptions: ParsableArguments {
/// If not specified, the default configuration will be used.
@Option(
name: .customLong("configuration"),
help: "The path to a JSON file containing the configuration of the linter/formatter.")
var configurationPath: String?
help: """
The path to a JSON file containing the configuration of the linter/formatter or a JSON \
string containing the configuration directly.
""")
var configuration: String?

/// The filename for the source code when reading from standard input, to include in diagnostic
/// messages.
Expand Down

0 comments on commit 0e5e7b9

Please sign in to comment.