// Copyright 2016-2024, Pulumi Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package property type EqualOption func(*eqOpts) // See the doc comment for Value.Equals for the effect of EqualRelaxComputed. func EqualRelaxComputed(opts *eqOpts) { opts.relaxComputed = true } type eqOpts struct { relaxComputed bool } // Check if two Values are equal. // // There are two corner cases that need to be called out here: // // - Secret equality is enforced. That means that: // // {"a", secret: false} == {"a", secret: false} // // {"a", secret: true} != {"a", secret: false} // // {"b", secret: false} != {"c", secret: false} // // - Computed value equality has two different modes. By default, it works like Null // equality: a.IsComputed() => (a.Equals(b) <=> b.IsComputed()) (up to secrets and // dependencies). // // If EqualRelaxComputed is passed, then computed values are considered equal to all // other values. (up to secrets and dependencies) func (v Value) Equals(other Value, opts ...EqualOption) bool { var eqOpts eqOpts for _, o := range opts { o(&eqOpts) } return v.equals(other, eqOpts) } func (v Value) equals(other Value, opts eqOpts) bool { if v.isSecret != other.isSecret { return false } if len(v.dependencies) != len(other.dependencies) { return false } for i, d := range v.dependencies { if other.dependencies[i] != d { return false } } if opts.relaxComputed && (v.IsComputed() || other.IsComputed()) { return true } switch { case v.IsBool() && other.IsBool(): return v.AsBool() == other.AsBool() case v.IsNumber() && other.IsNumber(): return v.AsNumber() == other.AsNumber() case v.IsString() && other.IsString(): return v.AsString() == other.AsString() case v.IsArray() && other.IsArray(): a1, a2 := v.AsArray(), other.AsArray() if len(a1) != len(a2) { return false } for i := range a1 { if !a1[i].equals(a2[i], opts) { return false } } return true case v.IsMap() && other.IsMap(): m1, m2 := v.AsMap(), other.AsMap() if len(m1) != len(m2) { return false } for k, v1 := range m1 { v2, ok := m2[k] if !ok || !v1.equals(v2, opts) { return false } } return true case v.IsAsset() && other.IsAsset(): a1, a2 := v.AsAsset(), other.AsAsset() return a1.Equals(a2) case v.IsArchive() && other.IsArchive(): a1, a2 := v.AsArchive(), other.AsArchive() return a1.Equals(a2) case v.IsResourceReference() && other.IsResourceReference(): r1, r2 := v.AsResourceReference(), other.AsResourceReference() return r1.Equal(r2) case v.IsNull() && other.IsNull(): return true case v.IsComputed() && other.IsComputed(): return true default: return false } }