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

HicServices / RDMP / 6245535001

20 Sep 2023 07:44AM UTC coverage: 57.013%. First build
6245535001

push

github

web-flow
8.1.0 Release (#1628)

* Bump Newtonsoft.Json from 13.0.1 to 13.0.2

Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 13.0.1 to 13.0.2.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/13.0.1...13.0.2)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump NLog from 5.0.5 to 5.1.0

Bumps [NLog](https://github.com/NLog/NLog) from 5.0.5 to 5.1.0.
- [Release notes](https://github.com/NLog/NLog/releases)
- [Changelog](https://github.com/NLog/NLog/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/NLog/NLog/compare/v5.0.5...v5.1.0)

---
updated-dependencies:
- dependency-name: NLog
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump NLog from 5.0.5 to 5.1.0

* Fix -r flag - should have been --results-directory all along

* Bump Newtonsoft.Json from 13.0.1 to 13.0.2

* Bump YamlDotNet from 12.0.2 to 12.1.0

Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 12.0.2 to 12.1.0.
- [Release notes](https://github.com/aaubry/YamlDotNet/releases)
- [Commits](https://github.com/aaubry/YamlDotNet/compare/v12.0.2...v12.1.0)

---
updated-dependencies:
- dependency-name: YamlDotNet
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump Moq from 4.18.2 to 4.18.3

Bumps [Moq](https://github.com/moq/moq4) from 4.18.2 to 4.18.3.
- [Release notes](https://github.com/moq/moq4/releases)
- [Changelog](https://github.com/moq/moq4/blob/main/CHANGELOG.md)
- [Commits](https://github.com/moq/moq4/compare/v4.18.2...v4.18.3)

---
updated-dependencies:
- dependency-name: Moq
... (continued)

10732 of 20257 branches covered (0.0%)

Branch coverage included in aggregate %.

48141 of 48141 new or added lines in 1086 files covered. (100.0%)

30685 of 52388 relevant lines covered (58.57%)

7387.88 hits per line

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

39.63
/Rdmp.Core/ReusableLibraryCode/UsefulStuff.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4
// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Collections.Generic;
9
using System.Data;
10
using System.Data.Common;
11
using System.Diagnostics;
12
using System.Globalization;
13
using System.IO;
14
using System.Linq;
15
using System.Reflection;
16
using System.Runtime.InteropServices;
17
using System.Security.Cryptography;
18
using System.Text;
19
using System.Text.RegularExpressions;
20
using System.Threading;
21
using System.Threading.Tasks;
22
using CommandLine;
23
using CsvHelper;
24

25
namespace Rdmp.Core.ReusableLibraryCode;
26

27
/// <summary>
28
/// Contains lots of generically useful static methods
29
/// </summary>
30
public static partial class UsefulStuff
31
{
32
    public static readonly Regex RegexThingsThatAreNotNumbersOrLetters = NonAlphaNumeric();
2✔
33

34
    public static readonly Regex RegexThingsThatAreNotNumbersOrLettersOrUnderscores =
2✔
35
        NonAlphaNumericUnderscore();
2✔
36

37
    private static readonly Regex NullWithSpaces = NullInSpace();
2✔
38

39
    public static bool IsBasicallyNull(this string result) =>
40
        string.IsNullOrWhiteSpace(result) ||
135,046✔
41
        // if user types the literal string null then return null (typically interpreted as - 'I don't want to pick one')
135,046✔
42
        // but not the same as task cancellation
135,046✔
43
        NullWithSpaces.IsMatch(result);
135,046✔
44

45
    public static bool IsBadName(string name) => name?.Any(Path.GetInvalidFileNameChars().Contains) == true;
50!
46

47
    public static void OpenUrl(string url)
48
    {
49
        try
50
        {
51
            Process.Start(url);
×
52
        }
×
53
        catch
×
54
        {
55
            // hack because of this: https://github.com/dotnet/corefx/issues/10361
56
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
×
57
            {
58
                url = url.Replace("&", "^&");
×
59
                Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
×
60
            }
61
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
×
62
            {
63
                Process.Start("xdg-open", url);
×
64
            }
65
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
×
66
            {
67
                Process.Start("open", url);
×
68
            }
69
            else
70
            {
71
                throw;
×
72
            }
73
        }
×
74
    }
×
75

76

77
    internal static DateTime LooseConvertToDate(object iO)
78
    {
79
        DateTime sDate;
80

81
        try
82
        {
83
            sDate = Convert.ToDateTime(iO);
×
84
        }
×
85
        catch (FormatException)
×
86
        {
87
            try
88
            {
89
                var sDateAsString = iO.ToString();
×
90

91
                switch (sDateAsString?.Length)
×
92
                {
93
                    case 6:
94
                    case 8:
95
                        sDate = Convert.ToDateTime($"{sDateAsString[..2]}/{sDateAsString[2..4]}/{sDateAsString[4..]}");
×
96
                        break;
×
97
                    default:
98
                        throw;
×
99
                }
100
            }
×
101
            catch (Exception)
×
102
            {
103
                throw new Exception($"Cannot recognise date format :{iO}");
×
104
            }
105
        }
×
106

107
        return sDate;
×
108
    }
109

110

111
    // find quoted field names at end of line
112
    private static readonly Regex RDoubleQuotes = new("\"([^\"]+)\"$");
2✔
113
    private static readonly Regex RSingleQuotes = new("'([^']+)'$");
2✔
114
    private static readonly Regex RBacktickQuotes = new("`([^']+)`$");
2✔
115
    private static readonly Regex RSquareBrackets = new(@"\[([^[]+)]$");
2✔
116
    private static readonly Regex RNoPunctuation = new(@"^([\w\s]+)$");
2✔
117

118
    public static IEnumerable<string> GetArrayOfColumnNamesFromStringPastedInByUser(string text)
119
    {
120
        if (string.IsNullOrWhiteSpace(text))
90!
121
            yield break;
×
122

123
        var split = text.Split(new char[] { '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);
90✔
124

125

126
        //trim off [db]..[tbl] 1
127
        for (var i = 0; i < split.Length; i++)
360✔
128
            split[i] = Regex.Replace(split[i], @"\s+[\d\s]*$", "");
90✔
129

130
        //identifies the last word in a collection of multiple words (requires you .Trim() so we don't get ending whitespace match)
131
        var regexLastWord = new Regex("\\s[^\\s]*$");
90✔
132
        foreach (var s in split)
360✔
133
        {
134
            //clean the string
135

136
            var toAdd = s.Trim();
90✔
137
            if (toAdd.Contains('.'))
90✔
138
                toAdd = toAdd[(toAdd.LastIndexOf(".", StringComparison.Ordinal) + 1)..];
40✔
139

140
            var gotDelimitedMatch = false;
90✔
141

142
            // if user has really horrible names like with spaces and stuff
143
            // then try expect them to have quoted them and pull out the capture
144
            // groups.  Remember different DBMS have different quoting symbols
145
            foreach (var r in new[]
670✔
146
                     {
90✔
147
                         RDoubleQuotes, RSingleQuotes,
90✔
148
                         RSquareBrackets, RBacktickQuotes, RNoPunctuation
90✔
149
                     })
90✔
150
            {
151
                var m = r.Matches(toAdd);
290✔
152
                if (!m.Any()) continue;
290✔
153
                yield return m.Last().Groups[1].Value;
90✔
154
                gotDelimitedMatch = true;
90✔
155
                break;
90✔
156
            }
157

158
            if (gotDelimitedMatch)
90!
159
                continue;
160

161
            toAdd = toAdd.Replace("]", "");
×
162
            toAdd = toAdd.Replace("[", "");
×
163

164
            toAdd = toAdd.Replace("`", "");
×
165

166
            if (string.IsNullOrWhiteSpace(toAdd))
×
167
                continue;
168

169
            if (regexLastWord.IsMatch(toAdd))
×
170
                toAdd = regexLastWord.Match(toAdd).Value.Trim();
×
171

172
            yield return toAdd;
×
173
        }
×
174
    }
90✔
175

176
    public static bool CHIisOK(string sCHI) =>
177
        long.TryParse(sCHI, NumberStyles.None, CultureInfo.InvariantCulture, out _) && sCHI.Length == 10 &&
8✔
178
        DateTime.TryParse($"{sCHI[..2]}/{sCHI[2..4]}/{sCHI[4..6]}", out _) && GetCHICheckDigit(sCHI) == sCHI[^1];
8✔
179

180
    private static char GetCHICheckDigit(string sCHI)
181
    {
182
        //sCHI = "120356785";
183
        var lsCHI = sCHI.Length; // Must be 10!!
4✔
184

185
        var sum = 0;
4✔
186
        var c = (int)'0';
4✔
187
        for (var i = 0; i < lsCHI - 1; i++)
80✔
188
            sum += (sCHI[i] - c) * (lsCHI - i);
36✔
189
        sum %= 11;
4✔
190

191
        c = 11 - sum;
4✔
192
        if (c == 11) c = 0;
4!
193

194
        return (char)(c + '0');
4✔
195
    }
196

197
    public static DirectoryInfo GetExecutableDirectory() =>
198
        new(AppDomain.CurrentDomain.BaseDirectory ?? throw new Exception("BaseDirectory was null?!"));
12!
199

200
    public static string HashFile(string filename, int retryCount = 6)
201
    {
202
        try
203
        {
204
            using var hashProvider = SHA512.Create();
72✔
205
            using var stream = File.OpenRead(filename);
72✔
206
            return BitConverter.ToString(hashProvider.ComputeHash(stream));
72✔
207
        }
208
        catch (IOException)
×
209
        {
210
            //couldn't access the file so wait 1 second then try again
211
            Thread.Sleep(1000);
×
212

213
            if (retryCount-- > 0)
×
214
                return HashFile(filename, retryCount); //try it again (recursively)
×
215
            throw;
×
216
        }
217
    }
72✔
218

219
    public static bool RequiresLength(string columnType)
220
    {
221
        return columnType.ToLowerInvariant() switch
×
222
        {
×
223
            "binary" => true,
×
224
            "bit" => false,
×
225
            "char" => true,
×
226
            "image" => true,
×
227
            "nchar" => true,
×
228
            "nvarchar" => true,
×
229
            "varbinary" => true,
×
230
            "varchar" => true,
×
231
            "numeric" => true,
×
232
            _ => false
×
233
        };
×
234
    }
235

236
    public static bool HasPrecisionAndScale(string columnType)
237
    {
238
        return columnType.ToLowerInvariant() switch
×
239
        {
×
240
            "decimal" => true,
×
241
            "numeric" => true,
×
242
            _ => false
×
243
        };
×
244
    }
245

246
    public static string RemoveIllegalFilenameCharacters(string value)
247
    {
248
        return string.IsNullOrWhiteSpace(value)
22!
249
            ? value
22✔
250
            : Path.GetInvalidFileNameChars().Aggregate(value,
22✔
251
                (current, invalidFileNameChar) => current.Replace(invalidFileNameChar.ToString(), ""));
924✔
252
    }
253

254

255
    public static void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction transaction = null,
256
        int timeout = 30)
257
    {
258
        ExecuteBatchNonQuery(sql, conn, transaction, out _, timeout);
784✔
259
    }
784✔
260

261
    /// <summary>
262
    /// Executes the given SQL against the database + sends GO delimited statements as separate batches
263
    /// </summary>
264
    /// <param name="sql"></param>
265
    /// <param name="conn"></param>
266
    /// <param name="transaction"></param>
267
    /// <param name="performanceFigures">Line number the batch started at and the time it took to complete it</param>
268
    /// <param name="timeout"></param>
269
    public static void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction transaction,
270
        out Dictionary<int, Stopwatch> performanceFigures, int timeout = 30)
271
    {
272
        performanceFigures = new Dictionary<int, Stopwatch>();
792✔
273

274
        var sqlBatch = string.Empty;
792✔
275
        var cmd = DatabaseCommandHelper.GetCommand(string.Empty, conn, transaction);
792✔
276
        var hadToOpen = false;
792✔
277

278
        if (conn.State != ConnectionState.Open)
792!
279
        {
280
            conn.Open();
×
281
            hadToOpen = true;
×
282
        }
283

284
        var lineNumber = 1;
792✔
285

286
        sql += "\nGO"; // make sure last batch is executed.
792✔
287
        try
288
        {
289
            foreach (var line in sql.Split(new[] { "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries))
112,140✔
290
            {
291
                lineNumber++;
55,278✔
292

293
                if (line.ToUpperInvariant().Trim() == "GO")
55,278✔
294
                {
295
                    if (string.IsNullOrWhiteSpace(sqlBatch))
1,970✔
296
                        continue;
297

298
                    if (!performanceFigures.ContainsKey(lineNumber))
1,862✔
299
                        performanceFigures.Add(lineNumber, new Stopwatch());
1,862✔
300
                    performanceFigures[lineNumber].Start();
1,862✔
301

302
                    cmd.CommandText = sqlBatch;
1,862✔
303
                    cmd.CommandTimeout = timeout;
1,862✔
304
                    cmd.ExecuteNonQuery();
1,862✔
305

306
                    performanceFigures[lineNumber].Stop();
1,862✔
307
                    sqlBatch = string.Empty;
1,862✔
308
                }
309
                else
310
                {
311
                    sqlBatch += $"{line}\n";
53,308✔
312
                }
313
            }
314
        }
792✔
315
        finally
316
        {
317
            if (hadToOpen)
792!
318
                conn.Close();
×
319
        }
792✔
320
    }
792✔
321

322

323
    /// <summary>
324
    /// Locates a manifest resource in the assembly under the manifest name subspace.  If you want to spray the resource MySoftwareSuite.MyApplication.MyResources.Bob.txt then pass:
325
    /// 1. the assembly containing the resource (e.g. typeof(MyClass1).Assembly)
326
    /// 2. the full path to the resource file: "MySoftwareSuite.MyApplication.MyResources.Bob.txt"
327
    /// 3. the filename "Bob.txt"
328
    /// </summary>
329
    /// <param name="assembly">The dll e.g. MySoftwareSuite.MyApplication.dll</param>
330
    /// <param name="manifestName">The full path to the manifest resource e.g. MySoftwareSuite.MyApplication.MyResources.Bob.txt</param>
331
    /// <param name="file">The filename ONLY of the resource e.g. Bob.txt</param>
332
    /// <param name="outputDirectory">The directory to put the generated file in.  Defaults to %appdata%/RDMP </param>
333
    public static FileInfo SprayFile(Assembly assembly, string manifestName, string file, string outputDirectory = null)
334
    {
335
        outputDirectory ??= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "RDMP");
6!
336

337
        using var fileToSpray = assembly.GetManifestResourceStream(manifestName) ?? throw new Exception(
6!
338
            $"assembly.GetManifestResourceStream returned null for manifest name {manifestName} in assembly {assembly}");
6✔
339

340
        if (!Directory.Exists(outputDirectory))
6!
341
            Directory.CreateDirectory(outputDirectory);
×
342

343
        var target = new FileInfo(Path.Combine(outputDirectory, file));
6✔
344
        using var destination = target.OpenWrite();
6✔
345
        fileToSpray.CopyTo(destination, 1 << 20);
6✔
346
        return target;
6✔
347
    }
6✔
348

349
    public static string GetHumanReadableByteSize(long len)
350
    {
351
        string[] sizes = { "bytes", "KB", "MB", "GB", "TB", "PB" };
×
352

353
        var order = 0;
×
354
        while (len >= 1024 && order + 1 < sizes.Length)
×
355
        {
356
            order++;
×
357
            len /= 1024;
×
358
        }
359

360
        // Adjust the format string to your preferences. For example "{0:0.#}{1}" would
361
        // show a single decimal place, and no space.
362
        return $"{len:0.##} {sizes[order]}";
×
363
    }
364

365
    public static bool VerifyFileExists(string path, int timeout)
366
    {
367
        var task = new Task<bool>(() =>
×
368
        {
×
369
            try
×
370
            {
×
371
                var fi = new FileInfo(path);
×
372
                return fi.Exists;
×
373
            }
×
374
            catch (Exception)
×
375
            {
×
376
                return false;
×
377
            }
×
378
        });
×
379
        task.Start();
×
380
        return task.Wait(timeout) && task.Result;
×
381
    }
382

383
    public static bool VerifyFileExists(Uri uri, int timeout) => VerifyFileExists(uri.LocalPath, timeout);
×
384

385
    public static string DataTableToHtmlDataTable(DataTable dt)
386
    {
387
        var sb = new StringBuilder();
×
388

389
        sb.AppendLine("<Table>");
×
390
        sb.Append("<TR>");
×
391

392
        foreach (DataColumn column in dt.Columns)
×
393
            sb.Append($"<TD>{column.ColumnName}</TD>");
×
394

395
        sb.AppendLine("</TR>");
×
396

397
        foreach (DataRow row in dt.Rows)
×
398
        {
399
            sb.Append("<TR>");
×
400

401
            foreach (var cellObject in row.ItemArray)
×
402
                sb.Append($"<TD>{cellObject}</TD>");
×
403

404
            sb.AppendLine("</TR>");
×
405
        }
406

407
        sb.Append("</Table>");
×
408

409
        return sb.ToString();
×
410
    }
411

412
    public static string DataTableToCsv(DataTable dt)
413
    {
414
        var sb = new StringBuilder();
×
415

416
        using var w = new CsvWriter(new StringWriter(sb), CultureInfo.CurrentCulture);
×
417
        foreach (DataColumn column in dt.Columns)
×
418
            w.WriteField(column.ColumnName);
×
419

420
        w.NextRecord();
×
421

422
        foreach (DataRow row in dt.Rows)
×
423
        {
424
            foreach (var cellObject in row.ItemArray)
×
425
                w.WriteField(cellObject);
×
426

427
            w.NextRecord();
×
428
        }
429

430
        w.Flush();
×
431

432
        return sb.ToString();
×
433
    }
×
434

435
    public static void ShowPathInWindowsExplorer(FileSystemInfo fileInfo)
436
    {
437
        var argument = $"{(fileInfo is FileInfo ? "/select," : "")} \"{fileInfo.FullName}\"";
×
438
        Process.Start("explorer.exe", argument);
×
439
    }
×
440

441
    public static string GetClipboardFormattedHtmlStringFromHtmlString(string s)
442
    {
443
        const int maxLength = 1_000_000_000 - 52; // Header is 51 characters, total length must fit in 10 digits
444
        if (s.Length > maxLength)
2!
445
            throw new ArgumentException(
×
446
                $"String s is too long, the maximum length is {maxLength} but argument s was length {s.Length}",
×
447
                nameof(s));
×
448

449
        // Circular dependency here: we need a string containing both its own length and the length of the string after it
450
        // {0:D6} is the same length as its own description
451
        // {1:D10} is 3 characters longer than its own description, so +3
452
        // {2} on the end adds 3, so the overall prefix length is constant
453
        var template = "Version:1.0\r\nStartHTML:{0:D6}\r\nEndHTML:{1:D10}\r\n{2}";
2✔
454
        return string.Format(
2✔
455
            template,
2✔
456
            template.Length,
2✔
457
            s.Length + template.Length, s);
2✔
458
    }
459

460
    public static bool IsAssignableToGenericType(Type givenType, Type genericType)
461
    {
462
        var interfaceTypes = givenType.GetInterfaces();
1,364✔
463

464
        if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
5,408!
465
            return true;
×
466

467
        if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
1,364!
468
            return true;
×
469

470
        var baseType = givenType.BaseType;
1,364✔
471
        return baseType != null && IsAssignableToGenericType(baseType, genericType);
1,364✔
472
    }
473

474
    public static string PascalCaseStringToHumanReadable(string pascalCaseString)
475
    {
476
        //Deal with legacy property names by replacing underscore with a space
477
        pascalCaseString = pascalCaseString.Replace("_", " ");
762✔
478

479
        //There are two clauses in this Regex
480
        //Part1: [A-Z][A-Z]*(?=[A-Z][a-z]|\b) - looks for any series of uppercase letters that have a ending uppercase then lowercase OR end of line charater: https://regex101.com/r/mCqVk6/2
481
        //Part2: [A-Z](?=[a-z])               - looks for any single  of uppercase letters followed by a lower case letter: https://regex101.com/r/hdSCqH/1
482
        //This is then made into a single group that is matched and we add a space on front during the replacement.
483
        pascalCaseString = Regex.Replace(pascalCaseString, @"([A-Z][A-Z]*(?=[A-Z][a-z]|\b)|[A-Z](?=[a-z]))", " $1");
762✔
484

485
        //Remove any double multiple white space
486
        //Because this matched the first capital letter in a string with Part2 of our regex above we should TRIM to remove the white space.
487
        pascalCaseString = Regex.Replace(pascalCaseString, @"\s\s+", " ").Trim();
762✔
488

489
        return pascalCaseString;
762✔
490
    }
491

492
    /// <summary>
493
    /// Returns the <paramref name="input"/> string split across multiple lines with the
494
    /// <paramref name="newline"/> (or <see cref="Environment.NewLine"/> if null) separator
495
    /// such that no lines are longer than <paramref name="maxLen"/>
496
    /// </summary>
497
    /// <param name="input"></param>
498
    /// <param name="maxLen"></param>
499
    /// <param name="newline"></param>
500
    /// <returns></returns>
501
    public static string SplitByLength(string input, int maxLen, string newline = null)
502
    {
503
        return
×
504
            string.Join(newline ?? Environment.NewLine,
×
505
                Regex.Split(input, $@"(.{{1,{maxLen}}})(?:\s|$)")
×
506
                    .Where(x => x.Length > 0)
×
507
                    .Select(x => x.Trim()));
×
508
    }
509

510

511
    public static void ConfirmContentsOfDirectoryAreTheSame(DirectoryInfo first, DirectoryInfo other)
512
    {
513
        if (first.EnumerateFiles().Count() != other.EnumerateFiles().Count())
×
514
            throw new Exception(
×
515
                $"found different number of files in Globals directory {first.FullName} and {other.FullName}");
×
516

517
        var filesInFirst = first.EnumerateFiles().ToArray();
×
518
        var filesInOther = other.EnumerateFiles().ToArray();
×
519

520
        for (var i = 0; i < filesInFirst.Length; i++)
×
521
        {
522
            var file1 = filesInFirst[i];
×
523
            var file2 = filesInOther[i];
×
524
            if (!file1.Name.Equals(file2.Name))
×
525
                throw new Exception(
×
526
                    $"Although there were the same number of files in Globals directories {first.FullName} and {other.FullName}, there were differing file names ({file1.Name} and {file2.Name})");
×
527

528
            if (!HashFile(file1.FullName).Equals(HashFile(file2.FullName)))
×
529
                throw new Exception(
×
530
                    $"File found in Globals directory which has a different MD5 from another Globals file.  Files were \"{file1.FullName}\" and \"{file2.FullName}\"");
×
531
        }
532
    }
×
533

534
    public static Parser GetParser()
535
    {
536
        var defaults = Parser.Default.Settings;
×
537

538
        var parser = new Parser(settings =>
×
539
        {
×
540
            settings.CaseSensitive = false;
×
541
            settings.CaseInsensitiveEnumValues = true;
×
542
            settings.EnableDashDash = defaults.EnableDashDash;
×
543
            settings.HelpWriter = defaults.HelpWriter;
×
544
            settings.IgnoreUnknownArguments = defaults.IgnoreUnknownArguments;
×
545
            settings.MaximumDisplayWidth = defaults.MaximumDisplayWidth;
×
546
            settings.ParsingCulture = defaults.ParsingCulture;
×
547
        });
×
548

549
        return parser;
×
550
    }
551

552
    /// <summary>
553
    /// Implementation of <see cref="Convert.ChangeType(object,Type)"/> that works with nullable types,
554
    /// dates etc
555
    /// </summary>
556
    /// <param name="value"></param>
557
    /// <param name="conversionType"></param>
558
    /// <returns></returns>
559
    public static object ChangeType(object value, Type conversionType)
560
    {
561
        var t = Nullable.GetUnderlyingType(conversionType) ?? conversionType;
52✔
562

563
        return t == typeof(DateTime) && value is string s
52!
564
            ? string.Equals(s, "now", StringComparison.InvariantCultureIgnoreCase)
52✔
565
                ? DateTime.Now
52✔
566
                :
52✔
567
                //Convert.ChangeType doesn't handle dates, so let's deal with that
52✔
568
                DateTime.Parse(s)
52✔
569
            : value == null || (value is string sval && string.IsNullOrWhiteSpace(sval))
52✔
570
                ? null
52✔
571
                : Convert.ChangeType(value, t);
52✔
572
    }
573

574
    [GeneratedRegex("[^0-9A-Za-z]+", RegexOptions.Compiled | RegexOptions.CultureInvariant)]
575
    private static partial Regex NonAlphaNumeric();
576

577
    [GeneratedRegex("[^0-9A-Za-z_]+", RegexOptions.Compiled | RegexOptions.CultureInvariant)]
578
    private static partial Regex NonAlphaNumericUnderscore();
579

580
    [GeneratedRegex("^\\s*null\\s*$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)]
581
    private static partial Regex NullInSpace();
582
}
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

© 2025 Coveralls, Inc