Rather nice! One small improvement I'd make, is change the char[] LegalChars to IEnumerable<char> LegalChars so, for example, a string of invalidvalid characters can be passed in. Here's how I've modified the struct:
public struct SecurePasswordGenerator(int PasswordLength, IEnumerable<char>? LegalChars) : IEnumerable<char>
{
//was used for test purposes calling from a static method
////private readonly int PasswordLength => passwordLength;
private readonly char[] _legalChars = LegalChars?.ToArray() ?? [];
public readonly string NextPassword() => new(this.ToArray());
public static IEnumerable<string> GeneratePasswords(int size, SecurePasswordGenerator generator) =>
generator.Chunk(size).Select(chunk => new string(chunk));
readonly IEnumerator<char> IEnumerable<char>.GetEnumerator()
{
// ReSharper disable once ComplexConditionExpression
bool isInvalidChars = _legalChars.Length == 0;
if (isInvalidChars || PasswordLength <= 0)
{
yield break;
}
using RandomNumberGenerator rng = RandomNumberGenerator.Create();
byte[] buffer = new byte[4byte[sizeof(UInt32) * PasswordLength];
rng.GetBytes(buffer);
for (int i = 0; i < PasswordLength; i++)
{
int offset = 4sizeof(UInt32) * i;
uint rand = BitConverter.ToUInt32(buffer, offset);
yield return _legalChars[(int)(rand % _legalChars.Length)];
}
}
[DebuggerHidden, DebuggerStepThrough]
readonly IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<char>)this).GetEnumerator();
}
```