summaryrefslogtreecommitdiff
path: root/src/SMAPI/Utilities/AssetPathUtilities/AssetNamePartEnumerator.cs
blob: 11987ed6cd8593dfae3ec93629b1eff52bbf2f8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using System;
using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;

namespace StardewModdingAPI.Utilities.AssetPathUtilities
{
    /// <summary>Handles enumerating the normalized segments in an asset name.</summary>
    internal ref struct AssetNamePartEnumerator
    {
        /*********
        ** Fields
        *********/
        /// <summary>The backing field for <see cref="Remainder"/>.</summary>
        private ReadOnlySpan<char> RemainderImpl;


        /*********
        ** Properties
        *********/
        /// <summary>The remainder of the asset name being enumerated, ignoring segments which have already been yielded.</summary>
        public ReadOnlySpan<char> Remainder => this.RemainderImpl;

        /// <summary>Get the current segment.</summary>
        public ReadOnlySpan<char> Current { get; private set; } = default;


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="assetName">The asset name to enumerate.</param>
        public AssetNamePartEnumerator(ReadOnlySpan<char> assetName)
        {
            this.RemainderImpl = AssetNamePartEnumerator.TrimLeadingPathSeparators(assetName);
        }

        /// <summary>Move the enumerator to the next segment.</summary>
        /// <returns>Returns true if a new value was found (accessible via <see cref="Current"/>).</returns>
        public bool MoveNext()
        {
            if (this.RemainderImpl.Length == 0)
                return false;

            int index = this.RemainderImpl.IndexOfAny(ToolkitPathUtilities.PossiblePathSeparators);

            // no more separator characters found, I'm done.
            if (index < 0)
            {
                this.Current = this.RemainderImpl;
                this.RemainderImpl = ReadOnlySpan<char>.Empty;
                return true;
            }

            // Yield the next separate character bit
            this.Current = this.RemainderImpl[..index];
            this.RemainderImpl = AssetNamePartEnumerator.TrimLeadingPathSeparators(this.RemainderImpl[(index + 1)..]);
            return true;
        }


        /*********
        ** Private methods
        *********/
        /// <summary>Trim path separators at the start of the given path or segment.</summary>
        /// <param name="span">The path or segment to trim.</param>
        private static ReadOnlySpan<char> TrimLeadingPathSeparators(ReadOnlySpan<char> span)
        {
            return span.TrimStart(new ReadOnlySpan<char>(ToolkitPathUtilities.PossiblePathSeparators));
        }
    }
}