Merge branch 'main' into packing
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Configurations>Debug;Release;DebugLocal</Configurations>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
|
||||
@@ -11,32 +10,21 @@
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageId>AMWD.Common</PackageId>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
|
||||
<Product>AM.WD Common Library</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(GITLAB_CI)' == 'true'">
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(GITLAB_CI)' == 'true'">
|
||||
<SourceLinkGitLabHost Include="git.am-wd.de" Version="$(CI_SERVER_VERSION)" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitLab" Version="1.1.1" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../icon.png" Pack="true" PackagePath="/" />
|
||||
<None Include="../README.md" Pack="true" PackagePath="/" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="2.4.35" />
|
||||
<PackageReference Include="MessagePack" Version="2.5.129" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
36
AMWD.Common/Cli/Argument.cs
Normal file
36
AMWD.Common/Cli/Argument.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace AMWD.Common.Cli
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a logical argument in the command line. Options with their additional
|
||||
/// parameters are combined in one argument.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
public class Argument
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialises a new instance of the <see cref="Argument"/> class.
|
||||
/// </summary>
|
||||
/// <param name="option">The <see cref="Option"/> that is set in this argument; or null.</param>
|
||||
/// <param name="values">The additional parameter values for the option; or the argument value.</param>
|
||||
internal Argument(Option option, string[] values)
|
||||
{
|
||||
Option = option;
|
||||
Values = values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Option"/> that is set in this argument; or null.
|
||||
/// </summary>
|
||||
public Option Option { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional parameter values for the option; or the argument value.
|
||||
/// </summary>
|
||||
public string[] Values { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item of <see cref="Values"/>; or null.
|
||||
/// </summary>
|
||||
public string Value => Values.Length > 0 ? Values[0] : null;
|
||||
}
|
||||
}
|
||||
366
AMWD.Common/Cli/CommandLineParser.cs
Normal file
366
AMWD.Common/Cli/CommandLineParser.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AMWD.Common.Cli
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides options and arguments parsing from command line arguments or a single string.
|
||||
/// </summary>
|
||||
public class CommandLineParser
|
||||
{
|
||||
#region Private data
|
||||
|
||||
private string[] _args;
|
||||
private List<Argument> _parsedArguments;
|
||||
private readonly List<Option> _options = new();
|
||||
|
||||
#endregion Private data
|
||||
|
||||
#region Configuration properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the option names are case-sensitive.
|
||||
/// (Default: false)
|
||||
/// </summary>
|
||||
public bool IsCaseSensitive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether incomplete options can be automatically
|
||||
/// completed if there is only a single matching option.
|
||||
/// (Default: true)
|
||||
/// </summary>
|
||||
public bool AutoCompleteOptions { get; set; } = true;
|
||||
|
||||
#endregion Configuration properties
|
||||
|
||||
#region Custom arguments line parsing
|
||||
|
||||
// Source: http://stackoverflow.com/a/23961658/143684
|
||||
/// <summary>
|
||||
/// Parses a single string into an arguments array.
|
||||
/// </summary>
|
||||
/// <param name="argsString">The string that contains the entire command line.</param>
|
||||
public static string[] ParseArgsString(string argsString)
|
||||
{
|
||||
// Collects the split argument strings
|
||||
var args = new List<string>();
|
||||
|
||||
// Builds the current argument
|
||||
var currentArg = new StringBuilder();
|
||||
|
||||
// Indicates whether the last character was a backslash escape character
|
||||
bool escape = false;
|
||||
|
||||
// Indicates whether we're in a quoted range
|
||||
bool inQuote = false;
|
||||
|
||||
// Indicates whether there were quotes in the current arguments
|
||||
bool hadQuote = false;
|
||||
|
||||
// Remembers the previous character
|
||||
char prevCh = '\0';
|
||||
|
||||
// Iterate all characters from the input string
|
||||
for (int i = 0; i < argsString.Length; i++)
|
||||
{
|
||||
char ch = argsString[i];
|
||||
if (ch == '\\' && !escape)
|
||||
{
|
||||
// Beginning of a backslash-escape sequence
|
||||
escape = true;
|
||||
}
|
||||
else if (ch == '\\' && escape)
|
||||
{
|
||||
// Double backslash, keep one
|
||||
currentArg.Append(ch);
|
||||
escape = false;
|
||||
}
|
||||
else if (ch == '"' && !escape)
|
||||
{
|
||||
// Toggle quoted range
|
||||
inQuote = !inQuote;
|
||||
hadQuote = true;
|
||||
if (inQuote && prevCh == '"')
|
||||
{
|
||||
// Doubled quote within a quoted range is like escaping
|
||||
currentArg.Append(ch);
|
||||
}
|
||||
}
|
||||
else if (ch == '"' && escape)
|
||||
{
|
||||
// Backslash-escaped quote, keep it
|
||||
currentArg.Append(ch);
|
||||
escape = false;
|
||||
}
|
||||
else if (char.IsWhiteSpace(ch) && !inQuote)
|
||||
{
|
||||
if (escape)
|
||||
{
|
||||
// Add pending escape char
|
||||
currentArg.Append('\\');
|
||||
escape = false;
|
||||
}
|
||||
// Accept empty arguments only if they are quoted
|
||||
if (currentArg.Length > 0 || hadQuote)
|
||||
{
|
||||
args.Add(currentArg.ToString());
|
||||
}
|
||||
// Reset for next argument
|
||||
currentArg.Clear();
|
||||
hadQuote = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (escape)
|
||||
{
|
||||
// Add pending escape char
|
||||
currentArg.Append('\\');
|
||||
escape = false;
|
||||
}
|
||||
// Copy character from input, no special meaning
|
||||
currentArg.Append(ch);
|
||||
}
|
||||
prevCh = ch;
|
||||
}
|
||||
// Save last argument
|
||||
if (currentArg.Length > 0 || hadQuote)
|
||||
{
|
||||
args.Add(currentArg.ToString());
|
||||
}
|
||||
return args.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the command line arguments from a single string.
|
||||
/// </summary>
|
||||
/// <param name="argsString">The string that contains the entire command line.</param>
|
||||
public void ReadArgs(string argsString)
|
||||
{
|
||||
_args = ParseArgsString(argsString);
|
||||
}
|
||||
|
||||
#endregion Custom arguments line parsing
|
||||
|
||||
#region Options management
|
||||
|
||||
/// <summary>
|
||||
/// Registers a named option without additional parameters.
|
||||
/// </summary>
|
||||
/// <param name="name">The option name.</param>
|
||||
/// <returns>The option instance.</returns>
|
||||
public Option RegisterOption(string name)
|
||||
{
|
||||
return RegisterOption(name, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a named option.
|
||||
/// </summary>
|
||||
/// <param name="name">The option name.</param>
|
||||
/// <param name="parameterCount">The number of additional parameters for this option.</param>
|
||||
/// <returns>The option instance.</returns>
|
||||
public Option RegisterOption(string name, int parameterCount)
|
||||
{
|
||||
var option = new Option(name, parameterCount);
|
||||
_options.Add(option);
|
||||
return option;
|
||||
}
|
||||
|
||||
#endregion Options management
|
||||
|
||||
#region Parsing method
|
||||
|
||||
/// <summary>
|
||||
/// Parses all command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments.</param>
|
||||
public void Parse(string[] args)
|
||||
{
|
||||
_args = args ?? throw new ArgumentNullException(nameof(args));
|
||||
Parse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses all command line arguments.
|
||||
/// </summary>
|
||||
public void Parse()
|
||||
{
|
||||
// Use args of the current process if no other source was given
|
||||
if (_args == null)
|
||||
{
|
||||
_args = Environment.GetCommandLineArgs();
|
||||
if (_args.Length > 0)
|
||||
{
|
||||
// Skip myself (args[0])
|
||||
_args = _args.Skip(1).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear/reset data
|
||||
_parsedArguments = new();
|
||||
foreach (var option in _options)
|
||||
{
|
||||
option.IsSet = false;
|
||||
option.SetCount = 0;
|
||||
option.Argument = null;
|
||||
}
|
||||
|
||||
var comparison = IsCaseSensitive
|
||||
? StringComparison.Ordinal
|
||||
: StringComparison.OrdinalIgnoreCase;
|
||||
var argumentWalker = new EnumerableWalker<string>(_args);
|
||||
bool optMode = true;
|
||||
foreach (string arg in argumentWalker.Cast<string>())
|
||||
{
|
||||
if (arg == "--")
|
||||
{
|
||||
optMode = false;
|
||||
}
|
||||
else if (optMode && (arg.StartsWith("/") || arg.StartsWith("-")))
|
||||
{
|
||||
string optName = arg.Substring(arg.StartsWith("--") ? 2 : 1);
|
||||
|
||||
// Split option value if separated with : or = instead of whitespace
|
||||
int separatorIndex = optName.IndexOfAny(new[] { ':', '=' });
|
||||
string optValue = null;
|
||||
if (separatorIndex != -1)
|
||||
{
|
||||
optValue = optName.Substring(separatorIndex + 1);
|
||||
optName = optName.Substring(0, separatorIndex);
|
||||
}
|
||||
|
||||
// Find the option with complete name match
|
||||
var option = _options.FirstOrDefault(o => o.Names.Any(n => n.Equals(optName, comparison)));
|
||||
if (option == null)
|
||||
{
|
||||
// Try to complete the name to a unique registered option
|
||||
var matchingOptions = _options.Where(o => o.Names.Any(n => n.StartsWith(optName, comparison))).ToList();
|
||||
if (AutoCompleteOptions && matchingOptions.Count > 1)
|
||||
throw new Exception("Invalid option, completion is not unique: " + arg);
|
||||
|
||||
if (!AutoCompleteOptions || matchingOptions.Count == 0)
|
||||
throw new Exception("Unknown option: " + arg);
|
||||
|
||||
// Accept the single auto-completed option
|
||||
option = matchingOptions[0];
|
||||
}
|
||||
|
||||
// Check for single usage
|
||||
if (option.IsSingle && option.IsSet)
|
||||
throw new Exception("Option cannot be set multiple times: " + arg);
|
||||
|
||||
// Collect option values from next argument strings
|
||||
string[] values = new string[option.ParameterCount];
|
||||
for (int i = 0; i < option.ParameterCount; i++)
|
||||
{
|
||||
if (optValue != null)
|
||||
{
|
||||
// The first value was included in this argument string
|
||||
values[i] = optValue;
|
||||
optValue = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fetch another argument string
|
||||
values[i] = argumentWalker.GetNext();
|
||||
}
|
||||
|
||||
if (values[i] == null)
|
||||
throw new Exception("Missing argument " + (i + 1) + " for option: " + arg);
|
||||
}
|
||||
var argument = new Argument(option, values);
|
||||
|
||||
// Set usage data on the option instance for quick access
|
||||
option.IsSet = true;
|
||||
option.SetCount++;
|
||||
option.Argument = argument;
|
||||
|
||||
if (option.Action != null)
|
||||
{
|
||||
option.Action(argument);
|
||||
}
|
||||
else
|
||||
{
|
||||
_parsedArguments.Add(argument);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_parsedArguments.Add(new Argument(null, new[] { arg }));
|
||||
}
|
||||
}
|
||||
|
||||
var missingOption = _options.FirstOrDefault(o => o.IsRequired && !o.IsSet);
|
||||
if (missingOption != null)
|
||||
throw new Exception("Missing required option: /" + missingOption.Names[0]);
|
||||
}
|
||||
|
||||
#endregion Parsing method
|
||||
|
||||
#region Parsed data properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parsed arguments.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To avoid exceptions thrown, call the <see cref="Parse()"/> method in advance for
|
||||
/// exception handling.
|
||||
/// </remarks>
|
||||
public Argument[] Arguments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_parsedArguments == null)
|
||||
Parse();
|
||||
|
||||
return _parsedArguments.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options that are set in the command line, including their value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To avoid exceptions thrown, call the <see cref="Parse()"/> method in advance for
|
||||
/// exception handling.
|
||||
/// </remarks>
|
||||
public Option[] SetOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_parsedArguments == null)
|
||||
Parse();
|
||||
|
||||
return _parsedArguments
|
||||
.Where(a => a.Option != null)
|
||||
.Select(a => a.Option)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the free arguments that are set in the command line and don't belong to an option.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To avoid exceptions thrown, call the <see cref="Parse()"/> method in advance for
|
||||
/// exception handling.
|
||||
/// </remarks>
|
||||
public string[] FreeArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_parsedArguments == null)
|
||||
Parse();
|
||||
|
||||
return _parsedArguments
|
||||
.Where(a => a.Option == null)
|
||||
.Select(a => a.Value)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Parsed data properties
|
||||
}
|
||||
}
|
||||
58
AMWD.Common/Cli/EnumerableWalker.cs
Normal file
58
AMWD.Common/Cli/EnumerableWalker.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AMWD.Common.Cli
|
||||
{
|
||||
/// <summary>
|
||||
/// Walks through an <see cref="IEnumerable{T}"/> and allows retrieving additional items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class EnumerableWalker<T> : IEnumerable<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly IEnumerable<T> _array;
|
||||
private IEnumerator<T> _enumerator;
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance of the <see cref="EnumerableWalker{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="array">The array to walk though.</param>
|
||||
public EnumerableWalker(IEnumerable<T> array)
|
||||
{
|
||||
_array = array ?? throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
_enumerator = _array.GetEnumerator();
|
||||
return _enumerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enumerator.
|
||||
/// </summary>
|
||||
/// <returns>The enumerator.</returns>
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
_enumerator = _array.GetEnumerator();
|
||||
return _enumerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next item.
|
||||
/// </summary>
|
||||
/// <returns>The next item.</returns>
|
||||
public T GetNext()
|
||||
{
|
||||
if (_enumerator.MoveNext())
|
||||
{
|
||||
return _enumerator.Current;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
AMWD.Common/Cli/Option.cs
Normal file
113
AMWD.Common/Cli/Option.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AMWD.Common.Cli
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a named option.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
public class Option
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialises a new instance of the <see cref="Option"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The primary name of the option.</param>
|
||||
/// <param name="parameterCount">The number of additional parameters for this option.</param>
|
||||
internal Option(string name, int parameterCount)
|
||||
{
|
||||
Names = new List<string>() { name };
|
||||
ParameterCount = parameterCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of this option.
|
||||
/// </summary>
|
||||
public List<string> Names { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of additional parameters for this option.
|
||||
/// </summary>
|
||||
public int ParameterCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this option is required.
|
||||
/// </summary>
|
||||
public bool IsRequired { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this option can only be specified once.
|
||||
/// </summary>
|
||||
public bool IsSingle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the action to invoke when the option is set.
|
||||
/// </summary>
|
||||
public Action<Argument> Action { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this option is set in the command line.
|
||||
/// </summary>
|
||||
public bool IsSet { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times that this option is set in the command line.
|
||||
/// </summary>
|
||||
public int SetCount { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Argument"/> instance that contains additional parameters set
|
||||
/// for this option.
|
||||
/// </summary>
|
||||
public Argument Argument { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the <see cref="Argument"/> instance for this option.
|
||||
/// </summary>
|
||||
public string Value => Argument?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Sets alias names for this option.
|
||||
/// </summary>
|
||||
/// <param name="names">The alias names for this option.</param>
|
||||
/// <returns>The current <see cref="Option"/> instance.</returns>
|
||||
public Option Alias(params string[] names)
|
||||
{
|
||||
Names.AddRange(names);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this option as required. If a required option is not set in the command line,
|
||||
/// an exception is thrown on parsing.
|
||||
/// </summary>
|
||||
/// <returns>The current <see cref="Option"/> instance.</returns>
|
||||
public Option Required()
|
||||
{
|
||||
IsRequired = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this option as single. If a single option is set multiple times in the
|
||||
/// command line, an exception is thrown on parsing.
|
||||
/// </summary>
|
||||
/// <returns>The current <see cref="Option"/> instance.</returns>
|
||||
public Option Single()
|
||||
{
|
||||
IsSingle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to invoke when the option is set.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to invoke when the option is set.</param>
|
||||
/// <returns>The current <see cref="Option"/> instance.</returns>
|
||||
public Option Do(Action<Argument> action)
|
||||
{
|
||||
Action = action;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Newtonsoft.Json
|
||||
/// <summary>
|
||||
/// Common JSON serializer settings.
|
||||
/// </summary>
|
||||
private static readonly JsonSerializerSettings jsonSerializerSettings = new()
|
||||
private static readonly JsonSerializerSettings _jsonSerializerSettings = new()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
Culture = CultureInfo.InvariantCulture
|
||||
@@ -32,7 +32,7 @@ namespace Newtonsoft.Json
|
||||
public static void DeserializeJson<T>(this T target, string json)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(json))
|
||||
JsonConvert.PopulateObject(json, target, jsonSerializerSettings);
|
||||
JsonConvert.PopulateObject(json, target, _jsonSerializerSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,7 +42,7 @@ namespace Newtonsoft.Json
|
||||
/// <param name="json">The JSON string to read the values from.</param>
|
||||
/// <returns>A new instance of <typeparamref name="T"/> with the deserialized values.</returns>
|
||||
public static T DeserializeJson<T>(this string json)
|
||||
=> JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
|
||||
=> JsonConvert.DeserializeObject<T>(json, _jsonSerializerSettings);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a JSON string into a new instance or using the fallback value.
|
||||
@@ -55,7 +55,7 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
|
||||
return JsonConvert.DeserializeObject<T>(json, _jsonSerializerSettings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -83,7 +83,7 @@ namespace Newtonsoft.Json
|
||||
jw.QuoteChar = '\'';
|
||||
|
||||
jw.Formatting = indented ? Formatting.Indented : Formatting.None;
|
||||
var serializer = useCamelCase ? JsonSerializer.Create(jsonSerializerSettings) : JsonSerializer.CreateDefault();
|
||||
var serializer = useCamelCase ? JsonSerializer.Create(_jsonSerializerSettings) : JsonSerializer.CreateDefault();
|
||||
|
||||
serializer.Error += (s, a) =>
|
||||
{
|
||||
@@ -107,7 +107,7 @@ namespace Newtonsoft.Json
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
var serializer = JsonSerializer.Create(jsonSerializerSettings);
|
||||
var serializer = JsonSerializer.Create(_jsonSerializerSettings);
|
||||
return JObject.FromObject(obj, serializer);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Newtonsoft.Json
|
||||
if (array == null)
|
||||
return null;
|
||||
|
||||
var serializer = JsonSerializer.Create(jsonSerializerSettings);
|
||||
var serializer = JsonSerializer.Create(_jsonSerializerSettings);
|
||||
return JArray.FromObject(array, serializer);
|
||||
}
|
||||
|
||||
@@ -146,13 +146,26 @@ namespace Newtonsoft.Json
|
||||
if (lvlObj == null)
|
||||
return defaultValue;
|
||||
|
||||
lvlObj = lvlObj[level];
|
||||
string lvl = level;
|
||||
if (lvlObj.Type == JTokenType.Object)
|
||||
{
|
||||
foreach (var prop in ((JObject)lvlObj).Properties())
|
||||
{
|
||||
if (prop.Name.Equals(lvl, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lvl = prop.Name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lvlObj = lvlObj[lvl];
|
||||
}
|
||||
|
||||
if (lvlObj == null)
|
||||
return defaultValue;
|
||||
|
||||
return DeepConvert.ChangeType<T>(lvlObj is JValue ? ((JValue)lvlObj).Value : lvlObj.Value<object>());
|
||||
return DeepConvert.ChangeType<T>(lvlObj is JValue lvlValue ? lvlValue.Value : lvlObj.Value<object>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -58,30 +58,30 @@
|
||||
|
||||
private struct DisposableReadWriteLock : IDisposable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim rwLock;
|
||||
private LockMode lockMode;
|
||||
private readonly ReaderWriterLockSlim _rwLock;
|
||||
private LockMode _lockMode;
|
||||
|
||||
public DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
|
||||
{
|
||||
this.rwLock = rwLock;
|
||||
this.lockMode = lockMode;
|
||||
_rwLock = rwLock;
|
||||
_lockMode = lockMode;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (lockMode == LockMode.Read)
|
||||
rwLock.ExitReadLock();
|
||||
if (_lockMode == LockMode.Read)
|
||||
_rwLock.ExitReadLock();
|
||||
|
||||
if (lockMode == LockMode.Upgradable && rwLock.IsWriteLockHeld) // Upgraded with EnterWriteLock alone
|
||||
rwLock.ExitWriteLock();
|
||||
if (_lockMode == LockMode.Upgradable && _rwLock.IsWriteLockHeld) // Upgraded with EnterWriteLock alone
|
||||
_rwLock.ExitWriteLock();
|
||||
|
||||
if (lockMode == LockMode.Upgradable)
|
||||
rwLock.ExitUpgradeableReadLock();
|
||||
if (_lockMode == LockMode.Upgradable)
|
||||
_rwLock.ExitUpgradeableReadLock();
|
||||
|
||||
if (lockMode == LockMode.Write)
|
||||
rwLock.ExitWriteLock();
|
||||
if (_lockMode == LockMode.Write)
|
||||
_rwLock.ExitWriteLock();
|
||||
|
||||
lockMode = LockMode.None;
|
||||
_lockMode = LockMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,8 @@ namespace System.IO
|
||||
/// <returns></returns>
|
||||
public static string ReadLine(this Stream stream, Encoding encoding = null, char? eol = null)
|
||||
{
|
||||
if (encoding == null)
|
||||
encoding = Encoding.Default;
|
||||
|
||||
if (eol == null)
|
||||
eol = Environment.NewLine.Last();
|
||||
encoding ??= Encoding.Default;
|
||||
eol ??= Environment.NewLine.Last();
|
||||
|
||||
if (!stream.CanRead)
|
||||
return null;
|
||||
@@ -56,11 +53,8 @@ namespace System.IO
|
||||
/// <returns></returns>
|
||||
public static async Task<string> ReadLineAsync(this Stream stream, Encoding encoding = null, char? eol = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (encoding == null)
|
||||
encoding = Encoding.Default;
|
||||
|
||||
if (eol == null)
|
||||
eol = Environment.NewLine.Last();
|
||||
encoding ??= Encoding.Default;
|
||||
eol ??= Environment.NewLine.Last();
|
||||
|
||||
if (!stream.CanRead)
|
||||
return null;
|
||||
|
||||
@@ -177,10 +177,7 @@ namespace System
|
||||
|
||||
if (isValid && nameservers?.Any() == true)
|
||||
{
|
||||
var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS");
|
||||
if (dnsClientType == null)
|
||||
throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0");
|
||||
|
||||
var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS") ?? throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0");
|
||||
var recordTypeType = Type.GetType("DNS.Protocol.RecordType, DNS");
|
||||
var resolveMethodInfo = dnsClientType.GetMethod("Resolve", new[] { typeof(string), recordTypeType, typeof(CancellationToken) });
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ namespace AMWD.Common.Logging
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private bool isDisposed = false;
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new();
|
||||
private bool _isDisposed = false;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
private readonly StreamWriter fileWriter;
|
||||
private readonly Task writeTask;
|
||||
private readonly StreamWriter _fileWriter;
|
||||
private readonly Task _writeTask;
|
||||
|
||||
private readonly AsyncQueue<QueueItem> queue = new();
|
||||
private readonly AsyncQueue<QueueItem> _queue = new();
|
||||
|
||||
#endregion Fields
|
||||
|
||||
@@ -45,8 +45,8 @@ namespace AMWD.Common.Logging
|
||||
public FileLogger(string file, bool append = false, Encoding encoding = null)
|
||||
{
|
||||
FileName = file;
|
||||
fileWriter = new StreamWriter(FileName, append, encoding ?? Encoding.UTF8);
|
||||
writeTask = Task.Run(() => WriteFileAsync(cancellationTokenSource.Token));
|
||||
_fileWriter = new StreamWriter(FileName, append, encoding ?? Encoding.UTF8);
|
||||
_writeTask = Task.Run(() => WriteFileAsync(_cancellationTokenSource.Token));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -148,7 +148,7 @@ namespace AMWD.Common.Logging
|
||||
/// <inheritdoc cref="ILogger.BeginScope{TState}(TState)" />
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
if (isDisposed)
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
return ScopeProvider?.Push(state) ?? NullScope.Instance;
|
||||
@@ -157,7 +157,7 @@ namespace AMWD.Common.Logging
|
||||
/// <inheritdoc cref="ILogger.IsEnabled(LogLevel)" />
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
if (isDisposed)
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
return logLevel >= MinLevel;
|
||||
@@ -166,7 +166,7 @@ namespace AMWD.Common.Logging
|
||||
/// <inheritdoc cref="ILogger.Log{TState}(LogLevel, EventId, TState, Exception, Func{TState, Exception, string})" />
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (isDisposed)
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
if (!IsEnabled(logLevel))
|
||||
@@ -197,13 +197,13 @@ namespace AMWD.Common.Logging
|
||||
/// <inheritdoc cref="IDisposable.Dispose" />
|
||||
public void Dispose()
|
||||
{
|
||||
if (!isDisposed)
|
||||
if (!_isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
_isDisposed = true;
|
||||
|
||||
cancellationTokenSource.Cancel();
|
||||
writeTask.GetAwaiter().GetResult();
|
||||
fileWriter.Dispose();
|
||||
_cancellationTokenSource.Cancel();
|
||||
_writeTask.GetAwaiter().GetResult();
|
||||
_fileWriter.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace AMWD.Common.Logging
|
||||
|
||||
private void WriteMessage(string name, LogLevel logLevel, int eventId, string message, Exception exception)
|
||||
{
|
||||
queue.Enqueue(new QueueItem
|
||||
_queue.Enqueue(new QueueItem
|
||||
{
|
||||
Timestamp = UseUtcTimestamp ? DateTime.UtcNow : DateTime.Now,
|
||||
Name = name,
|
||||
@@ -236,7 +236,7 @@ namespace AMWD.Common.Logging
|
||||
QueueItem[] items;
|
||||
try
|
||||
{
|
||||
items = await queue.DequeueAvailableAsync(cancellationToken: token);
|
||||
items = await _queue.DequeueAvailableAsync(cancellationToken: token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -302,10 +302,10 @@ namespace AMWD.Common.Logging
|
||||
|
||||
sb.Append(message.Replace("\n", "\n" + timestampPadding + logLevelPadding));
|
||||
|
||||
await fileWriter.WriteLineAsync(sb.ToString());
|
||||
await _fileWriter.WriteLineAsync(sb.ToString()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await fileWriter.FlushAsync();
|
||||
await _fileWriter.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace System.Collections.Generic
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly Queue<T> queue;
|
||||
private readonly Queue<T> _queue;
|
||||
|
||||
private TaskCompletionSource<bool> dequeueTcs = new();
|
||||
private TaskCompletionSource<bool> availableTcs = new();
|
||||
private TaskCompletionSource<bool> _dequeueTcs = new();
|
||||
private TaskCompletionSource<bool> _availableTcs = new();
|
||||
|
||||
#endregion Fields
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public AsyncQueue()
|
||||
{
|
||||
queue = new Queue<T>();
|
||||
_queue = new Queue<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,7 +40,7 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public AsyncQueue(IEnumerable<T> collection)
|
||||
{
|
||||
queue = new Queue<T>();
|
||||
_queue = new Queue<T>();
|
||||
Enqueue(collection);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public AsyncQueue(int capacity)
|
||||
{
|
||||
queue = new Queue<T>(capacity);
|
||||
_queue = new Queue<T>(capacity);
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
@@ -67,9 +67,9 @@ namespace System.Collections.Generic
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
return queue.Count;
|
||||
return _queue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,9 +84,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public void Clear()
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
queue.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public bool Contains(T item)
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
return queue.Contains(item);
|
||||
return _queue.Contains(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +115,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
queue.CopyTo(array, arrayIndex);
|
||||
_queue.CopyTo(array, arrayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,9 +129,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public T Dequeue()
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
return queue.Dequeue();
|
||||
return _queue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,11 +141,11 @@ namespace System.Collections.Generic
|
||||
/// <param name="item">The object to add to the <see cref="AsyncQueue{T}"/>. The value can be null for reference types.</param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
queue.Enqueue(item);
|
||||
SetToken(dequeueTcs);
|
||||
SetToken(availableTcs);
|
||||
_queue.Enqueue(item);
|
||||
SetToken(_dequeueTcs);
|
||||
SetToken(_availableTcs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,9 +157,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public T Peek()
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
return queue.Peek();
|
||||
return _queue.Peek();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +170,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public T[] ToArray()
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
return queue.ToArray();
|
||||
return _queue.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,9 +182,9 @@ namespace System.Collections.Generic
|
||||
[ExcludeFromCodeCoverage]
|
||||
public void TrimExcess()
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
queue.TrimExcess();
|
||||
_queue.TrimExcess();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,21 +203,21 @@ namespace System.Collections.Generic
|
||||
while (true)
|
||||
{
|
||||
TaskCompletionSource<bool> internalDequeueTcs;
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
if (queue.Count > 0)
|
||||
if (_queue.Count > 0)
|
||||
{
|
||||
int count = queue.Count;
|
||||
int count = _queue.Count;
|
||||
if (maxCount > 0 && count > maxCount)
|
||||
count = maxCount;
|
||||
|
||||
var items = new T[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
items[i] = queue.Dequeue();
|
||||
items[i] = _queue.Dequeue();
|
||||
|
||||
return items;
|
||||
}
|
||||
internalDequeueTcs = ResetToken(ref dequeueTcs);
|
||||
internalDequeueTcs = ResetToken(ref _dequeueTcs);
|
||||
}
|
||||
|
||||
await WaitAsync(internalDequeueTcs, cancellationToken).ConfigureAwait(false);
|
||||
@@ -238,17 +238,17 @@ namespace System.Collections.Generic
|
||||
while (true)
|
||||
{
|
||||
TaskCompletionSource<bool> internalDequeueTcs;
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
if (count <= queue.Count)
|
||||
if (count <= _queue.Count)
|
||||
{
|
||||
var items = new T[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
items[i] = queue.Dequeue();
|
||||
items[i] = _queue.Dequeue();
|
||||
|
||||
return items;
|
||||
}
|
||||
internalDequeueTcs = ResetToken(ref dequeueTcs);
|
||||
internalDequeueTcs = ResetToken(ref _dequeueTcs);
|
||||
}
|
||||
|
||||
await WaitAsync(internalDequeueTcs, cancellationToken).ConfigureAwait(false);
|
||||
@@ -265,12 +265,12 @@ namespace System.Collections.Generic
|
||||
while (true)
|
||||
{
|
||||
TaskCompletionSource<bool> internalDequeueTcs;
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
if (queue.Count > 0)
|
||||
return queue.Dequeue();
|
||||
if (_queue.Count > 0)
|
||||
return _queue.Dequeue();
|
||||
|
||||
internalDequeueTcs = ResetToken(ref dequeueTcs);
|
||||
internalDequeueTcs = ResetToken(ref _dequeueTcs);
|
||||
}
|
||||
|
||||
await WaitAsync(internalDequeueTcs, cancellationToken).ConfigureAwait(false);
|
||||
@@ -285,12 +285,12 @@ namespace System.Collections.Generic
|
||||
public async Task WaitAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
TaskCompletionSource<bool> internalAvailableTcs;
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
if (queue.Count > 0)
|
||||
if (_queue.Count > 0)
|
||||
return;
|
||||
|
||||
internalAvailableTcs = ResetToken(ref availableTcs);
|
||||
internalAvailableTcs = ResetToken(ref _availableTcs);
|
||||
}
|
||||
|
||||
await WaitAsync(internalAvailableTcs, cancellationToken).ConfigureAwait(false);
|
||||
@@ -347,10 +347,10 @@ namespace System.Collections.Generic
|
||||
/// <returns>true if item is successfully removed; otherwise, false. This method also returns false if item was not found in the <see cref="AsyncQueue{T}"/>.</returns>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
var copy = new Queue<T>(queue);
|
||||
queue.Clear();
|
||||
var copy = new Queue<T>(_queue);
|
||||
_queue.Clear();
|
||||
|
||||
bool found = false;
|
||||
int count = copy.Count;
|
||||
@@ -359,7 +359,7 @@ namespace System.Collections.Generic
|
||||
var element = copy.Dequeue();
|
||||
if (found)
|
||||
{
|
||||
queue.Enqueue(element);
|
||||
_queue.Enqueue(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace System.Collections.Generic
|
||||
continue;
|
||||
}
|
||||
|
||||
queue.Enqueue(element);
|
||||
_queue.Enqueue(element);
|
||||
}
|
||||
|
||||
return found;
|
||||
@@ -382,19 +382,19 @@ namespace System.Collections.Generic
|
||||
/// <param name="collection">The objects to add to the <see cref="AsyncQueue{T}"/>.</param>
|
||||
public void Enqueue(IEnumerable<T> collection)
|
||||
{
|
||||
lock (queue)
|
||||
lock (_queue)
|
||||
{
|
||||
bool hasElements = false;
|
||||
foreach (var element in collection)
|
||||
{
|
||||
hasElements = true;
|
||||
queue.Enqueue(element);
|
||||
_queue.Enqueue(element);
|
||||
}
|
||||
|
||||
if (hasElements)
|
||||
{
|
||||
SetToken(dequeueTcs);
|
||||
SetToken(availableTcs);
|
||||
SetToken(_dequeueTcs);
|
||||
SetToken(_availableTcs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,7 +425,7 @@ namespace System.Collections.Generic
|
||||
{
|
||||
if (await Task.WhenAny(tcs.Task, Task.Delay(-1, cancellationToken)) == tcs.Task)
|
||||
{
|
||||
await tcs.Task;
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,12 @@ namespace System.Security.Cryptography
|
||||
/// </summary>
|
||||
public class CryptographyHelper
|
||||
{
|
||||
private static int saltLength = 8;
|
||||
// "readonly" not added due to UnitTests
|
||||
#pragma warning disable IDE0044 // Add "readonly" modifier
|
||||
private static int _saltLength = 8;
|
||||
#pragma warning restore IDE0044 // Add "readonly" modifier
|
||||
|
||||
private readonly string masterKeyFile;
|
||||
private readonly string _masterKeyFile;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CryptographyHelper"/> class.
|
||||
@@ -18,17 +21,17 @@ namespace System.Security.Cryptography
|
||||
/// <param name="keyFile">The (absolute) path to the crypto key file. On <c>null</c> the file 'crypto.key' at the executing assembly location will be used.</param>
|
||||
public CryptographyHelper(string keyFile = null)
|
||||
{
|
||||
masterKeyFile = keyFile;
|
||||
_masterKeyFile = keyFile;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(masterKeyFile))
|
||||
masterKeyFile = "crypto.key";
|
||||
if (string.IsNullOrWhiteSpace(_masterKeyFile))
|
||||
_masterKeyFile = "crypto.key";
|
||||
|
||||
if (!Path.IsPathRooted(masterKeyFile))
|
||||
masterKeyFile = Path.Combine(AppContext.BaseDirectory, masterKeyFile);
|
||||
if (!Path.IsPathRooted(_masterKeyFile))
|
||||
_masterKeyFile = Path.Combine(AppContext.BaseDirectory, _masterKeyFile);
|
||||
|
||||
string pw = File.Exists(masterKeyFile) ? File.ReadAllText(masterKeyFile) : null;
|
||||
string pw = File.Exists(_masterKeyFile) ? File.ReadAllText(_masterKeyFile) : null;
|
||||
if (string.IsNullOrWhiteSpace(pw))
|
||||
File.WriteAllText(masterKeyFile, GetRandomString(64));
|
||||
File.WriteAllText(_masterKeyFile, GetRandomString(64));
|
||||
}
|
||||
|
||||
#region Instance methods
|
||||
@@ -46,8 +49,7 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The decrypted data.</returns>
|
||||
public byte[] DecryptAes(byte[] cipher, string password = null)
|
||||
{
|
||||
if (password == null)
|
||||
password = File.ReadAllText(masterKeyFile);
|
||||
password ??= File.ReadAllText(_masterKeyFile);
|
||||
|
||||
return AesDecrypt(cipher, password);
|
||||
}
|
||||
@@ -63,8 +65,7 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The encrypted data (cipher).</returns>
|
||||
public byte[] EncryptAes(byte[] plain, string password = null)
|
||||
{
|
||||
if (password == null)
|
||||
password = File.ReadAllText(masterKeyFile);
|
||||
password ??= File.ReadAllText(_masterKeyFile);
|
||||
|
||||
return AesEncrypt(plain, password);
|
||||
}
|
||||
@@ -110,8 +111,7 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The decrypted data.</returns>
|
||||
public byte[] DecryptTripleDes(byte[] cipher, string password = null)
|
||||
{
|
||||
if (password == null)
|
||||
password = File.ReadAllText(masterKeyFile);
|
||||
password ??= File.ReadAllText(_masterKeyFile);
|
||||
|
||||
return TripleDesDecrypt(cipher, password);
|
||||
}
|
||||
@@ -127,8 +127,7 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The encrypted data (cipher).</returns>
|
||||
public byte[] EncryptTripleDes(byte[] plain, string password = null)
|
||||
{
|
||||
if (password == null)
|
||||
password = File.ReadAllText(masterKeyFile);
|
||||
password ??= File.ReadAllText(_masterKeyFile);
|
||||
|
||||
return TripleDesEncrypt(plain, password);
|
||||
}
|
||||
@@ -183,8 +182,8 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The decrypted data.</returns>
|
||||
public static byte[] AesDecrypt(byte[] cipher, string password)
|
||||
{
|
||||
byte[] salt = new byte[saltLength];
|
||||
Array.Copy(cipher, salt, saltLength);
|
||||
byte[] salt = new byte[_saltLength];
|
||||
Array.Copy(cipher, salt, _saltLength);
|
||||
|
||||
using var gen = new Rfc2898DeriveBytes(password, salt);
|
||||
using var aes = Aes.Create();
|
||||
@@ -197,7 +196,7 @@ namespace System.Security.Cryptography
|
||||
using var ms = new MemoryStream();
|
||||
using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);
|
||||
|
||||
cs.Write(cipher, saltLength, cipher.Length - saltLength);
|
||||
cs.Write(cipher, _saltLength, cipher.Length - _saltLength);
|
||||
cs.FlushFinalBlock();
|
||||
|
||||
return ms.ToArray();
|
||||
@@ -224,7 +223,7 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The encrypted data (cipher).</returns>
|
||||
public static byte[] AesEncrypt(byte[] plain, string password)
|
||||
{
|
||||
byte[] salt = GetRandomBytes(saltLength);
|
||||
byte[] salt = GetRandomBytes(_saltLength);
|
||||
|
||||
using var gen = new Rfc2898DeriveBytes(password, salt);
|
||||
using var aes = Aes.Create();
|
||||
@@ -269,8 +268,8 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The decrypted data.</returns>
|
||||
public static byte[] TripleDesDecrypt(byte[] cipher, string password)
|
||||
{
|
||||
byte[] salt = new byte[saltLength];
|
||||
Array.Copy(cipher, salt, saltLength);
|
||||
byte[] salt = new byte[_saltLength];
|
||||
Array.Copy(cipher, salt, _saltLength);
|
||||
|
||||
using var gen = new Rfc2898DeriveBytes(password, salt);
|
||||
using var tdes = TripleDES.Create();
|
||||
@@ -283,7 +282,7 @@ namespace System.Security.Cryptography
|
||||
using var ms = new MemoryStream();
|
||||
using var cs = new CryptoStream(ms, tdes.CreateDecryptor(), CryptoStreamMode.Write);
|
||||
|
||||
cs.Write(cipher, saltLength, cipher.Length - saltLength);
|
||||
cs.Write(cipher, _saltLength, cipher.Length - _saltLength);
|
||||
cs.FlushFinalBlock();
|
||||
|
||||
return ms.ToArray();
|
||||
@@ -297,7 +296,7 @@ namespace System.Security.Cryptography
|
||||
/// <returns>The encrypted data (cipher).</returns>
|
||||
public static byte[] TripleDesEncrypt(byte[] plain, string password)
|
||||
{
|
||||
byte[] salt = GetRandomBytes(saltLength);
|
||||
byte[] salt = GetRandomBytes(_saltLength);
|
||||
|
||||
using var gen = new Rfc2898DeriveBytes(password, salt);
|
||||
using var tdes = TripleDES.Create();
|
||||
|
||||
@@ -15,9 +15,9 @@ namespace AMWD.Common.Utilities
|
||||
{
|
||||
#region Data
|
||||
|
||||
private Timer timer;
|
||||
private Timer _timer;
|
||||
|
||||
private bool nextRunPending;
|
||||
private bool _nextRunPending;
|
||||
|
||||
/// <summary>
|
||||
/// The synchronisation object.
|
||||
@@ -130,13 +130,13 @@ namespace AMWD.Common.Utilities
|
||||
tcs = CreateTcs();
|
||||
}
|
||||
IsWaitingToRun = true;
|
||||
if (timer != null)
|
||||
if (_timer != null)
|
||||
{
|
||||
timer.Change(Delay, Timeout.InfiniteTimeSpan);
|
||||
_timer.Change(Delay, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = new Timer(OnTimerCallback, null, Delay, Timeout.InfiniteTimeSpan);
|
||||
_timer = new Timer(OnTimerCallback, null, Delay, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,9 +152,9 @@ namespace AMWD.Common.Utilities
|
||||
lock (syncLock)
|
||||
{
|
||||
IsWaitingToRun = false;
|
||||
nextRunPending = false;
|
||||
timer?.Dispose();
|
||||
timer = null;
|
||||
_nextRunPending = false;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
if (!IsRunning)
|
||||
{
|
||||
localTcs = tcs;
|
||||
@@ -179,13 +179,13 @@ namespace AMWD.Common.Utilities
|
||||
return false;
|
||||
}
|
||||
IsWaitingToRun = true;
|
||||
if (timer != null)
|
||||
if (_timer != null)
|
||||
{
|
||||
timer.Change(TimeSpan.Zero, Timeout.InfiniteTimeSpan);
|
||||
_timer.Change(TimeSpan.Zero, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = new Timer(OnTimerCallback, null, TimeSpan.Zero, Timeout.InfiniteTimeSpan);
|
||||
_timer = new Timer(OnTimerCallback, null, TimeSpan.Zero, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -256,7 +256,7 @@ namespace AMWD.Common.Utilities
|
||||
{
|
||||
tcs = CreateTcs();
|
||||
IsWaitingToRun = true;
|
||||
timer = new Timer(OnTimerCallback, null, Delay, Timeout.InfiniteTimeSpan);
|
||||
_timer = new Timer(OnTimerCallback, null, Delay, Timeout.InfiniteTimeSpan);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ namespace AMWD.Common.Utilities
|
||||
if (IsRunning)
|
||||
{
|
||||
// Currently running, remember and do nothing for now
|
||||
nextRunPending = true;
|
||||
_nextRunPending = true;
|
||||
return;
|
||||
}
|
||||
IsRunning = true;
|
||||
@@ -308,12 +308,12 @@ namespace AMWD.Common.Utilities
|
||||
{
|
||||
runAgain = false;
|
||||
IsRunning = false;
|
||||
nextRunPending = false;
|
||||
_nextRunPending = false;
|
||||
localTcs = tcs;
|
||||
if (!IsWaitingToRun)
|
||||
{
|
||||
timer?.Dispose();
|
||||
timer = null;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
exceptionHandler?.Invoke(ex);
|
||||
@@ -322,16 +322,16 @@ namespace AMWD.Common.Utilities
|
||||
{
|
||||
lock (syncLock)
|
||||
{
|
||||
runAgain = nextRunPending;
|
||||
runAgain = _nextRunPending;
|
||||
IsRunning = runAgain;
|
||||
nextRunPending = false;
|
||||
_nextRunPending = false;
|
||||
if (!runAgain)
|
||||
{
|
||||
if (!IsWaitingToRun)
|
||||
{
|
||||
localTcs = tcs;
|
||||
timer?.Dispose();
|
||||
timer = null;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,19 +403,19 @@ namespace AMWD.Common.Utilities
|
||||
/// <typeparam name="TResult">The type of the result value.</typeparam>
|
||||
protected class TaskCompletionSourceWrapper<TResult> : TaskCompletionSourceWrapper
|
||||
{
|
||||
private readonly TaskCompletionSource<TResult> tcs;
|
||||
private readonly TaskCompletionSource<TResult> _tcs;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Task{TResult}"/> of the <see cref="TaskCompletionSource{TResult}"/>.
|
||||
/// </summary>
|
||||
public override Task Task => tcs.Task;
|
||||
public override Task Task => _tcs.Task;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TaskCompletionSourceWrapper{TResult}"/> class.
|
||||
/// </summary>
|
||||
public TaskCompletionSourceWrapper()
|
||||
{
|
||||
tcs = new TaskCompletionSource<TResult>();
|
||||
_tcs = new TaskCompletionSource<TResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -424,13 +424,13 @@ namespace AMWD.Common.Utilities
|
||||
/// </summary>
|
||||
/// <param name="result">The result value to bind to this <see cref="Task{TResult}"/>.</param>
|
||||
/// <seealso cref="TaskCompletionSource{TResult}.TrySetResult(TResult)"/>
|
||||
public void TrySetResult(TResult result) => tcs.TrySetResult(result);
|
||||
public void TrySetResult(TResult result) => _tcs.TrySetResult(result);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TrySetException(Exception exception) => tcs.TrySetException(exception);
|
||||
public override void TrySetException(Exception exception) => _tcs.TrySetException(exception);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TrySetCanceled() => tcs.TrySetCanceled();
|
||||
public override void TrySetCanceled() => _tcs.TrySetCanceled();
|
||||
}
|
||||
|
||||
#endregion Internal TaskCompletionSourceWrapper classes
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace AMWD.Common.Utilities
|
||||
if (addressFamily != AddressFamily.Unspecified && ipAddress.AddressFamily != addressFamily)
|
||||
return null;
|
||||
|
||||
return ipAddress;
|
||||
return ipAddress.IsIPv4MappedToIPv6 ? ipAddress.MapToIPv4() : ipAddress;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user