Skip to content

Commit

Permalink
Compatible with SwiftData
Browse files Browse the repository at this point in the history
support optional wiseInit: @codable(wiseInit: false)
support variable  FunctionCallExpr type: `var subModel = SubModel(subName: "", subAge: 0)`
fix @model @codable mixed usage may cause crashes
  • Loading branch information
winddpan committed Jun 4, 2024
1 parent 1856b3c commit d169f5a
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Sources/CodableWrapper/CodableWrapperMacros.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@attached(member, names: named(init(from:)), named(encode(to:)), arbitrary)
@attached(extension, conformances: Codable)
public macro Codable() = #externalMacro(module: "CodableWrapperMacros", type: "Codable")
public macro Codable(wiseInit: Bool = true) = #externalMacro(module: "CodableWrapperMacros", type: "Codable")

@attached(member, names: named(init(from:)), named(encode(to:)), arbitrary)
public macro CodableSubclass() = #externalMacro(module: "CodableWrapperMacros", type: "CodableSubclass")
Expand Down
13 changes: 7 additions & 6 deletions Sources/CodableWrapper/Decoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ public extension KeyedDecodingContainer where K == AnyCodingKey {
}

private extension KeyedDecodingContainer where K == AnyCodingKey {
func tryNormalKeyDecode<Value>(type: Value.Type, key: String) -> Value? {
func tryNormalKeyDecode<Value: Decodable>(type: Value.Type, key: String) -> Value? {
func _decode(key: String) -> Value? {
guard let key = Key(stringValue: key) else {
return nil
}
if let value = try? decodeIfPresent(type, forKey: key) {
return value
}
let value = try? decodeIfPresent(AnyDecodable.self, forKey: key)?.value
if let value = value {
if let converted = value as? Value {
Expand All @@ -37,10 +40,8 @@ private extension KeyedDecodingContainer where K == AnyCodingKey {
if let _bridged = (Value.self as? _BuiltInBridgeType.Type)?._transform(from: value), let __bridged = _bridged as? Value {
return __bridged
}
if let valueType = Value.self as? Decodable.Type {
if let value = try? valueType.decode(from: self, forKey: key) as? Value {
return value
}
if let value = try? Value.decode(from: self, forKey: key) {
return value
}
}
return nil
Expand All @@ -54,7 +55,7 @@ private extension KeyedDecodingContainer where K == AnyCodingKey {
return nil
}

private func tryNestedKeyDecode<Value>(type: Value.Type, key: String) -> Value? {
private func tryNestedKeyDecode<Value: Decodable>(type: Value.Type, key: String) -> Value? {
var keyComps = key.components(separatedBy: ".")
guard let rootKey = AnyCodingKey(stringValue: keyComps.removeFirst()) else {
return nil
Expand Down
22 changes: 15 additions & 7 deletions Sources/CodableWrapperMacros/Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SwiftSyntaxMacros
public struct Codable: ExtensionMacro, MemberMacro {
public static func expansion(of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext) throws -> [ExtensionDeclSyntax] {
var inheritedTypes: InheritedTypeListSyntax?
if let declaration = declaration.as(StructDeclSyntax.self) {
Expand All @@ -15,8 +15,7 @@ public struct Codable: ExtensionMacro, MemberMacro {
throw ASTError("use @Codable in `struct` or `class`")
}
if let inheritedTypes = inheritedTypes,
inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "Codable" })
{
inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "Codable" }) {
return []
}

Expand All @@ -30,14 +29,23 @@ public struct Codable: ExtensionMacro, MemberMacro {

public static func expansion(of node: SwiftSyntax.AttributeSyntax,
providingMembersOf declaration: some SwiftSyntax.DeclGroupSyntax,
in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax]
{
in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] {
// TODO: diagnostic do not implement `init(from:)` or `encode(to:))`

let propertyContainer = try ModelMemberPropertyContainer(decl: declaration, context: context)
let decoder = try propertyContainer.genDecoderInitializer(config: .init(isOverride: false))
let encoder = try propertyContainer.genEncodeFunction(config: .init(isOverride: false))
let memberwiseInit = try propertyContainer.genMemberwiseInit(config: .init(isOverride: false))
return [decoder, encoder, memberwiseInit]

var hasWiseInit = true
if case let .argumentList(list) = node.arguments, list.first?.expression.description == "false" {
hasWiseInit = false
}

if !hasWiseInit {
return [decoder, encoder]
} else {
let memberwiseInit = try propertyContainer.genMemberwiseInit(config: .init(isOverride: false))
return [decoder, encoder, memberwiseInit]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ struct ModelMemberPropertyContainer {

return text
} else {
let body = "container.decode(type: Swift.type(of: self.\(member.name)), keys: [\(member.codingKeys.joined(separator: ", "))], nestedKeys: [\(member.nestedKeys.joined(separator: ", "))])"
let body = "container.decode(type: \(member.type).self, keys: [\(member.codingKeys.joined(separator: ", "))], nestedKeys: [\(member.nestedKeys.joined(separator: ", "))])"

if let initializerExpr = member.initializerExpr {
return "self.\(member.name) = (try? \(body)) ?? (\(initializerExpr))"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension VariableDeclSyntax {
}

var inferType: String? {
var type = bindings.compactMap(\.typeAnnotation).first?.type.description
var type: String? = bindings.compactMap(\.typeAnnotation).first?.type.trimmedDescription
// try infer type
if type == nil, let initExpr = bindings.compactMap(\.initializer).first?.value {
if initExpr.is(StringLiteralExprSyntax.self) {
Expand All @@ -45,6 +45,9 @@ extension VariableDeclSyntax {
type = "Double"
} else if initExpr.is(BooleanLiteralExprSyntax.self) {
type = "Bool"
} else if let funcDecl = initExpr.as(FunctionCallExprSyntax.self),
let declRef = funcDecl.calledExpression.as(DeclReferenceExprSyntax.self) {
type = declRef.trimmedDescription
}
}
return type
Expand Down

0 comments on commit d169f5a

Please sign in to comment.