Skip to content

Allow JSON configuration text to be passed directly on the command line. #634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 20, 2023
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
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