// Copyright (c) 2025 Probo Inc . // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. package validator import ( "reflect" "time" ) // EqualTo validates that a value equals another value using deep equality. // Special handling for time.Time to compare instants rather than internal structure. func EqualTo(other any) ValidatorFunc { return func(value any) *ValidationError { if !areEqual(value, other) { return newValidationError(ErrorCodeInvalidFormat, "values must match") } return nil } } // NotEqualTo validates that a value does not equal another value using deep equality. // Special handling for time.Time to compare instants rather than internal structure. func NotEqualTo(other any) ValidatorFunc { return func(value any) *ValidationError { if areEqual(value, other) { return newValidationError(ErrorCodeInvalidFormat, "values must not match") } return nil } } // areEqual compares two values for equality with special handling for time.Time. func areEqual(a, b any) bool { // Dereference both values aVal, aIsNil := dereferenceValue(a) bVal, bIsNil := dereferenceValue(b) // If both are nil, they're equal if aIsNil && bIsNil { return true } // If only one is nil, they're not equal if aIsNil || bIsNil { return false } // Special handling for time.Time aTime, aIsTime := aVal.(time.Time) bTime, bIsTime := bVal.(time.Time) if aIsTime && bIsTime { // Use time.Time.Equal() which compares the instant, ignoring location and monotonic clock return aTime.Equal(bTime) } // Fall back to reflect.DeepEqual for all other types return reflect.DeepEqual(aVal, bVal) }