using System; using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities; namespace StardewModdingAPI.Utilities.AssetPathUtilities { /// Handles enumerating the normalized segments in an asset name. internal ref struct AssetNamePartEnumerator { /********* ** Fields *********/ /// The backing field for . private ReadOnlySpan RemainderImpl; /********* ** Properties *********/ /// The remainder of the asset name being enumerated, ignoring segments which have already been yielded. public ReadOnlySpan Remainder => this.RemainderImpl; /// Get the current segment. public ReadOnlySpan Current { get; private set; } = default; /********* ** Public methods *********/ /// Construct an instance. /// The asset name to enumerate. public AssetNamePartEnumerator(ReadOnlySpan assetName) { this.RemainderImpl = AssetNamePartEnumerator.TrimLeadingPathSeparators(assetName); } /// Move the enumerator to the next segment. /// Returns true if a new value was found (accessible via ). 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.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 *********/ /// Trim path separators at the start of the given path or segment. /// The path or segment to trim. private static ReadOnlySpan TrimLeadingPathSeparators(ReadOnlySpan span) { return span.TrimStart(new ReadOnlySpan(ToolkitPathUtilities.PossiblePathSeparators)); } } }