diff --git a/Sources/PluginCore/Attributes/Codable/Strategies/StrategyFinder.swift b/Sources/PluginCore/Attributes/Codable/Strategies/StrategyFinder.swift index 92854f97d..98bb3c29c 100644 --- a/Sources/PluginCore/Attributes/Codable/Strategies/StrategyFinder.swift +++ b/Sources/PluginCore/Attributes/Codable/Strategies/StrategyFinder.swift @@ -80,18 +80,19 @@ extension Registration where Var: DefaultPropertyVariable { from decl: some AttributableDeclSyntax ) -> Registration> { let finder = StrategyFinder(decl: decl) - let inStrategy = - finder.valueCodingStrategies.first { strategy in - strategy.trimmedDescription - == self.variable.type.trimmedDescription - } != nil + let type = finder.valueCodingStrategies.first { strategy in + return strategy.trimmedDescription + == self.variable.type.trimmedDescription + || strategy.trimmedDescription + == self.variable.type.wrappedType.trimmedDescription + } let newVariable: AnyPropertyVariable - if inStrategy { + if let type = type { newVariable = HelperCodedVariable( base: self.variable, - options: .helper("ValueCoder<\(variable.type)>()") + options: .helper("ValueCoder<\(type)>()") ).any } else { newVariable = self.variable.any diff --git a/Sources/PluginCore/Variables/Property/PropertyVariable.swift b/Sources/PluginCore/Variables/Property/PropertyVariable.swift index 6007e99f3..93d7ee076 100644 --- a/Sources/PluginCore/Variables/Property/PropertyVariable.swift +++ b/Sources/PluginCore/Variables/Property/PropertyVariable.swift @@ -195,35 +195,40 @@ extension CodeBlockItemListSyntax: ConditionalVariableSyntax { } extension TypeSyntax { - /// Check whether current type syntax represents an optional type. + /// Extract actual type of an optional type. /// - /// Checks whether the type syntax uses + /// Extracts type based on whether the type syntax uses /// `?` optional type syntax (i.e. `Type?`) or /// `!` implicitly unwrapped optional type syntax (i.e. `Type!`) or /// generic optional syntax (i.e. `Optional`). - var isOptionalTypeSyntax: Bool { - if self.is(OptionalTypeSyntax.self) { - return true - } else if self.is(ImplicitlyUnwrappedOptionalTypeSyntax.self) { - return true - } else if let type = self.as(IdentifierTypeSyntax.self), - type.name.trimmed.text == "Optional", - let gArgs = type.genericArgumentClause?.arguments, - gArgs.count == 1 + /// Otherwise, returns the type syntax as is. + var wrappedType: TypeSyntax { + if let type = self.as(OptionalTypeSyntax.self) { + return type.wrappedType + } else if let type = self.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { - return true - } else if let type = self.as(MemberTypeSyntax.self), - let baseType = type.baseType.as(IdentifierTypeSyntax.self), - baseType.trimmed.name.text == "Swift", - type.trimmed.name.text == "Optional", + return type.wrappedType + } else if let type = self.as(IdentifierTypeSyntax.self), + type.name.text == "Optional", let gArgs = type.genericArgumentClause?.arguments, - gArgs.count == 1 + gArgs.count == 1, + let wrappedType = gArgs.first?.argument.as(TypeSyntax.self) { - return true + return wrappedType } else { - return false + return self } } + + /// Check whether current type syntax represents an optional type. + /// + /// Checks whether the type syntax uses + /// `?` optional type syntax (i.e. `Type?`) or + /// `!` implicitly unwrapped optional type syntax (i.e. `Type!`) or + /// generic optional syntax (i.e. `Optional`). + var isOptionalTypeSyntax: Bool { + wrappedType != self + } } #if swift(<6.0) diff --git a/Tests/MetaCodableTests/Codable/CommonStrategiesValueCoderTests.swift b/Tests/MetaCodableTests/Codable/CommonStrategiesValueCoderTests.swift index 7e6bb3a0e..07567e5da 100644 --- a/Tests/MetaCodableTests/Codable/CommonStrategiesValueCoderTests.swift +++ b/Tests/MetaCodableTests/Codable/CommonStrategiesValueCoderTests.swift @@ -11,6 +11,18 @@ struct CommonStrategiesValueCoderTests { let int: Int let double: Double let string: String + let optBool: Bool? + let optInt: Int? + let optDouble: Double? + let optString: String? + let impBool: Bool! + let impInt: Int! + let impDouble: Double! + let impString: String! + let optGenBool: Optional + let optGenInt: Optional + let optGenDouble: Optional + let optGenString: Optional } @Test @@ -54,6 +66,18 @@ struct CommonStrategiesValueCoderTests { let int: Int let double: Double let string: String + let optBool: Bool? + let optInt: Int? + let optDouble: Double? + let optString: String? + let impBool: Bool! + let impInt: Int! + let impDouble: Double! + let impString: String! + let optGenBool: Optional + let optGenInt: Optional + let optGenDouble: Optional + let optGenString: Optional } """, expandedSource: @@ -63,6 +87,18 @@ struct CommonStrategiesValueCoderTests { let int: Int let double: Double let string: String + let optBool: Bool? + let optInt: Int? + let optDouble: Double? + let optString: String? + let impBool: Bool! + let impInt: Int! + let impDouble: Double! + let impString: String! + let optGenBool: Optional + let optGenInt: Optional + let optGenDouble: Optional + let optGenString: Optional } extension Model: Decodable { @@ -72,6 +108,18 @@ struct CommonStrategiesValueCoderTests { self.int = try ValueCoder().decode(from: container, forKey: CodingKeys.int) self.double = try ValueCoder().decode(from: container, forKey: CodingKeys.double) self.string = try ValueCoder().decode(from: container, forKey: CodingKeys.string) + self.optBool = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optBool) + self.optInt = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optInt) + self.optDouble = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optDouble) + self.optString = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optString) + self.impBool = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.impBool) + self.impInt = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.impInt) + self.impDouble = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.impDouble) + self.impString = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.impString) + self.optGenBool = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optGenBool) + self.optGenInt = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optGenInt) + self.optGenDouble = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optGenDouble) + self.optGenString = try ValueCoder().decodeIfPresent(from: container, forKey: CodingKeys.optGenString) } } @@ -82,6 +130,18 @@ struct CommonStrategiesValueCoderTests { try ValueCoder().encode(self.int, to: &container, atKey: CodingKeys.int) try ValueCoder().encode(self.double, to: &container, atKey: CodingKeys.double) try ValueCoder().encode(self.string, to: &container, atKey: CodingKeys.string) + try ValueCoder().encodeIfPresent(self.optBool, to: &container, atKey: CodingKeys.optBool) + try ValueCoder().encodeIfPresent(self.optInt, to: &container, atKey: CodingKeys.optInt) + try ValueCoder().encodeIfPresent(self.optDouble, to: &container, atKey: CodingKeys.optDouble) + try ValueCoder().encodeIfPresent(self.optString, to: &container, atKey: CodingKeys.optString) + try ValueCoder().encodeIfPresent(self.impBool, to: &container, atKey: CodingKeys.impBool) + try ValueCoder().encodeIfPresent(self.impInt, to: &container, atKey: CodingKeys.impInt) + try ValueCoder().encodeIfPresent(self.impDouble, to: &container, atKey: CodingKeys.impDouble) + try ValueCoder().encodeIfPresent(self.impString, to: &container, atKey: CodingKeys.impString) + try ValueCoder().encodeIfPresent(self.optGenBool, to: &container, atKey: CodingKeys.optGenBool) + try ValueCoder().encodeIfPresent(self.optGenInt, to: &container, atKey: CodingKeys.optGenInt) + try ValueCoder().encodeIfPresent(self.optGenDouble, to: &container, atKey: CodingKeys.optGenDouble) + try ValueCoder().encodeIfPresent(self.optGenString, to: &container, atKey: CodingKeys.optGenString) } } @@ -91,6 +151,18 @@ struct CommonStrategiesValueCoderTests { case int = "int" case double = "double" case string = "string" + case optBool = "optBool" + case optInt = "optInt" + case optDouble = "optDouble" + case optString = "optString" + case impBool = "impBool" + case impInt = "impInt" + case impDouble = "impDouble" + case impString = "impString" + case optGenBool = "optGenBool" + case optGenInt = "optGenInt" + case optGenDouble = "optGenDouble" + case optGenString = "optGenString" } } """