< Summary

Information
Class: MyNet.Text.Randomize.TextRandomGenerator
Assembly: MyNet.Text
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Text/Randomize/TextRandomGenerator.cs
Tag: 323_28699572109
Line coverage
28%
Covered lines: 19
Uncovered lines: 48
Coverable lines: 67
Total lines: 186
Line coverage: 28.3%
Branch coverage
20%
Covered branches: 13
Total branches: 62
Branch coverage: 20.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
Randomize(...)80%111076.47%
ExpandCharacterSet(...)0%110100%
ParseComplexToken(...)0%702260%
GenerateToken(...)31.25%711640%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Text/Randomize/TextRandomGenerator.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="TextRandomGenerator.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Generic;
 9using System.Text;
 10using MyNet.Generator;
 11using MyNet.Generator.Facade;
 12using MyNet.Primitives.Helpers;
 13
 14namespace MyNet.Text.Randomize;
 15
 16/// <summary>
 17/// Implements a random text generator that can replace specific symbols in a format string with random characters based
 18/// </summary>
 19/// <param name="random">The random generator used to generate random values.</param>
 20public sealed class TextRandomGenerator(IRandomGenerator random) : ITextRandomGenerator
 21{
 22    /// <summary>
 23    /// Gets the current instance of the <see cref="TextRandomGenerator"/> using the default random generator.
 24    /// </summary>
 225    public static TextRandomGenerator Current { get; } = new(RandomGenerator.Current);
 26
 27    /// <inheritdoc/>
 28    public string Randomize(string pattern)
 29    {
 1130        ArgumentNullException.ThrowIfNull(pattern);
 31
 1132        var builder = new StringBuilder(pattern.Length);
 33
 5434        for (var i = 0; i < pattern.Length; i++)
 35        {
 1936            var c = pattern[i];
 37
 1938            switch (c)
 39            {
 340                case '\\' when i + 1 >= pattern.Length:
 041                    throw new FormatException("Invalid escape sequence.");
 42
 43                case '\\':
 344                    builder.Append(pattern[++i]);
 345                    continue;
 46
 47                case '{':
 48                    {
 349                        var end = pattern.IndexOf('}', i);
 50
 351                        if (end < 0)
 352                            throw new FormatException("Missing closing brace.");
 53
 054                        ParseComplexToken(pattern.AsSpan(i + 1, end - i - 1), builder);
 55
 056                        i = end;
 057                        continue;
 58                    }
 59
 60                default:
 1361                    builder.Append(GenerateToken(c));
 62                    break;
 63            }
 64        }
 65
 866        return builder.ToString();
 67    }
 68
 69    /// <summary>
 70    /// Expands a character set defined in a token, supporting both individual characters and ranges (e.g., A-Z). The in
 71    /// </summary>
 72    /// <param name="pattern">The character set pattern to expand.</param>
 73    /// <returns>An array of characters representing the expanded character set.</returns>
 74    /// <exception cref="FormatException">Thrown when the character set pattern is invalid.</exception>
 75    private static char[] ExpandCharacterSet(ReadOnlySpan<char> pattern)
 76    {
 077        var chars = new List<char>();
 78
 079        for (var i = 0; i < pattern.Length; i++)
 80        {
 081            if (i + 2 < pattern.Length &&
 082                pattern[i + 1] == '-')
 83            {
 084                var start = pattern[i];
 085                var end = pattern[i + 2];
 86
 087                if (start > end)
 088                    throw new FormatException("Invalid character range.");
 89
 090                for (var c = start; c <= end; c++)
 091                    chars.Add(c);
 92
 093                i += 2;
 094                continue;
 95            }
 96
 097            chars.Add(pattern[i]);
 98        }
 99
 0100        return [.. chars];
 101    }
 102
 103    /// <summary>
 104    /// Parses a complex token enclosed in braces and appends the generated characters to the provided StringBuilder. Th
 105    /// </summary>
 106    /// <param name="token">The complex token to parse.</param>
 107    /// <param name="builder">The StringBuilder to append the generated characters to.</param>
 108    /// <exception cref="FormatException">Thrown when the token format is invalid.</exception>
 109    private void ParseComplexToken(ReadOnlySpan<char> token, StringBuilder builder)
 110    {
 0111        if (token.IsEmpty)
 0112            throw new FormatException("Empty token.");
 113
 114        // {#5}
 0115        if (token.Length >= 2 && char.IsDigit(token[^1]))
 116        {
 0117            var countStart = token.Length - 1;
 118
 0119            while (countStart > 0 && char.IsDigit(token[countStart - 1]))
 0120                countStart--;
 121
 0122            var countSpan = token[countStart..];
 123
 0124            if (!int.TryParse(countSpan, out var count))
 0125                throw new FormatException("Invalid repetition count.");
 126
 0127            var valueSpan = token[..countStart];
 128
 0129            switch (valueSpan.Length)
 130            {
 131                // {[ABC]5}
 0132                case >= 3 when
 0133                    valueSpan[0] == '[' &&
 0134                    valueSpan[^1] == ']':
 135                    {
 0136                        var charset = ExpandCharacterSet(valueSpan[1..^1]);
 137
 0138                        for (var i = 0; i < count; i++)
 0139                            builder.Append(charset[random.Int(0, charset.Length)]);
 140
 0141                        return;
 142                    }
 143
 144                // {#5}
 145                case 1:
 146                    {
 0147                        for (var i = 0; i < count; i++)
 0148                            builder.Append(GenerateToken(valueSpan[0]));
 149
 0150                        return;
 151                    }
 152
 153                default:
 0154                    throw new FormatException($"Invalid token '{token}'.");
 155            }
 156        }
 157
 158        // {ABC}
 0159        foreach (var c in token)
 0160            builder.Append(c);
 0161    }
 162
 163    /// <summary>
 164    /// Generates a random character based on the provided token. The token determines the type of character to generate
 165    /// - '#' generates a random digit (0-9).
 166    /// - '?' generates a random uppercase letter (A-Z).
 167    /// - 'a' generates a random lowercase letter (a-z).
 168    /// - '*' generates a random alphanumeric character (0-9, A-Z).
 169    /// - '&amp;' generates a random hexadecimal character (0-9, A-F).
 170    /// - '!' generates a random ASCII character (from 33 to 126).
 171    /// </summary>
 172    /// <param name="token">The token representing the type of character to generate.</param>
 173    /// <returns>A randomly generated character based on the token.</returns>
 174    private char GenerateToken(char token)
 13175        => token switch
 13176        {
 3177            '#' => (char)('0' + random.Int(0, 10)),
 0178            '?' => (char)('A' + random.Int(0, 26)),
 0179            'a' => (char)('a' + random.Int(0, 26)),
 0180            '*' => random.Bool() ? (char)('0' + random.Int(0, 10)) : (char)('A' + random.Int(0, 26)),
 0181            '&' => CharHelper.HexDigits[random.Int(0, 16)],
 0182            '!' => (char)random.Int(33, 127),
 10183            _ => token
 13184        };
 185}
 186