Skip to content

Commit

Permalink
Macro-based schema discovery proof of concept
Browse files Browse the repository at this point in the history
  • Loading branch information
tgoyne committed Jun 8, 2023
1 parent 45ea4a0 commit bfecc4a
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 169 deletions.
135 changes: 1 addition & 134 deletions .jenkins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,145 +4,12 @@
# This is a generated file produced by scripts/pr-ci-matrix.rb.

xcode_version:
- 14.1
- 14.2
- 14.3.1
- 15.0
target:
- docs
- swiftlint
- osx
- osx-encryption
- osx-object-server
- swiftpm
- swiftpm-debug
- swiftpm-address
- swiftpm-thread
- swiftpm-ios
- ios-static
- ios-dynamic
- watchos
- tvos
- osx-swift
- ios-swift
- tvos-swift
- osx-swift-evolution
- ios-swift-evolution
- tvos-swift-evolution
- catalyst
- catalyst-swift
- xcframework
- cocoapods-osx
- cocoapods-ios
- cocoapods-ios-dynamic
- cocoapods-watchos
- swiftui-ios
- swiftui-server-osx
configuration:
- N/A

exclude:

- xcode_version: 14.1
target: docs

- xcode_version: 14.2
target: docs

- xcode_version: 14.1
target: swiftlint

- xcode_version: 14.2
target: swiftlint

- xcode_version: 14.1
target: osx-encryption

- xcode_version: 14.2
target: osx-encryption

- xcode_version: 14.2
target: osx-object-server

- xcode_version: 14.2
target: swiftpm

- xcode_version: 14.1
target: swiftpm-address

- xcode_version: 14.2
target: swiftpm-address

- xcode_version: 14.1
target: swiftpm-thread

- xcode_version: 14.2
target: swiftpm-thread

- xcode_version: 14.2
target: ios-static

- xcode_version: 14.2
target: ios-dynamic

- xcode_version: 14.2
target: watchos

- xcode_version: 14.2
target: tvos

- xcode_version: 14.2
target: ios-swift

- xcode_version: 14.2
target: tvos-swift

- xcode_version: 14.1
target: osx-swift-evolution

- xcode_version: 14.2
target: osx-swift-evolution

- xcode_version: 14.1
target: ios-swift-evolution

- xcode_version: 14.2
target: ios-swift-evolution

- xcode_version: 14.1
target: tvos-swift-evolution

- xcode_version: 14.2
target: tvos-swift-evolution

- xcode_version: 14.2
target: catalyst

- xcode_version: 14.2
target: catalyst-swift

- xcode_version: 14.1
target: xcframework

- xcode_version: 14.2
target: xcframework

- xcode_version: 14.2
target: cocoapods-ios

- xcode_version: 14.2
target: cocoapods-ios-dynamic

- xcode_version: 14.2
target: cocoapods-watchos

- xcode_version: 14.1
target: swiftui-ios

- xcode_version: 14.2
target: swiftui-ios

- xcode_version: 14.1
target: swiftui-server-osx

