< Summary

Line coverage
100%
Covered lines: 66
Uncovered lines: 0
Coverable lines: 66
Total lines: 280
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%22100%
File 1: .ctor(...)100%22100%
File 1: .ctor(...)100%11100%
File 1: get_Capacity()100%11100%
File 1: get_Item(...)100%11100%
File 1: set_Item(...)100%22100%
File 1: get_State()100%22100%
File 1: set_State(...)100%22100%
File 1: ToArray()100%22100%
File 1: ValidateIndex(...)100%44100%
File 1: OnItemChanged(...)100%44100%
File 1: OnPropertyChanged(...)100%22100%
File 2: .ctor(...)100%11100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Observable/SwiftObservableArray.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftObservableArray.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.ComponentModel;
 10using System.Text.Json.Serialization;
 11using Chronicler;
 12using Chronicler.Diagnostics;
 13using Chronicler.Serialization;
 14using MemoryPack;
 15
 16namespace SwiftCollections.Observable;
 17
 18/// <summary>
 19/// Represents an array of observable properties, raising events whenever an element is updated.
 20/// Designed for performance-critical scenarios in Unity game development.
 21/// </summary>
 22/// <typeparam name="TValue">The type of elements in the array.</typeparam>
 23[Serializable]
 24[JsonConverter(typeof(StateJsonConverterFactory))]
 25[MemoryPackable]
 26public partial class SwiftObservableArray<TValue> : IStateBacked<SwiftArrayState<TValue>>, INotifyPropertyChanged
 27{
 28    #region Fields
 29
 30    /// <summary>
 31    /// Represents the collection of observable properties managed by this instance.
 32    /// </summary>
 33    /// <remarks>Each element in the array corresponds to an individual observable property.
 34    /// The array may be null or empty if no properties are currently managed.
 35    /// </remarks>
 36    protected SwiftObservableProperty<TValue>[] _items;
 37
 38    /// <summary>
 39    /// Represents the event handler that is invoked when a property value changes on an item.
 40    /// </summary>
 41    /// <remarks>
 42    /// This handler is typically used to subscribe to property change notifications for items within a collection or co
 43    /// Derived classes can use this field to manage event subscriptions for item property changes.
 44    /// </remarks>
 45    protected PropertyChangedEventHandler _itemChangedHandler;
 46
 47    #endregion
 48
 49    #region Events
 50
 51    /// <summary>
 52    /// Raised when any element in the array changes, providing the index and the new value.
 53    /// </summary>
 54    public event EventHandler<ElementChangedEventArgs<TValue>>? ElementChanged;
 55
 56    /// <summary>
 57    /// Raised when the array's state changes.
 58    /// </summary>
 59    public event PropertyChangedEventHandler? PropertyChanged;
 60
 61    #endregion
 62
 63    #region Constructors
 64
 65    /// <summary>
 66    /// Initializes a new instance of the SwiftObservableArray class with the specified capacity.
 67    /// </summary>
 68    /// <remarks>
 69    /// Each element in the array is initialized with a new <see cref="SwiftObservableProperty{TValue}"/> instance.
 70    /// The capacity determines the fixed size of the array and cannot be changed after construction.
 71    /// </remarks>
 72    /// <param name="capacity">The number of elements the array can contain. Must be a positive integer.</param>
 2173    public SwiftObservableArray(int capacity)
 74    {
 2175        ChronicleGuard.ThrowIfNegativeOrZero(capacity, nameof(capacity));
 76
 2177        _items = new SwiftObservableProperty<TValue>[capacity];
 78        _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 79
 20215080        for (int i = 0; i < capacity; i++)
 81        {
 10105482            _items[i] = new SwiftObservableProperty<TValue>
 10105483            {
 10105484                Index = i
 10105485            };
 10105486            _items[i].PropertyChanged += _itemChangedHandler;
 87        }
 2188    }
 89
 90    /// <summary>
 91    /// Initializes a new instance of the SwiftObservableArray class with the specified observable properties.
 92    /// </summary>
 93    /// <remarks>
 94    /// Each property in the array is assigned its index and subscribed to change notifications.
 95    /// Changes to any property will be observed by the array.
 96    /// </remarks>
 97    /// <param name="observableProperties">An array of <see cref="SwiftObservableProperty{TValue}"/> instances to be man
 298    public SwiftObservableArray(SwiftObservableProperty<TValue>[] observableProperties)
 99    {
 2100        ChronicleGuard.ThrowIfNull(observableProperties, nameof(observableProperties));
 101
 1102        _items = observableProperties;
 103        _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 104
 6105        for (int i = 0; i < _items.Length; i++)
 106        {
 2107            _items[i].Index = i;
 2108            _items[i].PropertyChanged += _itemChangedHandler;
 109        }
 1110    }
 111
 112    ///  <summary>
 113    ///  Initializes a new instance of the <see cref="SwiftObservableArray{TValue}"/> class with the specified <see cref
 114    ///  </summary>
 115    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 116    [MemoryPackConstructor]
 7117    public SwiftObservableArray(SwiftArrayState<TValue> state)
 118    {
 7119        State = state;
 120
 7121        ChronicleGuard.ThrowIfNull(_items, nameof(_items));
 7122        ChronicleGuard.ThrowIfNull(_itemChangedHandler, nameof(_itemChangedHandler));
 7123    }
 124
 125    #endregion
 126
 127    #region Properties
 128
 129    /// <summary>
 130    /// Gets the total number of elements that the internal data structure can hold without resizing.
 131    /// </summary>
 132    [JsonIgnore]
 133    [MemoryPackIgnore]
 2134    public int Capacity => _items.Length;
 135
 136    /// <summary>
 137    /// Gets or sets the value at the specified index.
 138    /// </summary>
 139    /// <param name="index">The zero-based index of the element to get or set. Must be within the valid range of the col
 140    [JsonIgnore]
 141    [MemoryPackIgnore]
 142    public TValue this[int index]
 143    {
 144        get
 145        {
 10146            ValidateIndex(index);
 8147            return _items[index].Value;
 148        }
 149        set
 150        {
 1029151            ValidateIndex(index);
 1029152            if (!Equals(_items[index].Value, value))
 1027153                _items[index].Value = value;
 1029154        }
 155    }
 156
 157    /// <summary>
 158    /// Gets or sets the current state of the array, including the values of all items.
 159    /// </summary>
 160    /// <remarks>
 161    /// Setting this property replaces the entire array state, including all item values and their order.
 162    /// Any existing item change handlers are reset when the state is set.
 163    /// </remarks>
 164    [JsonInclude]
 165    [MemoryPackInclude]
 166    public SwiftArrayState<TValue> State
 167    {
 168        get
 169        {
 8170            var values = new TValue[_items.Length];
 171
 54172            for (int i = 0; i < _items.Length; i++)
 19173                values[i] = _items[i].Value;
 174
 8175            return new SwiftArrayState<TValue>(values);
 176        }
 177        internal set
 178        {
 7179            ChronicleGuard.ThrowIfNull(value.Items, nameof(value.Items));
 180
 7181            var values = value.Items;
 182
 7183            int capacity = values.Length;
 184
 7185            _items = new SwiftObservableProperty<TValue>[capacity];
 7186            _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 187
 48188            for (int i = 0; i < capacity; i++)
 189            {
 17190                _items[i] = new SwiftObservableProperty<TValue>(values[i])
 17191                {
 17192                    Index = i
 17193                };
 17194                _items[i].PropertyChanged += _itemChangedHandler;
 195            }
 7196        }
 197    }
 198
 199    #endregion
 200
 201    #region Methods
 202
 203    /// <summary>
 204    /// Returns an array containing all elements in the collection.
 205    /// </summary>
 206    /// <returns>
 207    /// An array of type TValue that contains the values of the collection in order.
 208    /// The array will be empty if the collection contains no elements.
 209    /// </returns>
 210    public TValue[] ToArray()
 211    {
 7212        var array = new TValue[_items.Length];
 202046213        for (int i = 0; i < _items.Length; i++)
 101016214            array[i] = _items[i].Value;
 7215        return array;
 216    }
 217
 218    private void ValidateIndex(int index)
 219    {
 1039220        if (index < 0 || index >= _items.Length)
 2221            throw new IndexOutOfRangeException($"Index {index} is out of bounds for this array.");
 1037222    }
 223
 224    private void OnItemChanged(object? sender)
 225    {
 1031226        if (sender is SwiftObservableProperty<TValue> property)
 227        {
 1029228            int index = property.Index;
 229
 1029230            ElementChanged?.Invoke(
 1029231                this,
 1029232                new ElementChangedEventArgs<TValue>(index, property.Value)
 1029233            );
 234
 1029235            OnPropertyChanged("Items[]");
 236        }
 1031237    }
 238
 239    private void OnPropertyChanged(string propertyName)
 240    {
 1029241        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 2242    }
 243
 244    #endregion
 245}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Observable/SwiftObservableArray.ElementChangedEventArgs.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftObservableArray.ElementChangedEventArgs.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9
 10namespace SwiftCollections.Observable;
 11
 12public partial class SwiftObservableArray<TValue>
 13{
 14    /// <summary>
 15    /// Provides details about a changed element, including its index and new value.
 16    /// </summary>
 17    public class ElementChangedEventArgs<T> : EventArgs
 18    {
 19        /// <summary>
 20        /// The index of the changed element.
 21        /// </summary>
 22        public int Index { get; }
 23
 24        /// <summary>
 25        /// The new value of the changed element.
 26        /// </summary>
 27        public T NewValue { get; }
 28
 100929        internal ElementChangedEventArgs(int index, T newValue)
 30        {
 100931            Index = index;
 100932            NewValue = newValue;
 100933        }
 34    }
 35}