From b9a26dba1b4070e4ac73f6856d7bcb5b30d4dd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=C3=A4rki?= Date: Thu, 12 Sep 2024 17:32:48 +0200 Subject: [PATCH] extend print macros --- .../UBFoundation/Globals/GlobalLogging.swift | 1 + .../UBFoundation/Logging/Logger+Error.swift | 1 + Sources/UBFoundation/Logging/Logger.swift | 2 + .../UBFoundation/Logging/LoggerGroup.swift | 1 + .../Logging/LoggingMacros+Shadowing.swift | 22 ++++++ .../{Macros.swift => LoggingMacros.swift} | 21 +++--- Sources/UBMacros/UBMacroPlugin.swift | 7 +- Sources/UBMacros/UBPrintMacro.swift | 67 ++++++++++++++++--- .../Logging/MacroTests.swift | 41 +++++++++++- 9 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 Sources/UBFoundation/Logging/LoggingMacros+Shadowing.swift rename Sources/UBFoundation/Logging/{Macros.swift => LoggingMacros.swift} (74%) diff --git a/Sources/UBFoundation/Globals/GlobalLogging.swift b/Sources/UBFoundation/Globals/GlobalLogging.swift index 5f741491..03927696 100644 --- a/Sources/UBFoundation/Globals/GlobalLogging.swift +++ b/Sources/UBFoundation/Globals/GlobalLogging.swift @@ -13,6 +13,7 @@ import os.log private var loggerGroup: UBLoggerGroup = UBLoggerGroup() /// A domain for framework logging manipulation +@available(*, message: "Use #print or OS.Logger instead") public enum UBLogging { /// Sets the global log level for all framework loggers /// diff --git a/Sources/UBFoundation/Logging/Logger+Error.swift b/Sources/UBFoundation/Logging/Logger+Error.swift index 9769902d..716ec6d8 100644 --- a/Sources/UBFoundation/Logging/Logger+Error.swift +++ b/Sources/UBFoundation/Logging/Logger+Error.swift @@ -8,6 +8,7 @@ import Foundation /// Errors thrown by the localization +@available(*, message: "Use #print or OS.Logger instead") public enum UBLoggingError: Error { /// The bundle identifier is not found case bundelIdentifierNotFound diff --git a/Sources/UBFoundation/Logging/Logger.swift b/Sources/UBFoundation/Logging/Logger.swift index 5588f0b9..4e19fa86 100644 --- a/Sources/UBFoundation/Logging/Logger.swift +++ b/Sources/UBFoundation/Logging/Logger.swift @@ -9,11 +9,13 @@ import Foundation import os.log +@available(*, message: "Use #print or OS.Logger instead") public protocol UBLoggerListener: AnyObject { func log(message: String) } /// A logger wrapper for the OSLog that provide an easy way to log. The UBLogger is thread safe. +@available(*, message: "Use #print or OS.Logger instead") public class UBLogger { /// The logger to use private let logger: OSLog diff --git a/Sources/UBFoundation/Logging/LoggerGroup.swift b/Sources/UBFoundation/Logging/LoggerGroup.swift index 7315f410..383a45eb 100644 --- a/Sources/UBFoundation/Logging/LoggerGroup.swift +++ b/Sources/UBFoundation/Logging/LoggerGroup.swift @@ -9,6 +9,7 @@ import Foundation /// A group of loggers. The UBLoggerGroup is thread safe +@available(*, message: "Use #print or OS.Logger instead") public class UBLoggerGroup { /// The backing data of the group private var _loggers: [UBLogger] diff --git a/Sources/UBFoundation/Logging/LoggingMacros+Shadowing.swift b/Sources/UBFoundation/Logging/LoggingMacros+Shadowing.swift new file mode 100644 index 00000000..ade64d2a --- /dev/null +++ b/Sources/UBFoundation/Logging/LoggingMacros+Shadowing.swift @@ -0,0 +1,22 @@ +// +// LoggingMacros+Shadowing.swift +// UBKit +// +// Created by Nicolas Märki on 12.09.2024. +// + +@available(*, message: "Use #print instead") +public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { + Swift.print(items, separator: separator, terminator: terminator) +} + +@available(*, message: "Use #assert instead") +public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) { + Swift.assert(condition(), message(), file: file, line: line) +} + +@available(*, message: "Use #assertionFailure instead") +public func assertionFailure(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) { + Swift.assertionFailure(message(), file: file, line: line) +} + diff --git a/Sources/UBFoundation/Logging/Macros.swift b/Sources/UBFoundation/Logging/LoggingMacros.swift similarity index 74% rename from Sources/UBFoundation/Logging/Macros.swift rename to Sources/UBFoundation/Logging/LoggingMacros.swift index a8aef0e9..7d1ea59f 100644 --- a/Sources/UBFoundation/Logging/Macros.swift +++ b/Sources/UBFoundation/Logging/LoggingMacros.swift @@ -8,27 +8,32 @@ import Foundation import os -#if DEBUG @available(iOS 14.0, *) @freestanding(expression) public macro print(_ message: OSLogMessage) = #externalMacro( module: "UBMacros", - type: "UBPrintMacroDebug" + type: "UBPrintMacro" ) -#else + @available(iOS 14.0, *) @freestanding(expression) -public macro print(_ message: OSLogMessage) = #externalMacro( +public macro printError(_ message: String) = #externalMacro( module: "UBMacros", - type: "UBPrintMacroRelease" + type: "UBPrintErrorMacro" ) -#endif @available(iOS 14.0, *) @freestanding(expression) -public macro printError(_ message: String) = #externalMacro( +public macro assert(_ condition: Bool, _ message: @autoclosure () -> String = String()) = #externalMacro( module: "UBMacros", - type: "UBPrintErrorMacro" + type: "UBAssertMacro" +) + +@available(iOS 14.0, *) +@freestanding(expression) +public macro assertionFailure(_ message: @autoclosure () -> String = String()) = #externalMacro( + module: "UBMacros", + type: "UBAssertionFailureMacro" ) @available(iOS 14.0, *) diff --git a/Sources/UBMacros/UBMacroPlugin.swift b/Sources/UBMacros/UBMacroPlugin.swift index 219a0e73..dab862f6 100644 --- a/Sources/UBMacros/UBMacroPlugin.swift +++ b/Sources/UBMacros/UBMacroPlugin.swift @@ -13,8 +13,9 @@ import SwiftSyntaxMacros struct UBMacroPlugin: CompilerPlugin { let providingMacros: [Macro.Type] = [ URLMacro.self, - UBPrintMacroDebug.self, - UBPrintMacroRelease.self, - UBPrintErrorMacro.self + UBPrintMacro.self, + UBPrintErrorMacro.self, + UBAssertMacro.self, + UBAssertionFailureMacro.self ] } diff --git a/Sources/UBMacros/UBPrintMacro.swift b/Sources/UBMacros/UBPrintMacro.swift index ae237c32..3efaafdb 100644 --- a/Sources/UBMacros/UBPrintMacro.swift +++ b/Sources/UBMacros/UBPrintMacro.swift @@ -12,31 +12,82 @@ import SwiftSyntaxMacros import SwiftParser import os -public struct UBPrintMacroDebug: ExpressionMacro { +public struct UBPrintMacro: ExpressionMacro { public static func expansion(of node: Node, in context: Context) throws -> ExprSyntax where Node : FreestandingMacroExpansionSyntax, Context : MacroExpansionContext { - guard let firstArgument = node.argumentList.first?.expression else { + #if DEBUG + guard node.argumentList.count == 1, let firstArgument = node.argumentList.first?.expression else { throw CustomError.message("Expected a single argument for #print") } return "UBPrintMacro.Logger.debug(\(firstArgument))" + #elseif RELEASE + return "UBPrintMacro.noop()" + #else + throw CustomError.message("Neither DEBUG or RELEASE is defined.") + #endif } } -public struct UBPrintMacroRelease: ExpressionMacro { +public struct UBPrintErrorMacro: ExpressionMacro { public static func expansion(of node: Node, in context: Context) throws -> ExprSyntax where Node : FreestandingMacroExpansionSyntax, Context : MacroExpansionContext { - return "UBPrintMacro.noop()" + guard node.argumentList.count == 1, let firstArgument = node.argumentList.first?.expression else { + throw CustomError.message("Expected a single argument for #printError") + } + return "{UBPrintMacro.Logger.critical(\(firstArgument))\nUBPrintMacro.sendError(\(firstArgument))}()" } } -public struct UBPrintErrorMacro: ExpressionMacro { +public struct UBAssertMacro: ExpressionMacro { public static func expansion(of node: Node, in context: Context) throws -> ExprSyntax where Node : FreestandingMacroExpansionSyntax, Context : MacroExpansionContext { - guard let firstArgument = node.argumentList.first?.expression else { - throw CustomError.message("Expected a single argument for #print") + guard let firstArgument = node.argumentList.first?.expression + else { + throw CustomError.message("Expected at least one argument for #assert") + } + if node.argumentList.count > 1, let secondArgument = node.argumentList.last?.expression { +#if DEBUG + return "{if !(\(firstArgument)) { UBPrintMacro.Logger.critical(\"Assertion failed: \\(\(secondArgument))\")\nSwift.assertionFailure() }}()" +#elseif RELEASE + return "{if !(\(firstArgument)) { UBPrintMacro.Logger.critical(\"Assertion failed: \\(\(secondArgument))\")\nUBPrintMacro.sendError(\"Assertion failed: \" + \(secondArgument)) }}()" +#else + throw CustomError.message("Neither DEBUG or RELEASE is defined.") +#endif + } + else { +#if DEBUG + return "{if !(\(firstArgument)) { UBPrintMacro.Logger.critical(\"Assertion failed.\")\nSwift.assertionFailure() }}()" +#elseif RELEASE + return "{if !(\(firstArgument)) { UBPrintMacro.Logger.critical(\"Assertion failed.\")\nUBPrintMacro.sendError(\"Assertion failed. \") }}()" +#else + throw CustomError.message("Neither DEBUG or RELEASE is defined.") +#endif + } + } +} + +public struct UBAssertionFailureMacro: ExpressionMacro { + public static func expansion(of node: Node, in context: Context) throws -> ExprSyntax where Node : FreestandingMacroExpansionSyntax, Context : MacroExpansionContext { + + if let firstArgument = node.argumentList.first?.expression { +#if DEBUG + return "{UBPrintMacro.Logger.critical(\"Assertion failed: \\(\(firstArgument))\")\nSwift.assertionFailure() }()" +#elseif RELEASE + return "{UBPrintMacro.Logger.critical(\"Assertion failed: \\(\(firstArgument))\")\nUBPrintMacro.sendError(\"Assertion failed: \" + \(firstArgument)) }()" +#else + throw CustomError.message("Neither DEBUG or RELEASE is defined.") +#endif + } + else { +#if DEBUG + return "{UBPrintMacro.Logger.critical(\"Assertion failed.)\")\nSwift.assertionFailure() }()" +#elseif RELEASE + return "{UBPrintMacro.Logger.critical(\"Assertion failed.\")\nUBPrintMacro.sendError(\"Assertion failed.\") }()" +#else + throw CustomError.message("Neither DEBUG or RELEASE is defined.") +#endif } - return "{UBPrintMacro.Logger.critical(\(firstArgument))\nUBPrintMacro.sendError(\(firstArgument))}()" } } diff --git a/Tests/UBFoundationTests/Logging/MacroTests.swift b/Tests/UBFoundationTests/Logging/MacroTests.swift index 8cf8bce5..7bfd1b65 100644 --- a/Tests/UBFoundationTests/Logging/MacroTests.swift +++ b/Tests/UBFoundationTests/Logging/MacroTests.swift @@ -19,7 +19,44 @@ class MacroTests: XCTestCase { } func testError() { - var obe = "Hello" - #printError("Failed to not fail \(obe)") + let exp = expectation(description: "Failed") + UBNonFatalErrorReporter.handler = { _ in + exp.fulfill() + } + + #printError("Failed to not fail") + + wait(for: [exp]) + } + + func testAssertTrue() { + #assert(true, "Test") + #assert(true) + } + + func testAssertFalse() { + let exp = expectation(description: "Failed") + UBNonFatalErrorReporter.handler = { _ in + exp.fulfill() + } + + #assert(false, "Test") + + wait(for: [exp]) + } + + func testAssertionFailure() { + let exp = expectation(description: "Failed") + exp.expectedFulfillmentCount = 2 + UBNonFatalErrorReporter.handler = { _ in + exp.fulfill() + } + + #assertionFailure("Failed") + #assertionFailure() + + wait(for: [exp]) } } + +