• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Genfic / Ogma / 27881467695

20 Jun 2026 07:26PM UTC coverage: 1.31% (+0.03%) from 1.285%
27881467695

push

github

Atulin
Cleanup and scan fixes
- Deleted some obsolete stuff like the `biome-check` script, since Biome's json reporter is useless nowadays
- Comments now get min and max comment length from generated CTConfig, not from API
- Fixed theme names macro on Linux
- Email is now obfuscated in Postmark logging
- Removed Unlighthouse config, since Unlighthouse has been yeeted from the deps some time ago
- Fixed impersonate form not sending CSRF token, and the impersonation endpoint thus having antiforgery disabled
- Fixed paths in webtypes to use `/` path separators instead of `\`

7 of 22 new or added lines in 4 files covered. (31.82%)

4 existing lines in 3 files now uncovered.

358 of 27328 relevant lines covered (1.31%)

0.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.73
/Utils/Extensions/String.cs
1
using System.Collections.Immutable;
2
using System.Text;
3
using System.Text.RegularExpressions;
4

5
namespace Utils.Extensions;
6

7
public static partial class String
8
{
9
        /// <param name="input">String to friendlify</param>
10
        extension(string input)
11
        {
12
                /// <summary>
13
                /// Replace non-alphanumeric characters with underscores, and double underscores with single ones.
14
                /// </summary>
15
                /// <param name="separator">Char to use as a replacement for non-alpha characters</param>
16
                /// <returns>Friendlified string</returns>
17
                public string Friendlify(char separator)
18
                {
19
                        return NonAlphanumericCharactersRegex
1✔
20
                                .Replace(input, separator.ToString())
1✔
21
                                .ToLower()
1✔
22
                                .Trim(separator);
1✔
23
                }
24

25
                /// <summary>
26
                /// Because expression trees are fucking garbage piece of shit
27
                /// </summary>
28
                /// <returns></returns>
29
                public string Friendlify() => input.Friendlify('-');
1✔
30

31
                /// <summary>
32
                /// Replaces elements of the `template` according to the supplied `pattern`
33
                /// </summary>
34
                /// <param name="pattern">Dictionary in which keys are values to be replaced and values are values to replace them with</param>
35
                /// <returns>Resulting string</returns>
36
                public string ReplaceWithPattern(Dictionary<string, string> pattern)
37
                {
38
                        foreach (var (key, value) in pattern)
1✔
39
                        {
40
                                input = input.Replace(key, value);
1✔
41
                        }
42

43
                        return input;
1✔
44
                }
45

46
                /// <summary>
47
                /// Replaces elements of the `template` according to the supplied `pattern`
48
                /// </summary>
49
                /// <param name="pattern">Dictionary in which keys are values to be replaced and values are values to replace them with</param>
50
                /// <param name="comparisonType"></param>
51
                /// <returns>Resulting string</returns>
52
                public string ReplaceWithPattern(Dictionary<string, string> pattern, StringComparison comparisonType)
53
                {
54
                        foreach (var (key, value) in pattern)
1✔
55
                        {
56
                                input = input.Replace(key, value, comparisonType);
1✔
57
                        }
58

59
                        return input;
1✔
60
                }
61

62
                /// <summary>
63
                /// Removes all leading whitespace
64
                /// </summary>
65
                /// <returns>String without leading whitespace</returns>
66
                public string RemoveLeadingWhiteSpace()
67
                {
68
                        var sb = new StringBuilder();
1✔
69
                        var text = input.AsSpan();
1✔
70

71
                        foreach (var range in text.Split('\n'))
1✔
72
                        {
73
                                var line = text[range];
1✔
74
                                var trimmed = line.TrimStart();
1✔
75
                                sb.Append(trimmed);
1✔
76
                                sb.Append('\n');
1✔
77
                        }
78

79
                        sb.Remove(sb.Length - 1, 1);
1✔
80

81
                        return sb.ToString();
1✔
82
                }
83

84
                /// <summary>
85
                /// Truncates a string to the given length and caps it off
86
                /// </summary>
87
                /// <param name="length">Truncation length</param>
88
                /// <param name="cap">String cap, "..." by default</param>
89
                /// <returns>Truncated and capped string</returns>
90
                public string Truncate(int length, string cap = "...")
91
                {
92
                        return input.Length > length
1✔
93
                                ? input[..length] + cap
1✔
94
                                : input;
1✔
95
                }
96

97
                public string Trim(int length)
98
                {
99
                        return input.Length > length
1✔
100
                                ? input[..length]
1✔
101
                                : input;
1✔
102
                }
103

104
                /// <summary>
105
                /// Get the number of words in the given string
106
                /// </summary>
107
                /// <returns>Number of words</returns>
108
                public int Words()
109
                {
110
                        var wasLetter = false;
1✔
111
                        var count = 0;
1✔
112
                        foreach (var ch in input.AsSpan())
1✔
113
                        {
114
                                if (char.IsWhiteSpace(ch))
1✔
115
                                {
116
                                        wasLetter = false;
1✔
117
                                }
118
                                else
119
                                {
120
                                        if (!wasLetter)
1✔
121
                                        {
122
                                                count++;
1✔
123
                                        }
124

125
                                        wasLetter = true;
1✔
126
                                }
127
                        }
128

129
                        return count;
1✔
130
                }
131

132
                public ImmutableArray<string> FindHashtags()
133
                {
134
                        return [..HashtagRegex.Matches(input).Select(m => m.Groups["tag"].Value)];
1✔
135
                }
136
        }
137

138

139
        /// <param name="span">The ReadOnlySpan of characters to process.</param>
140
        extension(ReadOnlySpan<char> span)
141
        {
142
                /// <summary>
143
                /// Counts the number of lines in the given ReadOnlySpan of characters.
144
                /// </summary>
145
                /// <returns>The total number of lines in the provided span.</returns>
146
                public int CountLines()
147
                {
148
                        if (span.IsEmpty) return 0;
1✔
149

150
                        var count = 0;
1✔
151
                        var i = 0;
1✔
152

153
                        while (i < span.Length)
1✔
154
                        {
155
                                switch (span[i++])
1✔
156
                                {
157
                                        case '\r':
158
                                        {
159
                                                count++;
1✔
160
                                                if (i < span.Length && span[i] == '\n')
1✔
161
                                                {
162
                                                        i++; // Skip the `\n` in `\r\n`
1✔
163
                                                }
164
                                                break;
1✔
165
                                        }
166
                                        case '\n':
167
                                                count++;
1✔
168
                                                break;
169
                                }
170
                        }
171

172
                        // We need to add one more line in case the string does not end in a newline.
173
                        if (span is not [.., '\n' or '\r'])
1✔
174
                        {
175
                                count++;
1✔
176
                        }
177

178
                        return count;
1✔
179
                }
180

181
                public string Capitalize()
182
                {
183
                        if (span.IsEmpty)
1✔
184
                        {
185
                                return string.Empty;
1✔
186
                        }
187

188
                        if (span.Length == 1)
1✔
189
                        {
190
                                return char.ToUpper(span[0]).ToString();
1✔
191
                        }
192

193
                        var s = string.Create(span.Length, span, (chars, state) => {
1✔
194
                                chars[0] = char.ToUpper(state[0]);
1✔
195
                                state[1..].CopyTo(chars[1..]);
1✔
196
                        });
1✔
197

198
                        return s;
1✔
199
                }
200

201
                /// <summary>
202
                /// Obfuscates the input span by replacing all but the first few characters with a specified obfuscation character.
203
                /// </summary>
204
                /// <param name="maxVisibleChars">The maximum number of characters to remain visible at the start of the span. Defaults to 5.</param>
205
                /// <param name="obfuscationChar">The character to use for obfuscating the remaining portion of the span. Defaults to '*'.</param>
206
                /// <returns>A new string where all but the specified number of visible characters are replaced with the obfuscation character.</returns>
207
                public string Obfuscate(int maxVisibleChars = 5, char obfuscationChar = '*')
208
                {
209
                        var len = span.Length;
1✔
210
                        if (len <= maxVisibleChars)
1✔
211
                        {
NEW
212
                                maxVisibleChars = (int)Math.Floor(len / 2.0d);
×
213
                        }
214
                        var count = len - maxVisibleChars;
1✔
215

216
                        var sb = new StringBuilder(len);
1✔
217

218
                        sb.Append(span[..maxVisibleChars]);
1✔
219
                        sb.Append(obfuscationChar, count);
1✔
220

221
                        return sb.ToString();
1✔
222
                }
223
        }
224

225

226
        /// <summary>
227
        /// Parses a string containing comma-separated tags (possibly #tags) into an array of said tags.
228
        /// Each tag in the resulting array is trimmed of any whitespace and # characters.
229
        /// </summary>
230
        /// <param name="input">String to parse into tags</param>
231
        /// <returns>An array of strings representing the tags</returns>
232
        public static ImmutableArray<string> ParseHashtags(this string? input)
233
        {
234
                if (string.IsNullOrWhiteSpace(input)) return [];
1✔
235

236
                var builder = ImmutableArray.CreateBuilder<string>();
1✔
237
                var seen = new HashSet<string>();
1✔
238
                var span = input.AsSpan();
1✔
239

240
                foreach (var segment in span.Split(','))
1✔
241
                {
242
                        var trimmed = span[segment].Trim();
1✔
243
                        if (trimmed.IsEmpty) continue;
1✔
244

245
                        if (trimmed[0] == '#')
1✔
246
                        {
247
                                trimmed = trimmed[1..];
1✔
248
                        }
249

250
                        var processed = trimmed.ToString().Friendlify();
1✔
251
                        if (seen.Add(processed))
1✔
252
                        {
253
                                builder.Add(processed);
1✔
254
                        }
255
                }
256

257
                return builder.ToImmutable();
1✔
258
        }
259

260
        public record struct Header(byte Level, byte Occurrence, string Body);
261

262
        [GeneratedRegex("[^a-zA-Z0-9]+")]
263
        private static partial Regex NonAlphanumericCharactersRegex { get; }
264

265
        [GeneratedRegex(@"(^|\s)(?'tag'#[\w-]{3,})($|\s)")]
266
        private static partial Regex HashtagRegex { get; }
267
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc