Skip to content

Commit

Permalink
Enable non-lexigraphic equivalence checking for Compositions (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
dfed authored Jan 13, 2024
1 parent b3f0773 commit 2697747
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 4 deletions.
59 changes: 55 additions & 4 deletions Sources/SafeDICore/Models/TypeDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public enum TypeDescription: Codable, Hashable, Comparable, Sendable {
/// A nested type with possible generics. e.g. Array.Element or Swift.Array<Element>
indirect case nested(name: String, parentType: TypeDescription, generics: [TypeDescription])
/// A composed type. e.g. Identifiable & Equatable
indirect case composition([TypeDescription])
indirect case composition(UnorderedComparingCollection<TypeDescription>)
/// An optional type. e.g. Int?
indirect case optional(TypeDescription)
/// An implicitly unwrapped optional type. e.g. Int!
Expand Down Expand Up @@ -210,6 +210,8 @@ public enum TypeDescription: Codable, Hashable, Comparable, Sendable {
}
}

// MARK: - TypeSyntax

extension TypeSyntax {

/// - Returns: the type description for the receiver.
Expand All @@ -234,7 +236,7 @@ extension TypeSyntax {
generics: genericTypeVisitor.genericArguments)

} else if let typeIdentifiers = CompositionTypeSyntax(self) {
return .composition(typeIdentifiers.elements.map { $0.type.typeDescription })
return .composition(UnorderedComparingCollection(typeIdentifiers.elements.map { $0.type.typeDescription }))

} else if let typeIdentifier = OptionalTypeSyntax(self) {
return .optional(typeIdentifier.wrappedType.typeDescription)
Expand Down Expand Up @@ -304,6 +306,8 @@ extension TypeSyntax {
}
}

// MARK: - ExprSyntax

extension ExprSyntax {
public var typeDescription: TypeDescription {
if let typeExpr = TypeExprSyntax(self) {
Expand Down Expand Up @@ -378,12 +382,12 @@ extension ExprSyntax {
}
} else if let sequenceExpr = SequenceExprSyntax(self) {
if sequenceExpr.elements.contains(where: { BinaryOperatorExprSyntax($0) != nil }) {
return .composition(
return .composition(UnorderedComparingCollection(
sequenceExpr
.elements
.filter { BinaryOperatorExprSyntax($0) == nil }
.map(\.typeDescription)
)
))
} else if
sequenceExpr.elements.count == 3,
let arguments = TupleExprSyntax(sequenceExpr.elements.first),
Expand Down Expand Up @@ -425,6 +429,53 @@ extension ExprSyntax {
}
}

// MARK: - UnorderedComparingCollection

public struct UnorderedComparingCollection<Element: Codable & Hashable & Sendable>: Codable, Hashable, Sendable, Collection, ExpressibleByArrayLiteral {

// MARK: Initialization

public init(_ array: [Element]) {
self.array = array
set = Set(array)
}

// MARK: Equatable

public static func == (lhs: UnorderedComparingCollection, rhs: UnorderedComparingCollection) -> Bool {
lhs.set == rhs.set
}

// MARK: Collection

public func makeIterator() -> IndexingIterator<Array<Element>> {
array.makeIterator()
}
public var startIndex: Int { array.startIndex }
public var endIndex: Int { array.endIndex }
public func index(after i: Int) -> Int {
array.index(after: i)
}
public subscript(position: Int) -> Element {
array[position]
}

// MARK: ExpressibleByArrayLiteral

public init(arrayLiteral elements: Element...) {
self.init(elements)
}

public typealias ArrayLiteralElement = Element

// MARK: Private

private let array: [Element]
private let set: Set<Element>
}

// MARK: - GenericArgumentVisitor

private final class GenericArgumentVisitor: SyntaxVisitor {

private(set) var genericArguments = [TypeDescription]()
Expand Down
26 changes: 26 additions & 0 deletions Tests/SafeDICoreTests/TypeDescriptionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,32 @@ final class TypeDescriptionTests: XCTestCase {
XCTAssertEqual(typeDescription.asSource, "SomeTypeThatIsFormattedOddly")
}

func test_equality_isTrueWhenComparingLexigraphicallyEquivalentCompositions() {
XCTAssertEqual(
TypeDescription.composition([
.simple(name: "Foo"),
.simple(name: "Bar"),
]),
TypeDescription.composition([
.simple(name: "Foo"),
.simple(name: "Bar"),
])
)
}

func test_equality_isTrueWhenComparingReversedCompositions() {
XCTAssertEqual(
TypeDescription.composition([
.simple(name: "Foo"),
.simple(name: "Bar"),
]),
TypeDescription.composition([
.simple(name: "Bar"),
.simple(name: "Foo"),
])
)
}

// MARK: - Visitors

private final class TypeIdentifierSyntaxVisitor: SyntaxVisitor {
Expand Down

0 comments on commit 2697747

Please sign in to comment.