- xcode_version: 14.2
target: swiftui-server-osx
14 changes: 14 additions & 0 deletions Realm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@
3FA5E94D266064C4008F1345 /* ModernObjectCreationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FA5E94C266064C4008F1345 /* ModernObjectCreationTests.swift */; };
3FAF2D4129577100002EAC93 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FAF2D4029577100002EAC93 /* TestUtils.swift */; };
3FAF2D4229577100002EAC93 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FAF2D4029577100002EAC93 /* TestUtils.swift */; };
3FB0FF102A31019800E13BF3 /* RealmMacro in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB0FF0F2A31019800E13BF3 /* RealmMacro */; };
3FB19069265ECF0C00DA7C76 /* ModernObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB19068265ECF0C00DA7C76 /* ModernObjectTests.swift */; };
3FB1906B265ED23300DA7C76 /* ModernTestObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB1906A265ED23300DA7C76 /* ModernTestObjects.swift */; };
3FB4FA1719F5D2740020D53B /* SwiftTestObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8F8D90B196CB8DD00475368 /* SwiftTestObjects.swift */; };
Expand Down Expand Up @@ -941,6 +942,7 @@
3F9F53D42718E8E6000EEB4A /* CustomPersistableTestObjects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPersistableTestObjects.swift; sourceTree = "<group>"; };
3FA5E94C266064C4008F1345 /* ModernObjectCreationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModernObjectCreationTests.swift; sourceTree = "<group>"; };
3FAF2D4029577100002EAC93 /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
3FB0FF0E2A2FE6A200E13BF3 /* RealmMacro */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealmMacro; sourceTree = "<group>"; };
3FB19068265ECF0C00DA7C76 /* ModernObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModernObjectTests.swift; sourceTree = "<group>"; };
3FB1906A265ED23300DA7C76 /* ModernTestObjects.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModernTestObjects.swift; sourceTree = "<group>"; };
3FB56E7E250D457A00A6216B /* ObjectServerTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ObjectServerTests.xcconfig; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1306,6 +1308,7 @@
buildActionMask = 2147483647;
files = (
3F98162A2317763000C3543D /* libc++.tbd in Frameworks */,
3FB0FF102A31019800E13BF3 /* RealmMacro in Frameworks */,
3F98162B2317763600C3543D /* libz.tbd in Frameworks */,
5D66102A1BE98DD00021E04F /* Realm.framework in Frameworks */,
);
Expand Down Expand Up @@ -1840,6 +1843,7 @@
E8D89B8E1955FC6D00CF2B9A = {
isa = PBXGroup;
children = (
3FB0FF0E2A2FE6A200E13BF3 /* RealmMacro */,
5D660FB71BE98B770021E04F /* Configuration */,
1A7B82361D51254600750296 /* Frameworks */,
E81A1FB41955FCE000FDED82 /* LICENSE */,
Expand Down Expand Up @@ -2338,6 +2342,9 @@
5D66102C1BE98DF60021E04F /* PBXTargetDependency */,
);
name = RealmSwift;
packageProductDependencies = (
3FB0FF0F2A31019800E13BF3 /* RealmMacro */,
);
productName = RealmSwift;
productReference = 5D660FCC1BE98C560021E04F /* RealmSwift.framework */;
productType = "com.apple.product-type.framework";
Expand Down Expand Up @@ -3850,6 +3857,13 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
3FB0FF0F2A31019800E13BF3 /* RealmMacro */ = {
isa = XCSwiftPackageProductDependency;
productName = RealmMacro;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = E8D89B8F1955FC6D00CF2B9A /* Project object */;
}
8 changes: 8 additions & 0 deletions RealmMacro/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
41 changes: 41 additions & 0 deletions RealmMacro/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
import CompilerPluginSupport

let package = Package(
name: "RealmMacro",
platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)],
products: [
.library(
name: "RealmMacro",
targets: ["RealmMacro"]
),
.executable(
name: "RealmMacroClient",
targets: ["RealmMacroClient"]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b"),
],
targets: [
.macro(
name: "RealmMacroMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
.target(name: "RealmMacro", dependencies: ["RealmMacroMacros"]),
.executableTarget(name: "RealmMacroClient", dependencies: ["RealmMacro"]),
.testTarget(
name: "RealmMacroTests",
dependencies: [
"RealmMacroMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
]
),
]
)
4 changes: 4 additions & 0 deletions RealmMacro/Sources/RealmMacro/RealmMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@attached(conformance)
@attached(member, names: named(_rlmProperties))
//@attached(memberAttribute)
public macro RealmModel() -> () = #externalMacro(module: "RealmMacroMacros", type: "RealmObjectMacro")
1 change: 1 addition & 0 deletions RealmMacro/Sources/RealmMacroClient/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import RealmMacro
120 changes: 120 additions & 0 deletions RealmMacro/Sources/RealmMacroMacros/RealmMacroMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AttributeSyntax] {
return []
guard let property = member.as(VariableDeclSyntax.self), property.bindings.count == 1 else {
return []
}

if let attributes = property.attributes {
for attr in attributes {
if case let .attribute(attr) = attr {
if attr.attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text == "Ignored" {
return []
}
}
}
}

let binding = property.bindings.first!
switch binding.accessor {
case .none:
break
case .accessors(let node):
for accessor in node.accessors {
switch accessor.accessorKind.tokenKind {
case .keyword(.get), .keyword(.set):
return []
default:
break
}
}
break
case .getter:
return []
}

return [
AttributeSyntax(
attributeName: SimpleTypeIdentifierSyntax(name: .identifier("Persisted"))
)
.with(\.leadingTrivia, [.newlines(1), .spaces(2)])
]
}

public struct RealmObjectMacro: MemberMacro, ConformanceMacro {
public static func expansion(
of node: AttributeSyntax,
providingConformancesOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
return [(TypeSyntax("RealmSwift._RealmObjectSchemaDiscoverable"), nil)]
}

public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let declaration = declaration.as(ClassDeclSyntax.self) else { fatalError() }
let className = declaration.identifier
let properties = declaration.memberBlock.members.compactMap { (decl) -> (String, String, AttributeSyntax)? in
guard let property = decl.decl.as(VariableDeclSyntax.self), property.bindings.count == 1 else {
return nil
}
guard let attributes = property.attributes else { return nil }
let persistedAttr = attributes.compactMap { attr in
if case let .attribute(attr) = attr {
if attr.attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text == "Persisted" {
return attr
}
}
return nil
}.first
guard let persistedAttr else { return nil }

let binding = property.bindings.first!
guard let identifier = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil }
guard let typeAnnotation = binding.typeAnnotation else { return nil }
let name = identifier.identifier.text
let type = typeAnnotation.type.trimmedDescription
return (name, type, persistedAttr)
}

let rlmProperties = properties.map { (name, type, persistedAttr) in
let expr = ExprSyntax("RLMProperty(name: \(literal: name), type: \(raw: type).self, keyPath: \\\(className).\(raw: name))")
var functionCall = expr.as(FunctionCallExprSyntax.self)!

if let argument = persistedAttr.argument, case let .argumentList(argList) = argument {
var argumentList = Array(functionCall.argumentList)
argumentList[argumentList.count - 1].trailingComma = ", "
argumentList.append(contentsOf: argList)
functionCall.argumentList = TupleExprElementListSyntax(argumentList)
}
return functionCall.as(ExprSyntax.self)!
}
return ["""
static var _rlmProperties: [RLMProperty] = \(ArrayExprSyntax {
for property in rlmProperties {
ArrayElementSyntax(expression: property)
}
})
"""]
}
}

@main
struct RealmMacroPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
RealmObjectMacro.self,
]
}
Loading

0 comments on commit bfecc4a

Please sign in to comment.