Skip to content

Instantly share code, notes, and snippets.

@hannesoid
Created June 1, 2017 12:06
Show Gist options
  • Save hannesoid/0e76ccdd7789d9e9add940ff5585f0fa to your computer and use it in GitHub Desktop.
Save hannesoid/0e76ccdd7789d9e9add940ff5585f0fa to your computer and use it in GitHub Desktop.
AutoEquatable.stencil for Sourcery with softness for Doubles and Dates
// 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 %}
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