Skip to content

Commit

Permalink
Read (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
muukii authored Oct 31, 2023
1 parent 5066b3b commit 9033d04
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 216 deletions.
36 changes: 17 additions & 19 deletions Sources/StructTransaction/Source.swift
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@

@attached(extension, conformances: DetectingType, names: named(Modifying), named(modify(source:modifier:)), named(read(source:reader:)), named(ModifyingTarget))
@attached(
extension,
conformances: DetectingType,
names: named(Accessing),
named(modify(source:modifier:)),
named(read(source:reader:)),
named(AccessingTarget)
)
public macro Detecting() = #externalMacro(module: "StructTransactionMacros", type: "WriterMacro")

/**
Use ``Detecting()`` macro to adapt struct
*/
public protocol DetectingType {

associatedtype Modifying
associatedtype Accessing

@discardableResult
static func modify(source: inout Self, modifier: (inout Modifying) throws -> Void) rethrows -> ModifyingResult
static func modify(source: inout Self, modifier: (inout Accessing) throws -> Void) rethrows -> AccessingResult

@discardableResult
static func read(source: Self, reader: (Modifying) throws -> Void) rethrows -> ReadResult
static func read(source: Self, reader: (inout Accessing) throws -> Void) rethrows -> AccessingResult
}

extension DetectingType {

@discardableResult
public mutating func modify(modifier: (inout Modifying) throws -> Void) rethrows -> ModifyingResult {
public mutating func modify(modifier: (inout Accessing) throws -> Void) rethrows -> AccessingResult {
try Self.modify(source: &self, modifier: modifier)
}

@discardableResult
public mutating func read(reader: (Modifying) throws -> Void) rethrows -> ReadResult {
public borrowing func read(reader: (inout Accessing) throws -> Void) rethrows -> AccessingResult {
try Self.read(source: self, reader: reader)
}
}

public struct ReadResult {

public let readIdentifiers: Set<String>

public init(
readIdentifiers: Set<String>
) {
self.readIdentifiers = readIdentifiers
}
}


public struct ModifyingResult {
public struct AccessingResult {

public let readIdentifiers: Set<String>
public let modifiedIdentifiers: Set<String>
Expand Down
133 changes: 78 additions & 55 deletions Sources/StructTransactionMacros/WriterMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,48 @@ extension WriterMacro: ExtensionMacro {
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> [SwiftSyntax.ExtensionDeclSyntax] {

let accessingDecl = try Self.makeModifying(
of: node,
attachedTo: declaration,
providingExtensionsOf: type,
conformingTo: protocols,
in: context
)

let extensionDecl = """
extension \(type.trimmed): DetectingType {
// MARK: - Accessing
\(accessingDecl)
}
""" as DeclSyntax

return [
extensionDecl
.formatted(
using: .init(
indentationWidth: .spaces(2),
initialIndentation: [],
viewMode: .fixedUp
)
)
.cast(ExtensionDeclSyntax.self)
]

}

private static func makeModifying(
of node: SwiftSyntax.AttributeSyntax,
attachedTo declaration: some SwiftSyntax.DeclGroupSyntax,
providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol,
conformingTo protocols: [SwiftSyntax.TypeSyntax],
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> CodeBlockItemListSyntax {
// Decode the expansion arguments.
guard let structDecl = declaration.as(StructDeclSyntax.self) else {
context.addDiagnostics(from: WriterMacroError.foundNotStructType, node: node)
return []
return ""
}

let c = PropertyCollector(viewMode: .all)
Expand Down Expand Up @@ -75,14 +113,14 @@ extension WriterMacro: ExtensionMacro {
}

let modifyingStructDecl = """
public struct Modifying /* want to be ~Copyable */ {
public struct Accessing /* want to be ~Copyable */ {
public private(set) var $_readIdentifiers: Set<String> = .init()
public private(set) var $_modifiedIdentifiers: Set<String> = .init()
private let pointer: UnsafeMutablePointer<ModifyingTarget>
private let pointer: UnsafeMutablePointer<AccessingTarget>
init(pointer: UnsafeMutablePointer<ModifyingTarget>) {
init(pointer: UnsafeMutablePointer<AccessingTarget>) {
self.pointer = pointer
}
Expand All @@ -92,53 +130,41 @@ extension WriterMacro: ExtensionMacro {

let modifyingDecl =
("""
extension \(type.trimmed): DetectingType {
typealias ModifyingTarget = Self
typealias AccessingTarget = Self
@discardableResult
public static func modify(source: inout Self, modifier: (inout Modifying) throws -> Void) rethrows -> ModifyingResult {
@discardableResult
public static func modify(source: inout Self, modifier: (inout Accessing) throws -> Void) rethrows -> AccessingResult {
try withUnsafeMutablePointer(to: &source) { pointer in
var modifying = Modifying(pointer: pointer)
try modifier(&modifying)
return ModifyingResult(
readIdentifiers: modifying.$_readIdentifiers,
modifiedIdentifiers: modifying.$_modifiedIdentifiers
)
}
try withUnsafeMutablePointer(to: &source) { pointer in
var modifying = Accessing(pointer: pointer)
try modifier(&modifying)
return AccessingResult(
readIdentifiers: modifying.$_readIdentifiers,
modifiedIdentifiers: modifying.$_modifiedIdentifiers
)
}
}
@discardableResult
public static func read(source: Self, reader: (Modifying) throws -> Void) rethrows -> ReadResult {
// FIXME: avoid copying
var reading = source
return try withUnsafeMutablePointer(to: &reading) { pointer in
let modifying = Modifying(pointer: pointer)
try reader(modifying)
return ReadResult(
readIdentifiers: modifying.$_readIdentifiers
)
}
}
@discardableResult
public static func read(source: consuming Self, reader: (inout Accessing) throws -> Void) rethrows -> AccessingResult {
\(modifyingStructDecl)
}
""" as DeclSyntax)
// TODO: check copying costs
var tmp = source
return [
modifyingDecl
.formatted(
using: .init(
indentationWidth: .spaces(2),
initialIndentation: [],
viewMode: .fixedUp
return try withUnsafeMutablePointer(to: &tmp) { pointer in
var modifying = Accessing(pointer: pointer)
try reader(&modifying)
return AccessingResult(
readIdentifiers: modifying.$_readIdentifiers,
modifiedIdentifiers: modifying.$_modifiedIdentifiers
)
)
.cast(ExtensionDeclSyntax.self)
]
}
}
\(modifyingStructDecl)
""" as CodeBlockItemListSyntax)

return modifyingDecl
}

}
Expand All @@ -151,24 +177,22 @@ private func makeMutatingGetter(_ block: AccessorBlockSyntax) -> AccessorBlockSy
let decl = MutatingGetterConverter().visit(list)

return """
{
\(decl)
}
""" as AccessorBlockSyntax
{
\(decl)
}
""" as AccessorBlockSyntax

case .getter(let getter):
return """
{
mutating get { \(getter) }
}
""" as AccessorBlockSyntax
{
mutating get { \(getter) }
}
""" as AccessorBlockSyntax
}

}

/**
convert get-accessor into mutating-get-accessor
*/
/// convert get-accessor into mutating-get-accessor
final class MutatingGetterConverter: SyntaxRewriter {

override func visit(_ node: AccessorDeclSyntax) -> DeclSyntax {
Expand Down Expand Up @@ -257,4 +281,3 @@ final class PropertyCollector: SyntaxVisitor {
}

}

Loading

0 comments on commit 9033d04

Please sign in to comment.