From e38f63095abc939b9c7c0fd2b8e5b7f2936dc5c2 Mon Sep 17 00:00:00 2001 From: Tim Wang Date: Sat, 1 Jul 2023 17:34:30 +1000 Subject: [PATCH] Add Docc --- Package.resolved | 18 + Package.swift | 1 + README.md | 6 +- Sources/Client/main.swift | 12 +- Sources/Macros/AddInit.swift | 2 + Sources/SwiftMacros/AccessContentType.swift | 6 + .../documentation-art/macro-icon.png | Bin 0 -> 9152 bytes .../SwiftMacros.docc/SwiftMacros.md | 35 ++ Sources/SwiftMacros/SwiftMacros.swift | 444 +++++++++++++++++- 9 files changed, 518 insertions(+), 6 deletions(-) create mode 100644 Sources/SwiftMacros/SwiftMacros.docc/Resources/documentation-art/macro-icon.png create mode 100644 Sources/SwiftMacros/SwiftMacros.docc/SwiftMacros.md diff --git a/Package.resolved b/Package.resolved index c4f9794..d650310 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,23 @@ { "pins" : [ + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-plugin", + "state" : { + "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index ff86be9..d39d014 100644 --- a/Package.swift +++ b/Package.swift @@ -14,6 +14,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b"), + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0"), ], targets: [ .macro( diff --git a/README.md b/README.md index 0042709..f2ba15c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A practical collection of Swift Macros that help code correctly and swiftly. .package(url: "https://github.com/ShenghaiWang/SwiftMacros.git", from: "1.0.0") -## Macros +## Macros [API Doc](https://shenghaiwang.github.io/swiftmacros/documentation/swiftmacros/) | Macro | Description | |------------|------------------------------------------------------------| @@ -14,9 +14,9 @@ A practical collection of Swift Macros that help code correctly and swiftly. | |
struct TestAccess {
static let cache = NSCache()

// Please make sure the generic type is the same as the type of the variable
// Without defalut value
@Access(.userDefaults())
var isPaidUser: Bool?

// With default value
@Access< Bool>(.userDefaults())
var isPaidUser2: Bool = false

@Access(.nsCache(TestAccess.cache))
var hasPaid: NSObject?

@Access(.nsMapTable(TestAccess.mapTable))
var hasPaid2: NSObject?
}
| |@AddAssociatedValueVariable|Add variables to retrieve the associated values| | |
@AddAssociatedValueVariable
enum MyEnum {
case first
case second(Int)
case third(String, Int)
case forth(a: String, b: Int), forth2(String, Int)
case fifth(() -> Void)
}
| -| @AddPublisher |Generate a Combine publisher to a Combine subject so that we can have a limited ACL for the subject | +| @AddInit |Generate initialiser for the class/struct/actor. The variables with optional types will have nil as default values. Using `withMock: true` if want to generate mock data.
For custmoised data type, it will use `Type.mock`. In case there is no this value, need to define this yourself or use `@Mock` or `@AddInit(withMock: true)` to generate this variable. | +| @AddPublisher |Generate a Combine publisher to a Combine subject in order to avoid overexposing subject variable | | |
@AddPublisher
private let mySubject = PassthroughSubject()
| -| @AddInit |Generate initialiser for the class/struct/actor. the variables with optional types will have nil as default values. Using `withMock: true` if want to generate mock data.
For custmoised data type, it will use `Type.mock`. In case there is no this value, need to define this yourself or use `@Mock` or `@AddInit(withMock: true)` to generate this variable. | | |
@AddInit
struct InitStruct {
let a: Int
let b: Int?
let c: (Int?) -> Void
let d: ((Int?) -> Void)?
}
@AddInit(withMock: true)
class AStruct {
let a: Float
}
| | #buildDate |Build a Date from components
This solution addes in a resultBulder `DateBuilder`, which can be used directly if prefer builder pattern.
Note: this is for a simpler API. Please use it with caution in places that require efficiency.| | |
let date = #buildDate(DateString("03/05/2003", dateFormat: "MM/dd/yyyy"),
Date(),
Month(10),
Year(1909),
YearForWeekOfYear(2025))
| diff --git a/Sources/Client/main.swift b/Sources/Client/main.swift index bb56ca2..082c32e 100644 --- a/Sources/Client/main.swift +++ b/Sources/Client/main.swift @@ -22,6 +22,11 @@ struct TestStruct: Codable { var name = "Tim Wang" } +@Singleton +struct A { + +} + let data = try #encode(TestStruct(), dateEncodingStrategy: .iso8601, dataEncodingStrategy: .base64) let value = try #decode(TestStruct.self, from: data, dateDecodingStrategy: .deferredToDate) @@ -48,7 +53,7 @@ struct InitStruct { let d: ((Int?) -> Void)? } -@AddInit +@AddInit(withMock: true) actor InitActor { let a: Int let b: Int? @@ -65,6 +70,11 @@ enum MyEnum { case seventh(() -> Void) } +@AddAssociatedValueVariable + enum TestEnum { + case test(Int) +} + assert(MyEnum.first.forth2Value == nil) let url = #buildURL("http://google.com", diff --git a/Sources/Macros/AddInit.swift b/Sources/Macros/AddInit.swift index 53415bf..9acb3ad 100644 --- a/Sources/Macros/AddInit.swift +++ b/Sources/Macros/AddInit.swift @@ -89,6 +89,8 @@ extension SimpleTypeIdentifierSyntax { guard let type = self.as(SimpleTypeIdentifierSyntax.self)?.name.text else { return nil } if let fun = mockFunctions[type] { return fun(randomValue) + } else if name.text == "Void" { + return "return" } else if name.text != "Set" { return "\(type).mock" } diff --git a/Sources/SwiftMacros/AccessContentType.swift b/Sources/SwiftMacros/AccessContentType.swift index 12df3b7..ab2d497 100644 --- a/Sources/SwiftMacros/AccessContentType.swift +++ b/Sources/SwiftMacros/AccessContentType.swift @@ -1,7 +1,13 @@ import Foundation public enum AccessContentType { + /// Retrieve value from or store value to [UserDefault](https://developer.apple.com/documentation/foundation/userdefaults). + /// Default to [.standard](https://developer.apple.com/documentation/foundation/userdefaults/1416603-standard). case userDefaults(UserDefaults = .standard) + /// Retrieve value from or store value to [NSCache](https://developer.apple.com/documentation/foundation/nscache). + /// Have to provide an instance of ****. case nsCache(NSCache) + /// Retrieve value from or store value to [NSMAPTable](https://developer.apple.com/documentation/foundation/nsmaptable) + /// Have to provide an instance of **NSMapTable**. case nsMapTable(NSMapTable) } diff --git a/Sources/SwiftMacros/SwiftMacros.docc/Resources/documentation-art/macro-icon.png b/Sources/SwiftMacros/SwiftMacros.docc/Resources/documentation-art/macro-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17739cf6b1e931e36be868f27a29f2afba00c779 GIT binary patch literal 9152 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 literal 0 HcmV?d00001 diff --git a/Sources/SwiftMacros/SwiftMacros.docc/SwiftMacros.md b/Sources/SwiftMacros/SwiftMacros.docc/SwiftMacros.md new file mode 100644 index 0000000..0c1c988 --- /dev/null +++ b/Sources/SwiftMacros/SwiftMacros.docc/SwiftMacros.md @@ -0,0 +1,35 @@ +# ``SwiftMacros`` + +A practical collection of Swift Macros that help code correctly and swiftly. + +@Metadata { + @PageImage(purpose: icon, source: "macro-icon", alt: "Swift-Macros") + @PageColor(green) +} + +## Overview + +This collection of Swift Macros aims to remove boilerplate code by automatically generating needed code for a rich set of purposes. + +## Topics + +### Macros + +- ``Access(_:)`` +- ``AddAssociatedValueVariable`` +- ``AddInit(withMock:randomMockValue:)`` +- ``AddPublisher`` +- ``buildDate(_:)`` +- ``buildURL(_:)`` +- ``buildURLRequest(_:)`` +- ``encode(_:outputFormatting:dateEncodingStrategy:dataEncodingStrategy:nonConformingFloatEncodingStrategy:keyEncodingStrategy:userInfo:)`` +- ``decode(_:from:dateDecodingStrategy:dataDecodingStrategy:nonConformingFloatDecodingStrategy:keyDecodingStrategy:userInfo:allowsJSON5:assumesTopLevelDictionary:)`` +- ``formatDate(_:dateStyle:timeStyle:formattingContext:formatterBehavior:doesRelativeDateFormatting:amSymbol:pmSymbol:weekdaySymbols:shortWeekdaySymbols:veryShortWeekdaySymbols:standaloneWeekdaySymbols:shortStandaloneWeekdaySymbols:veryShortStandaloneWeekdaySymbols:monthSymbols:shortMonthSymbols:veryShortMonthSymbols:standaloneMonthSymbols:shortStandaloneMonthSymbols:veryShortStandaloneMonthSymbols:quarterSymbols:shortQuarterSymbols:standaloneQuarterSymbols:shortStandaloneQuarterSymbols:eraSymbols:longEraSymbols:)`` +- ``formatDateComponents(fromComponents:allowedUnits:allowsFractionalUnits:calendar:collapsesLargestUnit:includesApproximationPhrase:includesTimeRemainingPhrase:maximumUnitCount:unitsStyle:zeroFormattingBehavior:formattingContext:referenceDate:)`` +- ``formatDateComponents(from:to:allowedUnits:allowsFractionalUnits:calendar:collapsesLargestUnit:includesApproximationPhrase:includesTimeRemainingPhrase:maximumUnitCount:unitsStyle:zeroFormattingBehavior:formattingContext:referenceDate:)`` +- ``formatDateComponents(fromInterval:allowedUnits:allowsFractionalUnits:calendar:collapsesLargestUnit:includesApproximationPhrase:includesTimeRemainingPhrase:maximumUnitCount:unitsStyle:zeroFormattingBehavior:formattingContext:referenceDate:)`` +- ``formatDateInterval(from:to:dateStyle:timeStyle:dateTemplate:calendar:locale:timeZone:)`` +- ``Mock(type:randomMockValue:)`` +- ``postNotification(_:object:userInfo:from:)`` +- ``Singleton`` + diff --git a/Sources/SwiftMacros/SwiftMacros.swift b/Sources/SwiftMacros/SwiftMacros.swift index c37c7ba..a57d6d0 100644 --- a/Sources/SwiftMacros/SwiftMacros.swift +++ b/Sources/SwiftMacros/SwiftMacros.swift @@ -1,26 +1,280 @@ import Foundation +/// An easy interface to retrieve value from or store value to ``AccessContentType`` like [UserDefault](https://developer.apple.com/documentation/foundation/userdefaults), [NSCache](https://developer.apple.com/documentation/foundation/nscache), [NSMAPTable](https://developer.apple.com/documentation/foundation/nsmaptable). +/// +/// - Parameters: +/// - type: One type of ``AccessContentType``. +/// +/// For example: +/// ```swift +/// struct TestAccess { +/// static let cache = NSCache() +/// static let mapTable = NSMapTable(keyOptions: .copyIn, valueOptions: .weakMemory) +/// +/// // without defalut value +/// // make sure the generic type is the same as the type of the variable +/// @Access(.userDefaults()) +/// var isPaidUser: Bool? +/// +/// // with default value +/// @Access(.userDefaults()) +/// var isPaidUser2: Bool = false +/// +/// @Access(.nsCache(TestAccess.cache)) +/// var hasPaid: NSObject? +/// +/// @Access(.nsMapTable(TestAccess.mapTable)) +/// var hasPaid2: NSObject? +/// } +/// ``` +/// will expand to +/// +/// ```swift +/// struct TestAccess { +/// static let cache = NSCache() +/// static let mapTable = NSMapTable(keyOptions: .copyIn, valueOptions: .weakMemory) +/// +/// // without defalut value +/// // make sure the generic type is the same as the type of the variable +/// var isPaidUser: Bool? +/// { +/// get { +/// (UserDefaults.standard.object(forKey: "AccessKey_isPaidUser") as? Bool) +/// } +/// +/// set { +/// UserDefaults.standard.set(newValue, forKey: "AccessKey_isPaidUser") +/// } +/// } +/// +/// // with default value +/// var isPaidUser2: Bool = false +/// { +/// get { +/// (UserDefaults.standard.object(forKey: "AccessKey_isPaidUser2") as? Bool) ?? false +/// } +/// +/// set { +/// UserDefaults.standard.set(newValue, forKey: "AccessKey_isPaidUser2") +/// } +/// } +/// +/// var hasPaid: NSObject? +/// { +/// get { +/// (TestAccess.cache.object(forKey: "AccessKey_hasPaid") as? NSObject) +/// } +/// +/// set { +/// if let value = newValue { +/// TestAccess.cache.setObject(value, forKey: "AccessKey_hasPaid") +/// } else { +/// TestAccess.cache.removeObject(forKey: "AccessKey_hasPaid") +/// } +/// } +/// } +/// +/// var hasPaid2: NSObject? +/// { +/// get { +/// (TestAccess.mapTable.object(forKey: "AccessKey_hasPaid2") as? NSObject) +/// } +/// +/// set { +/// if let value = newValue { +/// TestAccess.mapTable.setObject(value, forKey: "AccessKey_hasPaid2") +/// } else { +/// TestAccess.mapTable.removeObject(forKey: "AccessKey_hasPaid2") +/// } +/// } +/// } +/// } +/// ``` @attached(accessor) public macro Access(_ type: AccessContentType) = #externalMacro(module: "Macros", type: "Access") +/// Add variables for cases that have associated values in order to easily retrieve them +/// +/// For example: +/// +/// ```swift +/// @AddAssociatedValueVariable +/// enum TestEnum { +/// case test(Int) +/// } +/// ``` +/// +/// will expand to +/// +/// ```swift +/// enum TestEnum { +/// case test(Int) +/// var testValue: Int? { +/// if case let .test(v0) = self { +/// return v0 +/// } +/// return nil +/// } +/// } +/// ``` @attached(member, names: arbitrary) public macro AddAssociatedValueVariable() = #externalMacro(module: "Macros", type: "AddAssociatedValueVariable") +/// Generate initialiser for the class/struct/actor. +/// +/// - Parameters: +/// - withMock: true - if want to add a mock value as the same time. Default to false. +/// - randomMockValue: true - if want to have random value for the mocked variable. Default to true. +/// +/// For example: +/// +/// ```swift +/// @AddInit +/// struct InitStruct { +/// let a: Int +/// let b: Int? +/// let c: (Int?) -> Void +/// let d: ((Int?) -> Void)? +/// } +/// ``` +/// will expand to +/// +/// ```swift +/// struct InitStruct { +/// let a: Int +/// let b: Int? +/// let c: (Int?) -> Void +/// let d: ((Int?) -> Void)? +/// init(a: Int, b: Int? = nil, c: @escaping (Int?) -> Void, d: ((Int?) -> Void)? = nil) { +/// self.a = a +/// self.b = b +/// self.c = c +/// self.d = d +/// } +/// } +///``` +/// And +/// +/// ```swift +/// @AddInit(withMock: true) +/// struct InitStruct { +/// let a: Int +/// let b: Int? +/// let c: (Int?) -> Void +/// let d: ((Int?) -> Void)? +/// } +/// ``` +/// will expand to +/// +/// ```swift +/// struct InitStruct { +/// let a: Int +/// let b: Int? +/// init(a: Int, b: Int? = nil) { +/// self.a = a +/// self.b = b +/// } +/// #if DEBUG +/// static let mock = InitStruct(a: 4285361067953494500, b: -2664036447940829071) +/// #endif +/// } +/// ``` @attached(member, names: named(init), named(mock)) public macro AddInit(withMock: Bool = false, randomMockValue: Bool = true) = #externalMacro(module: "Macros", type: "AddInit") +/// Generate a Combine publisher to a Combine subject in order to avoid overexposing subject variable +/// +/// For example: +/// struct MyType { +/// @AddPublisher +/// private let mySubject = PassthroughSubject() +/// } +/// +/// will expand to +/// +/// ```swift +/// struct MyType { +/// private let mySubject = PassthroughSubject() +/// var mySubjectPublisher: AnyPublisher { +/// mySubject.eraseToAnyPublisher() +/// } +/// } +/// ``` @attached(peer, names: suffixed(Publisher)) public macro AddPublisher() = #externalMacro(module: "Macros", type: "AddPublisher") +/// Build a Date from components. +/// This solution addes in a resultBulder `DateBuilder`, +/// which can be used directly if prefer builder pattern. +/// +/// For example: +/// +/// ```swift +/// let date = #buildDate(DateString("03/05/2003", dateFormat: "MM/dd/yyyy"), +/// Date(), +/// Month(10), +/// Year(1909), +/// YearForWeekOfYear(2025)) +/// +/// // or use build pattern directly +/// let date = buildDate(DateString("03/05/2003", dateFormat: "MM/dd/yyyy") +/// Date() +/// Month(10) +/// Year(1909) +/// YearForWeekOfYear(2025)) +/// ``` +/// Note: this is for a simpler API. Please use it with caution in places that require efficiency. @freestanding(expression) public macro buildDate(_ components: DateComponent...) -> Date? = #externalMacro(module: "Macros", type: "BuildDate") +/// Build a url from components. +/// This solution addes in a resultBulder `URLBuilder`, which can be used directly if prefer builder pattern. +/// +/// For exmaple: +/// ```swift +/// let url = #buildURL("http://google.com", +/// URLScheme.https, +/// URLQueryItems([.init(name: "q1", value: "q1v"), .init(name: "q2", value: "q2v")])) +/// +/// // or use build pattern directly: +/// let url2 = buildURL { +/// "http://google.com" +/// URLScheme.https +/// URLQueryItems([.init(name: "q1", value: "q1v"), .init(name: "q2", value: "q2v")]) +/// } +/// ``` @freestanding(expression) public macro buildURL(_ components: URLComponent...) -> URL? = #externalMacro(module: "Macros", type: "BuildURL") +/// Build a URLRequest from components. +/// This solution addes in a resultBulder URLRequestBuilder, which can be used directly if prefer builder pattern. +/// For example: +/// ```swift +/// let urlrequest = #buildURLRequest(url!, RequestTimeOutInterval(100)) +/// +/// // or use build pattern +/// let urlRequest2 = buildURLRequest { url! +/// RequestTimeOutInterval(100) +/// } +/// ``` @freestanding(expression) public macro buildURLRequest(_ components: URLRequestComponent...) -> URLRequest? = #externalMacro(module: "Macros", type: "BuildURLRequest") +/// Encode an Encodable to data using JSONEncoder +/// +/// - Parameters: +/// - value: the value to be encoded. Need to conform Encodable +/// - outputFormatting: [JSONEncoder.OutputFormatting](https://developer.apple.com/documentation/foundation/jsonencoder/outputformatting) +/// - dataEncodingStrategy: [JSONEncoder.DataEncodingStrategy](https://developer.apple.com/documentation/foundation/jsonencoder/dataencodingstrategy) +/// - nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy +/// - keyEncodingStrategy: [JSONEncoder.KeyEncodingStrategy](https://developer.apple.com/documentation/foundation/jsonencoder/nonconformingfloatencodingstrategy) +/// - userInfo: [[CodingUserInfoKey: Any]](https://developer.apple.com/documentation/foundation/jsonencoder/2895176-userinfo) +/// +/// For example: +/// ```swift +/// let data = #encode(value) +/// let anotherData = #encode(value, dateEncodingStrategy: .secondsSince1970) +/// ``` @freestanding(expression) public macro encode(_ value: Encodable, outputFormatting: JSONEncoder.OutputFormatting = [], @@ -29,6 +283,23 @@ public macro encode(_ value: Encodable, nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .throw, keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy = .useDefaultKeys, userInfo: [CodingUserInfoKey: Any] = [:]) -> Data = #externalMacro(module: "Macros", type: "Encode") + +/// Decode a Decodable to a typed value using JSONDecoder +/// - Parameters: +/// - type: The type of the value to decode from the supplied JSON object. +/// - dateDecodingStrategy: [JSONDecoder.DateDecodingStrategy](https://developer.apple.com/documentation/foundation/jsondecoder/datedecodingstrategy) +/// - dataDecodingStrategy: [JSONDecoder.DataDecodingStrategy](https://developer.apple.com/documentation/foundation/jsondecoder/datadecodingstrategy) +/// - nonConformingFloatDecodingStrategy: [JSONDecoder.NonConformingFloatDecodingStrategy](https://developer.apple.com/documentation/foundation/jsondecoder/nonconformingfloatdecodingstrategy) +/// - keyDecodingStrategy: [JSONDecoder.KeyDecodingStrategy](https://developer.apple.com/documentation/foundation/jsondecoder/keydecodingstrategy) +/// - userInfo: [[CodingUserInfoKey: Any]](https://developer.apple.com/documentation/foundation/jsondecoder/2895340-userinfo) +/// - allowsJSON5: [Bool](https://developer.apple.com/documentation/foundation/jsondecoder/3766916-allowsjson5) +/// - assumesTopLevelDictionary: [Bool](https://developer.apple.com/documentation/foundation/jsondecoder/3766917-assumestopleveldictionary) +/// +/// For exmample: +/// ```swift +/// let value = #decode(TestStruct.self, from: data) +/// let anotherValue = #decode(TestStruct.self, from: data, dateDecodingStrategy: .iso8601) +/// ``` @freestanding(expression) public macro decode(_ type: T.Type, from value: Data, @@ -40,11 +311,44 @@ public macro decode(_ type: T.Type, allowsJSON5: Bool = false, assumesTopLevelDictionary: Bool = false) -> T = #externalMacro(module: "Macros", type: "Decode") +/// Format date to a string +/// +/// - Parameters: +/// - date: the date to be formatted +/// - dateStyle: [DateFormatter.Style?](https://developer.apple.com/documentation/foundation/dateformatter/1415411-datestyle) +/// - timeStyle: [DateFormatter.Style?](https://developer.apple.com/documentation/foundation/dateformatter/1413467-timestyle) +/// - formattingContext: [Formatter.Context?](https://developer.apple.com/documentation/foundation/dateformatter/1408066-formattingcontext) +/// - formatterBehavior: [DateFormatter.Behavior?](https://developer.apple.com/documentation/foundation/dateformatter/1409720-formatterbehavior) +/// - doesRelativeDateFormatting: [Bool?](https://developer.apple.com/documentation/foundation/dateformatter/1415848-doesrelativedateformatting) +/// - amSymbol: [String?](https://developer.apple.com/documentation/foundation/dateformatter/1409506-amsymbol) +/// - pmSymbol: [String?](https://developer.apple.com/documentation/foundation/dateformatter/1412367-pmsymbol) +/// - weekdaySymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1412405-weekdaysymbols) +/// - shortWeekdaySymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1416121-shortweekdaysymbols) +/// - veryShortWeekdaySymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1415109-veryshortweekdaysymbols) +/// - standaloneWeekdaySymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1413618-standaloneweekdaysymbols) +/// - shortStandaloneWeekdaySymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1409119-shortstandaloneweekdaysymbols) +/// - veryShortStandaloneWeekdaySymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1418238-veryshortstandaloneweekdaysymbol) +/// - monthSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1412049-monthsymbols) +/// - shortMonthSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1409209-shortmonthsymbols) +/// - veryShortMonthSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1413632-veryshortmonthsymbols) +/// - standaloneMonthSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1416227-standalonemonthsymbols) +/// - shortStandaloneMonthSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1414771-shortstandalonemonthsymbols) +/// - veryShortStandaloneMonthSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1413322-veryshortstandalonemonthsymbols) +/// - quarterSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1417587-quartersymbols) +/// - shortQuarterSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1409851-shortquartersymbols) +/// - standaloneQuarterSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1411487-standalonequartersymbols) +/// - shortStandaloneQuarterSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1416421-shortstandalonequartersymbols) +/// - eraSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1418282-erasymbols) +/// - longEraSymbols: [[String]?](https://developer.apple.com/documentation/foundation/dateformatter/1418081-longerasymbols) +/// +/// For example: +/// ```swift +/// let string = #formatDate(Date(), dateStyle: .full) +/// ``` @freestanding(expression) public macro formatDate(_ date: Date, dateStyle: DateFormatter.Style? = nil, timeStyle: DateFormatter.Style? = nil, - localizedDateFormatFromTemplate: String? = nil, formattingContext: Formatter.Context? = nil, formatterBehavior: DateFormatter.Behavior? = nil, doesRelativeDateFormatting: Bool? = nil, @@ -68,6 +372,28 @@ public macro formatDate(_ date: Date, shortStandaloneQuarterSymbols: [String]? = nil, eraSymbols: [String]? = nil, longEraSymbols: [String]? = nil) -> String = #externalMacro(module: "Macros", type: "FormatDate") + +/// Format date differences to a string +/// +/// - Parameters: +/// - from: from date +/// - to: to date +/// - allowedUnits: [NSCalendar.Unit?](https://developer.apple.com/documentation/foundation/nscalendar/unit) +/// - allowsFractionalUnits: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1413084-allowsfractionalunits) +/// - calendar: [Calendar?](https://developer.apple.com/documentation/foundation/calendar) +/// - collapsesLargestUnit: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1410812-collapseslargestunit) +/// - includesApproximationPhrase: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416387-includesapproximationphrase) +/// - includesTimeRemainingPhrase:[ Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416416-includestimeremainingphrase) +/// - maximumUnitCount: [Int?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416214-maximumunitcount) +/// - unitsStyle: [DateComponentsFormatter.UnitsStyle?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/unitsstyle) +/// - zeroFormattingBehavior: [DateComponentsFormatter.ZeroFormattingBehavior?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/zeroformattingbehavior) +/// - formattingContext: [Formatter.Context?](https://developer.apple.com/documentation/foundation/formatter/context) +/// - referenceDate:[ Date?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/2878110-referencedate) +/// +/// For example: +/// ```swift +/// let string = #formatDateComponents(from: Date(), to: Date(), allowedUnits: [.day, .hour, .minute, .second]) +/// ``` @freestanding(expression) public macro formatDateComponents(from fromDate: Date, to toDate: Date, @@ -82,6 +408,27 @@ public macro formatDateComponents(from fromDate: Date, zeroFormattingBehavior: DateComponentsFormatter.ZeroFormattingBehavior? = nil, formattingContext: Formatter.Context? = nil, referenceDate: Date? = nil) -> String? = #externalMacro(module: "Macros", type: "FormatDateComponents") + +/// Format a timeInterval components to a string +/// +/// - Parameters: +/// - fromInterval: the time interval to be formatted +/// - allowedUnits: [NSCalendar.Unit?](https://developer.apple.com/documentation/foundation/nscalendar/unit) +/// - allowsFractionalUnits: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1413084-allowsfractionalunits) +/// - calendar: [Calendar?](https://developer.apple.com/documentation/foundation/calendar) +/// - collapsesLargestUnit: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1410812-collapseslargestunit) +/// - includesApproximationPhrase: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416387-includesapproximationphrase) +/// - includesTimeRemainingPhrase:[ Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416416-includestimeremainingphrase) +/// - maximumUnitCount: [Int?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416214-maximumunitcount) +/// - unitsStyle: [DateComponentsFormatter.UnitsStyle?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/unitsstyle) +/// - zeroFormattingBehavior: [DateComponentsFormatter.ZeroFormattingBehavior?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/zeroformattingbehavior) +/// - formattingContext: [Formatter.Context?](https://developer.apple.com/documentation/foundation/formatter/context) +/// - referenceDate:[ Date?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/2878110-referencedate) +/// +/// For example: +/// ```swift +/// let string = #formatDateComponents(fromInterval: 100, allowedUnits: [.day, .hour, .minute, .second]) +/// ``` @freestanding(expression) public macro formatDateComponents(fromInterval timeInterval: TimeInterval, allowedUnits: NSCalendar.Unit? = nil, @@ -96,6 +443,26 @@ public macro formatDateComponents(fromInterval timeInterval: TimeInterval, formattingContext: Formatter.Context? = nil, referenceDate: Date? = nil) -> String? = #externalMacro(module: "Macros", type: "FormatDateComponents") +/// Format a date components to a string +/// +/// - Parameters: +/// - fromComponents: the date components to be formatted +/// - allowedUnits: [NSCalendar.Unit?](https://developer.apple.com/documentation/foundation/nscalendar/unit) +/// - allowsFractionalUnits: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1413084-allowsfractionalunits) +/// - calendar: [Calendar?](https://developer.apple.com/documentation/foundation/calendar) +/// - collapsesLargestUnit: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1410812-collapseslargestunit) +/// - includesApproximationPhrase: [Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416387-includesapproximationphrase) +/// - includesTimeRemainingPhrase:[ Bool?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416416-includestimeremainingphrase) +/// - maximumUnitCount: [Int?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/1416214-maximumunitcount) +/// - unitsStyle: [DateComponentsFormatter.UnitsStyle?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/unitsstyle) +/// - zeroFormattingBehavior: [DateComponentsFormatter.ZeroFormattingBehavior?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/zeroformattingbehavior) +/// - formattingContext: [Formatter.Context?](https://developer.apple.com/documentation/foundation/formatter/context) +/// - referenceDate:[ Date?](https://developer.apple.com/documentation/foundation/datecomponentsformatter/2878110-referencedate) +/// +/// For example: +/// ```swift +/// let string = #formatDateComponents(fromComponents: DateComponents(hour: 10), allowedUnits: [.day, .hour, .minute, .second]) +/// ``` @freestanding(expression) public macro formatDateComponents(fromComponents components: DateComponents, allowedUnits: NSCalendar.Unit? = nil, @@ -109,7 +476,22 @@ public macro formatDateComponents(fromComponents components: DateComponents, zeroFormattingBehavior: DateComponentsFormatter.ZeroFormattingBehavior? = nil, formattingContext: Formatter.Context? = nil, referenceDate: Date? = nil) -> String? = #externalMacro(module: "Macros", type: "FormatDateComponents") - +/// Format two dates into interval string +/// +/// - Parameters: +/// - from: From date +/// - to: To date +/// - dateStyle: [DateIntervalFormatter.Style?](https://developer.apple.com/documentation/foundation/dateintervalformatter/style) +/// - timeStyle: [DateIntervalFormatter.Style?](https://developer.apple.com/documentation/foundation/dateintervalformatter/style) +/// - dateTemplate: [String? ](https://developer.apple.com/documentation/foundation/dateintervalformatter/1407373-datetemplate) +/// - calendar: [Calendar?](https://developer.apple.com/documentation/foundation/calendar) +/// - locale: [Locale?](https://developer.apple.com/documentation/foundation/locale) +/// - timeZone: [TimeZone?](https://developer.apple.com/documentation/foundation/timezone) +/// +/// For example: +/// ```swift +/// let string = #formatDateInterval(from: Date(), to: Date(), dateStyle: .short) +/// ``` @freestanding(expression) public macro formatDateInterval(from fromDate: Date, to toDate: Date, @@ -120,14 +502,72 @@ public macro formatDateInterval(from fromDate: Date, locale: Locale? = nil, timeZone: TimeZone? = nil) -> String = #externalMacro(module: "Macros", type: "FormatDateInterval") +/// Generate a static variable mock using the attached initializer. +/// For custmoised data type, it will use Type.mock. In case there is no this value, +/// need to define this yourself or use @Mock or @AddInit(withMock: true) to generate this variable. +/// +/// - Parameters: +/// - type: The value type +/// - randomMockValue: true - if want to have random value for the mocked variable. Default to true. +/// +/// For example: +/// ```swift +/// class AStruct { +/// let a: Float +/// @Mock(type: AStruct.self) +/// init(a: Float) { +/// self.a = a +/// } +/// } +/// ``` +/// will expand to +/// ```swift +/// class AStruct { +/// let a: Float +/// init(a: Float) { +/// self.a = a +/// } +/// #if DEBUG +/// static let mock = AStruct(a: 3.0339055e+37) +/// #endif +/// } +/// ``` @attached(peer, names: named(mock)) public macro Mock(type: Any.Type, randomMockValue: Bool = true) = #externalMacro(module: "Macros", type: "Mock") +/// An easy way to post notifications +/// - Parameters: +/// - name: The notification name to be posted +/// - object: The object posting the notification. +/// - userInfo: A user info dictionary with optional information about the notification. +/// - from: The notificationCenter used to send the notification. Default to [.default](https://developer.apple.com/documentation/foundation/notificationcenter/1414169-default) +/// +/// For example: +/// ```swift +/// #postNotification(.NSCalendarDayChanged) +/// ``` @freestanding(expression) public macro postNotification(_ name: NSNotification.Name, object: Any? = nil, userInfo: [AnyHashable : Any]? = nil, from notificationCenter: NotificationCenter = .default) = #externalMacro(module: "Macros", type: "PostNotification") +/// Generate Swift singleton code for struct and class types +/// +/// For example: +/// ```swift +/// @Singleton +/// struct A { +/// } +/// ``` +/// will expand to +/// ```swift +/// struct A { +/// private init() { +/// } +/// +/// static let shared = A() +/// } +/// ``` @attached(member, names: named(init), named(shared)) public macro Singleton() = #externalMacro(module: "Macros", type: "Singleton")