HumanoidRetargeter/Core/Target/KvValue.cs

Abstract base for a minimal KV3 value model used for vmdl files. Provides a static DeepEquals method that performs structural equality across KvObject, KvArray, KvString, KvLong, KvDouble, KvBool and KvNull types by recursively comparing kinds, keys/order and scalar values.

#nullable enable annotations

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace HumanoidRetargeter.Core.Target;

/// <summary>
/// Base of the minimal KV3 value model used for vmdl files: <see cref="KvObject"/>,
/// <see cref="KvArray"/>, <see cref="KvString"/>, <see cref="KvLong"/>, <see cref="KvDouble"/>,
/// <see cref="KvBool"/>, <see cref="KvNull"/>. Integers and doubles are distinct kinds so the
/// writer can preserve the shipped <c>1</c>-vs-<c>1.0</c> style.
/// </summary>
public abstract class KvValue
{
    /// <summary>Structural (semantic) equality over whole trees: same kinds, same object
    /// key order, same array order, equal scalar values.</summary>
    public static bool DeepEquals(KvValue? a, KvValue? b)
    {
        if (ReferenceEquals(a, b))
            return true;
        if (a is null || b is null)
            return false;

        switch (a)
        {
            case KvObject oa when b is KvObject ob:
                if (oa.Count != ob.Count)
                    return false;
                for (var i = 0; i < oa.Count; i++)
                {
                    if (!string.Equals(oa.Keys[i], ob.Keys[i], StringComparison.Ordinal))
                        return false;
                    if (!DeepEquals(oa[oa.Keys[i]], ob[ob.Keys[i]]))
                        return false;
                }
                return true;
            case KvArray ra when b is KvArray rb:
                if (ra.Items.Count != rb.Items.Count)
                    return false;
                for (var i = 0; i < ra.Items.Count; i++)
                {
                    if (!DeepEquals(ra.Items[i], rb.Items[i]))
                        return false;
                }
                return true;
            case KvString sa when b is KvString sb:
                return string.Equals(sa.Value, sb.Value, StringComparison.Ordinal);
            case KvLong la when b is KvLong lb:
                return la.Value == lb.Value;
            case KvDouble da when b is KvDouble db:
                return da.Value.Equals(db.Value);
            case KvBool ba when b is KvBool bb:
                return ba.Value == bb.Value;
            case KvNull when b is KvNull:
                return true;
            default:
                return false;
        }
    }
}