namespace AIStudio.Models.Matching; /// /// Walks the parts of a normalized model name without cutting it into strings. /// /// /// The index looks up every part of a name to find the rules which could possibly apply to it. That /// happens for every model of every configured provider, so the walk itself must not allocate: the /// parts stay slices of the name they came from. This is both the enumerable and the enumerator, /// which is what lets foreach use it without an interface in between. /// /// The normalized model name to walk. public ref struct ModelIdSegments(ReadOnlySpan normalizedId) { private ReadOnlySpan remaining = normalizedId; /// /// The part the walk currently stands on. /// public ReadOnlySpan Current { get; private set; } = default; /// /// Hands foreach the walk itself. /// /// This walk, at its beginning. public readonly ModelIdSegments GetEnumerator() => this; /// /// Steps to the next part of the name. /// /// True, as long as there was one. public bool MoveNext() { while (!this.remaining.IsEmpty) { var separator = this.remaining.IndexOf(ModelId.SEGMENT_SEPARATOR); if (separator is -1) { this.Current = this.remaining; this.remaining = default; return true; } this.Current = this.remaining[..separator]; this.remaining = this.remaining[(separator + 1)..]; // // Normalizing leaves no empty part behind, so this only guards against a name which // never went through it. Skipping is the right answer: an empty part matches nothing. // if (!this.Current.IsEmpty) return true; } return false; } }