using System.Collections; namespace Nbt; public static class NbtPath { public static readonly INbtPath Empty = new EmptyNbtPathImpl(); public static INbtPath CreatePath(INbtPathElement pathElement) => new NbtPathImpl([pathElement]); public static INbtPath CreatePath(params INbtPathElement[] pathElements) => CreatePath(pathElements.AsEnumerable()); public static INbtPath CreatePath(IEnumerable pathElements) => new NbtPathImpl(pathElements); public static INbtPath CreatePath(params object[] pathElements) => CreatePath(pathElements.AsEnumerable()); public static INbtPath CreatePath(IEnumerable pathElements) => CreatePath(pathElements.Select(WrapPathElement)); private static INbtPathElement WrapPathElement(object pathElement) => pathElement switch { null => throw new ArgumentNullException(nameof(pathElement)), string name => new NbtName(name), int index => new NbtIndex(index), _ => throw new ArgumentException($"Invalid path element type: {pathElement.GetType().Name}", nameof(pathElement)) }; } public interface INbtPath : IEnumerable { int Count { get; } bool IsEmpty { get; } INbtPathElement PopFirst(out INbtPath rest); INbtPathElement this[int index] { get; } INbtPath Combine(INbtPath other); INbtPath Append(INbtPathElement pathElement); INbtPath Insert(int index, INbtPathElement pathElement); INbtPath Replace(int index, INbtPathElement pathElement); } file class NbtPathImpl(IEnumerable pathElements) : INbtPath { private readonly IList _pathElements = pathElements.AsIList(); public int Count => _pathElements.Count; public bool IsEmpty => Count == 0; public INbtPathElement PopFirst(out INbtPath rest) { var count = Count; if (count < 1) { throw new IndexOutOfRangeException(); } rest = count > 1 ? new NbtPathImpl(_pathElements.Skip(1)) : NbtPath.Empty; return _pathElements[0]; } public INbtPathElement this[int index] => _pathElements[index]; public INbtPath Append(INbtPathElement pathElement) => new NbtPathImpl(_pathElements.Append(pathElement)); public INbtPath Insert(int index, INbtPathElement pathElement) => new NbtPathImpl(Utils.Insert(_pathElements, index, pathElement)); public INbtPath Replace(int index, INbtPathElement pathElement) => new NbtPathImpl(Utils.Replace(_pathElements, index, pathElement)); public INbtPath Combine(INbtPath other) => new NbtPathImpl(_pathElements.Concat(other)); public IEnumerator GetEnumerator() => _pathElements.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } file class EmptyNbtPathImpl : INbtPath { public int Count => 0; public bool IsEmpty => true; public INbtPathElement PopFirst(out INbtPath rest) => throw new IndexOutOfRangeException(); public INbtPathElement this[int index] => throw new IndexOutOfRangeException(); public INbtPath Append(INbtPathElement pathElement) => new NbtPathImpl(pathElement.Yield()); public INbtPath Insert(int index, INbtPathElement pathElement) { ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0); return new NbtPathImpl(pathElement.Yield()); } public INbtPath Replace(int index, INbtPathElement pathElement) { throw new ArgumentOutOfRangeException(nameof(index)); } public INbtPath Combine(INbtPath other) => other; public IEnumerator GetEnumerator() => Enumerable.Empty().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public interface INbtPathElement { object Key { get; } } public readonly struct NbtName(string name) : INbtPathElement { public string Name { get; } = name; object INbtPathElement.Key => Name; public static implicit operator NbtName(string tagName) => new(tagName); public static implicit operator string(NbtName nbtName) => nbtName.Name; } public readonly struct NbtIndex(int index) : INbtPathElement { public int Index { get; } = index; object INbtPathElement.Key => Index; public static implicit operator NbtIndex(int tagIndex) => new(tagIndex); public static implicit operator int(NbtIndex nbtIndex) => nbtIndex.Index; }