< Summary

Information
Class: ArturRios.Util.Text.CharacterChecks
Assembly: ArturRios.Util
File(s): /home/runner/work/dotnet-util/dotnet-util/src/Text/CharacterChecks.cs
Line coverage
100%
Covered lines: 28
Uncovered lines: 0
Coverable lines: 28
Total lines: 137
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage
100%
Covered methods: 13
Fully covered methods: 13
Total methods: 13
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
HasNumber(...)100%11100%
HasLowerChar(...)100%11100%
HasUpperChar(...)100%11100%
HasSpecialChar(...)100%11100%
HasNumber(...)100%11100%
HasLowerChar(...)100%11100%
HasUpperChar(...)100%11100%
HasSpecialChar(...)100%11100%
Classify(...)100%1818100%
Classify(...)100%11100%
Missing(...)100%11100%
Missing(...)100%11100%

File(s)

/home/runner/work/dotnet-util/dotnet-util/src/Text/CharacterChecks.cs

#LineLine coverage
 1using System.Buffers;
 2using ArturRios.Util.Collections;
 3
 4namespace ArturRios.Util.Text;
 5
 6/// <summary>
 7/// Allocation-free character class checks over strings and spans.
 8/// </summary>
 9/// <remarks>
 10/// These are vectorized equivalents of the corresponding patterns in
 11/// <see cref="RegularExpressions.RegexCollection"/> and agree with them on every input: both are ASCII
 12/// only. They deliberately do not use <see cref="char.IsDigit(char)"/>, <see cref="char.IsLower(char)"/>
 13/// or <see cref="char.IsUpper(char)"/>, which are Unicode-aware and would report, for example, the
 14/// Arabic-Indic digit three as a digit and the German sharp s as a lowercase letter.
 15/// </remarks>
 16public static class CharacterChecks
 17{
 118    private static readonly SearchValues<char> Digits = SearchValues.Create(Characters.Digits);
 119    private static readonly SearchValues<char> LowerLetters = SearchValues.Create(Characters.LowerLetters);
 120    private static readonly SearchValues<char> UpperLetters = SearchValues.Create(Characters.UpperLetters);
 121    private static readonly SearchValues<char> SpecialChars = SearchValues.Create(Characters.Special);
 22
 23    /// <summary>Checks whether <paramref name="value"/> contains at least one ASCII digit.</summary>
 24    /// <param name="value">Input text.</param>
 25    /// <returns><see langword="true"/> when a digit 0-9 is present.</returns>
 1526    public static bool HasNumber(this ReadOnlySpan<char> value) => value.ContainsAny(Digits);
 27
 28    /// <summary>Checks whether <paramref name="value"/> contains at least one ASCII lowercase letter.</summary>
 29    /// <param name="value">Input text.</param>
 30    /// <returns><see langword="true"/> when a letter a-z is present.</returns>
 1231    public static bool HasLowerChar(this ReadOnlySpan<char> value) => value.ContainsAny(LowerLetters);
 32
 33    /// <summary>Checks whether <paramref name="value"/> contains at least one ASCII uppercase letter.</summary>
 34    /// <param name="value">Input text.</param>
 35    /// <returns><see langword="true"/> when a letter A-Z is present.</returns>
 1136    public static bool HasUpperChar(this ReadOnlySpan<char> value) => value.ContainsAny(UpperLetters);
 37
 38    /// <summary>Checks whether <paramref name="value"/> contains at least one <see cref="Characters.Special"/> characte
 39    /// <param name="value">Input text.</param>
 40    /// <returns><see langword="true"/> when a special character is present.</returns>
 741    public static bool HasSpecialChar(this ReadOnlySpan<char> value) => value.ContainsAny(SpecialChars);
 42
 43    /// <summary>Checks whether <paramref name="value"/> contains at least one ASCII digit.</summary>
 44    /// <param name="value">Input text. A <see langword="null"/> value contains nothing.</param>
 45    /// <returns><see langword="true"/> when a digit 0-9 is present.</returns>
 1446    public static bool HasNumber(this string? value) => value.AsSpan().HasNumber();
 47
 48    /// <summary>Checks whether <paramref name="value"/> contains at least one ASCII lowercase letter.</summary>
 49    /// <param name="value">Input text. A <see langword="null"/> value contains nothing.</param>
 50    /// <returns><see langword="true"/> when a letter a-z is present.</returns>
 1251    public static bool HasLowerChar(this string? value) => value.AsSpan().HasLowerChar();
 52
 53    /// <summary>Checks whether <paramref name="value"/> contains at least one ASCII uppercase letter.</summary>
 54    /// <param name="value">Input text. A <see langword="null"/> value contains nothing.</param>
 55    /// <returns><see langword="true"/> when a letter A-Z is present.</returns>
 1156    public static bool HasUpperChar(this string? value) => value.AsSpan().HasUpperChar();
 57
 58    /// <summary>Checks whether <paramref name="value"/> contains at least one <see cref="Characters.Special"/> characte
 59    /// <param name="value">Input text. A <see langword="null"/> value contains nothing.</param>
 60    /// <returns><see langword="true"/> when a special character is present.</returns>
 761    public static bool HasSpecialChar(this string? value) => value.AsSpan().HasSpecialChar();
 62
 63    /// <summary>
 64    /// Reports every <see cref="CharacterClasses"/> present in <paramref name="value"/> in a single pass.
 65    /// </summary>
 66    /// <param name="value">Input text.</param>
 67    /// <returns>The classes present, or <see cref="CharacterClasses.None"/> when none are.</returns>
 68    /// <remarks>
 69    /// Unlike <see cref="RegularExpressions.RegexCollection.HasNumberLowerAndUpperCharPattern"/>, which walks
 70    /// the input once per lookahead and rejects anything spanning more than one line, this reads the input
 71    /// once, stops as soon as every class has been seen, and is indifferent to newlines.
 72    /// </remarks>
 73    public static CharacterClasses Classify(this ReadOnlySpan<char> value)
 74    {
 75        const CharacterClasses everything = CharacterClasses.Digit | CharacterClasses.Lower |
 76                                            CharacterClasses.Upper | CharacterClasses.Special;
 11577        var found = CharacterClasses.None;
 78
 53079        foreach (var character in value)
 80        {
 15181            if (character is >= '0' and <= '9')
 82            {
 2783                found |= CharacterClasses.Digit;
 84            }
 12485            else if (character is >= 'a' and <= 'z')
 86            {
 4587                found |= CharacterClasses.Lower;
 88            }
 7989            else if (character is >= 'A' and <= 'Z')
 90            {
 4091                found |= CharacterClasses.Upper;
 92            }
 3993            else if (SpecialChars.Contains(character))
 94            {
 3595                found |= CharacterClasses.Special;
 96            }
 97
 15198            if (found == everything)
 99            {
 2100                return everything;
 101            }
 102        }
 103
 113104        return found;
 105    }
 106
 107    /// <summary>
 108    /// Reports every <see cref="CharacterClasses"/> present in <paramref name="value"/> in a single pass.
 109    /// </summary>
 110    /// <param name="value">Input text. A <see langword="null"/> value contains nothing.</param>
 111    /// <returns>The classes present, or <see cref="CharacterClasses.None"/> when none are.</returns>
 106112    public static CharacterClasses Classify(this string? value) => value.AsSpan().Classify();
 113
 114    /// <summary>
 115    /// Reports which of the <paramref name="required"/> classes <paramref name="value"/> does not contain.
 116    /// </summary>
 117    /// <param name="value">Input text.</param>
 118    /// <param name="required">The classes the caller demands.</param>
 119    /// <returns>
 120    /// The required classes that are absent, or <see cref="CharacterClasses.None"/> when every requirement
 121    /// is met. Classes present but not required are never reported.
 122    /// </returns>
 123    public static CharacterClasses Missing(this ReadOnlySpan<char> value, CharacterClasses required) =>
 8124        required & ~value.Classify();
 125
 126    /// <summary>
 127    /// Reports which of the <paramref name="required"/> classes <paramref name="value"/> does not contain.
 128    /// </summary>
 129    /// <param name="value">Input text. A <see langword="null"/> value is missing every requirement.</param>
 130    /// <param name="required">The classes the caller demands.</param>
 131    /// <returns>
 132    /// The required classes that are absent, or <see cref="CharacterClasses.None"/> when every requirement
 133    /// is met. Classes present but not required are never reported.
 134    /// </returns>
 135    public static CharacterClasses Missing(this string? value, CharacterClasses required) =>
 8136        value.AsSpan().Missing(required);
 137}