Skip to content

Commit

Permalink
Move diagnostics collector logic into prepare function
Browse files Browse the repository at this point in the history
  • Loading branch information
PARAIPAN9 committed Aug 15, 2024
1 parent f2c4b43 commit 4c1d323
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 57 deletions.
2 changes: 1 addition & 1 deletion Sources/_OpenAPIGeneratorCore/Diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public protocol DiagnosticCollector {
///
/// If a diagnostic with a severity of `.error` is emitted, this collector will throw the diagnostic as an error.
public struct ErrorThrowingDiagnosticCollector: DiagnosticCollector {
private let upstream: any DiagnosticCollector
let upstream: any DiagnosticCollector

/// Initializes a new `ErrorThrowingDiagnosticCollector` with an upstream `DiagnosticCollector`.
///
Expand Down
49 changes: 49 additions & 0 deletions Sources/_OpenAPIGeneratorCore/DiagnosticsCollectorProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftOpenAPIGenerator open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// A feature that can be explicitly enabled before being released.
///
/// Commonly used to get early feedback on breaking changes, before
/// they are enabled by default, which can only be done in a major version.
///
/// Once a feature is enabled unconditionally in the next major version,
/// the corresponding feature flag should be removed at the same time.
///
/// For example: a breaking feature is being built while version 0.1 is out,
/// and is hidden behind a feature flag. Once ready, the feature is
/// enabled unconditionally on main and the feature flag removed, and version
/// 0.2 is tagged. (This is for pre-1.0 versioning, would be 1.0 and 2.0 after
/// 1.0 is released.)
import Foundation

/// Prepares a diagnostics collector.
/// - Parameter outputPath: A file path where to persist the YAML file. If `nil`, diagnostics will be printed to stderr.
/// - Returns: A tuple containing:
/// - An instance of `DiagnosticCollector` conforming to `Sendable`.
/// - A closure to finalize the diagnostics collection
public func preparedDiagnosticsCollector(outputPath: URL?) -> (any DiagnosticCollector & Sendable, () throws -> Void) {
let innerDiagnostics: any DiagnosticCollector & Sendable
let finalizeDiagnostics: () throws -> Void

if let outputPath {
let _diagnostics = _YamlFileDiagnosticsCollector(url: outputPath)
finalizeDiagnostics = _diagnostics.finalize
innerDiagnostics = _diagnostics
} else {
innerDiagnostics = StdErrPrintingDiagnosticCollector()
finalizeDiagnostics = {}
}
let diagnostics = ErrorThrowingDiagnosticCollector(upstream: innerDiagnostics)
return (diagnostics, finalizeDiagnostics)
}
17 changes: 4 additions & 13 deletions Sources/_OpenAPIGeneratorCore/YamlFileDiagnosticsCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct _DiagnosticsYamlFileContent: Encodable {
}

/// A collector that writes diagnostics to a YAML file.
final public class _YamlFileDiagnosticsCollector: DiagnosticCollector, @unchecked Sendable {
final class _YamlFileDiagnosticsCollector: DiagnosticCollector, @unchecked Sendable {
/// Protects `diagnostics`.
private let lock = NSLock()

Expand All @@ -32,19 +32,17 @@ final public class _YamlFileDiagnosticsCollector: DiagnosticCollector, @unchecke

/// Creates a new collector.
/// - Parameter url: A file path where to persist the YAML file.
public init(url: URL) { self.url = url }
init(url: URL) { self.url = url }

/// Emits a diagnostic message to the collector.
/// - Parameter diagnostic: The diagnostic message to be collected.
public func emit(_ diagnostic: Diagnostic) {
func emit(_ diagnostic: Diagnostic) {
lock.lock()
defer { lock.unlock() }
diagnostics.append(diagnostic)
}

/// Finishes writing to the collector by persisting the accumulated
/// diagnostics to a YAML file.
public func finalize() throws {
func finalize() throws {
lock.lock()
defer { lock.unlock() }
let sortedDiagnostics = diagnostics.sorted(by: { a, b in a.description < b.description })
Expand All @@ -55,10 +53,3 @@ final public class _YamlFileDiagnosticsCollector: DiagnosticCollector, @unchecke
try encoder.encode(container).write(to: url, atomically: true, encoding: .utf8)
}
}

/// Prepares a diagnostics collector.
/// - Parameter url: A file path where to persist the YAML file.
/// - Returns: An instance of `DiagnosticCollector` conforming to `Sendable`.
public func preparedDiagnosticsCollector(url: URL) -> any DiagnosticCollector & Sendable {
_YamlFileDiagnosticsCollector(url: url)
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,7 @@ extension _GenerateOptions {
featureFlags: resolvedFeatureFlags
)
}
let innerDiagnostics: any DiagnosticCollector & Sendable
let finalizeDiagnostics: () throws -> Void
if let diagnosticsOutputPath {
let _diagnostics = preparedDiagnosticsCollector(url: diagnosticsOutputPath)
if let yamlCollector = _diagnostics as? _YamlFileDiagnosticsCollector {
finalizeDiagnostics = { try yamlCollector.finalize() }
} else {
finalizeDiagnostics = {}
}
innerDiagnostics = _diagnostics
} else {
innerDiagnostics = StdErrPrintingDiagnosticCollector()
finalizeDiagnostics = {}
}
let diagnostics = ErrorThrowingDiagnosticCollector(upstream: innerDiagnostics)
let (diagnostics, finalizeDiagnostics) = preparedDiagnosticsCollector(outputPath: diagnosticsOutputPath)
let doc = self.docPath
print(
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftOpenAPIGenerator open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import XCTest
@testable import _OpenAPIGeneratorCore

final class Test_DiagnosticsCollectorProvider: XCTestCase {

func testPreparedDiagnosticsCollectorWithOutputPath() throws {
let outputPath = URL(fileURLWithPath: "/path/to/diagnostics.yaml")
let (diagnostics, _) = preparedDiagnosticsCollector(outputPath: outputPath)
XCTAssertTrue(diagnostics is ErrorThrowingDiagnosticCollector)

if let errorThrowingCollector = diagnostics as? ErrorThrowingDiagnosticCollector {
XCTAssertTrue(errorThrowingCollector.upstream is _YamlFileDiagnosticsCollector)
} else {
XCTFail("Expected diagnostics to be `ErrorThrowingDiagnosticCollector`")
}
}

func testPreparedDiagnosticsCollectorWithoutOutputPath() throws {
let outputPath: URL? = nil
let (diagnostics, _) = preparedDiagnosticsCollector(outputPath: outputPath)
XCTAssertTrue(diagnostics is ErrorThrowingDiagnosticCollector)
if let errorThrowingCollector = diagnostics as? ErrorThrowingDiagnosticCollector {
XCTAssertTrue(errorThrowingCollector.upstream is StdErrPrintingDiagnosticCollector)
} else {
XCTFail("Expected diagnostics to be `ErrorThrowingDiagnosticCollector`")
}
}
}

This file was deleted.

0 comments on commit 4c1d323

Please sign in to comment.