Created
June 1, 2017 12:06
-
-
Save hannesoid/0e76ccdd7789d9e9add940ff5585f0fa to your computer and use it in GitHub Desktop.
AutoEquatable.stencil for Sourcery with softness for Doubles and Dates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on https://github.com/krzysztofzablocki/Sourcery/blob/master/Templates/Templates/AutoEquatable.stencil | |
// 1. Uses =~= for Dates and Double comparison by default | |
// 2. Supports custom operators specified by annotation above property like // sourcery equalityOperator="=~=" | |
// Currently not supporting enum associated types | |
// swiftlint:disable file_length | |
fileprivate func compareOptionals<T>(lhs: T?, rhs: T?, compare: (_ lhs: T, _ rhs: T) -> Bool) -> Bool { | |
switch (lhs, rhs) { | |
case let (lValue?, rValue?): | |
return compare(lValue, rValue) | |
case (nil, nil): | |
return true | |
default: | |
return false | |
} | |
} | |
fileprivate func compareArrays<T>(lhs: [T], rhs: [T], compare: (_ lhs: T, _ rhs: T) -> Bool) -> Bool { | |
guard lhs.count == rhs.count else { return false } | |
for (idx, lhsItem) in lhs.enumerated() { | |
guard compare(lhsItem, rhs[idx]) else { return false } | |
} | |
return true | |
} | |
{% macro equalityOperator variable %}{% if not variable.annotations.equalityOperator %}{% if variable.type.name == "Double" or variable.type.name == "Date" %}=~={% else %}=={% endif %}{% else %}{{ variable.annotations.equalityOperator }}{% endif %}{% endmacro %} | |
{% macro compareVariables variables %} | |
{% for variable in variables %}{% if not variable.annotations.skipEquality %}guard {% if not variable.isOptional %}{% if not variable.annotations.arrayEquality %}lhs.{{ variable.name }} {% call equalityOperator variable %} rhs.{{ variable.name }}{% else %}compareArrays(lhs: lhs.{{ variable.name }}, rhs: rhs.{{ variable.name }}, compare: {% call equalityOperator variable %}){% endif %}{% else %}compareOptionals(lhs: lhs.{{ variable.name }}, rhs: rhs.{{ variable.name }}, compare: {% call equalityOperator variable %}){% endif %} else { return false }{% endif %} | |
{% endfor %} | |
{% endmacro %} | |
// MARK: - AutoEquatable for classes, protocols, structs | |
{% for type in types.implementing.AutoEquatable|!enum %} | |
// MARK: - {{ type.name }} AutoEquatable | |
{% if not type.kind == "protocol" %}extension {{ type.name }}: Equatable {}{% endif %} | |
{% if type.supertype.based.Equatable or type.supertype.implements.AutoEquatable %}THIS WONT COMPILE, WE DONT SUPPORT INHERITANCE for AutoEquatable{% endif %} | |
{{ type.accessLevel }} func == (lhs: {{ type.name }}, rhs: {{ type.name }}) -> Bool { | |
{% if not type.kind == "protocol" %} | |
{% call compareVariables type.storedVariables %} | |
{% else %} | |
{% call compareVariables type.allVariables %} | |
{% endif %} | |
return true | |
} | |
{% endfor %} | |
// MARK: - AutoEquatable for Enums | |
{% for type in types.implementing.AutoEquatable|enum %} | |
// MARK: - {{ type.name }} AutoEquatable | |
extension {{ type.name }}: Equatable {} | |
{{ type.accessLevel }} func == (lhs: {{ type.name }}, rhs: {{ type.name }}) -> Bool { | |
switch (lhs, rhs) { | |
{% for case in type.cases %} | |
{% if case.hasAssociatedValue %}case (.{{ case.name }}(let lhs), .{{ case.name }}(let rhs)):{% else %}case (.{{ case.name }}, .{{ case.name }}):{% endif %} | |
{% ifnot case.hasAssociatedValue %}return true{% else %} | |
{% if case.associatedValues.count == 1 %} | |
return lhs == rhs | |
{% else %} | |
{% for associated in case.associatedValues %}if lhs.{{ associated.externalName }} != rhs.{{ associated.externalName }} { return false } | |
{% endfor %}return true | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% if type.cases.count > 1 %}default: return false{% endif %} | |
} | |
} | |
{% endfor %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/// NearlyEqualOperator | |
infix operator =~= : ComparisonPrecedence | |
/// Compares two Double? with some tolerance (0.000001) | |
/// | |
/// - parameter left: first value | |
/// - parameter right: second value | |
/// | |
/// - returns: true if both values are nearly equal | |
func =~= (left: Double, right: Double) -> Bool { | |
return (abs(left - right)) < 0.000001 | |
} | |
/// Compares doubles with some tolerance (0.000001) | |
/// | |
/// - parameter left: first value | |
/// - parameter right: second value | |
/// | |
/// - returns: true if both values are nearly equal | |
func =~= (left: Double?, right: Double?) -> Bool { | |
if left == right { | |
return true | |
} | |
guard let l = left, let r = right else { | |
return false | |
} | |
return l =~= r | |
} | |
/// Compares two dates with to an granularity of < 0.001s | |
/// | |
/// - parameter left: first date | |
/// - parameter right: second date | |
/// | |
/// - returns: true if bothe dates are nearly equal | |
func =~= (left: Date, right: Date) -> Bool { | |
return (abs(left.timeIntervalSince1970 - right.timeIntervalSince1970)) < 0.001 | |
} | |
/// Compares two Date? with to an granularity of < 0.001s | |
/// | |
/// - parameter left: first date | |
/// - parameter right: second date | |
/// | |
/// - returns: true if both dates are nearly equal | |
func =~= (left: Date?, right: Date?) -> Bool { | |
if left == right { | |
return true | |
} | |
guard let l = left, let r = right else { | |
return false | |
} | |
return l =~= r | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment