From db7d1a98d414e8df12870e95387c409451352715 Mon Sep 17 00:00:00 2001 From: Ugo Date: Fri, 21 Jun 2024 21:49:59 +0200 Subject: [PATCH] Add readme and license files --- LICENSE | 21 +++++++++++ Package.swift | 10 ------ README.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f35d7b3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Ugochukwu Mmirikwe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Package.swift b/Package.swift index 0e84d7e..ff3ba62 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,4 @@ // swift-tools-version: 5.9 -// The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription import CompilerPluginSupport @@ -8,7 +7,6 @@ let package = Package( name: "CopyableMacro", platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)], products: [ - // Products define the executables and libraries a package produces, making them visible to other packages. .library( name: "CopyableMacro", targets: ["CopyableMacroMacros"] @@ -19,13 +17,9 @@ let package = Package( ), ], dependencies: [ - // Depend on the Swift 5.9 release of SwiftSyntax .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"), ], targets: [ - // Targets are the basic building blocks of a package, defining a module or a test suite. - // Targets can depend on other targets in this package and products from dependencies. - // Macro implementation that performs the source transformation of a macro. .macro( name: "CopyableMacroMacros", dependencies: [ @@ -33,14 +27,10 @@ let package = Package( .product(name: "SwiftCompilerPlugin", package: "swift-syntax") ] ), - - // Library that exposes a macro as part of its API, which is used in client programs. .target(name: "CopyableMacro", dependencies: ["CopyableMacroMacros"]), // A client of the library, which is able to use the macro in its own code. .executableTarget(name: "CopyableMacroClient", dependencies: ["CopyableMacro"]), - - // A test target used to develop the macro implementation. .testTarget( name: "CopyableMacroTests", dependencies: [ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3849b2 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Overview + +`SwiftCopyableMacro` is a very simple Swift macro library that auto-generates a `copy()` function for structs at compile time, which takes all the public properties of the struct as arguments in the function, enabling a runtime ability to elegantly duplicate an instance of the struct and setting new values for any or all these (even immutable) properties at the same time, similar to what [Kotlin's `data class`](https://kotlinlang.org/docs/data-classes.html#copying) offers. + +The only effort required is to mark the structs with a custom `@Copyable` annotation and the structs are automatically injected with a `copy()` method inside. For example: + +```swift +@Copyable // <= annotation is used here +struct User { + let name: String + let age: Int +} +``` + +automatically updates the struct at compile time (which happens instantly if editing the file inside of Xcode) as follows: + +```swift +struct User { + let name: String + let age: Int + + // Notice the arguments for the function 👇 + func copy(name: String? = nil, age: Int? = nil) -> Self { + .init( + name: name ?? self.name + age: age ?? self.age + ) + } +} +``` + +This struct can now be used at a call site like so: + +```swift +let ugo = User(name: "Ugo", age: 30) +print(ugo) // Ugo(name: "Ugo", age: 30) + +let ugoAsJack = ugo.copy(name: "Jack") // <= .copy() is used here +print(ugoAsJack) // Ugo(name: "Jack", age: 30) + +let ugoIsOlder = ugo.copy(age: 34) // <= .copy() is used here +print(ugoIsOlder) // Ugo(name: "Ugo", age: 34) +print(ugoAsJack) // Ugo(name: "Jack", age: 30) + +let jackIsYounger = ugoAsJack.copy(age: 25) // <= .copy() is used here +print(jackIsYounger) // Ugo(name: "Jack", age: 25) +``` + +Xcode will even offer autocomplete listing all the properties declared in the struct as parameters in this `.copy(...)` function. + +# Installation +This macro is available for installation via the Swift Package Manager (SPM) as follows: + +## Xcode +1. In Xcode, select the menu: `File` → `Add Package Dependencies…` to launch the SPM dialog; +2. in the "Search or Enter Package URL" text field, enter the package URL: https://github.com/ugommirikwe/SwiftCopyableMacro; +3. in "Dependency Rule" select "Branch"; +4. in the "Branch" text field enter the value "main" +5. select the project to add the + +## package.swift +For projects that use a `package.swift` file to manage their SPM dependencies, you can add it to your `package.swift` file like so: + +```swift +dependencies: [ + .package(url: "https://github.com/ugommirikwe/SwiftCopyableMacro", .branch("main")) +] +``` + +And then add the product to all targets that use CopyableMacro: + +```swift +... +targets: [ + .target( + name: "", + dependencies: ["CopyableMacro"], + ) +] +``` + +# Import & Usage +The [overview](#overview) section above already describes how to use the macro: prepend your struct name with the annotation `@Copyable` and that's all. Xcode 15+ should automatically add the import at the top of the file for you. Otherwise, go ahead and add it as follows to clear any errors in your file: + +```swift +import CopyableMacro +``` + +# Pre-Release +Please note that this code is still in pre-release stage and should be carefully evaluated before using in your production code. + +# Feedback and Issues +Feel free to submit any issues or suggestions. Pull requests are also welcomed. + +# License +SwiftCopyableMacro is available under the MIT license. See the [LICENSE](LICENSE) file for more info.