Got at least one data fetching method working; turns out, we can't use a patched LogicStack to get the data
This commit is contained in:
51
Vendor/Swan.Lite-3.1.0/Collections/CollectionCacheRepository.cs
vendored
Normal file
51
Vendor/Swan.Lite-3.1.0/Collections/CollectionCacheRepository.cs
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe collection cache repository for types.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of member to cache.</typeparam>
|
||||
public class CollectionCacheRepository<TValue>
|
||||
{
|
||||
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
|
||||
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() =>
|
||||
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the cache contains the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns>
|
||||
public bool ContainsKey(Type key) => _data.Value.ContainsKey(key);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the properties stored for the specified type.
|
||||
/// If the properties are not available, it calls the factory method to retrieve them
|
||||
/// and returns them as an array of PropertyInfo.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <returns>
|
||||
/// An array of the properties stored for the specified type.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// key
|
||||
/// or
|
||||
/// factory.
|
||||
/// </exception>
|
||||
public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
|
||||
if (factory == null)
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Vendor/Swan.Lite-3.1.0/CompositeHashCode.cs
vendored
Normal file
100
Vendor/Swan.Lite-3.1.0/CompositeHashCode.cs
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Provides a way for types that override <see cref="object.GetHashCode"/>
|
||||
/// to correctly generate a hash code from the actual status of an instance.</para>
|
||||
/// <para><c>CompositeHashCode</c> must be used ONLY as a helper when implementing
|
||||
/// <see cref="IEquatable{T}">IEquatable<T></see> in a STANDARD way, i.e. when:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>two instances having the same hash code are actually
|
||||
/// interchangeable, i.e. they represent exactly the same object (for instance,
|
||||
/// they should not coexist in a
|
||||
/// <see cref="SortedSet{T}">SortedSet</see>);</description></item>
|
||||
/// <item><description><see cref="object.GetHashCode">GetHashCode</see> and
|
||||
/// <see cref="object.Equals(object)">Equals</see> are BOTH overridden, and the <c>Equals</c>
|
||||
/// override either calls to the <see cref="IEquatable{T}.Equals(T)">IEquatable<T>.Equals</see>
|
||||
/// (recommended) or performs exactly the same equality checks;</description></item>
|
||||
/// <item><description>only "standard" equality checks are performed, i.e. by means of the
|
||||
/// <c>==</c> operator, <see cref="IEquatable{T}">IEquatable<T></see> interfaces, and
|
||||
/// the <see cref="object.Equals(object)">Equals</see> method (for instance, this excludes case-insensitive
|
||||
/// and/or culture-dependent string comparisons);
|
||||
/// </description></item>
|
||||
/// <item><description>the hash code is constructed (via <c>Using</c> calls) from the very same
|
||||
/// fields and / or properties that are checked for equality.</description></item>
|
||||
/// </list>
|
||||
/// <para>For hashing to work correctly, all fields and/or properties involved in hashing must either
|
||||
/// be immutable, or at least not change while an object is referenced in a hashtable.
|
||||
/// This does not refer just to <c>System.Collections.Hashtable</c>; the .NET
|
||||
/// Framework makes a fairly extensive use of hashing, for example in
|
||||
/// <see cref="SortedSet{T}">SortedSet<T></see>
|
||||
/// and in various parts of LINQ. As a thumb rule, an object must stay the same during the execution of a
|
||||
/// LINQ query on an <see cref="IEnumerable{T}">IEnumerable</see>
|
||||
/// in which it is contained, as well as all the time it is referenced in a <c>Hashtable</c> or <c>SortedSet</c>.</para>
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para>The following code constitutes a minimal use case for <c>CompositeHashCode</c>, as well
|
||||
/// as a reference for standard <see cref="System.IEquatable{T}">IEquatable<T></see> implementation.</para>
|
||||
/// <para>Notice that all relevant properties are immutable; this is not, as stated in the summary,
|
||||
/// an absolute requirement, but it surely helps and should be done every time it makes sense.</para>
|
||||
/// <code>using System;
|
||||
/// using Swan;
|
||||
///
|
||||
/// namespace Example
|
||||
/// {
|
||||
/// public class Person : IEquatable<Person>
|
||||
/// {
|
||||
/// public string Name { get; private set; }
|
||||
///
|
||||
/// public int Age { get; private set; }
|
||||
///
|
||||
/// public Person(string name, int age)
|
||||
/// {
|
||||
/// Name = name;
|
||||
/// Age = age;
|
||||
/// }
|
||||
///
|
||||
/// public override int GetHashCode() => CompositeHashCode.Using(Name, Age);
|
||||
///
|
||||
/// public override bool Equals(object obj) => obj is Person other && Equals(other);
|
||||
///
|
||||
/// public bool Equals(Person other)
|
||||
/// => other != null
|
||||
/// && other.Name == Name
|
||||
/// && other.Age == Age;
|
||||
/// }
|
||||
/// }</code>
|
||||
/// </example>
|
||||
public static class CompositeHashCode
|
||||
{
|
||||
#region Private constants
|
||||
|
||||
private const int InitialSeed = 17;
|
||||
private const int Multiplier = 29;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Computes a hash code, taking into consideration the values of the specified
|
||||
/// fields and/oror properties as part of an object's state. See the
|
||||
/// <see cref="CompositeHashCode">example</see>.
|
||||
/// </summary>
|
||||
/// <param name="fields">The values of the fields and/or properties.</param>
|
||||
/// <returns>The computed has code.</returns>
|
||||
public static int Using(params object[] fields)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return fields.Where(f => !(f is null))
|
||||
.Aggregate(InitialSeed, (current, field) => (Multiplier * current) + field.GetHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
77
Vendor/Swan.Lite-3.1.0/Configuration/ConfiguredObject.cs
vendored
Normal file
77
Vendor/Swan.Lite-3.1.0/Configuration/ConfiguredObject.cs
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for objects whose configuration may be locked,
|
||||
/// thus becoming read-only, at a certain moment in their lifetime.
|
||||
/// </summary>
|
||||
public abstract class ConfiguredObject
|
||||
{
|
||||
private readonly object _syncRoot = new object();
|
||||
private bool _configurationLocked;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether s configuration has already been locked
|
||||
/// and has therefore become read-only.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true"/> if the configuration is locked; otherwise, <see langword="false"/>.
|
||||
/// </value>
|
||||
/// <seealso cref="EnsureConfigurationNotLocked"/>
|
||||
protected bool ConfigurationLocked
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _configurationLocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Locks this instance's configuration, preventing further modifications.</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Configuration locking must be enforced by derived classes
|
||||
/// by calling <see cref="EnsureConfigurationNotLocked"/> at the start
|
||||
/// of methods and property setters that could change the object's
|
||||
/// configuration.</para>
|
||||
/// <para>Immediately before locking the configuration, this method calls <see cref="OnBeforeLockConfiguration"/>
|
||||
/// as a last chance to validate configuration data, and to lock the configuration of contained objects.</para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnBeforeLockConfiguration"/>
|
||||
protected void LockConfiguration()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_configurationLocked)
|
||||
return;
|
||||
|
||||
OnBeforeLockConfiguration();
|
||||
_configurationLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called immediately before locking the configuration.
|
||||
/// </summary>
|
||||
/// <seealso cref="LockConfiguration"/>
|
||||
protected virtual void OnBeforeLockConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a module's configuration has become read-only
|
||||
/// and, if so, throws an <see cref="InvalidOperationException"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">The configuration is locked.</exception>
|
||||
/// <seealso cref="ConfigurationLocked"/>
|
||||
protected void EnsureConfigurationNotLocked()
|
||||
{
|
||||
if (ConfigurationLocked)
|
||||
throw new InvalidOperationException($"Configuration of this {GetType().Name} instance is locked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Vendor/Swan.Lite-3.1.0/Configuration/SettingsProvider.cs
vendored
Normal file
107
Vendor/Swan.Lite-3.1.0/Configuration/SettingsProvider.cs
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
using Swan.Formatters;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Swan.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a provider to save and load settings using a plain JSON file.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example shows how to save and load settings.
|
||||
/// <code>
|
||||
/// using Swan.Configuration;
|
||||
///
|
||||
/// public class Example
|
||||
/// {
|
||||
/// public static void Main()
|
||||
/// {
|
||||
/// // get user from settings
|
||||
/// var user = SettingsProvider<Settings>.Instance.Global.User;
|
||||
///
|
||||
/// // modify the port
|
||||
/// SettingsProvider<Settings>.Instance.Global.Port = 20;
|
||||
///
|
||||
/// // if we want these settings to persist
|
||||
/// SettingsProvider<Settings>.Instance.PersistGlobalSettings();
|
||||
/// }
|
||||
///
|
||||
/// public class Settings
|
||||
/// {
|
||||
/// public int Port { get; set; } = 9696;
|
||||
///
|
||||
/// public string User { get; set; } = "User";
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <typeparam name="T">The type of settings model.</typeparam>
|
||||
public sealed class SettingsProvider<T>
|
||||
: SingletonBase<SettingsProvider<T>>
|
||||
{
|
||||
private readonly object _syncRoot = new object();
|
||||
|
||||
private T _global;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration file path. By default the entry assembly directory is used
|
||||
/// and the filename is 'appsettings.json'.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The configuration file path.
|
||||
/// </value>
|
||||
public string ConfigurationFilePath { get; set; } =
|
||||
Path.Combine(SwanRuntime.EntryAssemblyDirectory, "appsettings.json");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the global settings object.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The global settings object.
|
||||
/// </value>
|
||||
public T Global
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (Equals(_global, default(T)))
|
||||
ReloadGlobalSettings();
|
||||
|
||||
return _global;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the global settings.
|
||||
/// </summary>
|
||||
public void ReloadGlobalSettings()
|
||||
{
|
||||
if (File.Exists(ConfigurationFilePath) == false || File.ReadAllText(ConfigurationFilePath).Length == 0)
|
||||
{
|
||||
ResetGlobalSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_syncRoot)
|
||||
_global = Json.Deserialize<T>(File.ReadAllText(ConfigurationFilePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persists the global settings.
|
||||
/// </summary>
|
||||
public void PersistGlobalSettings() => File.WriteAllText(ConfigurationFilePath, Json.Serialize(Global, true));
|
||||
|
||||
/// <summary>
|
||||
/// Resets the global settings.
|
||||
/// </summary>
|
||||
public void ResetGlobalSettings()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
_global = Activator.CreateInstance<T>();
|
||||
|
||||
PersistGlobalSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
124
Vendor/Swan.Lite-3.1.0/Cryptography/Hasher.cs
vendored
Normal file
124
Vendor/Swan.Lite-3.1.0/Cryptography/Hasher.cs
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Swan.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class to compute a hash in MD4, SHA1, SHA256 or SHA512.
|
||||
/// </summary>
|
||||
public static class Hasher
|
||||
{
|
||||
private static readonly Lazy<MD5> Md5Hasher = new Lazy<MD5>(MD5.Create, true);
|
||||
private static readonly Lazy<SHA1> SHA1Hasher = new Lazy<SHA1>(SHA1.Create, true);
|
||||
private static readonly Lazy<SHA256> SHA256Hasher = new Lazy<SHA256>(SHA256.Create, true);
|
||||
private static readonly Lazy<SHA512> SHA512Hasher = new Lazy<SHA512>(SHA512.Create, true);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the MD5 hash of the given stream.
|
||||
/// Do not use for large streams as this reads ALL bytes at once.
|
||||
/// </summary>
|
||||
/// <param name="this">The stream.</param>
|
||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||
/// <returns>
|
||||
/// The computed hash code.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">stream.</exception>
|
||||
[Obsolete("Use a better hasher.")]
|
||||
public static byte[] ComputeMD5(Stream @this, bool createHasher = false)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
using var md5 = MD5.Create();
|
||||
const int bufferSize = 4096;
|
||||
|
||||
var readAheadBuffer = new byte[bufferSize];
|
||||
var readAheadBytesRead = @this.Read(readAheadBuffer, 0, readAheadBuffer.Length);
|
||||
|
||||
do
|
||||
{
|
||||
var bytesRead = readAheadBytesRead;
|
||||
var buffer = readAheadBuffer;
|
||||
|
||||
readAheadBuffer = new byte[bufferSize];
|
||||
readAheadBytesRead = @this.Read(readAheadBuffer, 0, readAheadBuffer.Length);
|
||||
|
||||
if (readAheadBytesRead == 0)
|
||||
md5.TransformFinalBlock(buffer, 0, bytesRead);
|
||||
else
|
||||
md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
|
||||
}
|
||||
while (readAheadBytesRead != 0);
|
||||
|
||||
return md5.Hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the MD5 hash of the given string using UTF8 byte encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The input string.</param>
|
||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||
/// <returns>The computed hash code.</returns>
|
||||
[Obsolete("Use a better hasher.")]
|
||||
public static byte[] ComputeMD5(string value, bool createHasher = false) =>
|
||||
ComputeMD5(Encoding.UTF8.GetBytes(value), createHasher);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the MD5 hash of the given byte array.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||
/// <returns>The computed hash code.</returns>
|
||||
[Obsolete("Use a better hasher.")]
|
||||
public static byte[] ComputeMD5(byte[] data, bool createHasher = false) =>
|
||||
(createHasher ? MD5.Create() : Md5Hasher.Value).ComputeHash(data);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the SHA-1 hash of the given string using UTF8 byte encoding.
|
||||
/// </summary>
|
||||
/// <param name="this">The input string.</param>
|
||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||
/// <returns>
|
||||
/// The computes a Hash-based Message Authentication Code (HMAC)
|
||||
/// using the SHA1 hash function.
|
||||
/// </returns>
|
||||
[Obsolete("Use a better hasher.")]
|
||||
public static byte[] ComputeSha1(string @this, bool createHasher = false)
|
||||
{
|
||||
var inputBytes = Encoding.UTF8.GetBytes(@this);
|
||||
return (createHasher ? SHA1.Create() : SHA1Hasher.Value).ComputeHash(inputBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the SHA-256 hash of the given string using UTF8 byte encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The input string.</param>
|
||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||
/// <returns>
|
||||
/// The computes a Hash-based Message Authentication Code (HMAC)
|
||||
/// by using the SHA256 hash function.
|
||||
/// </returns>
|
||||
public static byte[] ComputeSha256(string value, bool createHasher = false)
|
||||
{
|
||||
var inputBytes = Encoding.UTF8.GetBytes(value);
|
||||
return (createHasher ? SHA256.Create() : SHA256Hasher.Value).ComputeHash(inputBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the SHA-512 hash of the given string using UTF8 byte encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The input string.</param>
|
||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||
/// <returns>
|
||||
/// The computes a Hash-based Message Authentication Code (HMAC)
|
||||
/// using the SHA512 hash function.
|
||||
/// </returns>
|
||||
public static byte[] ComputeSha512(string value, bool createHasher = false)
|
||||
{
|
||||
var inputBytes = Encoding.UTF8.GetBytes(value);
|
||||
return (createHasher ? SHA512.Create() : SHA512Hasher.Value).ComputeHash(inputBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Vendor/Swan.Lite-3.1.0/DateTimeSpan.cs
vendored
Normal file
174
Vendor/Swan.Lite-3.1.0/DateTimeSpan.cs
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a struct of DateTimeSpan to compare dates and get in
|
||||
/// separate fields the amount of time between those dates.
|
||||
///
|
||||
/// Based on https://stackoverflow.com/a/9216404/1096693.
|
||||
/// </summary>
|
||||
public struct DateTimeSpan
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="years">The years.</param>
|
||||
/// <param name="months">The months.</param>
|
||||
/// <param name="days">The days.</param>
|
||||
/// <param name="hours">The hours.</param>
|
||||
/// <param name="minutes">The minutes.</param>
|
||||
/// <param name="seconds">The seconds.</param>
|
||||
/// <param name="milliseconds">The milliseconds.</param>
|
||||
public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
|
||||
{
|
||||
Years = years;
|
||||
Months = months;
|
||||
Days = days;
|
||||
Hours = hours;
|
||||
Minutes = minutes;
|
||||
Seconds = seconds;
|
||||
Milliseconds = milliseconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the years.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The years.
|
||||
/// </value>
|
||||
public int Years { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the months.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The months.
|
||||
/// </value>
|
||||
public int Months { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the days.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The days.
|
||||
/// </value>
|
||||
public int Days { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hours.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The hours.
|
||||
/// </value>
|
||||
public int Hours { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minutes.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The minutes.
|
||||
/// </value>
|
||||
public int Minutes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the seconds.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The seconds.
|
||||
/// </value>
|
||||
public int Seconds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the milliseconds.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The milliseconds.
|
||||
/// </value>
|
||||
public int Milliseconds { get; }
|
||||
|
||||
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
|
||||
{
|
||||
if (date2 < date1)
|
||||
{
|
||||
var sub = date1;
|
||||
date1 = date2;
|
||||
date2 = sub;
|
||||
}
|
||||
|
||||
var current = date1;
|
||||
var years = 0;
|
||||
var months = 0;
|
||||
var days = 0;
|
||||
|
||||
var phase = Phase.Years;
|
||||
var span = new DateTimeSpan();
|
||||
var officialDay = current.Day;
|
||||
|
||||
while (phase != Phase.Done)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case Phase.Years:
|
||||
if (current.AddYears(years + 1) > date2)
|
||||
{
|
||||
phase = Phase.Months;
|
||||
current = current.AddYears(years);
|
||||
}
|
||||
else
|
||||
{
|
||||
years++;
|
||||
}
|
||||
|
||||
break;
|
||||
case Phase.Months:
|
||||
if (current.AddMonths(months + 1) > date2)
|
||||
{
|
||||
phase = Phase.Days;
|
||||
current = current.AddMonths(months);
|
||||
if (current.Day < officialDay &&
|
||||
officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
|
||||
current = current.AddDays(officialDay - current.Day);
|
||||
}
|
||||
else
|
||||
{
|
||||
months++;
|
||||
}
|
||||
|
||||
break;
|
||||
case Phase.Days:
|
||||
if (current.AddDays(days + 1) > date2)
|
||||
{
|
||||
current = current.AddDays(days);
|
||||
var timespan = date2 - current;
|
||||
span = new DateTimeSpan(
|
||||
years,
|
||||
months,
|
||||
days,
|
||||
timespan.Hours,
|
||||
timespan.Minutes,
|
||||
timespan.Seconds,
|
||||
timespan.Milliseconds);
|
||||
phase = Phase.Done;
|
||||
}
|
||||
else
|
||||
{
|
||||
days++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
private enum Phase
|
||||
{
|
||||
Years,
|
||||
Months,
|
||||
Days,
|
||||
Done,
|
||||
}
|
||||
}
|
||||
}
|
||||
138
Vendor/Swan.Lite-3.1.0/Definitions.Types.cs
vendored
Normal file
138
Vendor/Swan.Lite-3.1.0/Definitions.Types.cs
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains useful constants and definitions.
|
||||
/// </summary>
|
||||
public static partial class Definitions
|
||||
{
|
||||
#region Main Dictionary Definition
|
||||
|
||||
/// <summary>
|
||||
/// The basic types information.
|
||||
/// </summary>
|
||||
public static readonly Lazy<Dictionary<Type, ExtendedTypeInfo>> BasicTypesInfo = new Lazy<Dictionary<Type, ExtendedTypeInfo>>(() =>
|
||||
new Dictionary<Type, ExtendedTypeInfo>
|
||||
{
|
||||
// Non-Nullables
|
||||
{typeof(DateTime), new ExtendedTypeInfo<DateTime>()},
|
||||
{typeof(byte), new ExtendedTypeInfo<byte>()},
|
||||
{typeof(sbyte), new ExtendedTypeInfo<sbyte>()},
|
||||
{typeof(int), new ExtendedTypeInfo<int>()},
|
||||
{typeof(uint), new ExtendedTypeInfo<uint>()},
|
||||
{typeof(short), new ExtendedTypeInfo<short>()},
|
||||
{typeof(ushort), new ExtendedTypeInfo<ushort>()},
|
||||
{typeof(long), new ExtendedTypeInfo<long>()},
|
||||
{typeof(ulong), new ExtendedTypeInfo<ulong>()},
|
||||
{typeof(float), new ExtendedTypeInfo<float>()},
|
||||
{typeof(double), new ExtendedTypeInfo<double>()},
|
||||
{typeof(char), new ExtendedTypeInfo<char>()},
|
||||
{typeof(bool), new ExtendedTypeInfo<bool>()},
|
||||
{typeof(decimal), new ExtendedTypeInfo<decimal>()},
|
||||
{typeof(Guid), new ExtendedTypeInfo<Guid>()},
|
||||
|
||||
// Strings is also considered a basic type (it's the only basic reference type)
|
||||
{typeof(string), new ExtendedTypeInfo<string>()},
|
||||
|
||||
// Nullables
|
||||
{typeof(DateTime?), new ExtendedTypeInfo<DateTime?>()},
|
||||
{typeof(byte?), new ExtendedTypeInfo<byte?>()},
|
||||
{typeof(sbyte?), new ExtendedTypeInfo<sbyte?>()},
|
||||
{typeof(int?), new ExtendedTypeInfo<int?>()},
|
||||
{typeof(uint?), new ExtendedTypeInfo<uint?>()},
|
||||
{typeof(short?), new ExtendedTypeInfo<short?>()},
|
||||
{typeof(ushort?), new ExtendedTypeInfo<ushort?>()},
|
||||
{typeof(long?), new ExtendedTypeInfo<long?>()},
|
||||
{typeof(ulong?), new ExtendedTypeInfo<ulong?>()},
|
||||
{typeof(float?), new ExtendedTypeInfo<float?>()},
|
||||
{typeof(double?), new ExtendedTypeInfo<double?>()},
|
||||
{typeof(char?), new ExtendedTypeInfo<char?>()},
|
||||
{typeof(bool?), new ExtendedTypeInfo<bool?>()},
|
||||
{typeof(decimal?), new ExtendedTypeInfo<decimal?>()},
|
||||
{typeof(Guid?), new ExtendedTypeInfo<Guid?>()},
|
||||
|
||||
// Additional Types
|
||||
{typeof(TimeSpan), new ExtendedTypeInfo<TimeSpan>()},
|
||||
{typeof(TimeSpan?), new ExtendedTypeInfo<TimeSpan?>()},
|
||||
{typeof(IPAddress), new ExtendedTypeInfo<IPAddress>()},
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Contains all basic types, including string, date time, and all of their nullable counterparts.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// All basic types.
|
||||
/// </value>
|
||||
public static IReadOnlyCollection<Type> AllBasicTypes { get; } = new ReadOnlyCollection<Type>(BasicTypesInfo.Value.Keys.ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Gets all numeric types including their nullable counterparts.
|
||||
/// Note that Booleans and Guids are not considered numeric types.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// All numeric types.
|
||||
/// </value>
|
||||
public static IReadOnlyCollection<Type> AllNumericTypes { get; } = new ReadOnlyCollection<Type>(
|
||||
BasicTypesInfo
|
||||
.Value
|
||||
.Where(kvp => kvp.Value.IsNumeric)
|
||||
.Select(kvp => kvp.Key).ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Gets all numeric types without their nullable counterparts.
|
||||
/// Note that Booleans and Guids are not considered numeric types.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// All numeric value types.
|
||||
/// </value>
|
||||
public static IReadOnlyCollection<Type> AllNumericValueTypes { get; } = new ReadOnlyCollection<Type>(
|
||||
BasicTypesInfo
|
||||
.Value
|
||||
.Where(kvp => kvp.Value.IsNumeric && !kvp.Value.IsNullableValueType)
|
||||
.Select(kvp => kvp.Key).ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Contains all basic value types. i.e. excludes string and nullables.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// All basic value types.
|
||||
/// </value>
|
||||
public static IReadOnlyCollection<Type> AllBasicValueTypes { get; } = new ReadOnlyCollection<Type>(
|
||||
BasicTypesInfo
|
||||
.Value
|
||||
.Where(kvp => kvp.Value.IsValueType)
|
||||
.Select(kvp => kvp.Key).ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Contains all basic value types including the string type. i.e. excludes nullables.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// All basic value and string types.
|
||||
/// </value>
|
||||
public static IReadOnlyCollection<Type> AllBasicValueAndStringTypes { get; } = new ReadOnlyCollection<Type>(
|
||||
BasicTypesInfo
|
||||
.Value
|
||||
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(string))
|
||||
.Select(kvp => kvp.Key).ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Gets all nullable value types. i.e. excludes string and all basic value types.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// All basic nullable value types.
|
||||
/// </value>
|
||||
public static IReadOnlyCollection<Type> AllBasicNullableValueTypes { get; } = new ReadOnlyCollection<Type>(
|
||||
BasicTypesInfo
|
||||
.Value
|
||||
.Where(kvp => kvp.Value.IsNullableValueType)
|
||||
.Select(kvp => kvp.Key).ToArray());
|
||||
}
|
||||
}
|
||||
39
Vendor/Swan.Lite-3.1.0/Definitions.cs
vendored
Normal file
39
Vendor/Swan.Lite-3.1.0/Definitions.cs
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains useful constants and definitions.
|
||||
/// </summary>
|
||||
public static partial class Definitions
|
||||
{
|
||||
/// <summary>
|
||||
/// The MS Windows codepage 1252 encoding used in some legacy scenarios
|
||||
/// such as default CSV text encoding from Excel.
|
||||
/// </summary>
|
||||
public static readonly Encoding Windows1252Encoding;
|
||||
|
||||
/// <summary>
|
||||
/// The encoding associated with the default ANSI code page in the operating
|
||||
/// system's regional and language settings.
|
||||
/// </summary>
|
||||
public static readonly Encoding CurrentAnsiEncoding;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="Definitions"/> class.
|
||||
/// </summary>
|
||||
static Definitions()
|
||||
{
|
||||
CurrentAnsiEncoding = Encoding.GetEncoding(default(int));
|
||||
try
|
||||
{
|
||||
Windows1252Encoding = Encoding.GetEncoding(1252);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore, the codepage is not available use default
|
||||
Windows1252Encoding = CurrentAnsiEncoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Vendor/Swan.Lite-3.1.0/Diagnostics/Benchmark.cs
vendored
Normal file
121
Vendor/Swan.Lite-3.1.0/Diagnostics/Benchmark.cs
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Swan.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple benchmarking class.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following code demonstrates how to create a simple benchmark.
|
||||
/// <code>
|
||||
/// namespace Examples.Benchmark.Simple
|
||||
/// {
|
||||
/// using Swan.Diagnostics;
|
||||
///
|
||||
/// public class SimpleBenchmark
|
||||
/// {
|
||||
/// public static void Main()
|
||||
/// {
|
||||
/// using (Benchmark.Start("Test"))
|
||||
/// {
|
||||
/// // do some logic in here
|
||||
/// }
|
||||
///
|
||||
/// // dump results into a string
|
||||
/// var results = Benchmark.Dump();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static partial class Benchmark
|
||||
{
|
||||
private static readonly object SyncLock = new object();
|
||||
private static readonly Dictionary<string, List<TimeSpan>> Measures = new Dictionary<string, List<TimeSpan>>();
|
||||
|
||||
/// <summary>
|
||||
/// Starts measuring with the given identifier.
|
||||
/// </summary>
|
||||
/// <param name="identifier">The identifier.</param>
|
||||
/// <returns>A disposable object that when disposed, adds a benchmark result.</returns>
|
||||
public static IDisposable Start(string identifier) => new BenchmarkUnit(identifier);
|
||||
|
||||
/// <summary>
|
||||
/// Outputs the benchmark statistics.
|
||||
/// </summary>
|
||||
/// <returns>A string containing human-readable statistics.</returns>
|
||||
public static string Dump()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
foreach (var kvp in Measures)
|
||||
{
|
||||
builder.Append($"BID: {kvp.Key,-30} | ")
|
||||
.Append($"CNT: {kvp.Value.Count,6} | ")
|
||||
.Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
||||
.Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
||||
.Append($"MIN: {kvp.Value.Min(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
||||
.Append(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the elapsed time of the given action as a TimeSpan
|
||||
/// This method uses a high precision Stopwatch if it is available.
|
||||
/// </summary>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <returns>
|
||||
/// A time interval that represents a specified time, where the specification is in units of ticks.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">target.</exception>
|
||||
public static TimeSpan BenchmarkAction(Action target)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
|
||||
var sw = Stopwatch.IsHighResolution ? new HighResolutionTimer() : new Stopwatch();
|
||||
|
||||
try
|
||||
{
|
||||
sw.Start();
|
||||
target.Invoke();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// swallow
|
||||
}
|
||||
finally
|
||||
{
|
||||
sw.Stop();
|
||||
}
|
||||
|
||||
return TimeSpan.FromTicks(sw.ElapsedTicks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified result to the given identifier.
|
||||
/// </summary>
|
||||
/// <param name="identifier">The identifier.</param>
|
||||
/// <param name="elapsed">The elapsed.</param>
|
||||
private static void Add(string identifier, TimeSpan elapsed)
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
if (Measures.ContainsKey(identifier) == false)
|
||||
Measures[identifier] = new List<TimeSpan>(1024 * 1024);
|
||||
|
||||
Measures[identifier].Add(elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Vendor/Swan.Lite-3.1.0/Diagnostics/BenchmarkUnit.cs
vendored
Normal file
50
Vendor/Swan.Lite-3.1.0/Diagnostics/BenchmarkUnit.cs
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Swan.Diagnostics
|
||||
{
|
||||
public static partial class Benchmark
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a disposable benchmark unit.
|
||||
/// </summary>
|
||||
/// <seealso cref="IDisposable" />
|
||||
private sealed class BenchmarkUnit : IDisposable
|
||||
{
|
||||
private readonly string _identifier;
|
||||
private bool _isDisposed; // To detect redundant calls
|
||||
private Stopwatch? _stopwatch = new Stopwatch();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BenchmarkUnit" /> class.
|
||||
/// </summary>
|
||||
/// <param name="identifier">The identifier.</param>
|
||||
public BenchmarkUnit(string identifier)
|
||||
{
|
||||
_identifier = identifier;
|
||||
_stopwatch?.Start();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool alsoManaged)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
if (alsoManaged)
|
||||
{
|
||||
Add(_identifier, _stopwatch?.Elapsed ?? default);
|
||||
_stopwatch?.Stop();
|
||||
}
|
||||
|
||||
_stopwatch = null;
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Vendor/Swan.Lite-3.1.0/Diagnostics/HighResolutionTimer.cs
vendored
Normal file
32
Vendor/Swan.Lite-3.1.0/Diagnostics/HighResolutionTimer.cs
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Swan.Diagnostics
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to a high-resolution, time measuring device.
|
||||
/// </summary>
|
||||
/// <seealso cref="Stopwatch" />
|
||||
public class HighResolutionTimer : Stopwatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HighResolutionTimer"/> class.
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">High-resolution timer not available.</exception>
|
||||
public HighResolutionTimer()
|
||||
{
|
||||
if (!IsHighResolution)
|
||||
throw new NotSupportedException("High-resolution timer not available");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of microseconds per timer tick.
|
||||
/// </summary>
|
||||
public static double MicrosecondsPerTick { get; } = 1000000d / Frequency;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the elapsed microseconds.
|
||||
/// </summary>
|
||||
public long ElapsedMicroseconds => (long)(ElapsedTicks * MicrosecondsPerTick);
|
||||
}
|
||||
}
|
||||
171
Vendor/Swan.Lite-3.1.0/EnumHelper.cs
vendored
Normal file
171
Vendor/Swan.Lite-3.1.0/EnumHelper.cs
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Swan.Collections;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide Enumerations helpers with internal cache.
|
||||
/// </summary>
|
||||
public class EnumHelper
|
||||
: SingletonBase<CollectionCacheRepository<Tuple<string, object>>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all the names and enumerators from a specific Enum type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
|
||||
/// <returns>A tuple of enumerator names and their value stored for the specified type.</returns>
|
||||
public static IEnumerable<Tuple<string, object>> Retrieve<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
|
||||
.Cast<object>()
|
||||
.Select(item => Tuple.Create(Enum.GetName(t, item), item)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached items with the enum item value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enumeration.</typeparam>
|
||||
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
|
||||
/// <returns>
|
||||
/// A collection of Type/Tuple pairs
|
||||
/// that represents items with the enum item value.
|
||||
/// </returns>
|
||||
public static IEnumerable<Tuple<int, string>> GetItemsWithValue<T>(bool humanize = true)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return Retrieve<T>()
|
||||
.Select(x => Tuple.Create((int) x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enum.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
|
||||
/// <returns>
|
||||
/// A list of values in the flag.
|
||||
/// </returns>
|
||||
public static IEnumerable<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
return Retrieve<TEnum>()
|
||||
.Select(x => (int) x.Item2)
|
||||
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
||||
.Where(x => (x & value) == x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enum.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
|
||||
/// <returns>
|
||||
/// A list of values in the flag.
|
||||
/// </returns>
|
||||
public static IEnumerable<long> GetFlagValues<TEnum>(long value, bool ignoreZero = false)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
return Retrieve<TEnum>()
|
||||
.Select(x => (long) x.Item2)
|
||||
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
||||
.Where(x => (x & value) == x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enum.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
|
||||
/// <returns>
|
||||
/// A list of values in the flag.
|
||||
/// </returns>
|
||||
public static IEnumerable<byte> GetFlagValues<TEnum>(byte value, bool ignoreZero = false)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
return Retrieve<TEnum>()
|
||||
.Select(x => (byte) x.Item2)
|
||||
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
||||
.Where(x => (x & value) == x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag names.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enum.</typeparam>
|
||||
/// <param name="value">the value.</param>
|
||||
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
|
||||
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
|
||||
/// <returns>
|
||||
/// A list of flag names.
|
||||
/// </returns>
|
||||
public static IEnumerable<string> GetFlagNames<TEnum>(int value, bool ignoreZero = false, bool humanize = true)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
return Retrieve<TEnum>()
|
||||
.When(() => ignoreZero, q => q.Where(f => (int) f.Item2 != 0))
|
||||
.Where(x => ((int) x.Item2 & value) == (int) x.Item2)
|
||||
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag names.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enum.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
|
||||
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
|
||||
/// <returns>
|
||||
/// A list of flag names.
|
||||
/// </returns>
|
||||
public static IEnumerable<string> GetFlagNames<TEnum>(long value, bool ignoreZero = false, bool humanize = true)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
return Retrieve<TEnum>()
|
||||
.When(() => ignoreZero, q => q.Where(f => (long) f.Item2 != 0))
|
||||
.Where(x => ((long) x.Item2 & value) == (long) x.Item2)
|
||||
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag names.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enum.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
|
||||
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
|
||||
/// <returns>
|
||||
/// A list of flag names.
|
||||
/// </returns>
|
||||
public static IEnumerable<string> GetFlagNames<TEnum>(byte value, bool ignoreZero = false, bool humanize = true)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
return Retrieve<TEnum>()
|
||||
.When(() => ignoreZero, q => q.Where(f => (byte) f.Item2 != 0))
|
||||
.Where(x => ((byte) x.Item2 & value) == (byte) x.Item2)
|
||||
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached items with the enum item index.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enumeration.</typeparam>
|
||||
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
|
||||
/// <returns>
|
||||
/// A collection of Type/Tuple pairs that represents items with the enum item value.
|
||||
/// </returns>
|
||||
public static IEnumerable<Tuple<int, string>> GetItemsWithIndex<T>(bool humanize = true)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
return Retrieve<T>()
|
||||
.Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Vendor/Swan.Lite-3.1.0/Enums.cs
vendored
Normal file
44
Vendor/Swan.Lite-3.1.0/Enums.cs
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of Operating Systems.
|
||||
/// </summary>
|
||||
public enum OperatingSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown OS
|
||||
/// </summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// Windows
|
||||
/// </summary>
|
||||
Windows,
|
||||
|
||||
/// <summary>
|
||||
/// UNIX/Linux
|
||||
/// </summary>
|
||||
Unix,
|
||||
|
||||
/// <summary>
|
||||
/// macOS (OSX)
|
||||
/// </summary>
|
||||
Osx,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines Endianness, big or little.
|
||||
/// </summary>
|
||||
public enum Endianness
|
||||
{
|
||||
/// <summary>
|
||||
/// In big endian, you store the most significant byte in the smallest address.
|
||||
/// </summary>
|
||||
Big,
|
||||
|
||||
/// <summary>
|
||||
/// In little endian, you store the least significant byte in the smallest address.
|
||||
/// </summary>
|
||||
Little,
|
||||
}
|
||||
}
|
||||
511
Vendor/Swan.Lite-3.1.0/Extensions.ByteArrays.cs
vendored
Normal file
511
Vendor/Swan.Lite-3.1.0/Extensions.ByteArrays.cs
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides various extension methods for byte arrays and streams.
|
||||
/// </summary>
|
||||
public static class ByteArrayExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an array of bytes to its lower-case, hexadecimal representation.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes.</param>
|
||||
/// <param name="addPrefix">if set to <c>true</c> add the 0x prefix tot he output.</param>
|
||||
/// <returns>
|
||||
/// The specified string instance; no actual conversion is performed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">bytes.</exception>
|
||||
public static string ToLowerHex(this byte[] bytes, bool addPrefix = false)
|
||||
=> ToHex(bytes, addPrefix, "x2");
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes to its upper-case, hexadecimal representation.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes.</param>
|
||||
/// <param name="addPrefix">if set to <c>true</c> [add prefix].</param>
|
||||
/// <returns>
|
||||
/// The specified string instance; no actual conversion is performed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">bytes.</exception>
|
||||
public static string ToUpperHex(this byte[] bytes, bool addPrefix = false)
|
||||
=> ToHex(bytes, addPrefix, "X2");
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes to a sequence of dash-separated, hexadecimal,
|
||||
/// uppercase characters.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes.</param>
|
||||
/// <returns>
|
||||
/// A string of hexadecimal pairs separated by hyphens, where each pair represents
|
||||
/// the corresponding element in value; for example, "7F-2C-4A-00".
|
||||
/// </returns>
|
||||
public static string ToDashedHex(this byte[] bytes) => BitConverter.ToString(bytes);
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes to a base-64 encoded string.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes.</param>
|
||||
/// <returns>A <see cref="string" /> converted from an array of bytes.</returns>
|
||||
public static string ToBase64(this byte[] bytes) => Convert.ToBase64String(bytes);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a set of hexadecimal characters (uppercase or lowercase)
|
||||
/// to a byte array. String length must be a multiple of 2 and
|
||||
/// any prefix (such as 0x) has to be avoided for this to work properly.
|
||||
/// </summary>
|
||||
/// <param name="this">The hexadecimal.</param>
|
||||
/// <returns>
|
||||
/// A byte array containing the results of encoding the specified set of characters.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">hex.</exception>
|
||||
public static byte[] ConvertHexadecimalToBytes(this string @this)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(@this))
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
return Enumerable
|
||||
.Range(0, @this.Length / 2)
|
||||
.Select(x => Convert.ToByte(@this.Substring(x * 2, 2), 16))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit value at the given offset.
|
||||
/// </summary>
|
||||
/// <param name="this">The b.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <returns>
|
||||
/// Bit value at the given offset.
|
||||
/// </returns>
|
||||
public static byte GetBitValueAt(this byte @this, byte offset, byte length = 1) => (byte)((@this >> offset) & ~(0xff << length));
|
||||
|
||||
/// <summary>
|
||||
/// Sets the bit value at the given offset.
|
||||
/// </summary>
|
||||
/// <param name="this">The b.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>Bit value at the given offset.</returns>
|
||||
public static byte SetBitValueAt(this byte @this, byte offset, byte length, byte value)
|
||||
{
|
||||
var mask = ~(0xff << length);
|
||||
var valueAt = (byte)(value & mask);
|
||||
|
||||
return (byte)((valueAt << offset) | (@this & ~(mask << offset)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the bit value at the given offset.
|
||||
/// </summary>
|
||||
/// <param name="this">The b.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>Bit value at the given offset.</returns>
|
||||
public static byte SetBitValueAt(this byte @this, byte offset, byte value) => @this.SetBitValueAt(offset, 1, value);
|
||||
|
||||
/// <summary>
|
||||
/// Splits a byte array delimited by the specified sequence of bytes.
|
||||
/// Each individual element in the result will contain the split sequence terminator if it is found to be delimited by it.
|
||||
/// For example if you split [1,2,3,4] by a sequence of [2,3] this method will return a list with 2 byte arrays, one containing [1,2,3] and the
|
||||
/// second one containing 4. Use the Trim extension methods to remove terminator sequences.
|
||||
/// </summary>
|
||||
/// <param name="this">The buffer.</param>
|
||||
/// <param name="offset">The offset at which to start splitting bytes. Any bytes before this will be discarded.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>
|
||||
/// A byte array containing the results the specified sequence of bytes.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// buffer
|
||||
/// or
|
||||
/// sequence.
|
||||
/// </exception>
|
||||
public static List<byte[]> Split(this byte[] @this, int offset, params byte[] sequence)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
if (sequence == null)
|
||||
throw new ArgumentNullException(nameof(sequence));
|
||||
|
||||
var seqOffset = offset.Clamp(0, @this.Length - 1);
|
||||
|
||||
var result = new List<byte[]>();
|
||||
|
||||
while (seqOffset < @this.Length)
|
||||
{
|
||||
var separatorStartIndex = @this.GetIndexOf(sequence, seqOffset);
|
||||
|
||||
if (separatorStartIndex >= 0)
|
||||
{
|
||||
var item = new byte[separatorStartIndex - seqOffset + sequence.Length];
|
||||
Array.Copy(@this, seqOffset, item, 0, item.Length);
|
||||
result.Add(item);
|
||||
seqOffset += item.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = new byte[@this.Length - seqOffset];
|
||||
Array.Copy(@this, seqOffset, item, 0, item.Length);
|
||||
result.Add(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the specified buffer, byte by byte.
|
||||
/// </summary>
|
||||
/// <param name="this">The buffer.</param>
|
||||
/// <returns>
|
||||
/// A byte array containing the results of encoding the specified set of characters.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">this</exception>
|
||||
public static byte[] DeepClone(this byte[] @this)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
var result = new byte[@this.Length];
|
||||
Array.Copy(@this, result, @this.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified sequence from the start of the buffer if the buffer begins with such sequence.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>
|
||||
/// A new trimmed byte array.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||
public static byte[] TrimStart(this byte[] buffer, params byte[] sequence)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (buffer.StartsWith(sequence) == false)
|
||||
return buffer.DeepClone();
|
||||
|
||||
var result = new byte[buffer.Length - sequence.Length];
|
||||
Array.Copy(buffer, sequence.Length, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified sequence from the end of the buffer if the buffer ends with such sequence.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>
|
||||
/// A byte array containing the results of encoding the specified set of characters.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||
public static byte[] TrimEnd(this byte[] buffer, params byte[] sequence)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (buffer.EndsWith(sequence) == false)
|
||||
return buffer.DeepClone();
|
||||
|
||||
var result = new byte[buffer.Length - sequence.Length];
|
||||
Array.Copy(buffer, 0, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified sequence from the end and the start of the buffer
|
||||
/// if the buffer ends and/or starts with such sequence.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
||||
public static byte[] Trim(this byte[] buffer, params byte[] sequence)
|
||||
{
|
||||
var trimStart = buffer.TrimStart(sequence);
|
||||
return trimStart.TrimEnd(sequence);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified buffer ends with the given sequence of bytes.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>
|
||||
/// True if the specified buffer is ends; otherwise, false.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||
public static bool EndsWith(this byte[] buffer, params byte[] sequence)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
var startIndex = buffer.Length - sequence.Length;
|
||||
return buffer.GetIndexOf(sequence, startIndex) == startIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified buffer starts with the given sequence of bytes.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns><c>true</c> if the specified buffer starts; otherwise, <c>false</c>.</returns>
|
||||
public static bool StartsWith(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the buffer contains the specified sequence.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [contains] [the specified sequence]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool Contains(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the buffer exactly matches, byte by byte the specified sequence.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [is equal to] [the specified sequence]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||
public static bool IsEqualTo(this byte[] buffer, params byte[] sequence)
|
||||
{
|
||||
if (ReferenceEquals(buffer, sequence))
|
||||
return true;
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
return buffer.Length == sequence.Length && buffer.GetIndexOf(sequence) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first instance of the matched sequence based on the given offset.
|
||||
/// If no matches are found then this method returns -1.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>The index of the sequence.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// buffer
|
||||
/// or
|
||||
/// sequence.
|
||||
/// </exception>
|
||||
public static int GetIndexOf(this byte[] buffer, byte[] sequence, int offset = 0)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
if (sequence == null)
|
||||
throw new ArgumentNullException(nameof(sequence));
|
||||
if (sequence.Length == 0)
|
||||
return -1;
|
||||
if (sequence.Length > buffer.Length)
|
||||
return -1;
|
||||
|
||||
var seqOffset = offset < 0 ? 0 : offset;
|
||||
|
||||
var matchedCount = 0;
|
||||
for (var i = seqOffset; i < buffer.Length; i++)
|
||||
{
|
||||
if (buffer[i] == sequence[matchedCount])
|
||||
matchedCount++;
|
||||
else
|
||||
matchedCount = 0;
|
||||
|
||||
if (matchedCount == sequence.Length)
|
||||
return i - (matchedCount - 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the Memory Stream with the specified buffer.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>
|
||||
/// The same MemoryStream instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// stream
|
||||
/// or
|
||||
/// buffer.
|
||||
/// </exception>
|
||||
public static MemoryStream Append(this MemoryStream stream, byte[] buffer)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the Memory Stream with the specified buffer.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>
|
||||
/// Block of bytes to the current stream using data read from a buffer.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte> buffer) => Append(stream, buffer?.ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Appends the Memory Stream with the specified set of buffers.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="buffers">The buffers.</param>
|
||||
/// <returns>
|
||||
/// Block of bytes to the current stream using data read from a buffer.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">buffers.</exception>
|
||||
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte[]> buffers)
|
||||
{
|
||||
if (buffers == null)
|
||||
throw new ArgumentNullException(nameof(buffers));
|
||||
|
||||
foreach (var buffer in buffers)
|
||||
Append(stream, buffer);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes into text with the specified encoding.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <returns>A <see cref="string" /> that contains the results of decoding the specified sequence of bytes.</returns>
|
||||
public static string ToText(this IEnumerable<byte> buffer, Encoding encoding) =>
|
||||
encoding == null
|
||||
? throw new ArgumentNullException(nameof(encoding))
|
||||
: encoding.GetString(buffer.ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes into text with UTF8 encoding.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>A <see cref="string" /> that contains the results of decoding the specified sequence of bytes.</returns>
|
||||
public static string ToText(this IEnumerable<byte> buffer) => buffer.ToText(Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the bytes asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="bufferLength">Length of the buffer.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// A byte array containing the results of encoding the specified set of characters.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">stream.</exception>
|
||||
public static async Task<byte[]> ReadBytesAsync(this Stream stream, long length, int bufferLength, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
using var dest = new MemoryStream();
|
||||
|
||||
try
|
||||
{
|
||||
var buff = new byte[bufferLength];
|
||||
while (length > 0)
|
||||
{
|
||||
if (length < bufferLength)
|
||||
bufferLength = (int)length;
|
||||
|
||||
var read = await stream.ReadAsync(buff, 0, bufferLength, cancellationToken).ConfigureAwait(false);
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
dest.Write(buff, 0, read);
|
||||
length -= read;
|
||||
}
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return dest.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the bytes asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// A byte array containing the results of encoding the specified set of characters.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">stream.</exception>
|
||||
public static async Task<byte[]> ReadBytesAsync(this Stream stream, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
var buff = new byte[length];
|
||||
var offset = 0;
|
||||
|
||||
try
|
||||
{
|
||||
while (length > 0)
|
||||
{
|
||||
var read = await stream.ReadAsync(buff, offset, length, cancellationToken).ConfigureAwait(false);
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
offset += read;
|
||||
length -= read;
|
||||
}
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return new ArraySegment<byte>(buff, 0, offset).ToArray();
|
||||
}
|
||||
|
||||
private static string ToHex(byte[] bytes, bool addPrefix, string format)
|
||||
{
|
||||
if (bytes == null)
|
||||
throw new ArgumentNullException(nameof(bytes));
|
||||
|
||||
var sb = new StringBuilder(bytes.Length * 2);
|
||||
|
||||
foreach (var item in bytes)
|
||||
sb.Append(item.ToString(format, CultureInfo.InvariantCulture));
|
||||
|
||||
return $"{(addPrefix ? "0x" : string.Empty)}{sb}";
|
||||
}
|
||||
}
|
||||
}
|
||||
234
Vendor/Swan.Lite-3.1.0/Extensions.Dates.cs
vendored
Normal file
234
Vendor/Swan.Lite-3.1.0/Extensions.Dates.cs
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="DateTime"/>.
|
||||
/// </summary>
|
||||
public static class DateExtensions
|
||||
{
|
||||
private static readonly Dictionary<string, int> DateRanges = new Dictionary<string, int>()
|
||||
{
|
||||
{ "minute", 59},
|
||||
{ "hour", 23},
|
||||
{ "dayOfMonth", 31},
|
||||
{ "month", 12},
|
||||
{ "dayOfWeek", 6},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts the date to a YYYY-MM-DD string.
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="DateTime"/> on which this method is called.</param>
|
||||
/// <returns>The concatenation of date.Year, date.Month and date.Day.</returns>
|
||||
public static string ToSortableDate(this DateTime @this)
|
||||
=> $"{@this.Year:0000}-{@this.Month:00}-{@this.Day:00}";
|
||||
|
||||
/// <summary>
|
||||
/// Converts the date to a YYYY-MM-DD HH:II:SS string.
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="DateTime"/> on which this method is called.</param>
|
||||
/// <returns>The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second.</returns>
|
||||
public static string ToSortableDateTime(this DateTime @this)
|
||||
=> $"{@this.Year:0000}-{@this.Month:00}-{@this.Day:00} {@this.Hour:00}:{@this.Minute:00}:{@this.Second:00}";
|
||||
|
||||
/// <summary>
|
||||
/// Parses a YYYY-MM-DD and optionally it time part, HH:II:SS into a DateTime.
|
||||
/// </summary>
|
||||
/// <param name="this">The sortable date.</param>
|
||||
/// <returns>
|
||||
/// A new instance of the DateTime structure to
|
||||
/// the specified year, month, day, hour, minute and second.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">sortableDate.</exception>
|
||||
/// <exception cref="Exception">
|
||||
/// Represents errors that occur during application execution.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Unable to parse sortable date and time. - sortableDate.
|
||||
/// </exception>
|
||||
public static DateTime ToDateTime(this string @this)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(@this))
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
var hour = 0;
|
||||
var minute = 0;
|
||||
var second = 0;
|
||||
|
||||
var dateTimeParts = @this.Split(' ');
|
||||
|
||||
try
|
||||
{
|
||||
if (dateTimeParts.Length != 1 && dateTimeParts.Length != 2)
|
||||
throw new Exception();
|
||||
|
||||
var dateParts = dateTimeParts[0].Split('-');
|
||||
if (dateParts.Length != 3) throw new Exception();
|
||||
|
||||
var year = int.Parse(dateParts[0]);
|
||||
var month = int.Parse(dateParts[1]);
|
||||
var day = int.Parse(dateParts[2]);
|
||||
|
||||
if (dateTimeParts.Length > 1)
|
||||
{
|
||||
var timeParts = dateTimeParts[1].Split(':');
|
||||
if (timeParts.Length != 3) throw new Exception();
|
||||
|
||||
hour = int.Parse(timeParts[0]);
|
||||
minute = int.Parse(timeParts[1]);
|
||||
second = int.Parse(timeParts[2]);
|
||||
}
|
||||
|
||||
return new DateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ArgumentException("Unable to parse sortable date and time.", nameof(@this));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a date range.
|
||||
/// </summary>
|
||||
/// <param name="startDate">The start date.</param>
|
||||
/// <param name="endDate">The end date.</param>
|
||||
/// <returns>
|
||||
/// A sequence of integral numbers within a specified date's range.
|
||||
/// </returns>
|
||||
public static IEnumerable<DateTime> DateRange(this DateTime startDate, DateTime endDate)
|
||||
=> Enumerable.Range(0, (endDate - startDate).Days + 1).Select(d => startDate.AddDays(d));
|
||||
|
||||
/// <summary>
|
||||
/// Rounds up a date to match a timespan.
|
||||
/// </summary>
|
||||
/// <param name="date">The datetime.</param>
|
||||
/// <param name="timeSpan">The timespan to match.</param>
|
||||
/// <returns>
|
||||
/// A new instance of the DateTime structure to the specified datetime and timespan ticks.
|
||||
/// </returns>
|
||||
public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan)
|
||||
=> new DateTime(((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks) * timeSpan.Ticks);
|
||||
|
||||
/// <summary>
|
||||
/// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC).
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="DateTime"/> on which this method is called.</param>
|
||||
/// <returns>Seconds since Unix epoch.</returns>
|
||||
public static long ToUnixEpochDate(this DateTime @this) => new DateTimeOffset(@this).ToUniversalTime().ToUnixTimeSeconds();
|
||||
|
||||
/// <summary>
|
||||
/// Compares a Date to another and returns a <c>DateTimeSpan</c>.
|
||||
/// </summary>
|
||||
/// <param name="dateStart">The date start.</param>
|
||||
/// <param name="dateEnd">The date end.</param>
|
||||
/// <returns>A DateTimeSpan with the Years, Months, Days, Hours, Minutes, Seconds and Milliseconds between the dates.</returns>
|
||||
public static DateTimeSpan GetDateTimeSpan(this DateTime dateStart, DateTime dateEnd)
|
||||
=> DateTimeSpan.CompareDates(dateStart, dateEnd);
|
||||
|
||||
/// <summary>
|
||||
/// Compare the Date elements(Months, Days, Hours, Minutes).
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="DateTime"/> on which this method is called.</param>
|
||||
/// <param name="minute">The minute (0-59).</param>
|
||||
/// <param name="hour">The hour. (0-23).</param>
|
||||
/// <param name="dayOfMonth">The day of month. (1-31).</param>
|
||||
/// <param name="month">The month. (1-12).</param>
|
||||
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
|
||||
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
|
||||
public static bool AsCronCanRun(this DateTime @this, int? minute = null, int? hour = null, int? dayOfMonth = null, int? month = null, int? dayOfWeek = null)
|
||||
{
|
||||
var results = new List<bool?>
|
||||
{
|
||||
GetElementParts(minute, @this.Minute),
|
||||
GetElementParts(hour, @this.Hour),
|
||||
GetElementParts(dayOfMonth, @this.Day),
|
||||
GetElementParts(month, @this.Month),
|
||||
GetElementParts(dayOfWeek, (int) @this.DayOfWeek),
|
||||
};
|
||||
|
||||
return results.Any(x => x != false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the Date elements(Months, Days, Hours, Minutes).
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="DateTime"/> on which this method is called.</param>
|
||||
/// <param name="minute">The minute (0-59).</param>
|
||||
/// <param name="hour">The hour. (0-23).</param>
|
||||
/// <param name="dayOfMonth">The day of month. (1-31).</param>
|
||||
/// <param name="month">The month. (1-12).</param>
|
||||
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
|
||||
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
|
||||
public static bool AsCronCanRun(this DateTime @this, string minute = "*", string hour = "*", string dayOfMonth = "*", string month = "*", string dayOfWeek = "*")
|
||||
{
|
||||
var results = new List<bool?>
|
||||
{
|
||||
GetElementParts(minute, nameof(minute), @this.Minute),
|
||||
GetElementParts(hour, nameof(hour), @this.Hour),
|
||||
GetElementParts(dayOfMonth, nameof(dayOfMonth), @this.Day),
|
||||
GetElementParts(month, nameof(month), @this.Month),
|
||||
GetElementParts(dayOfWeek, nameof(dayOfWeek), (int) @this.DayOfWeek),
|
||||
};
|
||||
|
||||
return results.Any(x => x != false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="DateTime"/> to the <see href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#RFC1123">RFC1123 format</see>.
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="DateTime"/> on which this method is called.</param>
|
||||
/// <returns>The string representation of <paramref name="this"/> according to <see href="https://tools.ietf.org/html/rfc1123#page-54">RFC1123</see>.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If <paramref name="this"/> is not a UTC date / time, its UTC equivalent is converted, leaving <paramref name="this"/> unchanged.</para>
|
||||
/// </remarks>
|
||||
public static string ToRfc1123String(this DateTime @this)
|
||||
=> @this.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture);
|
||||
|
||||
private static bool? GetElementParts(int? status, int value) => status.HasValue ? status.Value == value : (bool?) null;
|
||||
|
||||
private static bool? GetElementParts(string parts, string type, int value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(parts) || parts == "*")
|
||||
return null;
|
||||
|
||||
if (parts.Contains(","))
|
||||
{
|
||||
return parts.Split(',').Select(int.Parse).Contains(value);
|
||||
}
|
||||
|
||||
var stop = DateRanges[type];
|
||||
|
||||
if (parts.Contains("/"))
|
||||
{
|
||||
var multiple = int.Parse(parts.Split('/').Last());
|
||||
var start = type == "dayOfMonth" || type == "month" ? 1 : 0;
|
||||
|
||||
for (var i = start; i <= stop; i += multiple)
|
||||
if (i == value) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parts.Contains("-"))
|
||||
{
|
||||
var range = parts.Split('-');
|
||||
var start = int.Parse(range.First());
|
||||
stop = Math.Max(stop, int.Parse(range.Last()));
|
||||
|
||||
if ((type == "dayOfMonth" || type == "month") && start == 0)
|
||||
start = 1;
|
||||
|
||||
for (var i = start; i <= stop; i++)
|
||||
if (i == value) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return int.Parse(parts) == value;
|
||||
}
|
||||
}
|
||||
}
|
||||
86
Vendor/Swan.Lite-3.1.0/Extensions.Dictionaries.cs
vendored
Normal file
86
Vendor/Swan.Lite-3.1.0/Extensions.Dictionaries.cs
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods.
|
||||
/// </summary>
|
||||
public static partial class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value if exists or default.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <param name="dict">The dictionary.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <returns>
|
||||
/// The value of the provided key or default.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">dict.</exception>
|
||||
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException(nameof(dict));
|
||||
|
||||
return dict.ContainsKey(key) ? dict[key] : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a key/value pair to the Dictionary if the key does not already exist.
|
||||
/// If the value is null, the key will not be updated.
|
||||
/// Based on <c>ConcurrentDictionary.GetOrAdd</c> method.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <param name="dict">The dictionary.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="valueFactory">The value factory.</param>
|
||||
/// <returns>
|
||||
/// The value for the key.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// dict
|
||||
/// or
|
||||
/// valueFactory.
|
||||
/// </exception>
|
||||
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException(nameof(dict));
|
||||
|
||||
if (valueFactory == null)
|
||||
throw new ArgumentNullException(nameof(valueFactory));
|
||||
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
var value = valueFactory(key);
|
||||
if (Equals(value, default)) return default;
|
||||
dict[key] = value;
|
||||
}
|
||||
|
||||
return dict[key];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the item action for each element in the Dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <param name="dict">The dictionary.</param>
|
||||
/// <param name="itemAction">The item action.</param>
|
||||
/// <exception cref="ArgumentNullException">dict.</exception>
|
||||
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException(nameof(dict));
|
||||
|
||||
foreach (var kvp in dict)
|
||||
{
|
||||
itemAction(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Vendor/Swan.Lite-3.1.0/Extensions.Enumerable.cs
vendored
Normal file
43
Vendor/Swan.Lite-3.1.0/Extensions.Enumerable.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains extension methods for types implementing IEnumerable<TSource>
|
||||
/// </summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This method returns the <see cref="Enumerable.Union{TSource}">Union</see>
|
||||
/// of all non-null parameters.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
|
||||
/// <param name="this">An IEnumerable<TSource> whose distinct elements forms the first set of the union.</param>
|
||||
/// <param name="second">An IEnumerable<TSource> whose distinct elements forms the second set of the union.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="IEnumerable{TSource}" /> that contains the elements from non-null input sequences, excluding duplicates.
|
||||
/// </returns>
|
||||
public static IEnumerable<TSource> UnionExcludingNulls<TSource>(this IEnumerable<TSource> @this, IEnumerable<TSource> second)
|
||||
=> Enumerable.Union(
|
||||
@this ?? Enumerable.Empty<TSource>(),
|
||||
second ?? Enumerable.Empty<TSource>());
|
||||
|
||||
/// <summary>
|
||||
/// This method returns the <see cref="Enumerable.Union{TSource}">Union</see>
|
||||
/// of all non-null parameters.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
|
||||
/// <param name="this">An IEnumerable<TSource> whose distinct elements forms the first set of the union.</param>
|
||||
/// <param name="second">An IEnumerable<TSource> whose distinct elements forms the second set of the union.</param>
|
||||
/// <param name="comparer">The IEqualityComparer<TSource> to compare values.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="IEnumerable{TSource}" /> that contains the elements from non-null input sequences, excluding duplicates.
|
||||
/// </returns>
|
||||
public static IEnumerable<TSource> UnionExcludingNulls<TSource>(this IEnumerable<TSource> @this, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
|
||||
=> Enumerable.Union(
|
||||
@this ?? Enumerable.Empty<TSource>(),
|
||||
second ?? Enumerable.Empty<TSource>(),
|
||||
comparer);
|
||||
}
|
||||
}
|
||||
66
Vendor/Swan.Lite-3.1.0/Extensions.Exceptions.cs
vendored
Normal file
66
Vendor/Swan.Lite-3.1.0/Extensions.Exceptions.cs
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="Exception"/>.
|
||||
/// </summary>
|
||||
public static class ExceptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a value that tells whether an <see cref="Exception"/> is of a type that
|
||||
/// we better not catch and ignore.
|
||||
/// </summary>
|
||||
/// <param name="this">The exception being thrown.</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="this"/> is a critical exception;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
public static bool IsCriticalException(this Exception @this)
|
||||
=> @this.IsCriticalExceptionCore()
|
||||
|| (@this.InnerException?.IsCriticalException() ?? false)
|
||||
|| (@this is AggregateException aggregateException && aggregateException.InnerExceptions.Any(e => e.IsCriticalException()));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that tells whether an <see cref="Exception"/> is of a type that
|
||||
/// will likely cause application failure.
|
||||
/// </summary>
|
||||
/// <param name="this">The exception being thrown.</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="this"/> is a fatal exception;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
public static bool IsFatalException(this Exception @this)
|
||||
=> @this.IsFatalExceptionCore()
|
||||
|| (@this.InnerException?.IsFatalException() ?? false)
|
||||
|| (@this is AggregateException aggregateException && aggregateException.InnerExceptions.Any(e => e.IsFatalException()));
|
||||
|
||||
/// <summary>
|
||||
/// <para>Rethrows an already-thrown exception, preserving the stack trace of the original throw.</para>
|
||||
/// <para>This method does not return; its return type is an exception type so it can be used
|
||||
/// with <c>throw</c> semantics, e.g.: <c>throw ex.RethrowPreservingStackTrace();</c>,
|
||||
/// to let static code analysis tools that it throws instead of returning.</para>
|
||||
/// </summary>
|
||||
/// <param name="this">The exception to rethrow.</param>
|
||||
/// <returns>This method should never return; if it does, it is an indication of an internal error,
|
||||
/// so it returns an instance of <see cref="InternalErrorException"/>.</returns>
|
||||
public static InternalErrorException RethrowPreservingStackTrace(this Exception @this)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(@this).Throw();
|
||||
return SelfCheck.Failure("Reached unreachable code.");
|
||||
}
|
||||
|
||||
private static bool IsCriticalExceptionCore(this Exception @this)
|
||||
=> IsFatalExceptionCore(@this)
|
||||
|| @this is AppDomainUnloadedException
|
||||
|| @this is BadImageFormatException
|
||||
|| @this is CannotUnloadAppDomainException
|
||||
|| @this is InvalidProgramException
|
||||
|| @this is NullReferenceException;
|
||||
|
||||
private static bool IsFatalExceptionCore(this Exception @this)
|
||||
=> @this is StackOverflowException
|
||||
|| @this is OutOfMemoryException
|
||||
|| @this is ThreadAbortException
|
||||
|| @this is AccessViolationException;
|
||||
}
|
||||
}
|
||||
179
Vendor/Swan.Lite-3.1.0/Extensions.Functional.cs
vendored
Normal file
179
Vendor/Swan.Lite-3.1.0/Extensions.Functional.cs
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Functional programming extension methods.
|
||||
/// </summary>
|
||||
public static class FunctionalExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Whens the specified condition.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of IQueryable.</typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="condition">The condition.</param>
|
||||
/// <param name="fn">The function.</param>
|
||||
/// <returns>
|
||||
/// The IQueryable.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// this
|
||||
/// or
|
||||
/// condition
|
||||
/// or
|
||||
/// fn.
|
||||
/// </exception>
|
||||
public static IQueryable<T> When<T>(
|
||||
this IQueryable<T> list,
|
||||
Func<bool> condition,
|
||||
Func<IQueryable<T>, IQueryable<T>> fn)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
if (condition == null)
|
||||
throw new ArgumentNullException(nameof(condition));
|
||||
|
||||
if (fn == null)
|
||||
throw new ArgumentNullException(nameof(fn));
|
||||
|
||||
return condition() ? fn(list) : list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whens the specified condition.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of IEnumerable.</typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="condition">The condition.</param>
|
||||
/// <param name="fn">The function.</param>
|
||||
/// <returns>
|
||||
/// The IEnumerable.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// this
|
||||
/// or
|
||||
/// condition
|
||||
/// or
|
||||
/// fn.
|
||||
/// </exception>
|
||||
public static IEnumerable<T> When<T>(
|
||||
this IEnumerable<T> list,
|
||||
Func<bool> condition,
|
||||
Func<IEnumerable<T>, IEnumerable<T>> fn)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
if (condition == null)
|
||||
throw new ArgumentNullException(nameof(condition));
|
||||
|
||||
if (fn == null)
|
||||
throw new ArgumentNullException(nameof(fn));
|
||||
|
||||
return condition() ? fn(list) : list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the value when the condition is true.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of IList element.</typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="condition">The condition.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>
|
||||
/// The IList.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// this
|
||||
/// or
|
||||
/// condition
|
||||
/// or
|
||||
/// value.
|
||||
/// </exception>
|
||||
public static IList<T> AddWhen<T>(
|
||||
this IList<T> list,
|
||||
Func<bool> condition,
|
||||
Func<T> value)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
if (condition == null)
|
||||
throw new ArgumentNullException(nameof(condition));
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (condition())
|
||||
list.Add(value());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the value when the condition is true.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of IList element.</typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="condition">if set to <c>true</c> [condition].</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>
|
||||
/// The IList.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">list.</exception>
|
||||
public static IList<T> AddWhen<T>(
|
||||
this IList<T> list,
|
||||
bool condition,
|
||||
T value)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
if (condition)
|
||||
list.Add(value);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the range when the condition is true.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of List element.</typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="condition">The condition.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>
|
||||
/// The List.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// this
|
||||
/// or
|
||||
/// condition
|
||||
/// or
|
||||
/// value.
|
||||
/// </exception>
|
||||
public static List<T> AddRangeWhen<T>(
|
||||
this List<T> list,
|
||||
Func<bool> condition,
|
||||
Func<IEnumerable<T>> value)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
if (condition == null)
|
||||
throw new ArgumentNullException(nameof(condition));
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (condition())
|
||||
list.AddRange(value());
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Vendor/Swan.Lite-3.1.0/Extensions.IEnumerable.cs
vendored
Normal file
25
Vendor/Swan.Lite-3.1.0/Extensions.IEnumerable.cs
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains extensions methods for IEnumerable
|
||||
/// </summary>
|
||||
public static class IEnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This method make an union of two IEnumerables
|
||||
/// validation when some of them is null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">T</typeparam>
|
||||
/// <param name="this">The this.</param>
|
||||
/// <param name="second">The second.</param>
|
||||
/// <returns> The Union </returns>
|
||||
public static IEnumerable<T> UnionNull<T>(this IEnumerable<T> @this, IEnumerable<T> second)
|
||||
{
|
||||
if (@this == null) return second;
|
||||
return second == null ? @this : @this.Union(second);
|
||||
}
|
||||
}
|
||||
}
|
||||
175
Vendor/Swan.Lite-3.1.0/Extensions.IPropertyProxy.cs
vendored
Normal file
175
Vendor/Swan.Lite-3.1.0/Extensions.IPropertyProxy.cs
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to access <see cref="IPropertyProxy"/> objects
|
||||
/// associated with types. Getters and setters are stored as delegates compiled
|
||||
/// from constructed lambda expressions for fast access.
|
||||
/// </summary>
|
||||
public static class PropertyProxyExtensions
|
||||
{
|
||||
private static readonly object SyncLock = new object();
|
||||
private static readonly Dictionary<Type, Dictionary<string, IPropertyProxy>> ProxyCache =
|
||||
new Dictionary<Type, Dictionary<string, IPropertyProxy>>(32);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property proxies associated with a given type.
|
||||
/// </summary>
|
||||
/// <param name="t">The type to retrieve property proxies from.</param>
|
||||
/// <returns>A dictionary with property names as keys and <see cref="IPropertyProxy"/> objects as values.</returns>
|
||||
public static Dictionary<string, IPropertyProxy> PropertyProxies(this Type t)
|
||||
{
|
||||
if (t == null)
|
||||
throw new ArgumentNullException(nameof(t));
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
if (ProxyCache.ContainsKey(t))
|
||||
return ProxyCache[t];
|
||||
|
||||
var properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
||||
var result = new Dictionary<string, IPropertyProxy>(properties.Length, StringComparer.InvariantCultureIgnoreCase);
|
||||
foreach (var propertyInfo in properties)
|
||||
result[propertyInfo.Name] = new PropertyInfoProxy(t, propertyInfo);
|
||||
|
||||
ProxyCache[t] = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property proxies associated with the provided instance type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The instance type.</typeparam>
|
||||
/// <param name="obj">The instance.</param>
|
||||
/// <returns>A dictionary with property names as keys and <see cref="IPropertyProxy"/> objects as values.</returns>
|
||||
public static Dictionary<string, IPropertyProxy> PropertyProxies<T>(this T obj) =>
|
||||
(obj?.GetType() ?? typeof(T)).PropertyProxies();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property proxy given the property name.
|
||||
/// </summary>
|
||||
/// <param name="t">The associated type.</param>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns>The associated <see cref="IPropertyProxy"/></returns>
|
||||
public static IPropertyProxy PropertyProxy(this Type t, string propertyName)
|
||||
{
|
||||
var proxies = t.PropertyProxies();
|
||||
return proxies.ContainsKey(propertyName) ? proxies[propertyName] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property proxy given the property name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of instance to extract proxies from.</typeparam>
|
||||
/// <param name="obj">The instance to extract proxies from.</param>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns>The associated <see cref="IPropertyProxy"/></returns>
|
||||
public static IPropertyProxy PropertyProxy<T>(this T obj, string propertyName)
|
||||
{
|
||||
if (propertyName == null)
|
||||
throw new ArgumentNullException(nameof(propertyName));
|
||||
var proxies = (obj?.GetType() ?? typeof(T)).PropertyProxies();
|
||||
|
||||
return proxies?.ContainsKey(propertyName) == true ? proxies[propertyName] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property proxy given the property name as an expression.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The instance type.</typeparam>
|
||||
/// <typeparam name="V">The property value type.</typeparam>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="propertyExpression">The property expression.</param>
|
||||
/// <returns>The associated <see cref="IPropertyProxy"/></returns>
|
||||
public static IPropertyProxy PropertyProxy<T, V>(this T obj, Expression<Func<T, V>> propertyExpression)
|
||||
{
|
||||
if (propertyExpression == null)
|
||||
throw new ArgumentNullException(nameof(propertyExpression));
|
||||
|
||||
var proxies = (obj?.GetType() ?? typeof(T)).PropertyProxies();
|
||||
var propertyName = propertyExpression.PropertyName();
|
||||
return proxies?.ContainsKey(propertyName) == true ? proxies[propertyName] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the property value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get property proxies from.</typeparam>
|
||||
/// <typeparam name="V">The type of the property.</typeparam>
|
||||
/// <param name="obj">The instance.</param>
|
||||
/// <param name="propertyExpression">The property expression.</param>
|
||||
/// <returns>
|
||||
/// The value obtained from the associated <see cref="IPropertyProxy" />
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">obj.</exception>
|
||||
public static V ReadProperty<T, V>(this T obj, Expression<Func<T, V>> propertyExpression)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
var proxy = obj.PropertyProxy(propertyExpression);
|
||||
return (V)(proxy?.GetValue(obj));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the property value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get property proxies from.</typeparam>
|
||||
/// <param name="obj">The instance.</param>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns>
|
||||
/// The value obtained from the associated <see cref="IPropertyProxy" />
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">obj.</exception>
|
||||
public static object? ReadProperty<T>(this T obj, string propertyName)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
var proxy = obj.PropertyProxy(propertyName);
|
||||
return proxy?.GetValue(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get property proxies from.</typeparam>
|
||||
/// <typeparam name="TV">The type of the property.</typeparam>
|
||||
/// <param name="obj">The instance.</param>
|
||||
/// <param name="propertyExpression">The property expression.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public static void WriteProperty<T, TV>(this T obj, Expression<Func<T, TV>> propertyExpression, TV value)
|
||||
{
|
||||
var proxy = obj.PropertyProxy(propertyExpression);
|
||||
proxy?.SetValue(obj, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property value using the property proxy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get property proxies from.</typeparam>
|
||||
/// <param name="obj">The instance.</param>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public static void WriteProperty<T>(this T obj, string propertyName, object? value)
|
||||
{
|
||||
var proxy = obj.PropertyProxy(propertyName);
|
||||
proxy?.SetValue(obj, value);
|
||||
}
|
||||
|
||||
private static string PropertyName<T, TV>(this Expression<Func<T, TV>> propertyExpression)
|
||||
{
|
||||
var memberExpression = !(propertyExpression.Body is MemberExpression)
|
||||
? (propertyExpression.Body as UnaryExpression).Operand as MemberExpression
|
||||
: propertyExpression.Body as MemberExpression;
|
||||
|
||||
return memberExpression.Member.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
335
Vendor/Swan.Lite-3.1.0/Extensions.Reflection.cs
vendored
Normal file
335
Vendor/Swan.Lite-3.1.0/Extensions.Reflection.cs
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides various extension methods for Reflection and Types.
|
||||
/// </summary>
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all types within an assembly in a safe manner.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly.</param>
|
||||
/// <returns>
|
||||
/// Array of Type objects representing the types specified by an assembly.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">assembly.</exception>
|
||||
public static IEnumerable<Type> GetAllTypes(this Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
return e.Types.Where(t => t != null);
|
||||
}
|
||||
}
|
||||
|
||||
#region Type Extensions
|
||||
|
||||
/// <summary>
|
||||
/// The closest programmatic equivalent of default(T).
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>
|
||||
/// Default value of this type.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">type.</exception>
|
||||
public static object? GetDefault(this Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
return type.IsValueType ? Activator.CreateInstance(type) : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this type is compatible with ICollection.
|
||||
/// </summary>
|
||||
/// <param name="sourceType">The type.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">sourceType.</exception>
|
||||
public static bool IsCollection(this Type sourceType)
|
||||
{
|
||||
if (sourceType == null)
|
||||
throw new ArgumentNullException(nameof(sourceType));
|
||||
|
||||
return sourceType != typeof(string) &&
|
||||
typeof(IEnumerable).IsAssignableFrom(sourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a method from a type given the method name, binding flags, generic types and parameter types.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the source.</param>
|
||||
/// <param name="bindingFlags">The binding flags.</param>
|
||||
/// <param name="methodName">Name of the method.</param>
|
||||
/// <param name="genericTypes">The generic types.</param>
|
||||
/// <param name="parameterTypes">The parameter types.</param>
|
||||
/// <returns>
|
||||
/// An object that represents the method with the specified name.
|
||||
/// </returns>
|
||||
/// <exception cref="System.Reflection.AmbiguousMatchException">
|
||||
/// The exception that is thrown when binding to a member results in more than one member matching the
|
||||
/// binding criteria. This class cannot be inherited.
|
||||
/// </exception>
|
||||
public static MethodInfo GetMethod(
|
||||
this Type type,
|
||||
BindingFlags bindingFlags,
|
||||
string methodName,
|
||||
Type[] genericTypes,
|
||||
Type[] parameterTypes)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (methodName == null)
|
||||
throw new ArgumentNullException(nameof(methodName));
|
||||
|
||||
if (genericTypes == null)
|
||||
throw new ArgumentNullException(nameof(genericTypes));
|
||||
|
||||
if (parameterTypes == null)
|
||||
throw new ArgumentNullException(nameof(parameterTypes));
|
||||
|
||||
var methods = type
|
||||
.GetMethods(bindingFlags)
|
||||
.Where(mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal))
|
||||
.Where(mi => mi.ContainsGenericParameters)
|
||||
.Where(mi => mi.GetGenericArguments().Length == genericTypes.Length)
|
||||
.Where(mi => mi.GetParameters().Length == parameterTypes.Length)
|
||||
.Select(mi => mi.MakeGenericMethod(genericTypes))
|
||||
.Where(mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes))
|
||||
.ToList();
|
||||
|
||||
return methods.Count > 1 ? throw new AmbiguousMatchException() : methods.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is i enumerable request].
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">type.</exception>
|
||||
public static bool IsIEnumerable(this Type type)
|
||||
=> type == null
|
||||
? throw new ArgumentNullException(nameof(type))
|
||||
: type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse using the basic types.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">type</exception>
|
||||
public static bool TryParseBasicType(this Type type, object value, out object? result)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (type != typeof(bool))
|
||||
return TryParseBasicType(type, value.ToStringInvariant(), out result);
|
||||
|
||||
result = value.ToBoolean();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse using the basic types.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">type</exception>
|
||||
public static bool TryParseBasicType(this Type type, string value, out object? result)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
result = null;
|
||||
|
||||
return Definitions.BasicTypesInfo.Value.ContainsKey(type) && Definitions.BasicTypesInfo.Value[type].TryParse(value, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries the type of the set basic value to a property.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The property information.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="target">The object.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">propertyInfo.</exception>
|
||||
public static bool TrySetBasicType(this PropertyInfo propertyInfo, object value, object target)
|
||||
{
|
||||
if (propertyInfo == null)
|
||||
throw new ArgumentNullException(nameof(propertyInfo));
|
||||
|
||||
try
|
||||
{
|
||||
if (propertyInfo.PropertyType.TryParseBasicType(value, out var propertyValue))
|
||||
{
|
||||
propertyInfo.SetValue(target, propertyValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// swallow
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries the type of the set to an array a basic type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="target">The array.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">type</exception>
|
||||
public static bool TrySetArrayBasicType(this Type type, object value, Array target, int index)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
target.SetValue(null, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.TryParseBasicType(value, out var propertyValue))
|
||||
{
|
||||
target.SetValue(propertyValue, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
target.SetValue(null, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// swallow
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to set a property array with another array.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The property.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">propertyInfo.</exception>
|
||||
public static bool TrySetArray(this PropertyInfo propertyInfo, IEnumerable<object>? value, object obj)
|
||||
{
|
||||
if (propertyInfo == null)
|
||||
throw new ArgumentNullException(nameof(propertyInfo));
|
||||
|
||||
var elementType = propertyInfo.PropertyType.GetElementType();
|
||||
|
||||
if (elementType == null || value == null)
|
||||
return false;
|
||||
|
||||
var targetArray = Array.CreateInstance(elementType, value.Count());
|
||||
|
||||
var i = 0;
|
||||
|
||||
foreach (var sourceElement in value)
|
||||
{
|
||||
var result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
|
||||
|
||||
if (!result) return false;
|
||||
}
|
||||
|
||||
propertyInfo.SetValue(obj, targetArray);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a string to a boolean.
|
||||
/// </summary>
|
||||
/// <param name="str">The string.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the string represents a valid truly value, otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool ToBoolean(this string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToBoolean(str);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Convert.ToBoolean(Convert.ToInt32(str));
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a object to a boolean.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the string represents a valid truly value, otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool ToBoolean(this object value) => value.ToStringInvariant().ToBoolean();
|
||||
}
|
||||
}
|
||||
400
Vendor/Swan.Lite-3.1.0/Extensions.Strings.cs
vendored
Normal file
400
Vendor/Swan.Lite-3.1.0/Extensions.Strings.cs
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Swan.Formatters;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// String related extension methods.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
#region Private Declarations
|
||||
|
||||
private const RegexOptions StandardRegexOptions =
|
||||
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant;
|
||||
|
||||
private static readonly string[] ByteSuffixes = { "B", "KB", "MB", "GB", "TB" };
|
||||
|
||||
private static readonly Lazy<Regex> SplitLinesRegex =
|
||||
new Lazy<Regex>(() => new Regex("\r\n|\r|\n", StandardRegexOptions));
|
||||
|
||||
private static readonly Lazy<Regex> UnderscoreRegex =
|
||||
new Lazy<Regex>(() => new Regex(@"_", StandardRegexOptions));
|
||||
|
||||
private static readonly Lazy<Regex> CamelCaseRegEx =
|
||||
new Lazy<Regex>(() => new Regex(@"[a-z][A-Z]", StandardRegexOptions));
|
||||
|
||||
private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() => m =>
|
||||
{
|
||||
var x = m.ToString();
|
||||
return x[0] + " " + x.Substring(1, x.Length - 1);
|
||||
});
|
||||
|
||||
private static readonly Lazy<string[]> InvalidFilenameChars =
|
||||
new Lazy<string[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToStringInvariant()).ToArray());
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the given item
|
||||
/// It tries to use InvariantCulture if the ToString(IFormatProvider)
|
||||
/// overload exists.
|
||||
/// </summary>
|
||||
/// <param name="this">The item.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
public static string ToStringInvariant(this object? @this)
|
||||
{
|
||||
if (@this == null)
|
||||
return string.Empty;
|
||||
|
||||
var itemType = @this.GetType();
|
||||
|
||||
if (itemType == typeof(string))
|
||||
return @this as string ?? string.Empty;
|
||||
|
||||
return Definitions.BasicTypesInfo.Value.ContainsKey(itemType)
|
||||
? Definitions.BasicTypesInfo.Value[itemType].ToStringInvariant(@this)
|
||||
: @this.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the given item
|
||||
/// It tries to use InvariantCulture if the ToString(IFormatProvider)
|
||||
/// overload exists.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get the string.</typeparam>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
public static string ToStringInvariant<T>(this T item)
|
||||
=> typeof(string) == typeof(T) ? item as string ?? string.Empty : ToStringInvariant(item as object);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the control characters from a string except for those specified.
|
||||
/// </summary>
|
||||
/// <param name="value">The input.</param>
|
||||
/// <param name="excludeChars">When specified, these characters will not be removed.</param>
|
||||
/// <returns>
|
||||
/// A string that represents the current object.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">input.</exception>
|
||||
public static string RemoveControlChars(this string value, params char[]? excludeChars)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (excludeChars == null)
|
||||
excludeChars = Array.Empty<char>();
|
||||
|
||||
return new string(value
|
||||
.Where(c => char.IsControl(c) == false || excludeChars.Contains(c))
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs JSON string representing this object.
|
||||
/// </summary>
|
||||
/// <param name="this">The object.</param>
|
||||
/// <param name="format">if set to <c>true</c> format the output.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
public static string ToJson(this object @this, bool format = true) =>
|
||||
@this == null ? string.Empty : Json.Serialize(@this, format);
|
||||
|
||||
/// <summary>
|
||||
/// Returns text representing the properties of the specified object in a human-readable format.
|
||||
/// While this method is fairly expensive computationally speaking, it provides an easy way to
|
||||
/// examine objects.
|
||||
/// </summary>
|
||||
/// <param name="this">The object.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
public static string Stringify(this object @this)
|
||||
{
|
||||
if (@this == null)
|
||||
return "(null)";
|
||||
|
||||
try
|
||||
{
|
||||
var jsonText = Json.Serialize(@this, false, "$type");
|
||||
var jsonData = Json.Deserialize(jsonText);
|
||||
|
||||
return new HumanizeJson(jsonData, 0).GetResult();
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
return @this.ToStringInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a section of the string, inclusive of both, the start and end indexes.
|
||||
/// This behavior is unlike JavaScript's Slice behavior where the end index is non-inclusive
|
||||
/// If the string is null it returns an empty string.
|
||||
/// </summary>
|
||||
/// <param name="this">The string.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <param name="endIndex">The end index.</param>
|
||||
/// <returns>Retrieves a substring from this instance.</returns>
|
||||
public static string Slice(this string @this, int startIndex, int endIndex)
|
||||
{
|
||||
if (@this == null)
|
||||
return string.Empty;
|
||||
|
||||
var end = endIndex.Clamp(startIndex, @this.Length - 1);
|
||||
|
||||
return startIndex >= end ? string.Empty : @this.Substring(startIndex, (end - startIndex) + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a part of the string clamping the length and startIndex parameters to safe values.
|
||||
/// If the string is null it returns an empty string. This is basically just a safe version
|
||||
/// of string.Substring.
|
||||
/// </summary>
|
||||
/// <param name="this">The string.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <returns>Retrieves a substring from this instance.</returns>
|
||||
public static string SliceLength(this string @this, int startIndex, int length)
|
||||
{
|
||||
if (@this == null)
|
||||
return string.Empty;
|
||||
|
||||
var start = startIndex.Clamp(0, @this.Length - 1);
|
||||
var len = length.Clamp(0, @this.Length - start);
|
||||
|
||||
return len == 0 ? string.Empty : @this.Substring(start, len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the specified text into r, n or rn separated lines.
|
||||
/// </summary>
|
||||
/// <param name="this">The text.</param>
|
||||
/// <returns>
|
||||
/// An array whose elements contain the substrings from this instance
|
||||
/// that are delimited by one or more characters in separator.
|
||||
/// </returns>
|
||||
public static string[] ToLines(this string @this) =>
|
||||
@this == null ? Array.Empty<string>() : SplitLinesRegex.Value.Split(@this);
|
||||
|
||||
/// <summary>
|
||||
/// Humanizes (make more human-readable) an identifier-style string
|
||||
/// in either camel case or snake case. For example, CamelCase will be converted to
|
||||
/// Camel Case and Snake_Case will be converted to Snake Case.
|
||||
/// </summary>
|
||||
/// <param name="value">The identifier-style string.</param>
|
||||
/// <returns>A <see cref="string" /> humanized.</returns>
|
||||
public static string Humanize(this string value)
|
||||
{
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
|
||||
var returnValue = UnderscoreRegex.Value.Replace(value, " ");
|
||||
returnValue = CamelCaseRegEx.Value.Replace(returnValue, SplitCamelCaseString.Value);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Humanizes (make more human-readable) an boolean.
|
||||
/// </summary>
|
||||
/// <param name="value">if set to <c>true</c> [value].</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current boolean.</returns>
|
||||
public static string Humanize(this bool value) => value ? "Yes" : "No";
|
||||
|
||||
/// <summary>
|
||||
/// Humanizes (make more human-readable) the specified value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
public static string Humanize(this object value) =>
|
||||
value switch
|
||||
{
|
||||
string stringValue => stringValue.Humanize(),
|
||||
bool boolValue => boolValue.Humanize(),
|
||||
_ => value.Stringify()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Indents the specified multi-line text with the given amount of leading spaces
|
||||
/// per line.
|
||||
/// </summary>
|
||||
/// <param name="value">The text.</param>
|
||||
/// <param name="spaces">The spaces.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
public static string Indent(this string value, int spaces = 4)
|
||||
{
|
||||
if (value == null) value = string.Empty;
|
||||
if (spaces <= 0) return value;
|
||||
|
||||
var lines = value.ToLines();
|
||||
var builder = new StringBuilder();
|
||||
var indentStr = new string(' ', spaces);
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
builder.AppendLine($"{indentStr}{line}");
|
||||
}
|
||||
|
||||
return builder.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line and column number (i.e. not index) of the
|
||||
/// specified character index. Useful to locate text in a multi-line
|
||||
/// string the same way a text editor does.
|
||||
/// Please not that the tuple contains first the line number and then the
|
||||
/// column number.
|
||||
/// </summary>
|
||||
/// <param name="value">The string.</param>
|
||||
/// <param name="charIndex">Index of the character.</param>
|
||||
/// <returns>A 2-tuple whose value is (item1, item2).</returns>
|
||||
public static Tuple<int, int> TextPositionAt(this string value, int charIndex)
|
||||
{
|
||||
if (value == null)
|
||||
return Tuple.Create(0, 0);
|
||||
|
||||
var index = charIndex.Clamp(0, value.Length - 1);
|
||||
|
||||
var lineIndex = 0;
|
||||
var colNumber = 0;
|
||||
|
||||
for (var i = 0; i <= index; i++)
|
||||
{
|
||||
if (value[i] == '\n')
|
||||
{
|
||||
lineIndex++;
|
||||
colNumber = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[i] != '\r')
|
||||
colNumber++;
|
||||
}
|
||||
|
||||
return Tuple.Create(lineIndex + 1, colNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the file name system safe.
|
||||
/// </summary>
|
||||
/// <param name="value">The s.</param>
|
||||
/// <returns>
|
||||
/// A string with a safe file name.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">s.</exception>
|
||||
public static string ToSafeFilename(this string value) =>
|
||||
value == null
|
||||
? throw new ArgumentNullException(nameof(value))
|
||||
: InvalidFilenameChars.Value
|
||||
.Aggregate(value, (current, c) => current.Replace(c, string.Empty))
|
||||
.Slice(0, 220);
|
||||
|
||||
/// <summary>
|
||||
/// Formats a long into the closest bytes string.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes length.</param>
|
||||
/// <returns>
|
||||
/// The string representation of the current Byte object, formatted as specified by the format parameter.
|
||||
/// </returns>
|
||||
public static string FormatBytes(this long bytes) => ((ulong)bytes).FormatBytes();
|
||||
|
||||
/// <summary>
|
||||
/// Formats a long into the closest bytes string.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes length.</param>
|
||||
/// <returns>
|
||||
/// A copy of format in which the format items have been replaced by the string
|
||||
/// representations of the corresponding arguments.
|
||||
/// </returns>
|
||||
public static string FormatBytes(this ulong bytes)
|
||||
{
|
||||
int i;
|
||||
double dblSByte = bytes;
|
||||
|
||||
for (i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024)
|
||||
{
|
||||
dblSByte = bytes / 1024.0;
|
||||
}
|
||||
|
||||
return $"{dblSByte:0.##} {ByteSuffixes[i]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates the specified value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="maximumLength">The maximum length.</param>
|
||||
/// <returns>
|
||||
/// Retrieves a substring from this instance.
|
||||
/// The substring starts at a specified character position and has a specified length.
|
||||
/// </returns>
|
||||
public static string? Truncate(this string value, int maximumLength) =>
|
||||
Truncate(value, maximumLength, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Truncates the specified value and append the omission last.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="maximumLength">The maximum length.</param>
|
||||
/// <param name="omission">The omission.</param>
|
||||
/// <returns>
|
||||
/// Retrieves a substring from this instance.
|
||||
/// The substring starts at a specified character position and has a specified length.
|
||||
/// </returns>
|
||||
public static string? Truncate(this string value, int maximumLength, string omission)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
return value.Length > maximumLength
|
||||
? value.Substring(0, maximumLength) + (omission ?? string.Empty)
|
||||
: value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="string"/> contains any of characters in
|
||||
/// the specified array of <see cref="char"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="value">
|
||||
/// A <see cref="string"/> to test.
|
||||
/// </param>
|
||||
/// <param name="chars">
|
||||
/// An array of <see cref="char"/> that contains characters to find.
|
||||
/// </param>
|
||||
public static bool Contains(this string value, params char[] chars) =>
|
||||
chars != null &&
|
||||
(chars.Length == 0 || (!string.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1));
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all chars in a string.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="replaceValue">The replace value.</param>
|
||||
/// <param name="chars">The chars.</param>
|
||||
/// <returns>The string with the characters replaced.</returns>
|
||||
public static string ReplaceAll(this string value, string replaceValue, params char[] chars) =>
|
||||
chars.Aggregate(value, (current, c) => current.Replace(new string(new[] { c }), replaceValue));
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex character to an integer. Return -1 if char is something
|
||||
/// other than a hex char.
|
||||
/// </summary>
|
||||
/// <param name="value">The c.</param>
|
||||
/// <returns>Converted integer.</returns>
|
||||
public static int Hex2Int(this char value) =>
|
||||
value >= '0' && value <= '9'
|
||||
? value - '0'
|
||||
: value >= 'A' && value <= 'F'
|
||||
? value - 'A' + 10
|
||||
: value >= 'a' && value <= 'f'
|
||||
? value - 'a' + 10
|
||||
: -1;
|
||||
}
|
||||
}
|
||||
85
Vendor/Swan.Lite-3.1.0/Extensions.Tasks.cs
vendored
Normal file
85
Vendor/Swan.Lite-3.1.0/Extensions.Tasks.cs
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="Task"/> and <see cref="Task{TResult}"/>.
|
||||
/// </summary>
|
||||
public static class TaskExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Suspends execution until the specified <see cref="Task"/> is completed.</para>
|
||||
/// <para>This method operates similarly to the <see langword="await"/> C# operator,
|
||||
/// but is meant to be called from a non-<see langword="async"/> method.</para>
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="Task"/> on which this method is called.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
public static void Await(this Task @this)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
@this.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Suspends execution until the specified <see cref="Task"/> is completed
|
||||
/// and returns its result.</para>
|
||||
/// <para>This method operates similarly to the <see langword="await"/> C# operator,
|
||||
/// but is meant to be called from a non-<see langword="async"/> method.</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type of the task's result.</typeparam>
|
||||
/// <param name="this">The <see cref="Task{TResult}"/> on which this method is called.</param>
|
||||
/// <returns>The result of <paramref name="this"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
public static TResult Await<TResult>(this Task<TResult> @this)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
return @this.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Suspends execution until the specified <see cref="Task"/> is completed.</para>
|
||||
/// <para>This method operates similarly to the <see langword="await" /> C# operator,
|
||||
/// but is meant to be called from a non-<see langword="async" /> method.</para>
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="Task" /> on which this method is called.</param>
|
||||
/// <param name="continueOnCapturedContext">If set to <see langword="true"/>,
|
||||
/// attempts to marshal the continuation back to the original context captured.
|
||||
/// This parameter has the same effect as calling the <see cref="Task.ConfigureAwait"/>
|
||||
/// method.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
public static void Await(this Task @this, bool continueOnCapturedContext)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
@this.ConfigureAwait(continueOnCapturedContext).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Suspends execution until the specified <see cref="Task"/> is completed
|
||||
/// and returns its result.</para>
|
||||
/// <para>This method operates similarly to the <see langword="await"/> C# operator,
|
||||
/// but is meant to be called from a non-<see langword="async"/> method.</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type of the task's result.</typeparam>
|
||||
/// <param name="this">The <see cref="Task{TResult}"/> on which this method is called.</param>
|
||||
/// <param name="continueOnCapturedContext">If set to <see langword="true"/>,
|
||||
/// attempts to marshal the continuation back to the original context captured.
|
||||
/// This parameter has the same effect as calling the <see cref="Task.ConfigureAwait"/>
|
||||
/// method.</param>
|
||||
/// <returns>The result of <paramref name="this"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
public static TResult Await<TResult>(this Task<TResult> @this, bool continueOnCapturedContext)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
return @this.ConfigureAwait(continueOnCapturedContext).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
165
Vendor/Swan.Lite-3.1.0/Extensions.ValueTypes.cs
vendored
Normal file
165
Vendor/Swan.Lite-3.1.0/Extensions.ValueTypes.cs
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides various extension methods for value types and structs.
|
||||
/// </summary>
|
||||
public static class ValueTypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Clamps the specified value between the minimum and the maximum.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of value to clamp.</typeparam>
|
||||
/// <param name="this">The value.</param>
|
||||
/// <param name="min">The minimum.</param>
|
||||
/// <param name="max">The maximum.</param>
|
||||
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
|
||||
public static T Clamp<T>(this T @this, T min, T max)
|
||||
where T : struct, IComparable
|
||||
{
|
||||
if (@this.CompareTo(min) < 0) return min;
|
||||
|
||||
return @this.CompareTo(max) > 0 ? max : @this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the specified value between the minimum and the maximum.
|
||||
/// </summary>
|
||||
/// <param name="this">The value.</param>
|
||||
/// <param name="min">The minimum.</param>
|
||||
/// <param name="max">The maximum.</param>
|
||||
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
|
||||
public static int Clamp(this int @this, int min, int max)
|
||||
=> @this < min ? min : (@this > max ? max : @this);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified value is between a minimum and a maximum value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of value to check.</typeparam>
|
||||
/// <param name="this">The value.</param>
|
||||
/// <param name="min">The minimum.</param>
|
||||
/// <param name="max">The maximum.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool IsBetween<T>(this T @this, T min, T max)
|
||||
where T : struct, IComparable
|
||||
{
|
||||
return @this.CompareTo(min) >= 0 && @this.CompareTo(max) <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes into the given struct type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
||||
/// <param name="this">The data.</param>
|
||||
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
|
||||
public static T ToStruct<T>(this byte[] @this)
|
||||
where T : struct
|
||||
{
|
||||
return @this == null ? throw new ArgumentNullException(nameof(@this)) : ToStruct<T>(@this, 0, @this.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of bytes into the given struct type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
||||
/// <param name="this">The data.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <returns>
|
||||
/// A managed object containing the data pointed to by the ptr parameter.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">data.</exception>
|
||||
public static T ToStruct<T>(this byte[] @this, int offset, int length)
|
||||
where T : struct
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
var buffer = new byte[length];
|
||||
Array.Copy(@this, offset, buffer, 0, buffer.Length);
|
||||
var handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
|
||||
|
||||
try
|
||||
{
|
||||
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a struct to an array of bytes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
||||
/// <param name="this">The object.</param>
|
||||
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
||||
public static byte[] ToBytes<T>(this T @this)
|
||||
where T : struct
|
||||
{
|
||||
var data = new byte[Marshal.SizeOf(@this)];
|
||||
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(@this, handle.AddrOfPinnedObject(), false);
|
||||
return GetStructBytes<T>(data);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps the endianness of an unsigned long to an unsigned integer.
|
||||
/// </summary>
|
||||
/// <param name="this">The bytes contained in a long.</param>
|
||||
/// <returns>
|
||||
/// A 32-bit unsigned integer equivalent to the ulong
|
||||
/// contained in longBytes.
|
||||
/// </returns>
|
||||
public static uint SwapEndianness(this ulong @this)
|
||||
=> (uint)(((@this & 0x000000ff) << 24) +
|
||||
((@this & 0x0000ff00) << 8) +
|
||||
((@this & 0x00ff0000) >> 8) +
|
||||
((@this & 0xff000000) >> 24));
|
||||
|
||||
private static byte[] GetStructBytes<T>(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
|
||||
var fields = typeof(T).GetTypeInfo()
|
||||
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var endian = AttributeCache.DefaultCache.Value.RetrieveOne<StructEndiannessAttribute, T>();
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false))
|
||||
continue;
|
||||
|
||||
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
|
||||
var length = Marshal.SizeOf(field.FieldType);
|
||||
|
||||
endian ??= AttributeCache.DefaultCache.Value.RetrieveOne<StructEndiannessAttribute>(field);
|
||||
|
||||
if (endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
|
||||
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian))
|
||||
{
|
||||
Array.Reverse(data, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
279
Vendor/Swan.Lite-3.1.0/Extensions.cs
vendored
Normal file
279
Vendor/Swan.Lite-3.1.0/Extensions.cs
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
using Swan.Formatters;
|
||||
using Swan.Mappers;
|
||||
using Swan.Reflection;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods.
|
||||
/// </summary>
|
||||
public static partial class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Iterates over the public, instance, readable properties of the source and
|
||||
/// tries to write a compatible value to a public, instance, writable property in the destination.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the source.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="ignoreProperties">The ignore properties.</param>
|
||||
/// <returns>
|
||||
/// Number of properties that was copied successful.
|
||||
/// </returns>
|
||||
public static int CopyPropertiesTo<T>(this T source, object target, params string[]? ignoreProperties)
|
||||
where T : class =>
|
||||
ObjectMapper.Copy(source, target, GetCopyableProperties(target), ignoreProperties);
|
||||
|
||||
/// <summary>
|
||||
/// Iterates over the public, instance, readable properties of the source and
|
||||
/// tries to write a compatible value to a public, instance, writable property in the destination.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="target">The destination.</param>
|
||||
/// <param name="propertiesToCopy">Properties to copy.</param>
|
||||
/// <returns>
|
||||
/// Number of properties that were successfully copied.
|
||||
/// </returns>
|
||||
public static int CopyOnlyPropertiesTo(this object source, object target, params string[]? propertiesToCopy)
|
||||
=> ObjectMapper.Copy(source, target, propertiesToCopy);
|
||||
|
||||
/// <summary>
|
||||
/// Copies the properties to new instance of T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The new object type.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="ignoreProperties">The ignore properties.</param>
|
||||
/// <returns>
|
||||
/// The specified type with properties copied.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">source.</exception>
|
||||
public static T CopyPropertiesToNew<T>(this object source, string[]? ignoreProperties = null)
|
||||
where T : class
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
var target = Activator.CreateInstance<T>();
|
||||
ObjectMapper.Copy(source, target, GetCopyableProperties(target), ignoreProperties);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the only properties to new instance of T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Object Type.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="propertiesToCopy">The properties to copy.</param>
|
||||
/// <returns>
|
||||
/// The specified type with properties copied.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">source.</exception>
|
||||
public static T CopyOnlyPropertiesToNew<T>(this object source, params string[] propertiesToCopy)
|
||||
where T : class
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
var target = Activator.CreateInstance<T>();
|
||||
ObjectMapper.Copy(source, target, propertiesToCopy);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates over the keys of the source and tries to write a compatible value to a public,
|
||||
/// instance, writable property in the destination.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="ignoreKeys">The ignore keys.</param>
|
||||
/// <returns>Number of properties that was copied successful.</returns>
|
||||
public static int CopyKeyValuePairTo(
|
||||
this IDictionary<string, object> source,
|
||||
object? target,
|
||||
params string[] ignoreKeys) =>
|
||||
source == null
|
||||
? throw new ArgumentNullException(nameof(source))
|
||||
: ObjectMapper.Copy(source, target, null, ignoreKeys);
|
||||
|
||||
/// <summary>
|
||||
/// Iterates over the keys of the source and tries to write a compatible value to a public,
|
||||
/// instance, writable property in the destination.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Object Type.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="ignoreKeys">The ignore keys.</param>
|
||||
/// <returns>
|
||||
/// The specified type with properties copied.
|
||||
/// </returns>
|
||||
public static T CopyKeyValuePairToNew<T>(
|
||||
this IDictionary<string, object> source,
|
||||
params string[] ignoreKeys)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
var target = Activator.CreateInstance<T>();
|
||||
source.CopyKeyValuePairTo(target, ignoreKeys);
|
||||
return target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the specified action.
|
||||
/// </summary>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <param name="retryInterval">The retry interval.</param>
|
||||
/// <param name="retryCount">The retry count.</param>
|
||||
public static void Retry(
|
||||
this Action action,
|
||||
TimeSpan retryInterval = default,
|
||||
int retryCount = 3)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
|
||||
Retry<object?>(() =>
|
||||
{
|
||||
action();
|
||||
return null;
|
||||
},
|
||||
retryInterval,
|
||||
retryCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the specified action.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the source.</typeparam>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <param name="retryInterval">The retry interval.</param>
|
||||
/// <param name="retryCount">The retry count.</param>
|
||||
/// <returns>
|
||||
/// The return value of the method that this delegate encapsulates.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">action.</exception>
|
||||
/// <exception cref="AggregateException">Represents one or many errors that occur during application execution.</exception>
|
||||
public static T Retry<T>(
|
||||
this Func<T> action,
|
||||
TimeSpan retryInterval = default,
|
||||
int retryCount = 3)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
|
||||
if (retryInterval == default)
|
||||
retryInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
for (var retry = 0; retry < retryCount; retry++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (retry > 0)
|
||||
Task.Delay(retryInterval).Wait();
|
||||
|
||||
return action();
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch (Exception ex)
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AggregateException(exceptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the copyable properties.
|
||||
///
|
||||
/// If there is no properties with the attribute <c>AttributeCache</c> returns all the properties.
|
||||
/// </summary>
|
||||
/// <param name="this">The object.</param>
|
||||
/// <returns>
|
||||
/// Array of properties.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">model.</exception>
|
||||
/// <seealso cref="AttributeCache"/>
|
||||
public static IEnumerable<string> GetCopyableProperties(this object @this)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
var collection = PropertyTypeCache.DefaultCache.Value
|
||||
.RetrieveAllProperties(@this.GetType(), true);
|
||||
|
||||
var properties = collection
|
||||
.Select(x => new
|
||||
{
|
||||
x.Name,
|
||||
HasAttribute = AttributeCache.DefaultCache.Value.RetrieveOne<CopyableAttribute>(x) != null,
|
||||
})
|
||||
.Where(x => x.HasAttribute)
|
||||
.Select(x => x.Name);
|
||||
|
||||
return properties.Any()
|
||||
? properties
|
||||
: collection.Select(x => x.Name);
|
||||
}
|
||||
|
||||
internal static void CreateTarget(
|
||||
this object source,
|
||||
Type targetType,
|
||||
bool includeNonPublic,
|
||||
ref object? target)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
// do nothing. Simply skip creation
|
||||
case string _:
|
||||
break;
|
||||
|
||||
// When using arrays, there is no default constructor, attempt to build a compatible array
|
||||
case IList sourceObjectList when targetType.IsArray:
|
||||
var elementType = targetType.GetElementType();
|
||||
|
||||
if (elementType != null)
|
||||
target = Array.CreateInstance(elementType, sourceObjectList.Count);
|
||||
break;
|
||||
default:
|
||||
var constructors = ConstructorTypeCache.DefaultCache.Value
|
||||
.RetrieveAllConstructors(targetType, includeNonPublic);
|
||||
|
||||
// Try to check if empty constructor is available
|
||||
if (constructors.Any(x => x.Item2.Length == 0))
|
||||
{
|
||||
target = Activator.CreateInstance(targetType, includeNonPublic);
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstCtor = constructors
|
||||
.OrderBy(x => x.Item2.Length)
|
||||
.FirstOrDefault();
|
||||
|
||||
target = Activator.CreateInstance(targetType,
|
||||
firstCtor?.Item2.Select(arg => arg.GetType().GetDefault()).ToArray());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetNameWithCase(this string name, JsonSerializerCase jsonSerializerCase) =>
|
||||
jsonSerializerCase switch
|
||||
{
|
||||
JsonSerializerCase.PascalCase => char.ToUpperInvariant(name[0]) + name.Substring(1),
|
||||
JsonSerializerCase.CamelCase => char.ToLowerInvariant(name[0]) + name.Substring(1),
|
||||
JsonSerializerCase.None => name,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(jsonSerializerCase), jsonSerializerCase, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
647
Vendor/Swan.Lite-3.1.0/Formatters/CsvReader.cs
vendored
Normal file
647
Vendor/Swan.Lite-3.1.0/Formatters/CsvReader.cs
vendored
Normal file
@@ -0,0 +1,647 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a reader designed for CSV text.
|
||||
/// It is capable of deserializing objects from individual lines of CSV text,
|
||||
/// transforming CSV lines of text into objects,
|
||||
/// or simply reading the lines of CSV as an array of strings.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
/// <example>
|
||||
/// The following example describes how to load a list of objects from a CSV file.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// class Person
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// public int Age { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // load records from a CSV file
|
||||
/// var loadedRecords =
|
||||
/// CsvReader.LoadRecords<Person>("C:\\Users\\user\\Documents\\file.csv");
|
||||
///
|
||||
/// // loadedRecords =
|
||||
/// // [
|
||||
/// // { Age = 20, Name = "George" }
|
||||
/// // { Age = 18, Name = "Juan" }
|
||||
/// // ]
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// The following code explains how to read a CSV formatted string.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
/// using System.Text;
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // data to be read
|
||||
/// var data = @"Company,OpenPositions,MainTechnology,Revenue
|
||||
/// Co,2,""C#, MySQL, JavaScript, HTML5 and CSS3"",500
|
||||
/// Ca,2,""C#, MySQL, JavaScript, HTML5 and CSS3"",600";
|
||||
///
|
||||
/// using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(data)))
|
||||
/// {
|
||||
/// // create a CSV reader
|
||||
/// var reader = new CsvReader(stream, false, Encoding.UTF8);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class CsvReader : IDisposable
|
||||
{
|
||||
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
private ulong _count;
|
||||
private char _escapeCharacter = '"';
|
||||
private char _separatorCharacter = ',';
|
||||
|
||||
private bool _hasDisposed; // To detect redundant calls
|
||||
private string[]? _headings;
|
||||
private Dictionary<string, string>? _defaultMap;
|
||||
private StreamReader? _reader;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvReader" /> class.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream.</param>
|
||||
/// <param name="leaveOpen">if set to <c>true</c> leaves the input stream open.</param>
|
||||
/// <param name="textEncoding">The text encoding.</param>
|
||||
public CsvReader(Stream inputStream, bool leaveOpen, Encoding textEncoding)
|
||||
{
|
||||
if (inputStream == null)
|
||||
throw new ArgumentNullException(nameof(inputStream));
|
||||
|
||||
if (textEncoding == null)
|
||||
throw new ArgumentNullException(nameof(textEncoding));
|
||||
|
||||
_reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvReader"/> class.
|
||||
/// It will automatically close the stream upon disposing.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="textEncoding">The text encoding.</param>
|
||||
public CsvReader(Stream stream, Encoding textEncoding)
|
||||
: this(stream, false, textEncoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvReader"/> class.
|
||||
/// It automatically closes the stream when disposing this reader
|
||||
/// and uses the Windows 1253 encoding.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
public CsvReader(Stream stream)
|
||||
: this(stream, false, Definitions.Windows1252Encoding)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvReader"/> class.
|
||||
/// It uses the Windows 1252 Encoding by default and it automatically closes the file
|
||||
/// when this reader is disposed of.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public CsvReader(string filename)
|
||||
: this(File.OpenRead(filename), false, Definitions.Windows1252Encoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvReader"/> class.
|
||||
/// It automatically closes the file when disposing this reader.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
public CsvReader(string filename, Encoding encoding)
|
||||
: this(File.OpenRead(filename), false, encoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets number of lines that have been read, including the headings.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The count.
|
||||
/// </value>
|
||||
public ulong Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the escape character.
|
||||
/// By default it is the double quote '"'.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The escape character.
|
||||
/// </value>
|
||||
public char EscapeCharacter
|
||||
{
|
||||
get => _escapeCharacter;
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
_escapeCharacter = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the separator character.
|
||||
/// By default it is the comma character ','.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The separator character.
|
||||
/// </value>
|
||||
public char SeparatorCharacter
|
||||
{
|
||||
get => _separatorCharacter;
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
_separatorCharacter = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the stream reader is at the end of the stream
|
||||
/// In other words, if no more data can be read, this will be set to true.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [end of stream]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool EndOfStream
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
return _reader?.EndOfStream ?? true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Generic, Main ReadLine method
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text into an array of strings.
|
||||
/// </summary>
|
||||
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||
public string[] ReadLine()
|
||||
{
|
||||
if (EndOfStream)
|
||||
throw new EndOfStreamException("Cannot read past the end of the stream");
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
var values = ParseRecord(_reader!, _escapeCharacter, _separatorCharacter);
|
||||
_count++;
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read Methods
|
||||
|
||||
/// <summary>
|
||||
/// Skips a line of CSV text.
|
||||
/// This operation does not increment the Count property and it is useful when you need to read the headings
|
||||
/// skipping over a few lines as Reading headings is only supported
|
||||
/// as the first read operation (i.e. while count is still 0).
|
||||
/// </summary>
|
||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||
public void SkipRecord()
|
||||
{
|
||||
if (EndOfStream)
|
||||
throw new EndOfStreamException("Cannot read past the end of the stream");
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
ParseRecord(_reader!, _escapeCharacter, _separatorCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text and stores the values read as a representation of the column names
|
||||
/// to be used for parsing objects. You have to call this method before calling ReadObject methods.
|
||||
/// </summary>
|
||||
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// Reading headings is only supported as the first read operation.
|
||||
/// or
|
||||
/// ReadHeadings.
|
||||
/// </exception>
|
||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||
public string[] ReadHeadings()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_headings != null)
|
||||
throw new InvalidOperationException($"The {nameof(ReadHeadings)} method had already been called.");
|
||||
|
||||
if (_count != 0)
|
||||
throw new InvalidOperationException("Reading headings is only supported as the first read operation.");
|
||||
|
||||
_headings = ReadLine();
|
||||
_defaultMap = _headings.ToDictionary(x => x, x => x);
|
||||
|
||||
return _headings.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text, converting it into a dynamic object in which properties correspond to the names of the headings.
|
||||
/// </summary>
|
||||
/// <param name="map">The mappings between CSV headings (keys) and object properties (values).</param>
|
||||
/// <returns>Object of the type of the elements in the collection of key/value pairs.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||
/// <exception cref="System.ArgumentNullException">map.</exception>
|
||||
public IDictionary<string, object> ReadObject(IDictionary<string, string> map)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_headings == null)
|
||||
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
|
||||
|
||||
if (map == null)
|
||||
throw new ArgumentNullException(nameof(map));
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
var values = ReadLine();
|
||||
|
||||
for (var i = 0; i < _headings.Length; i++)
|
||||
{
|
||||
if (i > values.Length - 1)
|
||||
break;
|
||||
|
||||
result[_headings[i]] = values[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text, converting it into a dynamic object
|
||||
/// The property names correspond to the names of the CSV headings.
|
||||
/// </summary>
|
||||
/// <returns>Object of the type of the elements in the collection of key/value pairs.</returns>
|
||||
public IDictionary<string, object> ReadObject() => ReadObject(_defaultMap);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary)
|
||||
/// where the keys are the names of the headings and the values are the names of the instance properties
|
||||
/// in the given Type. The result object must be already instantiated.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to map.</typeparam>
|
||||
/// <param name="map">The map.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <exception cref="System.ArgumentNullException">map
|
||||
/// or
|
||||
/// result.</exception>
|
||||
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||
public void ReadObject<T>(IDictionary<string, string> map, ref T result)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
// Check arguments
|
||||
{
|
||||
if (map == null)
|
||||
throw new ArgumentNullException(nameof(map));
|
||||
|
||||
if (_reader.EndOfStream)
|
||||
throw new EndOfStreamException("Cannot read past the end of the stream");
|
||||
|
||||
if (_headings == null)
|
||||
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
|
||||
|
||||
if (Equals(result, default(T)))
|
||||
throw new ArgumentNullException(nameof(result));
|
||||
}
|
||||
|
||||
// Read line and extract values
|
||||
var values = ReadLine();
|
||||
|
||||
// Extract properties from cache
|
||||
var properties = TypeCache
|
||||
.RetrieveFilteredProperties(typeof(T), true, x => x.CanWrite && Definitions.BasicTypesInfo.Value.ContainsKey(x.PropertyType));
|
||||
|
||||
// Assign property values for each heading
|
||||
for (var i = 0; i < _headings.Length; i++)
|
||||
{
|
||||
// break if no more headings are matched
|
||||
if (i > values.Length - 1)
|
||||
break;
|
||||
|
||||
// skip if no heading is available or the heading is empty
|
||||
if (map.ContainsKey(_headings[i]) == false &&
|
||||
string.IsNullOrWhiteSpace(map[_headings[i]]) == false)
|
||||
continue;
|
||||
|
||||
// Prepare the target property
|
||||
var propertyName = map[_headings[i]];
|
||||
|
||||
// Parse and assign the basic type value to the property if exists
|
||||
properties
|
||||
.FirstOrDefault(p => p.Name == propertyName)?
|
||||
.TrySetBasicType(values[i], result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary)
|
||||
/// where the keys are the names of the headings and the values are the names of the instance properties
|
||||
/// in the given Type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to map.</typeparam>
|
||||
/// <param name="map">The map of CSV headings (keys) and Type property names (values).</param>
|
||||
/// <returns>The conversion of specific type of object.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">map.</exception>
|
||||
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||
public T ReadObject<T>(IDictionary<string, string> map)
|
||||
where T : new()
|
||||
{
|
||||
if (map == null)
|
||||
throw new ArgumentNullException(nameof(map));
|
||||
|
||||
var result = Activator.CreateInstance<T>();
|
||||
ReadObject(map, ref result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of CSV text converting it into an object of the given type, and assuming
|
||||
/// the property names of the target type match the heading names of the file.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object.</typeparam>
|
||||
/// <returns>The conversion of specific type of object.</returns>
|
||||
public T ReadObject<T>()
|
||||
where T : new() =>
|
||||
ReadObject<T>(_defaultMap);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Support Methods
|
||||
|
||||
/// <summary>
|
||||
/// Parses a line of standard CSV text into an array of strings.
|
||||
/// Note that quoted values might have new line sequences in them. Field values will contain such sequences.
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader.</param>
|
||||
/// <param name="escapeCharacter">The escape character.</param>
|
||||
/// <param name="separatorCharacter">The separator character.</param>
|
||||
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
||||
private static string[] ParseRecord(StreamReader reader, char escapeCharacter = '"', char separatorCharacter = ',')
|
||||
{
|
||||
var values = new List<string>();
|
||||
var currentValue = new StringBuilder(1024);
|
||||
var currentState = ReadState.WaitingForNewField;
|
||||
string line;
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
for (var charIndex = 0; charIndex < line.Length; charIndex++)
|
||||
{
|
||||
// Get the current and next character
|
||||
var currentChar = line[charIndex];
|
||||
var nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : default(char?);
|
||||
|
||||
// Perform logic based on state and decide on next state
|
||||
switch (currentState)
|
||||
{
|
||||
case ReadState.WaitingForNewField:
|
||||
{
|
||||
currentValue.Clear();
|
||||
|
||||
if (currentChar == escapeCharacter)
|
||||
{
|
||||
currentState = ReadState.PushingQuoted;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentChar == separatorCharacter)
|
||||
{
|
||||
values.Add(currentValue.ToString());
|
||||
currentState = ReadState.WaitingForNewField;
|
||||
continue;
|
||||
}
|
||||
|
||||
currentValue.Append(currentChar);
|
||||
currentState = ReadState.PushingNormal;
|
||||
continue;
|
||||
}
|
||||
|
||||
case ReadState.PushingNormal:
|
||||
{
|
||||
// Handle field content delimiter by comma
|
||||
if (currentChar == separatorCharacter)
|
||||
{
|
||||
currentState = ReadState.WaitingForNewField;
|
||||
values.Add(currentValue.ToString());
|
||||
currentValue.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle double quote escaping
|
||||
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter)
|
||||
{
|
||||
// advance 1 character now. The loop will advance one more.
|
||||
currentValue.Append(currentChar);
|
||||
charIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
currentValue.Append(currentChar);
|
||||
break;
|
||||
}
|
||||
|
||||
case ReadState.PushingQuoted:
|
||||
{
|
||||
// Handle field content delimiter by ending double quotes
|
||||
if (currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter))
|
||||
{
|
||||
currentState = ReadState.PushingNormal;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle double quote escaping
|
||||
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter)
|
||||
{
|
||||
// advance 1 character now. The loop will advance one more.
|
||||
currentValue.Append(currentChar);
|
||||
charIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
currentValue.Append(currentChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determine if we need to continue reading a new line if it is part of the quoted
|
||||
// field value
|
||||
if (currentState == ReadState.PushingQuoted)
|
||||
{
|
||||
// we need to add the new line sequence to the output of the field
|
||||
// because we were pushing a quoted value
|
||||
currentValue.Append(Environment.NewLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
// push anything that has not been pushed (flush) into a last value
|
||||
values.Add(currentValue.ToString());
|
||||
currentValue.Clear();
|
||||
|
||||
// stop reading more lines we have reached the end of the CSV record
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we ended up pushing quoted and no closing quotes we might
|
||||
// have additional text in yt
|
||||
if (currentValue.Length > 0)
|
||||
{
|
||||
values.Add(currentValue.ToString());
|
||||
}
|
||||
|
||||
return values.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Loads the records from the stream
|
||||
/// This method uses Windows 1252 encoding.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of IList items to load.</typeparam>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
|
||||
public static IList<T> LoadRecords<T>(Stream stream)
|
||||
where T : new()
|
||||
{
|
||||
var result = new List<T>();
|
||||
|
||||
using (var reader = new CsvReader(stream))
|
||||
{
|
||||
reader.ReadHeadings();
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
result.Add(reader.ReadObject<T>());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the records from the give file path.
|
||||
/// This method uses Windows 1252 encoding.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of IList items to load.</typeparam>
|
||||
/// <param name="filePath">The file path.</param>
|
||||
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
|
||||
public static IList<T> LoadRecords<T>(string filePath)
|
||||
where T : new() =>
|
||||
LoadRecords<T>(File.OpenRead(filePath));
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_hasDisposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
_reader?.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_reader = null;
|
||||
}
|
||||
}
|
||||
|
||||
_hasDisposed = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Defines the 3 different read states
|
||||
/// for the parsing state machine.
|
||||
/// </summary>
|
||||
private enum ReadState
|
||||
{
|
||||
WaitingForNewField,
|
||||
PushingNormal,
|
||||
PushingQuoted,
|
||||
}
|
||||
}
|
||||
}
|
||||
460
Vendor/Swan.Lite-3.1.0/Formatters/CsvWriter.cs
vendored
Normal file
460
Vendor/Swan.Lite-3.1.0/Formatters/CsvWriter.cs
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// A CSV writer useful for exporting a set of objects.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following code describes how to save a list of objects into a CSV file.
|
||||
/// <code>
|
||||
/// using System.Collections.Generic;
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// class Person
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// public int Age { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // create a list of people
|
||||
/// var people = new List<Person>
|
||||
/// {
|
||||
/// new Person { Name = "Artyom", Age = 20 },
|
||||
/// new Person { Name = "Aloy", Age = 18 }
|
||||
/// }
|
||||
///
|
||||
/// // write items inside file.csv
|
||||
/// CsvWriter.SaveRecords(people, "C:\\Users\\user\\Documents\\file.csv");
|
||||
///
|
||||
/// // output
|
||||
/// // | Name | Age |
|
||||
/// // | Artyom | 20 |
|
||||
/// // | Aloy | 18 |
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class CsvWriter : IDisposable
|
||||
{
|
||||
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private readonly Stream _outputStream;
|
||||
private readonly Encoding _encoding;
|
||||
private readonly bool _leaveStreamOpen;
|
||||
private bool _isDisposing;
|
||||
private ulong _mCount;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvWriter" /> class.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">The output stream.</param>
|
||||
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
public CsvWriter(Stream outputStream, bool leaveOpen, Encoding encoding)
|
||||
{
|
||||
_outputStream = outputStream;
|
||||
_encoding = encoding;
|
||||
_leaveStreamOpen = leaveOpen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvWriter"/> class.
|
||||
/// It automatically closes the stream when disposing this writer.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">The output stream.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
public CsvWriter(Stream outputStream, Encoding encoding)
|
||||
: this(outputStream, false, encoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvWriter"/> class.
|
||||
/// It uses the Windows 1252 encoding and automatically closes
|
||||
/// the stream upon disposing this writer.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">The output stream.</param>
|
||||
public CsvWriter(Stream outputStream)
|
||||
: this(outputStream, false, Definitions.Windows1252Encoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvWriter"/> class.
|
||||
/// It opens the file given file, automatically closes the stream upon
|
||||
/// disposing of this writer, and uses the Windows 1252 encoding.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public CsvWriter(string filename)
|
||||
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsvWriter"/> class.
|
||||
/// It opens the file given file, automatically closes the stream upon
|
||||
/// disposing of this writer, and uses the given text encoding for output.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
public CsvWriter(string filename, Encoding encoding)
|
||||
: this(File.OpenWrite(filename), false, encoding)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the field separator character.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The separator character.
|
||||
/// </value>
|
||||
public char SeparatorCharacter { get; set; } = ',';
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the escape character to use to escape field values.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The escape character.
|
||||
/// </value>
|
||||
public char EscapeCharacter { get; set; } = '"';
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the new line character sequence to use when writing a line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The new line sequence.
|
||||
/// </value>
|
||||
public string NewLineSequence { get; set; } = Environment.NewLine;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list of properties to ignore when outputting CSV lines.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ignore property names.
|
||||
/// </value>
|
||||
public List<string> IgnorePropertyNames { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets number of lines that have been written, including the headings line.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The count.
|
||||
/// </value>
|
||||
public ulong Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
return _mCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Saves the items to a stream.
|
||||
/// It uses the Windows 1252 text encoding for output.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enumeration.</typeparam>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param>
|
||||
/// <returns>Number of item saved.</returns>
|
||||
public static int SaveRecords<T>(IEnumerable<T> items, Stream stream, bool truncateData = false)
|
||||
{
|
||||
// truncate the file if it had data
|
||||
if (truncateData && stream.Length > 0)
|
||||
stream.SetLength(0);
|
||||
|
||||
using var writer = new CsvWriter(stream);
|
||||
writer.WriteHeadings<T>();
|
||||
writer.WriteObjects(items);
|
||||
return (int)writer.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the items to a CSV file.
|
||||
/// If the file exits, it overwrites it. If it does not, it creates it.
|
||||
/// It uses the Windows 1252 text encoding for output.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enumeration.</typeparam>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="filePath">The file path.</param>
|
||||
/// <returns>Number of item saved.</returns>
|
||||
public static int SaveRecords<T>(IEnumerable<T> items, string filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Generic, main Write Line Method
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line of CSV text. Items are converted to strings.
|
||||
/// If items are found to be null, empty strings are written out.
|
||||
/// If items are not string, the ToStringInvariant() method is called on them.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
public void WriteLine(params object[] items)
|
||||
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant()));
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line of CSV text. Items are converted to strings.
|
||||
/// If items are found to be null, empty strings are written out.
|
||||
/// If items are not string, the ToStringInvariant() method is called on them.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
public void WriteLine(IEnumerable<object> items)
|
||||
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant()));
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line of CSV text.
|
||||
/// If items are found to be null, empty strings are written out.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
public void WriteLine(params string[] items) => WriteLine((IEnumerable<string>) items);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line of CSV text.
|
||||
/// If items are found to be null, empty strings are written out.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
public void WriteLine(IEnumerable<string> items)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
var length = items.Count();
|
||||
var separatorBytes = _encoding.GetBytes(new[] { SeparatorCharacter });
|
||||
var endOfLineBytes = _encoding.GetBytes(NewLineSequence);
|
||||
|
||||
// Declare state variables here to avoid recreation, allocation and
|
||||
// reassignment in every loop
|
||||
bool needsEnclosing;
|
||||
string textValue;
|
||||
byte[] output;
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
textValue = items.ElementAt(i);
|
||||
|
||||
// Determine if we need the string to be enclosed
|
||||
// (it either contains an escape, new line, or separator char)
|
||||
needsEnclosing = textValue.IndexOf(SeparatorCharacter) >= 0
|
||||
|| textValue.IndexOf(EscapeCharacter) >= 0
|
||||
|| textValue.IndexOf('\r') >= 0
|
||||
|| textValue.IndexOf('\n') >= 0;
|
||||
|
||||
// Escape the escape characters by repeating them twice for every instance
|
||||
textValue = textValue.Replace($"{EscapeCharacter}",
|
||||
$"{EscapeCharacter}{EscapeCharacter}");
|
||||
|
||||
// Enclose the text value if we need to
|
||||
if (needsEnclosing)
|
||||
textValue = string.Format($"{EscapeCharacter}{textValue}{EscapeCharacter}", textValue);
|
||||
|
||||
// Get the bytes to write to the stream and write them
|
||||
output = _encoding.GetBytes(textValue);
|
||||
_outputStream.Write(output, 0, output.Length);
|
||||
|
||||
// only write a separator if we are moving in between values.
|
||||
// the last value should not be written.
|
||||
if (i < length - 1)
|
||||
_outputStream.Write(separatorBytes, 0, separatorBytes.Length);
|
||||
}
|
||||
|
||||
// output the newline sequence
|
||||
_outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
|
||||
_mCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write Object Method
|
||||
|
||||
/// <summary>
|
||||
/// Writes a row of CSV text. It handles the special cases where the object is
|
||||
/// a dynamic object or and array. It also handles non-collection objects fine.
|
||||
/// If you do not like the way the output is handled, you can simply write an extension
|
||||
/// method of this class and use the WriteLine method instead.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <exception cref="System.ArgumentNullException">item.</exception>
|
||||
public void WriteObject(object? item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case IDictionary typedItem:
|
||||
WriteLine(GetFilteredDictionary(typedItem));
|
||||
return;
|
||||
case ICollection typedItem:
|
||||
WriteLine(typedItem.Cast<object>());
|
||||
return;
|
||||
default:
|
||||
WriteLine(GetFilteredTypeProperties(item.GetType()).Select(x => item.ReadProperty(x.Name)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a row of CSV text. It handles the special cases where the object is
|
||||
/// a dynamic object or and array. It also handles non-collection objects fine.
|
||||
/// If you do not like the way the output is handled, you can simply write an extension
|
||||
/// method of this class and use the WriteLine method instead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to write.</typeparam>
|
||||
/// <param name="item">The item.</param>
|
||||
public void WriteObject<T>(T item) => WriteObject(item as object);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a set of items, one per line and atomically by repeatedly calling the
|
||||
/// WriteObject method. For more info check out the description of the WriteObject
|
||||
/// method.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to write.</typeparam>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <exception cref="ArgumentNullException">items.</exception>
|
||||
public void WriteObjects<T>(IEnumerable<T> items)
|
||||
{
|
||||
if (items == null)
|
||||
throw new ArgumentNullException(nameof(items));
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
foreach (var item in items)
|
||||
WriteObject(item);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write Headings Methods
|
||||
|
||||
/// <summary>
|
||||
/// Writes the headings.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of object to extract headings.</param>
|
||||
/// <exception cref="System.ArgumentNullException">type.</exception>
|
||||
public void WriteHeadings(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
var properties = GetFilteredTypeProperties(type).Select(p => p.Name).Cast<object>();
|
||||
WriteLine(properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the headings.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to extract headings.</typeparam>
|
||||
public void WriteHeadings<T>() => WriteHeadings(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Writes the headings.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The dictionary to extract headings.</param>
|
||||
/// <exception cref="System.ArgumentNullException">dictionary.</exception>
|
||||
public void WriteHeadings(IDictionary dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
throw new ArgumentNullException(nameof(dictionary));
|
||||
|
||||
WriteLine(GetFilteredDictionary(dictionary, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the headings.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to extract headings.</param>
|
||||
/// <exception cref="ArgumentNullException">obj.</exception>
|
||||
public void WriteHeadings(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
WriteHeadings(obj.GetType());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposeAlsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposeAlsoManaged)
|
||||
{
|
||||
if (_isDisposing) return;
|
||||
|
||||
if (disposeAlsoManaged)
|
||||
{
|
||||
if (_leaveStreamOpen == false)
|
||||
{
|
||||
_outputStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_isDisposing = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Support Methods
|
||||
|
||||
private IEnumerable<string> GetFilteredDictionary(IDictionary dictionary, bool filterKeys = false)
|
||||
=> dictionary
|
||||
.Keys
|
||||
.Cast<object>()
|
||||
.Select(key => key == null ? string.Empty : key.ToStringInvariant())
|
||||
.Where(stringKey => !IgnorePropertyNames.Contains(stringKey))
|
||||
.Select(stringKey =>
|
||||
filterKeys
|
||||
? stringKey
|
||||
: dictionary[stringKey] == null ? string.Empty : dictionary[stringKey].ToStringInvariant());
|
||||
|
||||
private IEnumerable<PropertyInfo> GetFilteredTypeProperties(Type type)
|
||||
=> TypeCache.Retrieve(type, t =>
|
||||
t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p => p.CanRead))
|
||||
.Where(p => !IgnorePropertyNames.Contains(p.Name));
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
147
Vendor/Swan.Lite-3.1.0/Formatters/HumanizeJson.cs
vendored
Normal file
147
Vendor/Swan.Lite-3.1.0/Formatters/HumanizeJson.cs
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
internal class HumanizeJson
|
||||
{
|
||||
private readonly StringBuilder _builder = new StringBuilder();
|
||||
private readonly int _indent;
|
||||
private readonly string _indentStr;
|
||||
private readonly object? _obj;
|
||||
|
||||
public HumanizeJson(object? obj, int indent)
|
||||
{
|
||||
_obj = obj;
|
||||
_indent = indent;
|
||||
_indentStr = new string(' ', indent * 4);
|
||||
|
||||
ParseObject();
|
||||
}
|
||||
|
||||
public string GetResult() => _obj == null ? string.Empty : _builder.ToString().TrimEnd();
|
||||
|
||||
private void ParseObject()
|
||||
{
|
||||
if (_obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_obj)
|
||||
{
|
||||
case Dictionary<string, object> dictionary:
|
||||
AppendDictionary(dictionary);
|
||||
break;
|
||||
case List<object> list:
|
||||
AppendList(list);
|
||||
break;
|
||||
default:
|
||||
AppendString(_obj.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendDictionary(Dictionary<string, object> objects)
|
||||
{
|
||||
foreach (var kvp in objects)
|
||||
{
|
||||
if (kvp.Value == null) continue;
|
||||
|
||||
var writeOutput = false;
|
||||
|
||||
switch (kvp.Value)
|
||||
{
|
||||
case Dictionary<string, object> valueDictionary:
|
||||
if (valueDictionary.Count > 0)
|
||||
{
|
||||
writeOutput = true;
|
||||
_builder
|
||||
.Append($"{_indentStr}{kvp.Key,-16}: object")
|
||||
.AppendLine();
|
||||
}
|
||||
|
||||
break;
|
||||
case List<object> valueList:
|
||||
if (valueList.Count > 0)
|
||||
{
|
||||
writeOutput = true;
|
||||
_builder
|
||||
.Append($"{_indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
|
||||
.AppendLine();
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
writeOutput = true;
|
||||
_builder.Append($"{_indentStr}{kvp.Key,-16}: ");
|
||||
break;
|
||||
}
|
||||
|
||||
if (writeOutput)
|
||||
_builder.AppendLine(new HumanizeJson(kvp.Value, _indent + 1).GetResult());
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendList(IEnumerable<object> objects)
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var value in objects)
|
||||
{
|
||||
var writeOutput = false;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case Dictionary<string, object> valueDictionary:
|
||||
if (valueDictionary.Count > 0)
|
||||
{
|
||||
writeOutput = true;
|
||||
_builder
|
||||
.Append($"{_indentStr}[{index}]: object")
|
||||
.AppendLine();
|
||||
}
|
||||
|
||||
break;
|
||||
case List<object> valueList:
|
||||
if (valueList.Count > 0)
|
||||
{
|
||||
writeOutput = true;
|
||||
_builder
|
||||
.Append($"{_indentStr}[{index}]: array[{valueList.Count}]")
|
||||
.AppendLine();
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
writeOutput = true;
|
||||
_builder.Append($"{_indentStr}[{index}]: ");
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (writeOutput)
|
||||
_builder.AppendLine(new HumanizeJson(value, _indent + 1).GetResult());
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendString(string stringValue)
|
||||
{
|
||||
if (stringValue.Length + _indentStr.Length <= 96 && stringValue.IndexOf('\r') < 0 &&
|
||||
stringValue.IndexOf('\n') < 0)
|
||||
{
|
||||
_builder.Append($"{stringValue}");
|
||||
return;
|
||||
}
|
||||
|
||||
_builder.AppendLine();
|
||||
var stringLines = stringValue.ToLines().Select(l => l.Trim());
|
||||
|
||||
foreach (var line in stringLines)
|
||||
{
|
||||
_builder.AppendLine($"{_indentStr}{line}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
353
Vendor/Swan.Lite-3.1.0/Formatters/Json.Converter.cs
vendored
Normal file
353
Vendor/Swan.Lite-3.1.0/Formatters/Json.Converter.cs
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// A very simple, light-weight JSON library written by Mario
|
||||
/// to teach Geo how things are done
|
||||
///
|
||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||
/// serializer such as the beloved Json.NET.
|
||||
/// </summary>
|
||||
public static partial class Json
|
||||
{
|
||||
private class Converter
|
||||
{
|
||||
private static readonly ConcurrentDictionary<MemberInfo, string> MemberInfoNameCache =
|
||||
new ConcurrentDictionary<MemberInfo, string>();
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, Type?> ListAddMethodCache = new ConcurrentDictionary<Type, Type?>();
|
||||
|
||||
private readonly object? _target;
|
||||
private readonly Type _targetType;
|
||||
private readonly bool _includeNonPublic;
|
||||
private readonly JsonSerializerCase _jsonSerializerCase;
|
||||
|
||||
private Converter(
|
||||
object? source,
|
||||
Type targetType,
|
||||
ref object? targetInstance,
|
||||
bool includeNonPublic,
|
||||
JsonSerializerCase jsonSerializerCase)
|
||||
{
|
||||
_targetType = targetInstance != null ? targetInstance.GetType() : targetType;
|
||||
_includeNonPublic = includeNonPublic;
|
||||
_jsonSerializerCase = jsonSerializerCase;
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceType = source.GetType();
|
||||
|
||||
if (_targetType == null || _targetType == typeof(object)) _targetType = sourceType;
|
||||
if (sourceType == _targetType)
|
||||
{
|
||||
_target = source;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TrySetInstance(targetInstance, source, ref _target))
|
||||
return;
|
||||
|
||||
ResolveObject(source, ref _target);
|
||||
}
|
||||
|
||||
internal static object? FromJsonResult(
|
||||
object? source,
|
||||
JsonSerializerCase jsonSerializerCase,
|
||||
Type? targetType = null,
|
||||
bool includeNonPublic = false)
|
||||
{
|
||||
object? nullRef = null;
|
||||
return new Converter(source, targetType ?? typeof(object), ref nullRef, includeNonPublic, jsonSerializerCase).GetResult();
|
||||
}
|
||||
|
||||
private static object? FromJsonResult(object source,
|
||||
Type targetType,
|
||||
ref object? targetInstance,
|
||||
bool includeNonPublic)
|
||||
{
|
||||
return new Converter(source, targetType, ref targetInstance, includeNonPublic, JsonSerializerCase.None).GetResult();
|
||||
}
|
||||
|
||||
private static Type? GetAddMethodParameterType(Type targetType)
|
||||
=> ListAddMethodCache.GetOrAdd(targetType,
|
||||
x => x.GetMethods()
|
||||
.FirstOrDefault(
|
||||
m => m.Name == AddMethodName && m.IsPublic && m.GetParameters().Length == 1)?
|
||||
.GetParameters()[0]
|
||||
.ParameterType);
|
||||
|
||||
private static void GetByteArray(string sourceString, ref object? target)
|
||||
{
|
||||
try
|
||||
{
|
||||
target = Convert.FromBase64String(sourceString);
|
||||
} // Try conversion from Base 64
|
||||
catch (FormatException)
|
||||
{
|
||||
target = Encoding.UTF8.GetBytes(sourceString);
|
||||
} // Get the string bytes in UTF8
|
||||
}
|
||||
|
||||
private object? GetSourcePropertyValue(
|
||||
IDictionary<string, object> sourceProperties,
|
||||
MemberInfo targetProperty)
|
||||
{
|
||||
var targetPropertyName = MemberInfoNameCache.GetOrAdd(
|
||||
targetProperty,
|
||||
x => AttributeCache.DefaultCache.Value.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name.GetNameWithCase(_jsonSerializerCase));
|
||||
|
||||
return sourceProperties!.GetValueOrDefault(targetPropertyName);
|
||||
}
|
||||
|
||||
private bool TrySetInstance(object? targetInstance, object source, ref object? target)
|
||||
{
|
||||
if (targetInstance == null)
|
||||
{
|
||||
// Try to create a default instance
|
||||
try
|
||||
{
|
||||
source.CreateTarget(_targetType, _includeNonPublic, ref target);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = targetInstance;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private object? GetResult() => _target ?? _targetType.GetDefault();
|
||||
|
||||
private void ResolveObject(object source, ref object? target)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
// Case 0: Special Cases Handling (Source and Target are of specific convertible types)
|
||||
// Case 0.1: Source is string, Target is byte[]
|
||||
case string sourceString when _targetType == typeof(byte[]):
|
||||
GetByteArray(sourceString, ref target);
|
||||
break;
|
||||
|
||||
// Case 1.1: Source is Dictionary, Target is IDictionary
|
||||
case Dictionary<string, object> sourceProperties when target is IDictionary targetDictionary:
|
||||
PopulateDictionary(sourceProperties, targetDictionary);
|
||||
break;
|
||||
|
||||
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
|
||||
case Dictionary<string, object> sourceProperties:
|
||||
PopulateObject(sourceProperties);
|
||||
break;
|
||||
|
||||
// Case 2.1: Source is List, Target is Array
|
||||
case List<object> sourceList when target is Array targetArray:
|
||||
PopulateArray(sourceList, targetArray);
|
||||
break;
|
||||
|
||||
// Case 2.2: Source is List, Target is IList
|
||||
case List<object> sourceList when target is IList targetList:
|
||||
PopulateIList(sourceList, targetList);
|
||||
break;
|
||||
|
||||
// Case 3: Source is a simple type; Attempt conversion
|
||||
default:
|
||||
var sourceStringValue = source.ToStringInvariant();
|
||||
|
||||
// Handle basic types or enumerations if not
|
||||
if (!_targetType.TryParseBasicType(sourceStringValue, out target))
|
||||
GetEnumValue(sourceStringValue, ref target);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateIList(IEnumerable<object> objects, IList list)
|
||||
{
|
||||
var parameterType = GetAddMethodParameterType(_targetType);
|
||||
if (parameterType == null) return;
|
||||
|
||||
foreach (var item in objects)
|
||||
{
|
||||
try
|
||||
{
|
||||
list.Add(FromJsonResult(
|
||||
item,
|
||||
_jsonSerializerCase,
|
||||
parameterType,
|
||||
_includeNonPublic));
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateArray(IList<object> objects, Array array)
|
||||
{
|
||||
var elementType = _targetType.GetElementType();
|
||||
|
||||
for (var i = 0; i < objects.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var targetItem = FromJsonResult(
|
||||
objects[i],
|
||||
_jsonSerializerCase,
|
||||
elementType,
|
||||
_includeNonPublic);
|
||||
array.SetValue(targetItem, i);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetEnumValue(string sourceStringValue, ref object? target)
|
||||
{
|
||||
var enumType = Nullable.GetUnderlyingType(_targetType);
|
||||
if (enumType == null && _targetType.IsEnum) enumType = _targetType;
|
||||
if (enumType == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
target = Enum.Parse(enumType, sourceStringValue);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateDictionary(IDictionary<string, object> sourceProperties, IDictionary targetDictionary)
|
||||
{
|
||||
// find the add method of the target dictionary
|
||||
var addMethod = _targetType.GetMethods()
|
||||
.FirstOrDefault(
|
||||
m => m.Name == AddMethodName && m.IsPublic && m.GetParameters().Length == 2);
|
||||
|
||||
// skip if we don't have a compatible add method
|
||||
if (addMethod == null) return;
|
||||
var addMethodParameters = addMethod.GetParameters();
|
||||
if (addMethodParameters[0].ParameterType != typeof(string)) return;
|
||||
|
||||
// Retrieve the target entry type
|
||||
var targetEntryType = addMethodParameters[1].ParameterType;
|
||||
|
||||
// Add the items to the target dictionary
|
||||
foreach (var sourceProperty in sourceProperties)
|
||||
{
|
||||
try
|
||||
{
|
||||
var targetEntryValue = FromJsonResult(
|
||||
sourceProperty.Value,
|
||||
_jsonSerializerCase,
|
||||
targetEntryType,
|
||||
_includeNonPublic);
|
||||
targetDictionary.Add(sourceProperty.Key, targetEntryValue);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateObject(IDictionary<string, object> sourceProperties)
|
||||
{
|
||||
if (sourceProperties == null)
|
||||
return;
|
||||
|
||||
if (_targetType.IsValueType)
|
||||
PopulateFields(sourceProperties);
|
||||
|
||||
PopulateProperties(sourceProperties);
|
||||
}
|
||||
|
||||
private void PopulateProperties(IDictionary<string, object> sourceProperties)
|
||||
{
|
||||
var properties = PropertyTypeCache.DefaultCache.Value.RetrieveFilteredProperties(_targetType, false, p => p.CanWrite);
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
|
||||
if (sourcePropertyValue == null) continue;
|
||||
|
||||
try
|
||||
{
|
||||
var currentPropertyValue = !property.PropertyType.IsArray
|
||||
? _target.ReadProperty(property.Name)
|
||||
: null;
|
||||
|
||||
var targetPropertyValue = FromJsonResult(
|
||||
sourcePropertyValue,
|
||||
property.PropertyType,
|
||||
ref currentPropertyValue,
|
||||
_includeNonPublic);
|
||||
|
||||
_target.WriteProperty(property.Name, targetPropertyValue);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateFields(IDictionary<string, object> sourceProperties)
|
||||
{
|
||||
foreach (var field in FieldTypeCache.DefaultCache.Value.RetrieveAllFields(_targetType))
|
||||
{
|
||||
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
|
||||
if (sourcePropertyValue == null) continue;
|
||||
|
||||
var targetPropertyValue = FromJsonResult(
|
||||
sourcePropertyValue,
|
||||
_jsonSerializerCase,
|
||||
field.FieldType,
|
||||
_includeNonPublic);
|
||||
|
||||
try
|
||||
{
|
||||
field.SetValue(_target, targetPropertyValue);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
348
Vendor/Swan.Lite-3.1.0/Formatters/Json.Deserializer.cs
vendored
Normal file
348
Vendor/Swan.Lite-3.1.0/Formatters/Json.Deserializer.cs
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// A very simple, light-weight JSON library written by Mario
|
||||
/// to teach Geo how things are done
|
||||
///
|
||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||
/// serializer such as the beloved Json.NET.
|
||||
/// </summary>
|
||||
public partial class Json
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple JSON Deserializer.
|
||||
/// </summary>
|
||||
private class Deserializer
|
||||
{
|
||||
#region State Variables
|
||||
|
||||
private readonly object? _result;
|
||||
private readonly string _json;
|
||||
|
||||
private Dictionary<string, object?> _resultObject;
|
||||
private List<object?> _resultArray;
|
||||
private ReadState _state = ReadState.WaitingForRootOpen;
|
||||
private string? _currentFieldName;
|
||||
|
||||
private int _index;
|
||||
|
||||
#endregion
|
||||
|
||||
private Deserializer(string json, int startIndex)
|
||||
{
|
||||
_json = json;
|
||||
|
||||
for (_index = startIndex; _index < _json.Length; _index++)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case ReadState.WaitingForRootOpen:
|
||||
WaitForRootOpen();
|
||||
continue;
|
||||
case ReadState.WaitingForField when char.IsWhiteSpace(_json, _index):
|
||||
continue;
|
||||
case ReadState.WaitingForField when (_resultObject != null && _json[_index] == CloseObjectChar)
|
||||
|| (_resultArray != null && _json[_index] == CloseArrayChar):
|
||||
// Handle empty arrays and empty objects
|
||||
_result = _resultObject ?? _resultArray as object;
|
||||
return;
|
||||
case ReadState.WaitingForField when _json[_index] != StringQuotedChar:
|
||||
throw CreateParserException($"'{StringQuotedChar}'");
|
||||
case ReadState.WaitingForField:
|
||||
{
|
||||
var charCount = GetFieldNameCount();
|
||||
|
||||
_currentFieldName = Unescape(_json.SliceLength(_index + 1, charCount));
|
||||
_index += charCount + 1;
|
||||
_state = ReadState.WaitingForColon;
|
||||
continue;
|
||||
}
|
||||
|
||||
case ReadState.WaitingForColon when char.IsWhiteSpace(_json, _index):
|
||||
continue;
|
||||
case ReadState.WaitingForColon when _json[_index] != ValueSeparatorChar:
|
||||
throw CreateParserException($"'{ValueSeparatorChar}'");
|
||||
case ReadState.WaitingForColon:
|
||||
_state = ReadState.WaitingForValue;
|
||||
continue;
|
||||
case ReadState.WaitingForValue when char.IsWhiteSpace(_json, _index):
|
||||
continue;
|
||||
case ReadState.WaitingForValue when (_resultObject != null && _json[_index] == CloseObjectChar)
|
||||
|| (_resultArray != null && _json[_index] == CloseArrayChar):
|
||||
// Handle empty arrays and empty objects
|
||||
_result = _resultObject ?? _resultArray as object;
|
||||
return;
|
||||
case ReadState.WaitingForValue:
|
||||
ExtractValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_state != ReadState.WaitingForNextOrRootClose || char.IsWhiteSpace(_json, _index)) continue;
|
||||
|
||||
if (_json[_index] == FieldSeparatorChar)
|
||||
{
|
||||
if (_resultObject != null)
|
||||
{
|
||||
_state = ReadState.WaitingForField;
|
||||
_currentFieldName = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
_state = ReadState.WaitingForValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((_resultObject == null || _json[_index] != CloseObjectChar) &&
|
||||
(_resultArray == null || _json[_index] != CloseArrayChar))
|
||||
{
|
||||
throw CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
|
||||
}
|
||||
|
||||
_result = _resultObject ?? _resultArray as object;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal static object? DeserializeInternal(string json) => new Deserializer(json, 0)._result;
|
||||
|
||||
private void WaitForRootOpen()
|
||||
{
|
||||
if (char.IsWhiteSpace(_json, _index)) return;
|
||||
|
||||
switch (_json[_index])
|
||||
{
|
||||
case OpenObjectChar:
|
||||
_resultObject = new Dictionary<string, object?>();
|
||||
_state = ReadState.WaitingForField;
|
||||
return;
|
||||
case OpenArrayChar:
|
||||
_resultArray = new List<object?>();
|
||||
_state = ReadState.WaitingForValue;
|
||||
return;
|
||||
default:
|
||||
throw CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractValue()
|
||||
{
|
||||
// determine the value based on what it starts with
|
||||
switch (_json[_index])
|
||||
{
|
||||
case StringQuotedChar: // expect a string
|
||||
ExtractStringQuoted();
|
||||
break;
|
||||
|
||||
case OpenObjectChar: // expect object
|
||||
case OpenArrayChar: // expect array
|
||||
ExtractObject();
|
||||
break;
|
||||
|
||||
case 't': // expect true
|
||||
ExtractConstant(TrueLiteral, true);
|
||||
break;
|
||||
|
||||
case 'f': // expect false
|
||||
ExtractConstant(FalseLiteral, false);
|
||||
break;
|
||||
|
||||
case 'n': // expect null
|
||||
ExtractConstant(NullLiteral);
|
||||
break;
|
||||
|
||||
default: // expect number
|
||||
ExtractNumber();
|
||||
break;
|
||||
}
|
||||
|
||||
_currentFieldName = null;
|
||||
_state = ReadState.WaitingForNextOrRootClose;
|
||||
}
|
||||
|
||||
private static string Unescape(string str)
|
||||
{
|
||||
// check if we need to unescape at all
|
||||
if (str.IndexOf(StringEscapeChar) < 0)
|
||||
return str;
|
||||
|
||||
var builder = new StringBuilder(str.Length);
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
if (str[i] != StringEscapeChar)
|
||||
{
|
||||
builder.Append(str[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 > str.Length - 1)
|
||||
break;
|
||||
|
||||
// escape sequence begins here
|
||||
switch (str[i + 1])
|
||||
{
|
||||
case 'u':
|
||||
i = ExtractEscapeSequence(str, i, builder);
|
||||
break;
|
||||
case 'b':
|
||||
builder.Append('\b');
|
||||
i += 1;
|
||||
break;
|
||||
case 't':
|
||||
builder.Append('\t');
|
||||
i += 1;
|
||||
break;
|
||||
case 'n':
|
||||
builder.Append('\n');
|
||||
i += 1;
|
||||
break;
|
||||
case 'f':
|
||||
builder.Append('\f');
|
||||
i += 1;
|
||||
break;
|
||||
case 'r':
|
||||
builder.Append('\r');
|
||||
i += 1;
|
||||
break;
|
||||
default:
|
||||
builder.Append(str[i + 1]);
|
||||
i += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static int ExtractEscapeSequence(string str, int i, StringBuilder builder)
|
||||
{
|
||||
var startIndex = i + 2;
|
||||
var endIndex = i + 5;
|
||||
if (endIndex > str.Length - 1)
|
||||
{
|
||||
builder.Append(str[i + 1]);
|
||||
i += 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
var hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
|
||||
builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
|
||||
i += 5;
|
||||
return i;
|
||||
}
|
||||
|
||||
private int GetFieldNameCount()
|
||||
{
|
||||
var charCount = 0;
|
||||
for (var j = _index + 1; j < _json.Length; j++)
|
||||
{
|
||||
if (_json[j] == StringQuotedChar && _json[j - 1] != StringEscapeChar)
|
||||
break;
|
||||
|
||||
charCount++;
|
||||
}
|
||||
|
||||
return charCount;
|
||||
}
|
||||
|
||||
private void ExtractObject()
|
||||
{
|
||||
// Extract and set the value
|
||||
var deserializer = new Deserializer(_json, _index);
|
||||
|
||||
if (_currentFieldName != null)
|
||||
_resultObject[_currentFieldName] = deserializer._result;
|
||||
else
|
||||
_resultArray.Add(deserializer._result);
|
||||
|
||||
_index = deserializer._index;
|
||||
}
|
||||
|
||||
private void ExtractNumber()
|
||||
{
|
||||
var charCount = 0;
|
||||
for (var j = _index; j < _json.Length; j++)
|
||||
{
|
||||
if (char.IsWhiteSpace(_json[j]) || _json[j] == FieldSeparatorChar
|
||||
|| (_resultObject != null && _json[j] == CloseObjectChar)
|
||||
|| (_resultArray != null && _json[j] == CloseArrayChar))
|
||||
break;
|
||||
|
||||
charCount++;
|
||||
}
|
||||
|
||||
// Extract and set the value
|
||||
var stringValue = _json.SliceLength(_index, charCount);
|
||||
|
||||
if (decimal.TryParse(stringValue, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out var value) == false)
|
||||
throw CreateParserException("[number]");
|
||||
|
||||
if (_currentFieldName != null)
|
||||
_resultObject[_currentFieldName] = value;
|
||||
else
|
||||
_resultArray.Add(value);
|
||||
|
||||
_index += charCount - 1;
|
||||
}
|
||||
|
||||
private void ExtractConstant(string boolValue, bool? value = null)
|
||||
{
|
||||
if (_json.SliceLength(_index, boolValue.Length) != boolValue)
|
||||
throw CreateParserException($"'{ValueSeparatorChar}'");
|
||||
|
||||
// Extract and set the value
|
||||
if (_currentFieldName != null)
|
||||
_resultObject[_currentFieldName] = value;
|
||||
else
|
||||
_resultArray.Add(value);
|
||||
|
||||
_index += boolValue.Length - 1;
|
||||
}
|
||||
|
||||
private void ExtractStringQuoted()
|
||||
{
|
||||
var charCount = 0;
|
||||
var escapeCharFound = false;
|
||||
for (var j = _index + 1; j < _json.Length; j++)
|
||||
{
|
||||
if (_json[j] == StringQuotedChar && !escapeCharFound)
|
||||
break;
|
||||
|
||||
escapeCharFound = _json[j] == StringEscapeChar && !escapeCharFound;
|
||||
charCount++;
|
||||
}
|
||||
|
||||
// Extract and set the value
|
||||
var value = Unescape(_json.SliceLength(_index + 1, charCount));
|
||||
if (_currentFieldName != null)
|
||||
_resultObject[_currentFieldName] = value;
|
||||
else
|
||||
_resultArray.Add(value);
|
||||
|
||||
_index += charCount + 1;
|
||||
}
|
||||
|
||||
private FormatException CreateParserException(string expected)
|
||||
{
|
||||
var (line, col) = _json.TextPositionAt(_index);
|
||||
return new FormatException(
|
||||
$"Parser error (Line {line}, Col {col}, State {_state}): Expected {expected} but got '{_json[_index]}'.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the different JSON read states.
|
||||
/// </summary>
|
||||
private enum ReadState
|
||||
{
|
||||
WaitingForRootOpen,
|
||||
WaitingForField,
|
||||
WaitingForColon,
|
||||
WaitingForValue,
|
||||
WaitingForNextOrRootClose,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
370
Vendor/Swan.Lite-3.1.0/Formatters/Json.Serializer.cs
vendored
Normal file
370
Vendor/Swan.Lite-3.1.0/Formatters/Json.Serializer.cs
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// A very simple, light-weight JSON library written by Mario
|
||||
/// to teach Geo how things are done
|
||||
///
|
||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||
/// serializer such as the beloved Json.NET.
|
||||
/// </summary>
|
||||
public partial class Json
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple JSON serializer.
|
||||
/// </summary>
|
||||
private class Serializer
|
||||
{
|
||||
#region Private Declarations
|
||||
|
||||
private static readonly Dictionary<int, string> IndentStrings = new Dictionary<int, string>();
|
||||
|
||||
private readonly SerializerOptions _options;
|
||||
private readonly string _result;
|
||||
private readonly StringBuilder _builder;
|
||||
private readonly string _lastCommaSearch;
|
||||
private readonly string[]? _excludedNames = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Serializer" /> class.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="depth">The depth.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
private Serializer(object? obj, int depth, SerializerOptions options, string[]? excludedNames = null)
|
||||
{
|
||||
if (depth > 20)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The max depth (20) has been reached. Serializer can not continue.");
|
||||
}
|
||||
|
||||
// Basic Type Handling (nulls, strings, number, date and bool)
|
||||
_result = ResolveBasicType(obj);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_result))
|
||||
return;
|
||||
|
||||
_options = options;
|
||||
_excludedNames ??= excludedNames;
|
||||
_options.ExcludeProperties = GetExcludedNames(obj?.GetType(), _excludedNames);
|
||||
|
||||
// Handle circular references correctly and avoid them
|
||||
if (options.IsObjectPresent(obj!))
|
||||
{
|
||||
_result = $"{{ \"$circref\": \"{Escape(obj!.GetHashCode().ToStringInvariant(), false)}\" }}";
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, we will need to construct the object with a StringBuilder.
|
||||
_lastCommaSearch = FieldSeparatorChar + (_options.Format ? Environment.NewLine : string.Empty);
|
||||
_builder = new StringBuilder();
|
||||
|
||||
_result = obj switch
|
||||
{
|
||||
IDictionary itemsZero when itemsZero.Count == 0 => EmptyObjectLiteral,
|
||||
IDictionary items => ResolveDictionary(items, depth),
|
||||
IEnumerable enumerableZero when !enumerableZero.Cast<object>().Any() => EmptyArrayLiteral,
|
||||
IEnumerable enumerableBytes when enumerableBytes is byte[] bytes => Serialize(bytes.ToBase64(), depth, _options, _excludedNames),
|
||||
IEnumerable enumerable => ResolveEnumerable(enumerable, depth),
|
||||
_ => ResolveObject(obj!, depth)
|
||||
};
|
||||
}
|
||||
|
||||
internal static string Serialize(object? obj, int depth, SerializerOptions options, string[]? excludedNames = null) => new Serializer(obj, depth, options, excludedNames)._result;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
internal static string[]? GetExcludedNames(Type? type, string[]? excludedNames)
|
||||
{
|
||||
if (type == null)
|
||||
return excludedNames;
|
||||
|
||||
var excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
|
||||
.Where(x => AttributeCache.DefaultCache.Value.RetrieveOne<JsonPropertyAttribute>(x)?.Ignored == true)
|
||||
.Select(x => x.Name));
|
||||
|
||||
if (excludedByAttr?.Any() != true)
|
||||
return excludedNames;
|
||||
|
||||
return excludedNames?.Any(string.IsNullOrWhiteSpace) == true
|
||||
? excludedByAttr.Intersect(excludedNames.Where(y => !string.IsNullOrWhiteSpace(y))).ToArray()
|
||||
: excludedByAttr.ToArray();
|
||||
}
|
||||
|
||||
private static string ResolveBasicType(object? obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case null:
|
||||
return NullLiteral;
|
||||
case string s:
|
||||
return Escape(s, true);
|
||||
case bool b:
|
||||
return b ? TrueLiteral : FalseLiteral;
|
||||
case Type _:
|
||||
case Assembly _:
|
||||
case MethodInfo _:
|
||||
case PropertyInfo _:
|
||||
case EventInfo _:
|
||||
return Escape(obj.ToString(), true);
|
||||
case DateTime d:
|
||||
return $"{StringQuotedChar}{d:s}{StringQuotedChar}";
|
||||
default:
|
||||
var targetType = obj.GetType();
|
||||
|
||||
if (!Definitions.BasicTypesInfo.Value.ContainsKey(targetType))
|
||||
return string.Empty;
|
||||
|
||||
var escapedValue = Escape(Definitions.BasicTypesInfo.Value[targetType].ToStringInvariant(obj), false);
|
||||
|
||||
return decimal.TryParse(escapedValue, out _)
|
||||
? $"{escapedValue}"
|
||||
: $"{StringQuotedChar}{escapedValue}{StringQuotedChar}";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNonEmptyJsonArrayOrObject(string serialized)
|
||||
{
|
||||
if (serialized == EmptyObjectLiteral || serialized == EmptyArrayLiteral) return false;
|
||||
|
||||
// find the first position the character is not a space
|
||||
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
|
||||
}
|
||||
|
||||
private static string Escape(string str, bool quoted)
|
||||
{
|
||||
if (str == null)
|
||||
return string.Empty;
|
||||
|
||||
var builder = new StringBuilder(str.Length * 2);
|
||||
if (quoted) builder.Append(StringQuotedChar);
|
||||
Escape(str, builder);
|
||||
if (quoted) builder.Append(StringQuotedChar);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static void Escape(string str, StringBuilder builder)
|
||||
{
|
||||
foreach (var currentChar in str)
|
||||
{
|
||||
switch (currentChar)
|
||||
{
|
||||
case '\\':
|
||||
case '"':
|
||||
case '/':
|
||||
builder
|
||||
.Append('\\')
|
||||
.Append(currentChar);
|
||||
break;
|
||||
case '\b':
|
||||
builder.Append("\\b");
|
||||
break;
|
||||
case '\t':
|
||||
builder.Append("\\t");
|
||||
break;
|
||||
case '\n':
|
||||
builder.Append("\\n");
|
||||
break;
|
||||
case '\f':
|
||||
builder.Append("\\f");
|
||||
break;
|
||||
case '\r':
|
||||
builder.Append("\\r");
|
||||
break;
|
||||
default:
|
||||
if (currentChar < ' ')
|
||||
{
|
||||
var escapeBytes = BitConverter.GetBytes((ushort)currentChar);
|
||||
if (BitConverter.IsLittleEndian == false)
|
||||
Array.Reverse(escapeBytes);
|
||||
|
||||
builder.Append("\\u")
|
||||
.Append(escapeBytes[1].ToString("X", CultureInfo.InvariantCulture).PadLeft(2, '0'))
|
||||
.Append(escapeBytes[0].ToString("X", CultureInfo.InvariantCulture).PadLeft(2, '0'));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(currentChar);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, object?> CreateDictionary(
|
||||
Dictionary<string, MemberInfo> fields,
|
||||
string targetType,
|
||||
object target)
|
||||
{
|
||||
// Create the dictionary and extract the properties
|
||||
var objectDictionary = new Dictionary<string, object?>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_options.TypeSpecifier))
|
||||
objectDictionary[_options.TypeSpecifier!] = targetType;
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
// Build the dictionary using property names and values
|
||||
// Note: used to be: property.GetValue(target); but we would be reading private properties
|
||||
try
|
||||
{
|
||||
objectDictionary[field.Key] = field.Value is PropertyInfo property
|
||||
? target.ReadProperty(property.Name)
|
||||
: (field.Value as FieldInfo)?.GetValue(target);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
/* ignored */
|
||||
}
|
||||
}
|
||||
|
||||
return objectDictionary;
|
||||
}
|
||||
|
||||
private string ResolveDictionary(IDictionary items, int depth)
|
||||
{
|
||||
Append(OpenObjectChar, depth);
|
||||
AppendLine();
|
||||
|
||||
// Iterate through the elements and output recursively
|
||||
var writeCount = 0;
|
||||
foreach (var key in items.Keys)
|
||||
{
|
||||
// Serialize and append the key (first char indented)
|
||||
Append(StringQuotedChar, depth + 1);
|
||||
Escape(key.ToString(), _builder);
|
||||
_builder
|
||||
.Append(StringQuotedChar)
|
||||
.Append(ValueSeparatorChar)
|
||||
.Append(" ");
|
||||
|
||||
// Serialize and append the value
|
||||
var serializedValue = Serialize(items[key], depth + 1, _options, _excludedNames);
|
||||
|
||||
if (IsNonEmptyJsonArrayOrObject(serializedValue)) AppendLine();
|
||||
Append(serializedValue, 0);
|
||||
|
||||
// Add a comma and start a new line -- We will remove the last one when we are done writing the elements
|
||||
Append(FieldSeparatorChar, 0);
|
||||
AppendLine();
|
||||
writeCount++;
|
||||
}
|
||||
|
||||
// Output the end of the object and set the result
|
||||
RemoveLastComma();
|
||||
Append(CloseObjectChar, writeCount > 0 ? depth : 0);
|
||||
return _builder.ToString();
|
||||
}
|
||||
|
||||
private string ResolveObject(object target, int depth)
|
||||
{
|
||||
var targetType = target.GetType();
|
||||
|
||||
if (targetType.IsEnum)
|
||||
return Convert.ToInt64(target, CultureInfo.InvariantCulture).ToStringInvariant();
|
||||
|
||||
var fields = _options.GetProperties(targetType);
|
||||
|
||||
if (fields.Count == 0 && string.IsNullOrWhiteSpace(_options.TypeSpecifier))
|
||||
return EmptyObjectLiteral;
|
||||
|
||||
// If we arrive here, then we convert the object into a
|
||||
// dictionary of property names and values and call the serialization
|
||||
// function again
|
||||
var objectDictionary = CreateDictionary(fields, targetType.ToString(), target);
|
||||
|
||||
return Serialize(objectDictionary, depth, _options, _excludedNames);
|
||||
}
|
||||
|
||||
private string ResolveEnumerable(IEnumerable target, int depth)
|
||||
{
|
||||
// Cast the items as a generic object array
|
||||
var items = target.Cast<object>();
|
||||
|
||||
Append(OpenArrayChar, depth);
|
||||
AppendLine();
|
||||
|
||||
// Iterate through the elements and output recursively
|
||||
var writeCount = 0;
|
||||
foreach (var entry in items)
|
||||
{
|
||||
var serializedValue = Serialize(entry, depth + 1, _options, _excludedNames);
|
||||
|
||||
if (IsNonEmptyJsonArrayOrObject(serializedValue))
|
||||
Append(serializedValue, 0);
|
||||
else
|
||||
Append(serializedValue, depth + 1);
|
||||
|
||||
Append(FieldSeparatorChar, 0);
|
||||
AppendLine();
|
||||
writeCount++;
|
||||
}
|
||||
|
||||
// Output the end of the array and set the result
|
||||
RemoveLastComma();
|
||||
Append(CloseArrayChar, writeCount > 0 ? depth : 0);
|
||||
return _builder.ToString();
|
||||
}
|
||||
|
||||
private void SetIndent(int depth)
|
||||
{
|
||||
if (_options.Format == false || depth <= 0) return;
|
||||
|
||||
_builder.Append(IndentStrings.GetOrAdd(depth, x => new string(' ', x * 4)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the last comma in the current string builder.
|
||||
/// </summary>
|
||||
private void RemoveLastComma()
|
||||
{
|
||||
if (_builder.Length < _lastCommaSearch.Length)
|
||||
return;
|
||||
|
||||
if (_lastCommaSearch.Where((t, i) => _builder[_builder.Length - _lastCommaSearch.Length + i] != t).Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got this far, we simply remove the comma character
|
||||
_builder.Remove(_builder.Length - _lastCommaSearch.Length, 1);
|
||||
}
|
||||
|
||||
private void Append(string text, int depth)
|
||||
{
|
||||
SetIndent(depth);
|
||||
_builder.Append(text);
|
||||
}
|
||||
|
||||
private void Append(char text, int depth)
|
||||
{
|
||||
SetIndent(depth);
|
||||
_builder.Append(text);
|
||||
}
|
||||
|
||||
private void AppendLine()
|
||||
{
|
||||
if (_options.Format == false) return;
|
||||
_builder.Append(Environment.NewLine);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
152
Vendor/Swan.Lite-3.1.0/Formatters/Json.SerializerOptions.cs
vendored
Normal file
152
Vendor/Swan.Lite-3.1.0/Formatters/Json.SerializerOptions.cs
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// A very simple, light-weight JSON library written by Mario
|
||||
/// to teach Geo how things are done
|
||||
///
|
||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||
/// serializer such as the beloved Json.NET.
|
||||
/// </summary>
|
||||
public class SerializerOptions
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>
|
||||
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>();
|
||||
|
||||
private readonly string[]? _includeProperties;
|
||||
private readonly Dictionary<int, List<WeakReference>> _parentReferences = new Dictionary<int, List<WeakReference>>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SerializerOptions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="format">if set to <c>true</c> [format].</param>
|
||||
/// <param name="typeSpecifier">The type specifier.</param>
|
||||
/// <param name="includeProperties">The include properties.</param>
|
||||
/// <param name="excludeProperties">The exclude properties.</param>
|
||||
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
|
||||
/// <param name="parentReferences">The parent references.</param>
|
||||
/// <param name="jsonSerializerCase">The json serializer case.</param>
|
||||
public SerializerOptions(
|
||||
bool format,
|
||||
string? typeSpecifier,
|
||||
string[]? includeProperties,
|
||||
string[]? excludeProperties = null,
|
||||
bool includeNonPublic = true,
|
||||
IReadOnlyCollection<WeakReference>? parentReferences = null,
|
||||
JsonSerializerCase jsonSerializerCase = JsonSerializerCase.None)
|
||||
{
|
||||
_includeProperties = includeProperties;
|
||||
|
||||
ExcludeProperties = excludeProperties;
|
||||
IncludeNonPublic = includeNonPublic;
|
||||
Format = format;
|
||||
TypeSpecifier = typeSpecifier;
|
||||
JsonSerializerCase = jsonSerializerCase;
|
||||
|
||||
if (parentReferences == null)
|
||||
return;
|
||||
|
||||
foreach (var parentReference in parentReferences.Where(x => x.IsAlive))
|
||||
{
|
||||
IsObjectPresent(parentReference.Target);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="SerializerOptions"/> is format.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if format; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool Format { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type specifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type specifier.
|
||||
/// </value>
|
||||
public string? TypeSpecifier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [include non public].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [include non public]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IncludeNonPublic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the json serializer case.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The json serializer case.
|
||||
/// </value>
|
||||
public JsonSerializerCase JsonSerializerCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exclude properties.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The exclude properties.
|
||||
/// </value>
|
||||
public string[]? ExcludeProperties { get; set; }
|
||||
|
||||
internal bool IsObjectPresent(object target)
|
||||
{
|
||||
var hashCode = target.GetHashCode();
|
||||
|
||||
if (_parentReferences.ContainsKey(hashCode))
|
||||
{
|
||||
if (_parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target)))
|
||||
return true;
|
||||
|
||||
_parentReferences[hashCode].Add(new WeakReference(target));
|
||||
return false;
|
||||
}
|
||||
|
||||
_parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
|
||||
return false;
|
||||
}
|
||||
|
||||
internal Dictionary<string, MemberInfo> GetProperties(Type targetType)
|
||||
=> GetPropertiesCache(targetType)
|
||||
.When(() => _includeProperties?.Length > 0,
|
||||
query => query.Where(p => _includeProperties.Contains(p.Key.Item1)))
|
||||
.When(() => ExcludeProperties?.Length > 0,
|
||||
query => query.Where(p => !ExcludeProperties.Contains(p.Key.Item1)))
|
||||
.ToDictionary(x => x.Key.Item2, x => x.Value);
|
||||
|
||||
private Dictionary<Tuple<string, string>, MemberInfo> GetPropertiesCache(Type targetType)
|
||||
{
|
||||
if (TypeCache.TryGetValue(targetType, out var current))
|
||||
return current;
|
||||
|
||||
var fields =
|
||||
new List<MemberInfo>(PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(targetType).Where(p => p.CanRead));
|
||||
|
||||
// If the target is a struct (value type) navigate the fields.
|
||||
if (targetType.IsValueType)
|
||||
{
|
||||
fields.AddRange(FieldTypeCache.DefaultCache.Value.RetrieveAllFields(targetType));
|
||||
}
|
||||
|
||||
var value = fields
|
||||
.Where(x => x.GetCustomAttribute<JsonPropertyAttribute>()?.Ignored != true)
|
||||
.ToDictionary(
|
||||
x => Tuple.Create(x.Name,
|
||||
x.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? x.Name.GetNameWithCase(JsonSerializerCase)),
|
||||
x => x);
|
||||
|
||||
TypeCache.TryAdd(targetType, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
373
Vendor/Swan.Lite-3.1.0/Formatters/Json.cs
vendored
Normal file
373
Vendor/Swan.Lite-3.1.0/Formatters/Json.cs
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Swan.Collections;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// A very simple, light-weight JSON library written by Mario
|
||||
/// to teach Geo how things are done
|
||||
///
|
||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||
/// serializer such as the beloved Json.NET.
|
||||
/// </summary>
|
||||
public static partial class Json
|
||||
{
|
||||
#region Constants
|
||||
|
||||
internal const string AddMethodName = "Add";
|
||||
|
||||
private const char OpenObjectChar = '{';
|
||||
private const char CloseObjectChar = '}';
|
||||
|
||||
private const char OpenArrayChar = '[';
|
||||
private const char CloseArrayChar = ']';
|
||||
|
||||
private const char FieldSeparatorChar = ',';
|
||||
private const char ValueSeparatorChar = ':';
|
||||
|
||||
private const char StringEscapeChar = '\\';
|
||||
private const char StringQuotedChar = '"';
|
||||
|
||||
private const string EmptyObjectLiteral = "{ }";
|
||||
private const string EmptyArrayLiteral = "[ ]";
|
||||
private const string TrueLiteral = "true";
|
||||
private const string FalseLiteral = "false";
|
||||
private const string NullLiteral = "null";
|
||||
|
||||
#endregion
|
||||
|
||||
private static readonly CollectionCacheRepository<string> IgnoredPropertiesCache = new CollectionCacheRepository<string>();
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object into a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
|
||||
/// <param name="typeSpecifier">The type specifier. Leave null or empty to avoid setting.</param>
|
||||
/// <param name="includeNonPublic">if set to <c>true</c> non-public getters will be also read.</param>
|
||||
/// <param name="includedNames">The included property names.</param>
|
||||
/// <param name="excludedNames">The excluded property names.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents the current object.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// The following example describes how to serialize a simple object.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// var obj = new { One = "One", Two = "Two" };
|
||||
///
|
||||
/// var serial = Json.Serialize(obj); // {"One": "One","Two": "Two"}
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// The following example details how to serialize an object using the <see cref="JsonPropertyAttribute"/>.
|
||||
///
|
||||
/// <code>
|
||||
/// using Swan.Attributes;
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// class JsonPropertyExample
|
||||
/// {
|
||||
/// [JsonProperty("data")]
|
||||
/// public string Data { get; set; }
|
||||
///
|
||||
/// [JsonProperty("ignoredData", true)]
|
||||
/// public string IgnoredData { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// var obj = new JsonPropertyExample() { Data = "OK", IgnoredData = "OK" };
|
||||
///
|
||||
/// // {"data": "OK"}
|
||||
/// var serializedObj = Json.Serialize(obj);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static string Serialize(
|
||||
object? obj,
|
||||
bool format = false,
|
||||
string? typeSpecifier = null,
|
||||
bool includeNonPublic = false,
|
||||
string[]? includedNames = null,
|
||||
params string[] excludedNames) =>
|
||||
Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null, JsonSerializerCase.None);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object into a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="jsonSerializerCase">The json serializer case.</param>
|
||||
/// <param name="format">if set to <c>true</c> [format].</param>
|
||||
/// <param name="typeSpecifier">The type specifier.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents the current object.
|
||||
/// </returns>
|
||||
public static string Serialize(
|
||||
object? obj,
|
||||
JsonSerializerCase jsonSerializerCase,
|
||||
bool format = false,
|
||||
string? typeSpecifier = null) => Serialize(obj, format, typeSpecifier, false, null, null, null, jsonSerializerCase);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object into a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
|
||||
/// <param name="typeSpecifier">The type specifier. Leave null or empty to avoid setting.</param>
|
||||
/// <param name="includeNonPublic">if set to <c>true</c> non-public getters will be also read.</param>
|
||||
/// <param name="includedNames">The included property names.</param>
|
||||
/// <param name="excludedNames">The excluded property names.</param>
|
||||
/// <param name="parentReferences">The parent references.</param>
|
||||
/// <param name="jsonSerializerCase">The json serializer case.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents the current object.
|
||||
/// </returns>
|
||||
public static string Serialize(
|
||||
object? obj,
|
||||
bool format,
|
||||
string? typeSpecifier,
|
||||
bool includeNonPublic,
|
||||
string[]? includedNames,
|
||||
string[]? excludedNames,
|
||||
List<WeakReference>? parentReferences,
|
||||
JsonSerializerCase jsonSerializerCase)
|
||||
{
|
||||
if (obj != null && (obj is string || Definitions.AllBasicValueTypes.Contains(obj.GetType())))
|
||||
{
|
||||
return SerializePrimitiveValue(obj);
|
||||
}
|
||||
|
||||
var options = new SerializerOptions(
|
||||
format,
|
||||
typeSpecifier,
|
||||
includedNames,
|
||||
Serializer.GetExcludedNames(obj?.GetType(), excludedNames),
|
||||
includeNonPublic,
|
||||
parentReferences,
|
||||
jsonSerializerCase);
|
||||
|
||||
return Serializer.Serialize(obj, 0, options, excludedNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object using the SerializerOptions provided.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="string" /> that represents the current object.
|
||||
/// </returns>
|
||||
public static string Serialize(object? obj, SerializerOptions options) => Serializer.Serialize(obj, 0, options);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object only including the specified property names.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
|
||||
/// <param name="includeNames">The include names.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
/// <example>
|
||||
/// The following example shows how to serialize a simple object including the specified properties.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // object to serialize
|
||||
/// var obj = new { One = "One", Two = "Two", Three = "Three" };
|
||||
///
|
||||
/// // the included names
|
||||
/// var includedNames = new[] { "Two", "Three" };
|
||||
///
|
||||
/// // serialize only the included names
|
||||
/// var data = Json.SerializeOnly(basicObject, true, includedNames);
|
||||
/// // {"Two": "Two","Three": "Three" }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static string SerializeOnly(object? obj, bool format, params string[] includeNames)
|
||||
=> Serialize(obj, new SerializerOptions(format, null, includeNames));
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object excluding the specified property names.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
|
||||
/// <param name="excludeNames">The exclude names.</param>
|
||||
/// <returns>A <see cref="string" /> that represents the current object.</returns>
|
||||
/// <example>
|
||||
/// The following code shows how to serialize a simple object excluding the specified properties.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // object to serialize
|
||||
/// var obj = new { One = "One", Two = "Two", Three = "Three" };
|
||||
///
|
||||
/// // the excluded names
|
||||
/// var excludeNames = new[] { "Two", "Three" };
|
||||
///
|
||||
/// // serialize excluding
|
||||
/// var data = Json.SerializeExcluding(basicObject, false, includedNames);
|
||||
/// // {"One": "One"}
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static string SerializeExcluding(object? obj, bool format, params string[] excludeNames)
|
||||
=> Serializer.Serialize(obj, 0, new SerializerOptions(format, null, null), excludeNames);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the specified json string as either a Dictionary[string, object] or as a List[object]
|
||||
/// depending on the syntax of the JSON string.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON string.</param>
|
||||
/// <param name="jsonSerializerCase">The json serializer case.</param>
|
||||
/// <returns>
|
||||
/// Type of the current deserializes.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// The following code shows how to deserialize a JSON string into a Dictionary.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // json to deserialize
|
||||
/// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
|
||||
/// // deserializes the specified json into a Dictionary<string, object>.
|
||||
/// var data = Json.Deserialize(basicJson, JsonSerializerCase.None);
|
||||
/// }
|
||||
/// }
|
||||
/// </code></example>
|
||||
public static object? Deserialize(string json, JsonSerializerCase jsonSerializerCase) =>
|
||||
json == null
|
||||
? throw new ArgumentNullException(nameof(json))
|
||||
: Converter.FromJsonResult(Deserializer.DeserializeInternal(json), jsonSerializerCase);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the specified json string as either a Dictionary[string, object] or as a List[object]
|
||||
/// depending on the syntax of the JSON string.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON string.</param>
|
||||
/// <returns>
|
||||
/// Type of the current deserializes.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// The following code shows how to deserialize a JSON string into a Dictionary.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // json to deserialize
|
||||
/// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
|
||||
/// // deserializes the specified json into a Dictionary<string, object>.
|
||||
/// var data = Json.Deserialize(basicJson);
|
||||
/// }
|
||||
/// }
|
||||
/// </code></example>
|
||||
public static object? Deserialize(string json) =>
|
||||
json == null
|
||||
? throw new ArgumentNullException(nameof(json))
|
||||
: Deserialize(json, JsonSerializerCase.None);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the specified JSON string and converts it to the specified object type.
|
||||
/// Non-public constructors and property setters are ignored.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to deserialize.</typeparam>
|
||||
/// <param name="json">The JSON string.</param>
|
||||
/// <param name="jsonSerializerCase">The JSON serializer case.</param>
|
||||
/// <returns>
|
||||
/// The deserialized specified type object.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// The following code describes how to deserialize a JSON string into an object of type T.
|
||||
/// <code>
|
||||
/// using Swan.Formatters;
|
||||
/// class Example
|
||||
/// {
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // json type BasicJson to serialize
|
||||
/// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
|
||||
/// // deserializes the specified string in a new instance of the type BasicJson.
|
||||
/// var data = Json.Deserialize<BasicJson>(basicJson);
|
||||
/// }
|
||||
/// }
|
||||
/// </code></example>
|
||||
public static T Deserialize<T>(string json, JsonSerializerCase jsonSerializerCase = JsonSerializerCase.None) =>
|
||||
json == null
|
||||
? throw new ArgumentNullException(nameof(json))
|
||||
: (T)Deserialize(json, typeof(T), jsonSerializerCase: jsonSerializerCase);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the specified JSON string and converts it to the specified object type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to deserialize.</typeparam>
|
||||
/// <param name="json">The JSON string.</param>
|
||||
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
|
||||
/// <returns>
|
||||
/// The deserialized specified type object.
|
||||
/// </returns>
|
||||
public static T Deserialize<T>(string json, bool includeNonPublic) =>
|
||||
json == null
|
||||
? throw new ArgumentNullException(nameof(json))
|
||||
: (T)Deserialize(json, typeof(T), includeNonPublic);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the specified JSON string and converts it to the specified object type.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON string.</param>
|
||||
/// <param name="resultType">Type of the result.</param>
|
||||
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
|
||||
/// <param name="jsonSerializerCase">The json serializer case.</param>
|
||||
/// <returns>
|
||||
/// Type of the current conversion from json result.
|
||||
/// </returns>
|
||||
public static object? Deserialize(string json, Type resultType, bool includeNonPublic = false, JsonSerializerCase jsonSerializerCase = JsonSerializerCase.None) =>
|
||||
json == null
|
||||
? throw new ArgumentNullException(nameof(json))
|
||||
: Converter.FromJsonResult(
|
||||
Deserializer.DeserializeInternal(json),
|
||||
jsonSerializerCase,
|
||||
resultType,
|
||||
includeNonPublic);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private API
|
||||
private static string SerializePrimitiveValue(object obj) =>
|
||||
obj switch
|
||||
{
|
||||
string stringValue => $"\"{stringValue}\"",
|
||||
bool boolValue => boolValue ? TrueLiteral : FalseLiteral,
|
||||
_ => obj.ToString()
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
Vendor/Swan.Lite-3.1.0/Formatters/JsonPropertyAttribute.cs
vendored
Normal file
39
Vendor/Swan.Lite-3.1.0/Formatters/JsonPropertyAttribute.cs
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute used to help setup a property behavior when serialize/deserialize JSON.
|
||||
/// </summary>
|
||||
/// <seealso cref="Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class JsonPropertyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <param name="ignored">if set to <c>true</c> [ignored].</param>
|
||||
public JsonPropertyAttribute(string propertyName, bool ignored = false)
|
||||
{
|
||||
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
|
||||
Ignored = ignored;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the property.
|
||||
/// </value>
|
||||
public string PropertyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if ignored; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool Ignored { get; }
|
||||
}
|
||||
}
|
||||
23
Vendor/Swan.Lite-3.1.0/Formatters/JsonSerializerCase.cs
vendored
Normal file
23
Vendor/Swan.Lite-3.1.0/Formatters/JsonSerializerCase.cs
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace Swan.Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the JSON serializer cases to use: None (keeps the same case), PascalCase, or camelCase.
|
||||
/// </summary>
|
||||
public enum JsonSerializerCase
|
||||
{
|
||||
/// <summary>
|
||||
/// The none
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The pascal case (eg. PascalCase)
|
||||
/// </summary>
|
||||
PascalCase,
|
||||
|
||||
/// <summary>
|
||||
/// The camel case (eg. camelCase)
|
||||
/// </summary>
|
||||
CamelCase,
|
||||
}
|
||||
}
|
||||
347
Vendor/Swan.Lite-3.1.0/FromString.cs
vendored
Normal file
347
Vendor/Swan.Lite-3.1.0/FromString.cs
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a standard way to convert strings to different types.
|
||||
/// </summary>
|
||||
public static class FromString
|
||||
{
|
||||
// It doesn't matter which converter we get here: ConvertFromInvariantString is not virtual.
|
||||
private static readonly MethodInfo ConvertFromInvariantStringMethod
|
||||
= new Func<string, object>(TypeDescriptor.GetConverter(typeof(int)).ConvertFromInvariantString).Method;
|
||||
|
||||
private static readonly MethodInfo TryConvertToInternalMethod
|
||||
= typeof(FromString).GetMethod(nameof(TryConvertToInternal), BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
private static readonly MethodInfo ConvertToInternalMethod
|
||||
= typeof(FromString).GetMethod(nameof(ConvertToInternal), BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, Func<string[], (bool Success, object Result)>> GenericTryConvertToMethods
|
||||
= new ConcurrentDictionary<Type, Func<string[], (bool Success, object Result)>>();
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, Func<string[], object>> GenericConvertToMethods
|
||||
= new ConcurrentDictionary<Type, Func<string[], object>>();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a string can be converted to the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type resulting from the conversion.</param>
|
||||
/// <returns><see langword="true" /> if the conversion is possible;
|
||||
/// otherwise, <see langword="false" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
||||
public static bool CanConvertTo(Type type)
|
||||
=> TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a string can be converted to the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type resulting from the conversion.</typeparam>
|
||||
/// <returns><see langword="true" /> if the conversion is possible;
|
||||
/// otherwise, <see langword="false" />.</returns>
|
||||
public static bool CanConvertTo<TResult>()
|
||||
=> TypeDescriptor.GetConverter(typeof(TResult)).CanConvertFrom(typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to convert a string to the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type resulting from the conversion.</param>
|
||||
/// <param name="str">The string to convert.</param>
|
||||
/// <param name="result">When this method returns <see langword="true" />,
|
||||
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true" /> if the conversion is successful;
|
||||
/// otherwise, <see langword="false" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
||||
public static bool TryConvertTo(Type type, string str, out object? result)
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(type);
|
||||
if (!converter.CanConvertFrom(typeof(string)))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = converter.ConvertFromInvariantString(str);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to convert a string to the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type resulting from the conversion.</typeparam>
|
||||
/// <param name="str">The string to convert.</param>
|
||||
/// <param name="result">When this method returns <see langword="true" />,
|
||||
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true" /> if the conversion is successful;
|
||||
/// otherwise, <see langword="false" />.</returns>
|
||||
public static bool TryConvertTo<TResult>(string str, out TResult result)
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TResult));
|
||||
if (!converter.CanConvertFrom(typeof(string)))
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = (TResult)converter.ConvertFromInvariantString(str);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type resulting from the conversion.</param>
|
||||
/// <param name="str">The string to convert.</param>
|
||||
/// <returns>An instance of <paramref name="type" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="StringConversionException">The conversion was not successful.</exception>
|
||||
public static object ConvertTo(Type type, string str)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
try
|
||||
{
|
||||
return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(str);
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
throw new StringConversionException(type, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type resulting from the conversion.</typeparam>
|
||||
/// <param name="str">The string to convert.</param>
|
||||
/// <returns>An instance of <typeparamref name="TResult" />.</returns>
|
||||
/// <exception cref="StringConversionException">
|
||||
/// The conversion was not successful.
|
||||
/// </exception>
|
||||
public static TResult ConvertTo<TResult>(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (TResult)TypeDescriptor.GetConverter(typeof(TResult)).ConvertFromInvariantString(str);
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
throw new StringConversionException(typeof(TResult), e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to convert an array of strings to an array of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type resulting from the conversion of each
|
||||
/// element of <paramref name="strings"/>.</param>
|
||||
/// <param name="strings">The array to convert.</param>
|
||||
/// <param name="result">When this method returns <see langword="true" />,
|
||||
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true" /> if the conversion is successful;
|
||||
/// otherwise, <see langword="false" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
||||
public static bool TryConvertTo(Type type, string[] strings, out object? result)
|
||||
{
|
||||
if (strings == null)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var method = GenericTryConvertToMethods.GetOrAdd(type, BuildNonGenericTryConvertLambda);
|
||||
var (success, methodResult) = method(strings);
|
||||
result = methodResult;
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to convert an array of strings to an array of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type resulting from the conversion of each
|
||||
/// element of <paramref name="strings"/>.</typeparam>
|
||||
/// <param name="strings">The array to convert.</param>
|
||||
/// <param name="result">When this method returns <see langword="true" />,
|
||||
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true" /> if the conversion is successful;
|
||||
/// otherwise, <see langword="false" />.</returns>
|
||||
public static bool TryConvertTo<TResult>(string[] strings, out TResult[]? result)
|
||||
{
|
||||
if (strings == null)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TResult));
|
||||
if (!converter.CanConvertFrom(typeof(string)))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = new TResult[strings.Length];
|
||||
var i = 0;
|
||||
foreach (var str in strings)
|
||||
result[i++] = (TResult)converter.ConvertFromInvariantString(str);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of strings to an array of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type resulting from the conversion of each
|
||||
/// element of <paramref name="strings"/>.</param>
|
||||
/// <param name="strings">The array to convert.</param>
|
||||
/// <returns>An array of <paramref name="type" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="StringConversionException">The conversion of at least one
|
||||
/// of the elements of <paramref name="strings"/>was not successful.</exception>
|
||||
public static object? ConvertTo(Type type, string[] strings)
|
||||
{
|
||||
if (strings == null)
|
||||
return null;
|
||||
|
||||
var method = GenericConvertToMethods.GetOrAdd(type, BuildNonGenericConvertLambda);
|
||||
return method(strings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of strings to an array of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type resulting from the conversion of each
|
||||
/// element of <paramref name="strings"/>.</typeparam>
|
||||
/// <param name="strings">The array to convert.</param>
|
||||
/// <returns>An array of <typeparamref name="TResult" />.</returns>
|
||||
/// <exception cref="StringConversionException">The conversion of at least one
|
||||
/// of the elements of <paramref name="strings"/>was not successful.</exception>
|
||||
public static TResult[]? ConvertTo<TResult>(string[] strings)
|
||||
{
|
||||
if (strings == null)
|
||||
return null;
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TResult));
|
||||
var result = new TResult[strings.Length];
|
||||
var i = 0;
|
||||
try
|
||||
{
|
||||
foreach (var str in strings)
|
||||
result[i++] = (TResult)converter.ConvertFromInvariantString(str);
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
throw new StringConversionException(typeof(TResult), e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a expression, if the type can be converted to string, to a new expression including
|
||||
/// the conversion to string.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="str">The string.</param>
|
||||
/// <returns>A new expression where the previous expression is converted to string.</returns>
|
||||
public static Expression? ConvertExpressionTo(Type type, Expression str)
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(type);
|
||||
|
||||
return converter.CanConvertFrom(typeof(string))
|
||||
? Expression.Convert(
|
||||
Expression.Call(Expression.Constant(converter), ConvertFromInvariantStringMethod, str),
|
||||
type)
|
||||
: null;
|
||||
}
|
||||
|
||||
private static Func<string[], (bool Success, object Result)> BuildNonGenericTryConvertLambda(Type type)
|
||||
{
|
||||
var methodInfo = TryConvertToInternalMethod.MakeGenericMethod(type);
|
||||
var parameter = Expression.Parameter(typeof(string[]));
|
||||
var body = Expression.Call(methodInfo, parameter);
|
||||
var lambda = Expression.Lambda<Func<string[], (bool Success, object Result)>>(body, parameter);
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
private static (bool Success, object? Result) TryConvertToInternal<TResult>(string[] strings)
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TResult));
|
||||
if (!converter.CanConvertFrom(typeof(string)))
|
||||
return (false, null);
|
||||
|
||||
var result = new TResult[strings.Length];
|
||||
var i = 0;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var str in strings)
|
||||
result[i++] = (TResult)converter.ConvertFromInvariantString(str);
|
||||
|
||||
return (true, result);
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static Func<string[], object> BuildNonGenericConvertLambda(Type type)
|
||||
{
|
||||
var methodInfo = ConvertToInternalMethod.MakeGenericMethod(type);
|
||||
var parameter = Expression.Parameter(typeof(string[]));
|
||||
var body = Expression.Call(methodInfo, parameter);
|
||||
var lambda = Expression.Lambda<Func<string[], object>>(body, parameter);
|
||||
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
private static object ConvertToInternal<TResult>(string[] strings)
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TResult));
|
||||
var result = new TResult[strings.Length];
|
||||
var i = 0;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var str in strings)
|
||||
result[i++] = (TResult)converter.ConvertFromInvariantString(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e) when (!e.IsCriticalException())
|
||||
{
|
||||
throw new StringConversionException(typeof(TResult), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Vendor/Swan.Lite-3.1.0/InternalErrorException.cs
vendored
Normal file
46
Vendor/Swan.Lite-3.1.0/InternalErrorException.cs
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/*
|
||||
* NOTE TO CONTRIBUTORS:
|
||||
*
|
||||
* Never use this exception directly.
|
||||
* Use the methods in the SelfCheck class instead.
|
||||
*/
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
#pragma warning disable CA1032 // Add standard exception constructors.
|
||||
/// <summary>
|
||||
/// <para>The exception that is thrown by methods of the <see cref="SelfCheck"/> class
|
||||
/// to signal a condition most probably caused by an internal error in a library
|
||||
/// or application.</para>
|
||||
/// <para>Do not use this class directly; use the methods of the <see cref="SelfCheck"/> class instead.</para>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class InternalErrorException : Exception
|
||||
{
|
||||
#pragma warning disable SA1642 // Constructor summary documentation should begin with standard text - the <para> tag confuses the analyzer.
|
||||
/// <summary>
|
||||
/// <para>Initializes a new instance of the <see cref="InternalErrorException"/> class.</para>
|
||||
/// <para>Do not call this constrcutor directly; use the methods of the <see cref="SelfCheck"/> class instead.</para>
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
#pragma warning disable SA1642
|
||||
internal InternalErrorException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InternalErrorException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="SerializationInfo"></see> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="StreamingContext"></see> that contains contextual information about the source or destination.</param>
|
||||
private InternalErrorException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1032
|
||||
}
|
||||
145
Vendor/Swan.Lite-3.1.0/Logging/ConsoleLogger.cs
vendored
Normal file
145
Vendor/Swan.Lite-3.1.0/Logging/ConsoleLogger.cs
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Console implementation of <c>ILogger</c>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ILogger" />
|
||||
public class ConsoleLogger : TextLogger, ILogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsoleLogger"/> class.
|
||||
/// </summary>
|
||||
protected ConsoleLogger()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current instance of ConsoleLogger.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The instance.
|
||||
/// </value>
|
||||
public static ConsoleLogger Instance { get; } = new ConsoleLogger();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the debug logging prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The debug prefix.
|
||||
/// </value>
|
||||
public static string DebugPrefix { get; set; } = "DBG";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the trace logging prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The trace prefix.
|
||||
/// </value>
|
||||
public static string TracePrefix { get; set; } = "TRC";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the warning logging prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The warn prefix.
|
||||
/// </value>
|
||||
public static string WarnPrefix { get; set; } = "WRN";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fatal logging prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The fatal prefix.
|
||||
/// </value>
|
||||
public static string FatalPrefix { get; set; } = "FAT";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error logging prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The error prefix.
|
||||
/// </value>
|
||||
public static string ErrorPrefix { get; set; } = "ERR";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the information logging prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The information prefix.
|
||||
/// </value>
|
||||
public static string InfoPrefix { get; set; } = "INF";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the information output logging.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the information.
|
||||
/// </value>
|
||||
public static ConsoleColor InfoColor { get; set; } = ConsoleColor.Cyan;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the debug output logging.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the debug.
|
||||
/// </value>
|
||||
public static ConsoleColor DebugColor { get; set; } = ConsoleColor.Gray;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the trace output logging.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the trace.
|
||||
/// </value>
|
||||
public static ConsoleColor TraceColor { get; set; } = ConsoleColor.DarkGray;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the warning logging.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the warn.
|
||||
/// </value>
|
||||
public static ConsoleColor WarnColor { get; set; } = ConsoleColor.Yellow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the error logging.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the error.
|
||||
/// </value>
|
||||
public static ConsoleColor ErrorColor { get; set; } = ConsoleColor.DarkRed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the error logging.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the error.
|
||||
/// </value>
|
||||
public static ConsoleColor FatalColor { get; set; } = ConsoleColor.Red;
|
||||
|
||||
/// <inheritdoc />
|
||||
public LogLevel LogLevel { get; set; } = DebugLogger.IsDebuggerAttached ? LogLevel.Trace : LogLevel.Info;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Log(LogMessageReceivedEventArgs logEvent)
|
||||
{
|
||||
// Select the writer based on the message type
|
||||
var writer = logEvent.MessageType == LogLevel.Error
|
||||
? TerminalWriters.StandardError
|
||||
: TerminalWriters.StandardOutput;
|
||||
|
||||
var (outputMessage, color) = GetOutputAndColor(logEvent);
|
||||
|
||||
Terminal.Write(outputMessage, color, writer);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Vendor/Swan.Lite-3.1.0/Logging/DebugLogger.cs
vendored
Normal file
51
Vendor/Swan.Lite-3.1.0/Logging/DebugLogger.cs
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a logger target. This target will write to the
|
||||
/// Debug console using System.Diagnostics.Debug.
|
||||
/// </summary>
|
||||
/// <seealso cref="ILogger" />
|
||||
public class DebugLogger : TextLogger, ILogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DebugLogger"/> class.
|
||||
/// </summary>
|
||||
protected DebugLogger()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current instance of DebugLogger.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The instance.
|
||||
/// </value>
|
||||
public static DebugLogger Instance { get; } = new DebugLogger();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a debugger is attached.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is debugger attached; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool IsDebuggerAttached => System.Diagnostics.Debugger.IsAttached;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LogLevel LogLevel { get; set; } = IsDebuggerAttached ? LogLevel.Trace : LogLevel.None;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Log(LogMessageReceivedEventArgs logEvent)
|
||||
{
|
||||
var (outputMessage, _) = GetOutputAndColor(logEvent);
|
||||
|
||||
System.Diagnostics.Debug.Write(outputMessage);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
131
Vendor/Swan.Lite-3.1.0/Logging/FileLogger.cs
vendored
Normal file
131
Vendor/Swan.Lite-3.1.0/Logging/FileLogger.cs
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Swan.Threading;
|
||||
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to write into files the messages sent by the <see cref="Terminal" />.
|
||||
/// </summary>
|
||||
/// <seealso cref="ILogger" />
|
||||
public class FileLogger : TextLogger, ILogger
|
||||
{
|
||||
private readonly ManualResetEventSlim _doneEvent = new ManualResetEventSlim(true);
|
||||
private readonly ConcurrentQueue<string> _logQueue = new ConcurrentQueue<string>();
|
||||
private readonly ExclusiveTimer _timer;
|
||||
private readonly string _filePath;
|
||||
|
||||
private bool _disposedValue; // To detect redundant calls
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileLogger"/> class.
|
||||
/// </summary>
|
||||
public FileLogger()
|
||||
: this(SwanRuntime.EntryAssemblyDirectory, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileLogger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The filePath.</param>
|
||||
/// <param name="dailyFile">if set to <c>true</c> [daily file].</param>
|
||||
public FileLogger(string filePath, bool dailyFile)
|
||||
{
|
||||
_filePath = filePath;
|
||||
DailyFile = dailyFile;
|
||||
|
||||
_timer = new ExclusiveTimer(
|
||||
async () => await WriteLogEntries().ConfigureAwait(false),
|
||||
TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public LogLevel LogLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The file path.
|
||||
/// </value>
|
||||
public string FilePath => DailyFile
|
||||
? Path.Combine(Path.GetDirectoryName(_filePath), Path.GetFileNameWithoutExtension(_filePath) + $"_{DateTime.UtcNow:yyyyMMdd}" + Path.GetExtension(_filePath))
|
||||
: _filePath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [daily file].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [daily file]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool DailyFile { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Log(LogMessageReceivedEventArgs logEvent)
|
||||
{
|
||||
var (outputMessage, _) = GetOutputAndColor(logEvent);
|
||||
|
||||
_logQueue.Enqueue(outputMessage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposedValue) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_timer.Pause();
|
||||
_timer.Dispose();
|
||||
|
||||
_doneEvent.Wait();
|
||||
_doneEvent.Reset();
|
||||
WriteLogEntries(true).Await();
|
||||
_doneEvent.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
|
||||
private async Task WriteLogEntries(bool finalCall = false)
|
||||
{
|
||||
if (_logQueue.IsEmpty)
|
||||
return;
|
||||
|
||||
if (!finalCall && !_doneEvent.IsSet)
|
||||
return;
|
||||
|
||||
_doneEvent.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
using var file = File.AppendText(FilePath);
|
||||
while (!_logQueue.IsEmpty)
|
||||
{
|
||||
if (_logQueue.TryDequeue(out var entry))
|
||||
await file.WriteAsync(entry).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!finalCall)
|
||||
_doneEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Logging/ILogger.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Logging/ILogger.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Swan.Logging
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for a logger implementation.
|
||||
/// </summary>
|
||||
public interface ILogger : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the log level.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The log level.
|
||||
/// </value>
|
||||
LogLevel LogLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Logs the specified log event.
|
||||
/// </summary>
|
||||
/// <param name="logEvent">The <see cref="LogMessageReceivedEventArgs"/> instance containing the event data.</param>
|
||||
void Log(LogMessageReceivedEventArgs logEvent);
|
||||
}
|
||||
}
|
||||
43
Vendor/Swan.Lite-3.1.0/Logging/LogLevel.cs
vendored
Normal file
43
Vendor/Swan.Lite-3.1.0/Logging/LogLevel.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the log levels.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// The none message type
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The trace message type
|
||||
/// </summary>
|
||||
Trace,
|
||||
|
||||
/// <summary>
|
||||
/// The debug message type
|
||||
/// </summary>
|
||||
Debug,
|
||||
|
||||
/// <summary>
|
||||
/// The information message type
|
||||
/// </summary>
|
||||
Info,
|
||||
|
||||
/// <summary>
|
||||
/// The warning message type
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// The error message type
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// The fatal message type
|
||||
/// </summary>
|
||||
Fatal,
|
||||
}
|
||||
}
|
||||
131
Vendor/Swan.Lite-3.1.0/Logging/LogMessageReceivedEventArgs.cs
vendored
Normal file
131
Vendor/Swan.Lite-3.1.0/Logging/LogMessageReceivedEventArgs.cs
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Event arguments representing the message that is logged
|
||||
/// on to the terminal. Use the properties to forward the data to
|
||||
/// your logger of choice.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.EventArgs" />
|
||||
public class LogMessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="sequence">The sequence.</param>
|
||||
/// <param name="messageType">Type of the message.</param>
|
||||
/// <param name="utcDate">The UTC date.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public LogMessageReceivedEventArgs(
|
||||
ulong sequence,
|
||||
LogLevel messageType,
|
||||
DateTime utcDate,
|
||||
string? source,
|
||||
string message,
|
||||
object? extendedData,
|
||||
string callerMemberName,
|
||||
string callerFilePath,
|
||||
int callerLineNumber)
|
||||
{
|
||||
Sequence = sequence;
|
||||
MessageType = messageType;
|
||||
UtcDate = utcDate;
|
||||
Source = source;
|
||||
Message = message;
|
||||
CallerMemberName = callerMemberName;
|
||||
CallerFilePath = callerFilePath;
|
||||
CallerLineNumber = callerLineNumber;
|
||||
ExtendedData = extendedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets logging message sequence.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The sequence.
|
||||
/// </value>
|
||||
public ulong Sequence { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the message.
|
||||
/// It can be a combination as the enumeration is a set of bitwise flags.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the message.
|
||||
/// </value>
|
||||
public LogLevel MessageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UTC date at which the event at which the message was logged.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The UTC date.
|
||||
/// </value>
|
||||
public DateTime UtcDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the source where the logging message
|
||||
/// came from. This can come empty if the logger did not set it.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The source.
|
||||
/// </value>
|
||||
public string? Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body of the message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The message.
|
||||
/// </value>
|
||||
public string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the caller member.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the caller member.
|
||||
/// </value>
|
||||
public string CallerMemberName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the caller file path.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The caller file path.
|
||||
/// </value>
|
||||
public string CallerFilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the caller line number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The caller line number.
|
||||
/// </value>
|
||||
public int CallerLineNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing extended data.
|
||||
/// It could be an exception or anything else.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The extended data.
|
||||
/// </value>
|
||||
public object? ExtendedData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Extended Data properties cast as an Exception (if possible)
|
||||
/// Otherwise, it return null.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The exception.
|
||||
/// </value>
|
||||
public Exception? Exception => ExtendedData as Exception;
|
||||
}
|
||||
}
|
||||
664
Vendor/Swan.Lite-3.1.0/Logging/Logger.cs
vendored
Normal file
664
Vendor/Swan.Lite-3.1.0/Logging/Logger.cs
vendored
Normal file
@@ -0,0 +1,664 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry-point for logging. Use this static class to register/unregister
|
||||
/// loggers instances. By default, the <c>ConsoleLogger</c> is registered.
|
||||
/// </summary>
|
||||
public static class Logger
|
||||
{
|
||||
private static readonly object SyncLock = new object();
|
||||
private static readonly List<ILogger> Loggers = new List<ILogger>();
|
||||
|
||||
private static ulong _loggingSequence;
|
||||
|
||||
static Logger()
|
||||
{
|
||||
if (Terminal.IsConsolePresent)
|
||||
Loggers.Add(ConsoleLogger.Instance);
|
||||
|
||||
if (DebugLogger.IsDebuggerAttached)
|
||||
Loggers.Add(DebugLogger.Instance);
|
||||
}
|
||||
|
||||
#region Standard Public API
|
||||
|
||||
/// <summary>
|
||||
/// Registers the logger.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of logger to register.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">There is already a logger with that class registered.</exception>
|
||||
public static void RegisterLogger<T>()
|
||||
where T : ILogger
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
var loggerInstance = Loggers.FirstOrDefault(x => x.GetType() == typeof(T));
|
||||
|
||||
if (loggerInstance != null)
|
||||
throw new InvalidOperationException("There is already a logger with that class registered.");
|
||||
|
||||
Loggers.Add(Activator.CreateInstance<T>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the logger.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public static void RegisterLogger(ILogger logger)
|
||||
{
|
||||
lock (SyncLock)
|
||||
Loggers.Add(logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the logger.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">logger.</exception>
|
||||
public static void UnregisterLogger(ILogger logger) => RemoveLogger(x => x == logger);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the logger.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of logger to unregister.</typeparam>
|
||||
public static void UnregisterLogger<T>() => RemoveLogger(x => x.GetType() == typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Remove all the loggers.
|
||||
/// </summary>
|
||||
public static void NoLogging()
|
||||
{
|
||||
lock (SyncLock)
|
||||
Loggers.Clear();
|
||||
}
|
||||
|
||||
#region Debug
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Debug(
|
||||
this string message,
|
||||
string? source = null,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Debug(
|
||||
this string message,
|
||||
Type source,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Debug, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message to the console.
|
||||
/// </summary>
|
||||
/// <param name="extendedData">The exception.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Debug(
|
||||
this Exception extendedData,
|
||||
string source,
|
||||
string message,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Trace
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The text.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Trace(
|
||||
this string message,
|
||||
string? source = null,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Trace(
|
||||
this string message,
|
||||
Type source,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Trace, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message to the console.
|
||||
/// </summary>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Trace(
|
||||
this Exception extendedData,
|
||||
string source,
|
||||
string message,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Warn
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The text.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Warn(
|
||||
this string message,
|
||||
string? source = null,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Warn(
|
||||
this string message,
|
||||
Type source,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Warning, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Warn(
|
||||
this Exception extendedData,
|
||||
string source,
|
||||
string message,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fatal
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The text.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Fatal(
|
||||
this string message,
|
||||
string? source = null,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Fatal(
|
||||
this string message,
|
||||
Type source,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Fatal, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to the console.
|
||||
/// </summary>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Fatal(
|
||||
this Exception extendedData,
|
||||
string source,
|
||||
string message,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Info
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The text.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Info(
|
||||
this string message,
|
||||
string? source = null,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Info(
|
||||
this string message,
|
||||
Type source,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Info, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info message to the console.
|
||||
/// </summary>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Info(
|
||||
this Exception extendedData,
|
||||
string source,
|
||||
string message,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Error
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console's standard error.
|
||||
/// </summary>
|
||||
/// <param name="message">The text.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Error(
|
||||
this string message,
|
||||
string? source = null,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Error, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console's standard error.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Error(
|
||||
this string message,
|
||||
Type source,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Error, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console's standard error.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Error(
|
||||
this Exception ex,
|
||||
string source,
|
||||
string message,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(LogLevel.Error, message, source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extended Public API
|
||||
|
||||
/// <summary>
|
||||
/// Logs the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="messageType">Type of the message.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Log(
|
||||
this string message,
|
||||
string source,
|
||||
LogLevel messageType,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(messageType, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="messageType">Type of the message.</param>
|
||||
/// <param name="extendedData">The extended data.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Log(
|
||||
this string message,
|
||||
Type source,
|
||||
LogLevel messageType,
|
||||
object? extendedData = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
LogMessage(messageType, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console's standard error.
|
||||
/// </summary>
|
||||
/// <param name="ex">The ex.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Log(
|
||||
this Exception ex,
|
||||
string? source = null,
|
||||
string? message = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
if (ex is null)
|
||||
return;
|
||||
|
||||
LogMessage(LogLevel.Error, message ?? ex.Message, source ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to the console's standard error.
|
||||
/// </summary>
|
||||
/// <param name="ex">The ex.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Log(
|
||||
this Exception ex,
|
||||
Type? source = null,
|
||||
string? message = null,
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
if (ex is null)
|
||||
return;
|
||||
|
||||
LogMessage(LogLevel.Error, message ?? ex.Message, source?.FullName ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message showing all possible non-null properties of the given object
|
||||
/// This method is expensive as it uses Stringify internally.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="text">The title.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
|
||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||
public static void Dump(
|
||||
this object? obj,
|
||||
string source,
|
||||
string text = "Object Dump",
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
if (obj == null)
|
||||
return;
|
||||
|
||||
var message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
|
||||
LogMessage(LogLevel.Trace, message, source, obj, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message showing all possible non-null properties of the given object
|
||||
/// This method is expensive as it uses Stringify internally.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="callerMemberName">Name of the caller member.</param>
|
||||
/// <param name="callerFilePath">The caller file path.</param>
|
||||
/// <param name="callerLineNumber">The caller line number.</param>
|
||||
public static void Dump(
|
||||
this object? obj,
|
||||
Type source,
|
||||
string text = "Object Dump",
|
||||
[CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
if (obj == null)
|
||||
return;
|
||||
|
||||
var message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
|
||||
LogMessage(LogLevel.Trace, message, source?.FullName, obj, callerMemberName, callerFilePath, callerLineNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static void RemoveLogger(Func<ILogger, bool> criteria)
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
var loggerInstance = Loggers.FirstOrDefault(criteria);
|
||||
|
||||
if (loggerInstance == null)
|
||||
throw new InvalidOperationException("The logger is not registered.");
|
||||
|
||||
loggerInstance.Dispose();
|
||||
|
||||
Loggers.Remove(loggerInstance);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogMessage(
|
||||
LogLevel logLevel,
|
||||
string message,
|
||||
string? sourceName,
|
||||
object? extendedData,
|
||||
string callerMemberName,
|
||||
string callerFilePath,
|
||||
int callerLineNumber)
|
||||
{
|
||||
var sequence = _loggingSequence;
|
||||
var date = DateTime.UtcNow;
|
||||
_loggingSequence++;
|
||||
|
||||
var loggerMessage = string.IsNullOrWhiteSpace(message) ?
|
||||
string.Empty : message.RemoveControlChars('\n');
|
||||
|
||||
var eventArgs = new LogMessageReceivedEventArgs(
|
||||
sequence,
|
||||
logLevel,
|
||||
date,
|
||||
sourceName,
|
||||
loggerMessage,
|
||||
extendedData,
|
||||
callerMemberName,
|
||||
callerFilePath,
|
||||
callerLineNumber);
|
||||
|
||||
foreach (var logger in Loggers)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (logger.LogLevel <= logLevel)
|
||||
logger.Log(eventArgs);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Vendor/Swan.Lite-3.1.0/Logging/TextLogger.cs
vendored
Normal file
79
Vendor/Swan.Lite-3.1.0/Logging/TextLogger.cs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class for text-based logger.
|
||||
/// </summary>
|
||||
public abstract class TextLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the logging time format.
|
||||
/// set to null or empty to prevent output.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The logging time format.
|
||||
/// </value>
|
||||
public static string LoggingTimeFormat { get; set; } = "HH:mm:ss.fff";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color of the output of the message (the output message has a new line char in the end).
|
||||
/// </summary>
|
||||
/// <param name="logEvent">The <see cref="LogMessageReceivedEventArgs" /> instance containing the event data.</param>
|
||||
/// <returns>
|
||||
/// The output message formatted and the color of the console to be used.
|
||||
/// </returns>
|
||||
protected (string outputMessage, ConsoleColor color) GetOutputAndColor(LogMessageReceivedEventArgs logEvent)
|
||||
{
|
||||
var (prefix , color) = GetConsoleColorAndPrefix(logEvent.MessageType);
|
||||
|
||||
var loggerMessage = string.IsNullOrWhiteSpace(logEvent.Message)
|
||||
? string.Empty
|
||||
: logEvent.Message.RemoveControlChars('\n');
|
||||
|
||||
var outputMessage = CreateOutputMessage(logEvent.Source, loggerMessage, prefix, logEvent.UtcDate);
|
||||
|
||||
// Further format the output in the case there is an exception being logged
|
||||
if (logEvent.MessageType == LogLevel.Error && logEvent.Exception != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
outputMessage += $"{logEvent.Exception.Stringify().Indent()}{Environment.NewLine}";
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
return (outputMessage, color);
|
||||
}
|
||||
|
||||
private static (string Prefix, ConsoleColor color) GetConsoleColorAndPrefix(LogLevel messageType) =>
|
||||
messageType switch
|
||||
{
|
||||
LogLevel.Debug => (ConsoleLogger.DebugPrefix, ConsoleLogger.DebugColor),
|
||||
LogLevel.Error => (ConsoleLogger.ErrorPrefix, ConsoleLogger.ErrorColor),
|
||||
LogLevel.Info => (ConsoleLogger.InfoPrefix, ConsoleLogger.InfoColor),
|
||||
LogLevel.Trace => (ConsoleLogger.TracePrefix, ConsoleLogger.TraceColor),
|
||||
LogLevel.Warning => (ConsoleLogger.WarnPrefix, ConsoleLogger.WarnColor),
|
||||
LogLevel.Fatal => (ConsoleLogger.FatalPrefix, ConsoleLogger.FatalColor),
|
||||
_ => (new string(' ', ConsoleLogger.InfoPrefix.Length), Terminal.Settings.DefaultColor)
|
||||
};
|
||||
|
||||
private static string CreateOutputMessage(string sourceName, string loggerMessage, string prefix, DateTime date)
|
||||
{
|
||||
var friendlySourceName = string.IsNullOrWhiteSpace(sourceName)
|
||||
? string.Empty
|
||||
: sourceName.SliceLength(sourceName.LastIndexOf('.') + 1, sourceName.Length);
|
||||
|
||||
var outputMessage = string.IsNullOrWhiteSpace(sourceName)
|
||||
? loggerMessage
|
||||
: $"[{friendlySourceName}] {loggerMessage}";
|
||||
|
||||
return string.IsNullOrWhiteSpace(LoggingTimeFormat)
|
||||
? $" {prefix} >> {outputMessage}{Environment.NewLine}"
|
||||
: $" {date.ToLocalTime().ToString(LoggingTimeFormat)} {prefix} >> {outputMessage}{Environment.NewLine}";
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Vendor/Swan.Lite-3.1.0/Mappers/CopyableAttribute.cs
vendored
Normal file
13
Vendor/Swan.Lite-3.1.0/Mappers/CopyableAttribute.cs
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an attribute to select which properties are copyable between objects.
|
||||
/// </summary>
|
||||
/// <seealso cref="Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class CopyableAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
27
Vendor/Swan.Lite-3.1.0/Mappers/IObjectMap.cs
vendored
Normal file
27
Vendor/Swan.Lite-3.1.0/Mappers/IObjectMap.cs
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface object map.
|
||||
/// </summary>
|
||||
public interface IObjectMap
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the map.
|
||||
/// </summary>
|
||||
Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the source.
|
||||
/// </summary>
|
||||
Type SourceType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the destination.
|
||||
/// </summary>
|
||||
Type DestinationType { get; }
|
||||
}
|
||||
}
|
||||
120
Vendor/Swan.Lite-3.1.0/Mappers/ObjectMap.cs
vendored
Normal file
120
Vendor/Swan.Lite-3.1.0/Mappers/ObjectMap.cs
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an object map.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the source.</typeparam>
|
||||
/// <typeparam name="TDestination">The type of the destination.</typeparam>
|
||||
/// <seealso cref="IObjectMap" />
|
||||
public class ObjectMap<TSource, TDestination> : IObjectMap
|
||||
{
|
||||
internal ObjectMap(IEnumerable<PropertyInfo> intersect)
|
||||
{
|
||||
SourceType = typeof(TSource);
|
||||
DestinationType = typeof(TDestination);
|
||||
Map = intersect.ToDictionary(
|
||||
property => DestinationType.GetProperty(property.Name),
|
||||
property => new List<PropertyInfo> {SourceType.GetProperty(property.Name)});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Type SourceType { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Type DestinationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps the property.
|
||||
/// </summary>
|
||||
/// <typeparam name="TDestinationProperty">The type of the destination property.</typeparam>
|
||||
/// <typeparam name="TSourceProperty">The type of the source property.</typeparam>
|
||||
/// <param name="destinationProperty">The destination property.</param>
|
||||
/// <param name="sourceProperty">The source property.</param>
|
||||
/// <returns>
|
||||
/// An object map representation of type of the destination property
|
||||
/// and type of the source property.
|
||||
/// </returns>
|
||||
public ObjectMap<TSource, TDestination> MapProperty
|
||||
<TDestinationProperty, TSourceProperty>(
|
||||
Expression<Func<TDestination, TDestinationProperty>> destinationProperty,
|
||||
Expression<Func<TSource, TSourceProperty>> sourceProperty)
|
||||
{
|
||||
if (destinationProperty == null)
|
||||
throw new ArgumentNullException(nameof(destinationProperty));
|
||||
|
||||
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
|
||||
|
||||
if (propertyDestinationInfo == null)
|
||||
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
|
||||
|
||||
var sourceMembers = GetSourceMembers(sourceProperty);
|
||||
|
||||
if (!sourceMembers.Any())
|
||||
throw new ArgumentException("Invalid source expression", nameof(sourceProperty));
|
||||
|
||||
// reverse order
|
||||
sourceMembers.Reverse();
|
||||
Map[propertyDestinationInfo] = sourceMembers;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the map property.
|
||||
/// </summary>
|
||||
/// <typeparam name="TDestinationProperty">The type of the destination property.</typeparam>
|
||||
/// <param name="destinationProperty">The destination property.</param>
|
||||
/// <returns>
|
||||
/// An object map representation of type of the destination property
|
||||
/// and type of the source property.
|
||||
/// </returns>
|
||||
/// <exception cref="System.Exception">Invalid destination expression.</exception>
|
||||
public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
|
||||
Expression<Func<TDestination, TDestinationProperty>> destinationProperty)
|
||||
{
|
||||
if (destinationProperty == null)
|
||||
throw new ArgumentNullException(nameof(destinationProperty));
|
||||
|
||||
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
|
||||
|
||||
if (propertyDestinationInfo == null)
|
||||
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
|
||||
|
||||
if (Map.ContainsKey(propertyDestinationInfo))
|
||||
{
|
||||
Map.Remove(propertyDestinationInfo);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty)
|
||||
{
|
||||
if (sourceProperty == null)
|
||||
throw new ArgumentNullException(nameof(sourceProperty));
|
||||
|
||||
var sourceMembers = new List<PropertyInfo>();
|
||||
var initialExpression = sourceProperty.Body as MemberExpression;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var propertySourceInfo = initialExpression?.Member as PropertyInfo;
|
||||
|
||||
if (propertySourceInfo == null) break;
|
||||
sourceMembers.Add(propertySourceInfo);
|
||||
initialExpression = initialExpression.Expression as MemberExpression;
|
||||
}
|
||||
|
||||
return sourceMembers;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Mappers/ObjectMapper.PropertyInfoComparer.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Mappers/ObjectMapper.PropertyInfoComparer.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an AutoMapper-like object to map from one object type
|
||||
/// to another using defined properties map or using the default behaviour
|
||||
/// to copy same named properties from one object to another.
|
||||
///
|
||||
/// The extension methods like CopyPropertiesTo use the default behaviour.
|
||||
/// </summary>
|
||||
public partial class ObjectMapper
|
||||
{
|
||||
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo>
|
||||
{
|
||||
public bool Equals(PropertyInfo x, PropertyInfo y)
|
||||
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
|
||||
|
||||
public int GetHashCode(PropertyInfo obj)
|
||||
=> obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
374
Vendor/Swan.Lite-3.1.0/Mappers/ObjectMapper.cs
vendored
Normal file
374
Vendor/Swan.Lite-3.1.0/Mappers/ObjectMapper.cs
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an AutoMapper-like object to map from one object type
|
||||
/// to another using defined properties map or using the default behaviour
|
||||
/// to copy same named properties from one object to another.
|
||||
///
|
||||
/// The extension methods like CopyPropertiesTo use the default behaviour.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following code explains how to map an object's properties into an instance of type T.
|
||||
/// <code>
|
||||
/// using Swan.Mappers;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// class Person
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// public int Age { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// var obj = new { Name = "John", Age = 42 };
|
||||
///
|
||||
/// var person = Runtime.ObjectMapper.Map<Person>(obj);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// The following code explains how to explicitly map certain properties.
|
||||
/// <code>
|
||||
/// using Swan.Mappers;
|
||||
///
|
||||
/// class Example
|
||||
/// {
|
||||
/// class User
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// public Role Role { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// public class Role
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// class UserDto
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// public string Role { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // create a User object
|
||||
/// var person =
|
||||
/// new User { Name = "Phillip", Role = new Role { Name = "Admin" } };
|
||||
///
|
||||
/// // create an Object Mapper
|
||||
/// var mapper = new ObjectMapper();
|
||||
///
|
||||
/// // map the User's Role.Name to UserDto's Role
|
||||
/// mapper.CreateMap<User, UserDto>()
|
||||
/// .MapProperty(d => d.Role, x => x.Role.Name);
|
||||
///
|
||||
/// // apply the previous map and retrieve a UserDto object
|
||||
/// var destination = mapper.Map<UserDto>(person);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public partial class ObjectMapper
|
||||
{
|
||||
private static readonly Lazy<ObjectMapper> LazyInstance = new Lazy<ObjectMapper>(() => new ObjectMapper());
|
||||
|
||||
private readonly List<IObjectMap> _maps = new List<IObjectMap>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The current.
|
||||
/// </value>
|
||||
public static ObjectMapper Current => LazyInstance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Copies the specified source.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="propertiesToCopy">The properties to copy.</param>
|
||||
/// <param name="ignoreProperties">The ignore properties.</param>
|
||||
/// <returns>
|
||||
/// Copied properties count.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// source
|
||||
/// or
|
||||
/// target.
|
||||
/// </exception>
|
||||
public static int Copy(
|
||||
object source,
|
||||
object target,
|
||||
IEnumerable<string>? propertiesToCopy = null,
|
||||
params string[]? ignoreProperties)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
if (target == null)
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
|
||||
return CopyInternal(
|
||||
target,
|
||||
GetSourceMap(source),
|
||||
propertiesToCopy,
|
||||
ignoreProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the specified source.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="propertiesToCopy">The properties to copy.</param>
|
||||
/// <param name="ignoreProperties">The ignore properties.</param>
|
||||
/// <returns>
|
||||
/// Copied properties count.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// source
|
||||
/// or
|
||||
/// target.
|
||||
/// </exception>
|
||||
public static int Copy(
|
||||
IDictionary<string, object>? source,
|
||||
object? target,
|
||||
IEnumerable<string>? propertiesToCopy = null,
|
||||
params string[] ignoreProperties)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
if (target == null)
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
|
||||
return CopyInternal(
|
||||
target,
|
||||
source.ToDictionary(
|
||||
x => x.Key.ToLowerInvariant(),
|
||||
x => Tuple.Create(typeof(object), x.Value)),
|
||||
propertiesToCopy,
|
||||
ignoreProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the map.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the source.</typeparam>
|
||||
/// <typeparam name="TDestination">The type of the destination.</typeparam>
|
||||
/// <returns>
|
||||
/// An object map representation of type of the destination property
|
||||
/// and type of the source property.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// You can't create an existing map
|
||||
/// or
|
||||
/// Types doesn't match.
|
||||
/// </exception>
|
||||
public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>()
|
||||
{
|
||||
if (_maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination)))
|
||||
throw new InvalidOperationException("You can't create an existing map");
|
||||
|
||||
var sourceType = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties<TSource>(true);
|
||||
var destinationType = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties<TDestination>(true);
|
||||
|
||||
var intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
|
||||
|
||||
if (!intersect.Any())
|
||||
throw new InvalidOperationException("Types doesn't match");
|
||||
|
||||
var map = new ObjectMap<TSource, TDestination>(intersect);
|
||||
|
||||
_maps.Add(map);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the specified source.
|
||||
/// </summary>
|
||||
/// <typeparam name="TDestination">The type of the destination.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="autoResolve">if set to <c>true</c> [automatic resolve].</param>
|
||||
/// <returns>
|
||||
/// A new instance of the map.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">source.</exception>
|
||||
/// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception>
|
||||
public TDestination Map<TDestination>(object source, bool autoResolve = true)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
var destination = Activator.CreateInstance<TDestination>();
|
||||
var map = _maps
|
||||
.FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination));
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
foreach (var property in map.Map)
|
||||
{
|
||||
var finalSource = property.Value.Aggregate(source,
|
||||
(current, sourceProperty) => sourceProperty.GetValue(current));
|
||||
|
||||
property.Key.SetValue(destination, finalSource);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!autoResolve)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}");
|
||||
}
|
||||
|
||||
// Missing mapping, try to use default behavior
|
||||
Copy(source, destination!);
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private static int CopyInternal(
|
||||
object target,
|
||||
Dictionary<string, Tuple<Type, object>> sourceProperties,
|
||||
IEnumerable<string>? propertiesToCopy,
|
||||
IEnumerable<string>? ignoreProperties)
|
||||
{
|
||||
// Filter properties
|
||||
var requiredProperties = propertiesToCopy?
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.Select(p => p.ToLowerInvariant());
|
||||
|
||||
var ignoredProperties = ignoreProperties?
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.Select(p => p.ToLowerInvariant());
|
||||
|
||||
var properties = PropertyTypeCache.DefaultCache.Value
|
||||
.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
|
||||
|
||||
return properties
|
||||
.Select(x => x.Name)
|
||||
.Distinct()
|
||||
.ToDictionary(x => x.ToLowerInvariant(), x => properties.First(y => y.Name == x))
|
||||
.Where(x => sourceProperties.Keys.Contains(x.Key))
|
||||
.When(() => requiredProperties != null, q => q.Where(y => requiredProperties!.Contains(y.Key)))
|
||||
.When(() => ignoredProperties != null, q => q.Where(y => !ignoredProperties!.Contains(y.Key)))
|
||||
.ToDictionary(x => x.Value, x => sourceProperties[x.Key])
|
||||
.Sum(x => TrySetValue(x.Key, x.Value, target) ? 1 : 0);
|
||||
}
|
||||
|
||||
private static bool TrySetValue(PropertyInfo propertyInfo, Tuple<Type, object> property, object target)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (type, value) = property;
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
propertyInfo.SetValue(target,
|
||||
Enum.ToObject(propertyInfo.PropertyType, value));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.IsValueType || propertyInfo.PropertyType != type)
|
||||
return propertyInfo.TrySetBasicType(value, target);
|
||||
|
||||
if (propertyInfo.PropertyType.IsArray)
|
||||
{
|
||||
propertyInfo.TrySetArray(value as IEnumerable<object>, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
propertyInfo.SetValue(target, GetValue(value, propertyInfo.PropertyType));
|
||||
|
||||
return true;
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// swallow
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static object? GetValue(object source, Type targetType)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
object? target = null;
|
||||
|
||||
source.CreateTarget(targetType, false, ref target);
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case string _:
|
||||
target = source;
|
||||
break;
|
||||
case IList sourceList when target is IList targetList:
|
||||
var addMethod = targetType.GetMethods()
|
||||
.FirstOrDefault(
|
||||
m => m.Name == Formatters.Json.AddMethodName && m.IsPublic && m.GetParameters().Length == 1);
|
||||
|
||||
if (addMethod == null) return target;
|
||||
|
||||
var isItemValueType = targetList.GetType().GetElementType().IsValueType;
|
||||
|
||||
foreach (var item in sourceList)
|
||||
{
|
||||
try
|
||||
{
|
||||
targetList.Add(isItemValueType
|
||||
? item
|
||||
: item.CopyPropertiesToNew<object>());
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
source.CopyPropertiesTo(target!);
|
||||
break;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
private static Dictionary<string, Tuple<Type, object>> GetSourceMap(object source)
|
||||
{
|
||||
// select distinct properties because they can be duplicated by inheritance
|
||||
var sourceProperties = PropertyTypeCache.DefaultCache.Value
|
||||
.RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead)
|
||||
.ToArray();
|
||||
|
||||
return sourceProperties
|
||||
.Select(x => x.Name)
|
||||
.Distinct()
|
||||
.ToDictionary(
|
||||
x => x.ToLowerInvariant(),
|
||||
x => Tuple.Create(sourceProperties.First(y => y.Name == x).PropertyType,
|
||||
sourceProperties.First(y => y.Name == x).GetValue(source)));
|
||||
}
|
||||
}
|
||||
}
|
||||
552
Vendor/Swan.Lite-3.1.0/Net/IPAddressRange.cs
vendored
Normal file
552
Vendor/Swan.Lite-3.1.0/Net/IPAddressRange.cs
vendored
Normal file
@@ -0,0 +1,552 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Swan.Net.Internal;
|
||||
|
||||
namespace Swan.Net
|
||||
{
|
||||
// NOTE TO CONTRIBUTORS: When adding a check on a public method parameter,
|
||||
// please do not just "throw new ArgumentException(...)".
|
||||
// Instead, look at the exception-returning private methods at the bottom of this file
|
||||
// and either find one suitable for your case, or add a new one.
|
||||
// This way we can keep the exception messages consistent.
|
||||
|
||||
/// <summary>
|
||||
/// Represents an inclusive range of IP addresses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This class makes no distinction between IPv4 addresses and the same addresses mapped to IPv6
|
||||
/// for the purpose of determining whether it belongs to a range: that is, the <see cref="Contains"/> method
|
||||
/// of an instance initialized with IPv4 addresses, or with the same addresses mapped to IPv6,
|
||||
/// will return <see langword="true"/> for both an in-range IPv4 address and the same address mapped to IPv6.</para>
|
||||
/// <para>The <see cref="IPAddressRange(IPAddress,IPAddress)"/> constructor, however,
|
||||
/// does make such distinction: you cannot initialize a range using an IPv4 address and an IPv6 address,
|
||||
/// even if the latter is an IPv4 address mapped to IPv6, nor the other way around.</para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IEquatable{IPAddressRange}" />
|
||||
[Serializable]
|
||||
public sealed class IPAddressRange : IEquatable<IPAddressRange>
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Gets an instance of <see cref="IPAddressRange"/> that contains no addresses.</para>
|
||||
/// <para>The <see cref="Contains"/> method of the returned instance will always return <see langword="false"/>.</para>
|
||||
/// <para>This property is useful to initialize non-nullable properties
|
||||
/// of type <see cref="IPAddressRange"/>.</para>
|
||||
/// </summary>
|
||||
public static readonly IPAddressRange None = new IPAddressRange(IPAddressValue.MaxValue, IPAddressValue.MinValue, true, 0);
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets an instance of <see cref="IPAddressRange"/> that contains all possible IP addresses.</para>
|
||||
/// <para>The <see cref="Contains"/> method of the returned instance will always return <see langword="true"/>.</para>
|
||||
/// </summary>
|
||||
public static readonly IPAddressRange All = new IPAddressRange(IPAddressValue.MinValue, IPAddressValue.MaxValue, true, 128);
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets an instance of <see cref="IPAddressRange"/> that contains all IPv4 addresses.</para>
|
||||
/// <para>The <see cref="Contains"/> method of the returned instance will return <see langword="true"/>
|
||||
/// for all IPv4 addresses, as well as their IPv6 mapped counterparts, and <see langword="false"/>
|
||||
/// for all other IPv6 addresses.</para>
|
||||
/// </summary>
|
||||
public static readonly IPAddressRange AllIPv4 = new IPAddressRange(IPAddressValue.MinIPv4Value, IPAddressValue.MaxIPv4Value, false, 32);
|
||||
|
||||
private readonly IPAddressValue _start;
|
||||
private readonly IPAddressValue _end;
|
||||
private readonly bool _isV6;
|
||||
private readonly byte _prefixLength;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IPAddressRange"/> class,
|
||||
/// representing a single IP address.
|
||||
/// </summary>
|
||||
/// <param name="address">The IP address.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="address"/> is <see langword="null"/>.</exception>
|
||||
public IPAddressRange(IPAddress address)
|
||||
{
|
||||
if (address == null)
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
|
||||
_start = _end = new IPAddressValue(address);
|
||||
_isV6 = address.AddressFamily == AddressFamily.InterNetworkV6;
|
||||
_prefixLength = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IPAddressRange"/> class,
|
||||
/// representing a range of IP addresses between <paramref name="start"/>
|
||||
/// and <paramref name="end"/>, extremes included.
|
||||
/// </summary>
|
||||
/// <param name="start">The starting address of the range.</param>
|
||||
/// <param name="end">The ending address of the range.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <para><paramref name="start"/> is <see langword="null"/>.</para>
|
||||
/// <para>- or -</para>
|
||||
/// <para><paramref name="end"/> is <see langword="null"/>.</para>
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <para><paramref name="end"/> has a different <see cref="IPAddress.AddressFamily">AddressFamily</see>
|
||||
/// from <paramref name="start"/>.</para>
|
||||
/// <para>- or -</para>
|
||||
/// <para><paramref name="end"/> is a lower address than <paramref name="start"/>,
|
||||
/// i.e. the binary representation of <paramref name="end"/> in network byte order
|
||||
/// is a lower number than the same representation of <paramref name="start"/>.</para>
|
||||
/// </exception>
|
||||
public IPAddressRange(IPAddress start, IPAddress end)
|
||||
{
|
||||
if (start == null)
|
||||
throw new ArgumentNullException(nameof(start));
|
||||
|
||||
if (end == null)
|
||||
throw new ArgumentNullException(nameof(end));
|
||||
|
||||
var startFamily = start.AddressFamily;
|
||||
_isV6 = startFamily == AddressFamily.InterNetworkV6;
|
||||
if (end.AddressFamily != startFamily)
|
||||
throw MismatchedEndFamily(nameof(end));
|
||||
|
||||
_start = new IPAddressValue(start);
|
||||
_end = new IPAddressValue(end);
|
||||
if (_end.CompareTo(_start) < 0)
|
||||
throw EndLowerThanStart(nameof(end));
|
||||
|
||||
_prefixLength = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IPAddressRange"/> class,
|
||||
/// representing a CIDR subnet.
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">The base address of the subnet.</param>
|
||||
/// <param name="prefixLength">The prefix length of the subnet.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="baseAddress"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <para><paramref name="prefixLength"/> is zero.</para>
|
||||
/// <para>- or -</para>
|
||||
/// <para><paramref name="prefixLength"/> is greater than the number of bits in
|
||||
/// the binary representation of <paramref name="baseAddress"/> (32 for IPv4 addresses,
|
||||
/// 128 for IPv6 addresses.)</para>
|
||||
/// <para>- or -</para>
|
||||
/// <para><paramref name="baseAddress"/> cannot be the base address of a subnet with a prefix length
|
||||
/// equal to <paramref name="prefixLength"/>, because the remaining bits after the prefix
|
||||
/// are not all zeros.</para>
|
||||
/// </exception>
|
||||
public IPAddressRange(IPAddress baseAddress, byte prefixLength)
|
||||
{
|
||||
if (baseAddress == null)
|
||||
throw new ArgumentNullException(nameof(baseAddress));
|
||||
|
||||
byte maxPrefixLength;
|
||||
if (baseAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
_isV6 = true;
|
||||
maxPrefixLength = 128;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isV6 = false;
|
||||
maxPrefixLength = 32;
|
||||
}
|
||||
|
||||
if (prefixLength < 1 || prefixLength > maxPrefixLength)
|
||||
throw InvalidPrefixLength(nameof(prefixLength));
|
||||
|
||||
_start = new IPAddressValue(baseAddress);
|
||||
if (!_start.IsStartOfSubnet(prefixLength))
|
||||
throw InvalidSubnetBaseAddress(nameof(baseAddress));
|
||||
|
||||
_end = _start.GetEndOfSubnet(prefixLength);
|
||||
_prefixLength = prefixLength;
|
||||
}
|
||||
|
||||
private IPAddressRange(IPAddressValue start, IPAddressValue end, bool isV6, byte prefixLength)
|
||||
{
|
||||
_start = start;
|
||||
_end = end;
|
||||
_isV6 = isV6;
|
||||
_prefixLength = prefixLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address family of the IP address range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Regardless of the value of this property, IPv4 addresses
|
||||
/// and their IPv6 mapped counterparts will be considered the same
|
||||
/// for the purposes of the <see cref="Contains"/> method.</para>
|
||||
/// </remarks>
|
||||
public AddressFamily AddressFamily => _isV6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance represents a CIDR subnet.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This property is <see langword="true"/> only for instances
|
||||
/// initialized via the <see cref="IPAddressRange(IPAddress,byte)"/> constructor.
|
||||
/// Instances constructed by specifying a range will have this property
|
||||
/// set to <see langword="false"/> even when they actually represent a subnet.</para>
|
||||
/// <para>For example, the instance returned by <c>IPAddressRange.Parse("192.168.0.0-192.168.0.255")</c>
|
||||
/// will have this property set to <see langword="false"/>; for this property to be <see langword="true"/>,
|
||||
/// the string passed to <see cref="Parse"/> should instead be <c>"192.168.0.0/24"</c>
|
||||
/// (a CIDR subnet specification) or "192.168.0.0/255.255.255.0" (a base address / netmask pair,
|
||||
/// only accepted by <see cref="Parse"/> and <see cref="TryParse"/> for IPv4 addresses.)</para>
|
||||
/// </remarks>
|
||||
public bool IsSubnet => _prefixLength > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPAddress"/> representing
|
||||
/// the first address in the range.
|
||||
/// </summary>
|
||||
public IPAddress Start => _start.ToIPAddress(_isV6);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPAddress"/> representing
|
||||
/// the last address in the range.
|
||||
/// </summary>
|
||||
public IPAddress End => _end.ToIPAddress(_isV6);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to convert the string representation of a range of IP addresses
|
||||
/// to an instance of <see cref="IPAddressRange"/>.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to convert.</param>
|
||||
/// <param name="result">When this method returns <see langword="true"/>,
|
||||
/// an instance of <see cref="IPAddressRange"/> representing the same range of
|
||||
/// IP addresses represented by <paramref name="str"/>.</param>
|
||||
/// <returns><see langword="true"/> if the conversion was successful;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
/// <remarks>See the "Remarks" section of <see cref="Parse"/>
|
||||
/// for an overview of the formats accepted for <paramref name="str"/>.</remarks>
|
||||
/// <seealso cref="Parse"/>
|
||||
public static bool TryParse(string str, out IPAddressRange result)
|
||||
=> TryParseInternal(nameof(str), str, out result) == null;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the string representation of a range of IP addresses
|
||||
/// to an instance of <see cref="IPAddressRange"/>.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to convert.</param>
|
||||
/// <returns>An instance of <see cref="IPAddressRange"/> representing the same range of
|
||||
/// IP addresses represented by <paramref name="str"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="str"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="FormatException"><paramref name="str"/> is in none of the supported formats.</exception>
|
||||
/// <remarks>
|
||||
/// <para>This method supports the following formats for <paramref name="str"/>:</para>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Format</term>
|
||||
/// <term>Description</term>
|
||||
/// <term>Examples</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>Single address</term>
|
||||
/// <term>A single IP address.</term>
|
||||
/// <term>
|
||||
/// <para><c>192.168.23.199</c></para>
|
||||
/// <para><c>2001:db8:a0b:12f0::1</c></para>
|
||||
/// </term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Range of addresses</term>
|
||||
/// <term>Start and end address, separated by a hyphen (<c>-</c>).</term>
|
||||
/// <term>
|
||||
/// <para><c>192.168.0.100-192.168.11.255</c></para>
|
||||
/// <para><c>2001:db8:a0b:12f0::-2001:db8:a0b:12f0::ffff</c></para>
|
||||
/// </term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>CIDR subnet</term>
|
||||
/// <term>Base address and prefix length, separated by a slash (<c>/</c>).</term>
|
||||
/// <term>
|
||||
/// <para><c>169.254.0.0/16</c></para>
|
||||
/// <para><c>192.168.123.0/24</c></para>
|
||||
/// <para><c>2001:db8:a0b:12f0::/64</c></para>
|
||||
/// </term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>"Legacy" subnet</term>
|
||||
/// <term>
|
||||
/// <para>Base address and netmask, separated by a slash (<c>/</c>).</para>
|
||||
/// <para>Only accepted for IPv4 addresses.</para>
|
||||
/// </term>
|
||||
/// <term>
|
||||
/// <para><c>169.254.0.0/255.255.0.0</c></para>
|
||||
/// <para><c>192.168.123.0/255.255.255.0</c></para>
|
||||
/// </term>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <seealso cref="TryParse"/>
|
||||
public static IPAddressRange Parse(string str)
|
||||
{
|
||||
var exception = TryParseInternal(nameof(str), str, out var result);
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// <para>The result of this method will be a string that,
|
||||
/// if passed to the <see cref="Parse"/> or <see cref="TryParse"/> method,
|
||||
/// will result in an instance identical to this one.</para>
|
||||
/// <para>If this instance has been created by means of the <see cref="Parse"/>
|
||||
/// or <see cref="TryParse"/> method, the returned string will not
|
||||
/// necessarily be identical to the parsed string. The possible differences
|
||||
/// include the following:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item>ranges consisting of just one IP address will result in a
|
||||
/// string representing that single address;</item>
|
||||
/// <item>addresses in the returned string are passed to the
|
||||
/// <see cref="IPAddress.ToString"/> method, resulting in standardized
|
||||
/// representations that may be different from the originally parsed
|
||||
/// strings;</item>
|
||||
/// <item>the returned string will contain no blank characters;</item>
|
||||
/// <item>address ranges parsed as <c>address/netmask</c> will be
|
||||
/// rendered as CIDR subnets: for example,
|
||||
/// <c>IPAddressRange.Parse("192.168.19.0/255.255.255.0").ToString()</c>
|
||||
/// will return <c>"192.168.19.0/24"</c>.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public override string ToString()
|
||||
=> _prefixLength > 0
|
||||
? $"{Start}/{_prefixLength}"
|
||||
: _start.CompareTo(_end) == 0
|
||||
? Start.ToString()
|
||||
: $"{Start}-{End}";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given <paramref name="address"/>
|
||||
/// sa contained in this range.
|
||||
/// </summary>
|
||||
/// <param name="address">The IP address to check.</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="address"/>
|
||||
/// is between <see cref="Start"/> and <see cref="End"/>, inclusive;
|
||||
/// otherwise, <see lamgword="false"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="address"/> is <see langword="null"/>.</exception>
|
||||
/// <remarks>
|
||||
/// <para>This method treats IPv4 addresses and their IPv6-mapped counterparts
|
||||
/// the same; that is, given a range obtained by parsing the string <c>192.168.1.0/24</c>,
|
||||
/// <c>Contains(IPAddress.Parse("192.168.1.55"))</c> will return <see langword="true"/>,
|
||||
/// as will <c>Contains(IPAddress.Parse("192.168.1.55").MapToIPv6())</c>. This is true
|
||||
/// as well if a range is initialized with IPv6 addresses.</para>
|
||||
/// </remarks>
|
||||
public bool Contains(IPAddress address)
|
||||
{
|
||||
if (address == null)
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
|
||||
var addressValue = new IPAddressValue(address);
|
||||
return addressValue.CompareTo(_start) >= 0
|
||||
&& addressValue.CompareTo(_end) <= 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj) => obj is IPAddressRange other && Equals(other);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(IPAddressRange? other)
|
||||
=> other != null
|
||||
&& other._start.Equals(_start)
|
||||
&& other._end.Equals(_end)
|
||||
&& other._isV6 == _isV6
|
||||
&& other._prefixLength == _prefixLength;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => CompositeHashCode.Using(_start, _end, _isV6, _prefixLength);
|
||||
|
||||
private static bool TryNetmaskToCidrPrefixLength(byte[] bytes, out byte result)
|
||||
{
|
||||
result = 0;
|
||||
var length = bytes.Length;
|
||||
var prefixFound = false;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
if (prefixFound)
|
||||
{
|
||||
if (bytes[i] != 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (bytes[i])
|
||||
{
|
||||
case 0x00:
|
||||
if (result == 0)
|
||||
return false;
|
||||
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0x80:
|
||||
result += 1;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xC0:
|
||||
result += 2;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xE0:
|
||||
result += 3;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xF0:
|
||||
result += 4;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xF8:
|
||||
result += 5;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xFC:
|
||||
result += 6;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xFE:
|
||||
result += 7;
|
||||
prefixFound = true;
|
||||
break;
|
||||
case 0xFF:
|
||||
result += 8;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Exception? TryParseInternal(string paramName, string? str, out IPAddressRange result)
|
||||
{
|
||||
result = None;
|
||||
|
||||
if (str == null)
|
||||
return new ArgumentNullException(paramName);
|
||||
|
||||
// Try CIDR format (e.g. 192.168.99.0/24) and address/netmask format (192.168.99.0/255.255.255.0)
|
||||
var separatorPos = str.IndexOf('/');
|
||||
if (separatorPos >= 0)
|
||||
return TryParseCidrOrAddressNetmaskFormat(str, separatorPos, out result);
|
||||
|
||||
// Try range format (e.g. 192.168.99.100-192.168.99.199)
|
||||
separatorPos = str.IndexOf('-');
|
||||
if (separatorPos >= 0)
|
||||
return TryParseStartEndFormat(str, separatorPos, out result);
|
||||
|
||||
// Try single address format (e.g. 192.168.99.123)
|
||||
return TryParseSingleAddressFormat(str, out result);
|
||||
}
|
||||
|
||||
private static Exception? TryParseCidrOrAddressNetmaskFormat(string str, int separatorPos, out IPAddressRange result)
|
||||
{
|
||||
result = None;
|
||||
|
||||
var s = str.Substring(0, separatorPos).Trim();
|
||||
if (!IPAddressUtility.TryParse(s, out var address))
|
||||
return InvalidIPAddress();
|
||||
|
||||
var addressValue = new IPAddressValue(address);
|
||||
|
||||
s = str.Substring(separatorPos + 1).Trim();
|
||||
if (byte.TryParse(s, NumberStyles.None, CultureInfo.InvariantCulture, out var prefixLength))
|
||||
{
|
||||
var maxPrefixLength = address.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32;
|
||||
if (prefixLength < 1 || prefixLength > maxPrefixLength)
|
||||
return InvalidPrefixLength();
|
||||
|
||||
if (!addressValue.IsStartOfSubnet(prefixLength))
|
||||
return InvalidSubnetBaseAddress();
|
||||
|
||||
result = new IPAddressRange(addressValue, addressValue.GetEndOfSubnet(prefixLength), address.AddressFamily == AddressFamily.InterNetworkV6, prefixLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only accept a netmask for IPv4
|
||||
if (address.AddressFamily != AddressFamily.InterNetwork)
|
||||
return InvalidPrefixLength();
|
||||
|
||||
if (!IPAddressUtility.TryParse(s, out var netmask))
|
||||
return InvalidPrefixLengthOrNetmask();
|
||||
|
||||
var addressFamily = address.AddressFamily;
|
||||
if (netmask.AddressFamily != addressFamily)
|
||||
return MismatchedNetmaskAddressFamily();
|
||||
|
||||
var netmaskBytes = netmask.GetAddressBytes();
|
||||
if (!TryNetmaskToCidrPrefixLength(netmaskBytes, out prefixLength))
|
||||
return InvalidNetmask();
|
||||
|
||||
if (!addressValue.IsStartOfSubnet(prefixLength))
|
||||
return InvalidSubnetBaseAddress();
|
||||
|
||||
result = new IPAddressRange(addressValue, addressValue.GetEndOfSubnet(prefixLength), false, prefixLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Exception? TryParseStartEndFormat(string str, int separatorPos, out IPAddressRange result)
|
||||
{
|
||||
result = None;
|
||||
|
||||
var s = str.Substring(0, separatorPos).Trim();
|
||||
if (!IPAddressUtility.TryParse(s, out var startAddress))
|
||||
return InvalidStartAddress();
|
||||
|
||||
s = str.Substring(separatorPos + 1).Trim();
|
||||
if (!IPAddressUtility.TryParse(s, out var endAddress))
|
||||
return InvalidEndAddress();
|
||||
|
||||
var addressFamily = startAddress.AddressFamily;
|
||||
if (endAddress.AddressFamily != addressFamily)
|
||||
return MismatchedStartEndFamily();
|
||||
|
||||
var start = new IPAddressValue(startAddress);
|
||||
var end = new IPAddressValue(endAddress);
|
||||
if (end.CompareTo(start) < 0)
|
||||
return EndLowerThanStart();
|
||||
|
||||
result = new IPAddressRange(start, end, addressFamily == AddressFamily.InterNetworkV6, 0);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Exception? TryParseSingleAddressFormat(string str, out IPAddressRange result)
|
||||
{
|
||||
result = None;
|
||||
|
||||
if (!IPAddressUtility.TryParse(str, out var address))
|
||||
return InvalidIPAddress();
|
||||
|
||||
var addressValue = new IPAddressValue(address);
|
||||
result = new IPAddressRange(addressValue, addressValue, address.AddressFamily == AddressFamily.InterNetworkV6, 0);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Exception InvalidIPAddress() => new FormatException("An invalid IP address was specified.");
|
||||
|
||||
private static Exception InvalidPrefixLengthOrNetmask() => new FormatException("An invalid prefix length or netmask was specified.");
|
||||
|
||||
private static Exception MismatchedNetmaskAddressFamily() => new FormatException("Address and netmask are different types of addresses.");
|
||||
|
||||
private static Exception InvalidPrefixLength() => new FormatException("An invalid prefix length was specified.");
|
||||
|
||||
private static Exception InvalidPrefixLength(string paramName) => new ArgumentException("The prefix length is invalid.", paramName);
|
||||
|
||||
private static Exception InvalidNetmask() => new FormatException("An invalid netmask was specified.");
|
||||
|
||||
private static Exception InvalidSubnetBaseAddress() => new FormatException("The specified address is not the base address of the specified subnet.");
|
||||
|
||||
private static Exception InvalidSubnetBaseAddress(string paramName) => new ArgumentException("The specified address is not the base address of the specified subnet.", paramName);
|
||||
|
||||
private static Exception InvalidStartAddress() => new FormatException("An invalid start address was specified for a range.");
|
||||
|
||||
private static Exception InvalidEndAddress() => new FormatException("An invalid end address was specified for a range.");
|
||||
|
||||
private static Exception MismatchedStartEndFamily() => new FormatException("Start and end are different types of addresses.");
|
||||
|
||||
private static Exception MismatchedEndFamily(string paramName) => new ArgumentException("The end address of a range must be of the same family as the start address.", paramName);
|
||||
|
||||
private static Exception EndLowerThanStart() => new FormatException("An end address was specified for a range that is lower than the start address.");
|
||||
|
||||
private static Exception EndLowerThanStart(string paramName) => new ArgumentException("The end address of a range cannot be lower than the start address.", paramName);
|
||||
}
|
||||
}
|
||||
51
Vendor/Swan.Lite-3.1.0/Net/IPAddressRangeExtensions.cs
vendored
Normal file
51
Vendor/Swan.Lite-3.1.0/Net/IPAddressRangeExtensions.cs
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Swan.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for instances and collections of <see cref="IPAddressRange"/>.
|
||||
/// </summary>
|
||||
public static class IPAddressRangeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether any element of a sequence of <see cref="IPAddressRange"/> instances
|
||||
/// contains the given <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="IEnumerable{T}">IEnumerable<IPAddressRange></see> interface
|
||||
/// on which this method is called.</param>
|
||||
/// <param name="address">The <see cref="IPAddress"/> to look for.</param>
|
||||
/// <returns><see langword="true"/> if any of the ranges in <paramref name="this"/>
|
||||
/// contains <paramref name="address"/>; otherwise, <see langword="false"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
public static bool AnyContains(this IEnumerable<IPAddressRange> @this, IPAddress address)
|
||||
{
|
||||
if (@this == null)
|
||||
throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
foreach (var range in @this)
|
||||
{
|
||||
if (range.Contains(address))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether no element of a sequence of <see cref="IPAddressRange"/> instances
|
||||
/// contains the given <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="this">The <see cref="IEnumerable{T}">IEnumerable<IPAddressRange></see> interface
|
||||
/// on which this method is called.</param>
|
||||
/// <param name="address">The <see cref="IPAddress"/> to look for.</param>
|
||||
/// <returns><see langword="true"/> if none of the ranges in <paramref name="this"/>
|
||||
/// contains <paramref name="address"/>; otherwise, <see langword="false"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool NoneContains(this IEnumerable<IPAddressRange> @this, IPAddress address)
|
||||
=> !AnyContains(@this, address);
|
||||
}
|
||||
}
|
||||
43
Vendor/Swan.Lite-3.1.0/Net/IPAddressUtility.cs
vendored
Normal file
43
Vendor/Swan.Lite-3.1.0/Net/IPAddressUtility.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Swan.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods to work with the <see cref="IPAddress"/> class.
|
||||
/// </summary>
|
||||
public static class IPAddressUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Tries to convert the string representation of an IP address
|
||||
/// into an instance of <see cref="IPAddress"/></para>
|
||||
/// <para>This method works the same way as <see cref="IPAddress.TryParse"/>,
|
||||
/// with the exception that it will not recognize a decimal number alone
|
||||
/// as an IPv4 address.</para>
|
||||
/// </summary>
|
||||
/// <param name="str">The string to be converted.</param>
|
||||
/// <param name="address">When this method returns <see langword="true"/>,
|
||||
/// an instance of <see cref="IPAddress"/> representing the same address
|
||||
/// as <paramref name="str"/>.</param>
|
||||
/// <returns><see langword="true"/> if the conversion was successful;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
public static bool TryParse(string? str, out IPAddress address)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.tryparse
|
||||
// "Note that this method accepts as valid an ipString value that can be parsed as an Int64,
|
||||
// and then treats that Int64 as the long value of an IP address in network byte order,
|
||||
// similar to the way that the IPAddress constructor does.
|
||||
// This means that this method returns true if the Int64 is parsed successfully,
|
||||
// even if it represents an address that's not a valid IP address.
|
||||
// For example, if str is "1", this method returns true even though "1" (or 0.0.0.1)
|
||||
// is not a valid IP address and you might expect this method to return false.
|
||||
// Fixing this bug would break existing apps, so the current behavior will not be changed.
|
||||
// Your code can avoid this behavior by ensuring that it only uses this method
|
||||
// to parse IP addresses in dotted-decimal format."
|
||||
// ---
|
||||
// Thus, if it parses as an Int64, let's just refuse it.
|
||||
// One-part IPv4 addresses be darned.
|
||||
address = IPAddress.None;
|
||||
return !long.TryParse(str, out _) && IPAddress.TryParse(str, out address);
|
||||
}
|
||||
}
|
||||
}
|
||||
139
Vendor/Swan.Lite-3.1.0/Net/Internal/IPAddressValue.cs
vendored
Normal file
139
Vendor/Swan.Lite-3.1.0/Net/Internal/IPAddressValue.cs
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Swan.Net.Internal
|
||||
{
|
||||
// NOTE TO CONTRIBUTORS: If you decide to use this type
|
||||
// in any context other than IPAddressRange, please be aware
|
||||
// that consistency checks were kept to the bare minimum
|
||||
// for use by IPAddressRange.
|
||||
// If you add consistency checks, please ensure
|
||||
// that IPAddressRange still works as intended.
|
||||
// Add regression tests if needed.
|
||||
[Serializable]
|
||||
internal struct IPAddressValue : IEquatable<IPAddressValue>, IComparable<IPAddressValue>
|
||||
{
|
||||
public static readonly IPAddressValue MinValue = new IPAddressValue(ulong.MinValue, ulong.MinValue, false);
|
||||
public static readonly IPAddressValue MaxValue = new IPAddressValue(ulong.MaxValue, ulong.MaxValue, false);
|
||||
public static readonly IPAddressValue MinIPv4Value = new IPAddressValue(0UL, 0xFFFF00000000UL, true);
|
||||
public static readonly IPAddressValue MaxIPv4Value = new IPAddressValue(0UL, 0xFFFFFFFFFFFFUL, true);
|
||||
|
||||
private static readonly IReadOnlyList<ulong> LowBitMasks = BuildLowBitMasks();
|
||||
private static readonly IReadOnlyList<ulong> HighBitMasks = BuildHighBitMasks();
|
||||
|
||||
private const long V4Mask0 = 0L;
|
||||
private const long V4Mask1 = 0xFFFF00000000L;
|
||||
|
||||
private readonly ulong _n0;
|
||||
private readonly ulong _n1;
|
||||
private readonly bool _isV4;
|
||||
|
||||
public IPAddressValue(IPAddress address)
|
||||
{
|
||||
// There are no overloads of IPAddress.NetworkToHostOrder for unsigned types;
|
||||
// hence the unchecked casts to signed types.
|
||||
static ulong ToHostUInt32(byte[] bytes, int startIndex)
|
||||
=> unchecked((uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(bytes, startIndex)));
|
||||
|
||||
static ulong ToHostUInt64(byte[] bytes, int startIndex)
|
||||
=> unchecked((ulong)IPAddress.NetworkToHostOrder(BitConverter.ToInt64(bytes, startIndex)));
|
||||
|
||||
if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
var addressBytes = address.GetAddressBytes();
|
||||
_n0 = ToHostUInt64(addressBytes, 0);
|
||||
_n1 = ToHostUInt64(addressBytes, 8);
|
||||
_isV4 = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_n0 = V4Mask0;
|
||||
_n1 = V4Mask1 + ToHostUInt32(address.GetAddressBytes(), 0);
|
||||
_isV4 = true;
|
||||
}
|
||||
}
|
||||
|
||||
private IPAddressValue(ulong n0, ulong n1, bool isV4)
|
||||
{
|
||||
_n0 = n0;
|
||||
_n1 = n1;
|
||||
_isV4 = isV4;
|
||||
}
|
||||
|
||||
// There are no overloads of IPAddress.HostToNetworkOrder for unsigned types;
|
||||
// hence the unchecked casts to signed types.
|
||||
public IPAddress ToIPAddress(bool forceV6)
|
||||
=> new IPAddress(_isV4 && !forceV6
|
||||
? BitConverter.GetBytes(IPAddress.HostToNetworkOrder(unchecked((int)(uint)_n1)))
|
||||
: BitConverter.GetBytes(IPAddress.HostToNetworkOrder(unchecked((long) _n0)))
|
||||
.Concat(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(unchecked((long) _n1))))
|
||||
.ToArray());
|
||||
|
||||
public override int GetHashCode() => CompositeHashCode.Using(_n0, _n1, _isV4);
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is IPAddressValue other && Equals(other);
|
||||
|
||||
public bool Equals(IPAddressValue other)
|
||||
=> other._n0 == _n0
|
||||
&& other._n1 == _n1;
|
||||
|
||||
public int CompareTo(IPAddressValue other)
|
||||
{
|
||||
var result = _n0.CompareTo(other._n0);
|
||||
return result == 0 ?_n1.CompareTo(other._n1) : result;
|
||||
}
|
||||
|
||||
public bool IsStartOfSubnet(byte prefixLength)
|
||||
{
|
||||
var maxPrefixLength = _isV4 ? 32 : 128;
|
||||
if (prefixLength > maxPrefixLength)
|
||||
throw SelfCheck.Failure($"Invalid prefix length {prefixLength} in {nameof(IsStartOfSubnet)}");
|
||||
|
||||
if (_isV4)
|
||||
prefixLength += 96;
|
||||
|
||||
var bitsToCheck = 128 - prefixLength;
|
||||
return bitsToCheck < 64
|
||||
? (_n1 & LowBitMasks[bitsToCheck]) == 0
|
||||
: _n1 == 0 && (_n0 & LowBitMasks[bitsToCheck]) == 0;
|
||||
}
|
||||
|
||||
public IPAddressValue GetEndOfSubnet(byte prefixLength)
|
||||
{
|
||||
var maxPrefixLength = _isV4 ? 32 : 128;
|
||||
if (prefixLength > maxPrefixLength)
|
||||
throw SelfCheck.Failure($"Invalid prefix length {prefixLength} in {nameof(GetEndOfSubnet)}");
|
||||
|
||||
if (_isV4)
|
||||
prefixLength += 96;
|
||||
|
||||
var (n0, n1) = prefixLength > 64
|
||||
? (_n0, (_n1 & HighBitMasks[prefixLength - 64]) | LowBitMasks[128 - prefixLength])
|
||||
: ((_n0 & HighBitMasks[prefixLength]) | LowBitMasks[64 - prefixLength], 0xFFFFFFFFFFFFFFFFUL);
|
||||
|
||||
return new IPAddressValue(n0, n1, _isV4);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ulong> BuildLowBitMasks()
|
||||
{
|
||||
var masks = new ulong[65];
|
||||
for (var i = 0; i < 64; i++)
|
||||
masks[i + 1] = (masks[i] << 1) | 1;
|
||||
|
||||
return masks;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ulong> BuildHighBitMasks()
|
||||
{
|
||||
var masks = new ulong[65];
|
||||
for (var i = 0; i < 64; i++)
|
||||
masks[i + 1] = (masks[i] >> 1) | 0x8000000000000000UL;
|
||||
|
||||
return masks;
|
||||
}
|
||||
}
|
||||
}
|
||||
188
Vendor/Swan.Lite-3.1.0/ObjectComparer.cs
vendored
Normal file
188
Vendor/Swan.Lite-3.1.0/ObjectComparer.cs
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a quick object comparer using the public properties of an object
|
||||
/// or the public members in a structure.
|
||||
/// </summary>
|
||||
public static class ObjectComparer
|
||||
{
|
||||
/// <summary>
|
||||
/// Compare if two variables of the same type are equal.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects to compare.</typeparam>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Compare if two variables of the same type are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <param name="targetType">Type of the target.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">targetType.</exception>
|
||||
public static bool AreEqual(object left, object right, Type targetType)
|
||||
{
|
||||
if (targetType == null)
|
||||
throw new ArgumentNullException(nameof(targetType));
|
||||
|
||||
if (Definitions.BasicTypesInfo.Value.ContainsKey(targetType))
|
||||
return Equals(left, right);
|
||||
|
||||
return targetType.IsValueType || targetType.IsArray
|
||||
? AreStructsEqual(left, right, targetType)
|
||||
: AreObjectsEqual(left, right, targetType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare if two objects of the same type are equal.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects to compare.</typeparam>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool AreObjectsEqual<T>(T left, T right)
|
||||
where T : class
|
||||
{
|
||||
return AreObjectsEqual(left, right, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare if two objects of the same type are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <param name="targetType">Type of the target.</param>
|
||||
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">targetType.</exception>
|
||||
public static bool AreObjectsEqual(object left, object right, Type targetType)
|
||||
{
|
||||
if (targetType == null)
|
||||
throw new ArgumentNullException(nameof(targetType));
|
||||
|
||||
var properties = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(targetType).ToArray();
|
||||
|
||||
foreach (var propertyTarget in properties)
|
||||
{
|
||||
if (propertyTarget.PropertyType.IsArray)
|
||||
{
|
||||
var leftObj = left.ReadProperty(propertyTarget.Name) as IEnumerable;
|
||||
var rightObj = right.ReadProperty(propertyTarget.Name) as IEnumerable;
|
||||
|
||||
if (!AreEnumerationsEquals(leftObj, rightObj))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Equals(left.ReadProperty(propertyTarget.Name), right.ReadProperty(propertyTarget.Name)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare if two structures of the same type are equal.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of structs to compare.</typeparam>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool AreStructsEqual<T>(T left, T right)
|
||||
where T : struct
|
||||
{
|
||||
return AreStructsEqual(left, right, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare if two structures of the same type are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <param name="targetType">Type of the target.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">targetType.</exception>
|
||||
public static bool AreStructsEqual(object left, object right, Type targetType)
|
||||
{
|
||||
if (targetType == null)
|
||||
throw new ArgumentNullException(nameof(targetType));
|
||||
|
||||
var fields = new List<MemberInfo>(FieldTypeCache.DefaultCache.Value.RetrieveAllFields(targetType))
|
||||
.Union(PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(targetType));
|
||||
|
||||
foreach (var targetMember in fields)
|
||||
{
|
||||
switch (targetMember)
|
||||
{
|
||||
case FieldInfo field:
|
||||
if (!Equals(field.GetValue(left), field.GetValue(right)))
|
||||
return false;
|
||||
break;
|
||||
case PropertyInfo property:
|
||||
if (!Equals(left.ReadProperty(property.Name), right.ReadProperty(property.Name)))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare if two enumerables are equal.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enums to compare.</typeparam>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="right">The right.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if two specified types are equal; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// left
|
||||
/// or
|
||||
/// right.
|
||||
/// </exception>
|
||||
public static bool AreEnumerationsEquals<T>(T left, T right)
|
||||
where T : IEnumerable?
|
||||
{
|
||||
if (Equals(left, default(T)))
|
||||
throw new ArgumentNullException(nameof(left));
|
||||
|
||||
if (Equals(right, default(T)))
|
||||
throw new ArgumentNullException(nameof(right));
|
||||
|
||||
var leftEnumerable = left.Cast<object>().ToArray();
|
||||
var rightEnumerable = right.Cast<object>().ToArray();
|
||||
|
||||
if (leftEnumerable.Length != rightEnumerable.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < leftEnumerable.Length; i++)
|
||||
{
|
||||
var leftEl = leftEnumerable[i];
|
||||
var rightEl = rightEnumerable[i];
|
||||
|
||||
if (!AreEqual(leftEl, rightEl, leftEl.GetType()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Vendor/Swan.Lite-3.1.0/Paginator.cs
vendored
Normal file
99
Vendor/Swan.Lite-3.1.0/Paginator.cs
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// A utility class to compute paging or batching offsets.
|
||||
/// </summary>
|
||||
public class Paginator
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Paginator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="totalCount">The total count of items to page over.</param>
|
||||
/// <param name="pageSize">The desired size of individual pages.</param>
|
||||
public Paginator(int totalCount, int pageSize)
|
||||
{
|
||||
TotalCount = totalCount;
|
||||
PageSize = pageSize;
|
||||
PageCount = ComputePageCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the desired number of items per page.
|
||||
/// </summary>
|
||||
public int PageSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of items to page over.
|
||||
/// </summary>
|
||||
public int TotalCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the computed number of pages.
|
||||
/// </summary>
|
||||
public int PageCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start item index of the given page.
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Zero-based index of the page.</param>
|
||||
/// <returns>The start item index.</returns>
|
||||
public int GetFirstItemIndex(int pageIndex)
|
||||
{
|
||||
pageIndex = FixPageIndex(pageIndex);
|
||||
return pageIndex * PageSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end item index of the given page.
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Zero-based index of the page.</param>
|
||||
/// <returns>The end item index.</returns>
|
||||
public int GetLastItemIndex(int pageIndex)
|
||||
{
|
||||
var startIndex = GetFirstItemIndex(pageIndex);
|
||||
return Math.Min(startIndex + PageSize - 1, TotalCount - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item count of the given page index.
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Zero-based index of the page.</param>
|
||||
/// <returns>The number of items that the page contains.</returns>
|
||||
public int GetItemCount(int pageIndex)
|
||||
{
|
||||
pageIndex = FixPageIndex(pageIndex);
|
||||
return (pageIndex >= PageCount - 1)
|
||||
? GetLastItemIndex(pageIndex) - GetFirstItemIndex(pageIndex) + 1
|
||||
: PageSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes the index of the page by applying bound logic.
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Index of the page.</param>
|
||||
/// <returns>A limit-bound index.</returns>
|
||||
private int FixPageIndex(int pageIndex)
|
||||
{
|
||||
if (pageIndex < 0) return 0;
|
||||
|
||||
return pageIndex >= PageCount ? PageCount - 1 : pageIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the number of pages for the paginator.
|
||||
/// </summary>
|
||||
/// <returns>The page count.</returns>
|
||||
private int ComputePageCount()
|
||||
{
|
||||
// include this if when you always want at least 1 page
|
||||
if (TotalCount == 0)
|
||||
return 0;
|
||||
|
||||
return TotalCount % PageSize != 0
|
||||
? (TotalCount / PageSize) + 1
|
||||
: TotalCount / PageSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentOptionAttribute.cs
vendored
Normal file
101
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentOptionAttribute.cs
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Models an option specification.
|
||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class ArgumentOptionAttribute
|
||||
: Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="longName">The long name of the option.</param>
|
||||
public ArgumentOptionAttribute(string longName)
|
||||
: this(string.Empty, longName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="shortName">The short name of the option.</param>
|
||||
/// <param name="longName">The long name of the option or null if not used.</param>
|
||||
public ArgumentOptionAttribute(char shortName, string longName)
|
||||
: this(new string(shortName, 1), longName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="shortName">The short name of the option..</param>
|
||||
public ArgumentOptionAttribute(char shortName)
|
||||
: this(new string(shortName, 1), string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
private ArgumentOptionAttribute(string shortName, string longName)
|
||||
{
|
||||
ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
|
||||
LongName = longName ?? throw new ArgumentNullException(nameof(longName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets long name of this command line option. This name is usually a single English word.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The long name.
|
||||
/// </value>
|
||||
public string LongName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a short name of this command line option, made of one character.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The short name.
|
||||
/// </value>
|
||||
public string ShortName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties,
|
||||
/// it allows you to split an argument and consume its content as a sequence.
|
||||
/// </summary>
|
||||
public char Separator { get; set; } = '\0';
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets mapped property default value.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default value.
|
||||
/// </value>
|
||||
public object? DefaultValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a command line option is required.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if required; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool Required { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a short description of this command line option. Usually a sentence summary.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The help text.
|
||||
/// </value>
|
||||
public string? HelpText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default argument.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default argument.
|
||||
/// </value>
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
}
|
||||
180
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParse.Validator.cs
vendored
Normal file
180
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParse.Validator.cs
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to parse command line arguments.
|
||||
///
|
||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors).
|
||||
/// </summary>
|
||||
public partial class ArgumentParser
|
||||
{
|
||||
private sealed class Validator
|
||||
{
|
||||
private const char OptionSwitchChar = '-';
|
||||
private readonly object _instance;
|
||||
private readonly IEnumerable<string> _args;
|
||||
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
|
||||
private readonly ArgumentParserSettings _settings;
|
||||
|
||||
private readonly PropertyInfo[] _properties;
|
||||
|
||||
public Validator(
|
||||
PropertyInfo[] properties,
|
||||
IEnumerable<string> args,
|
||||
object instance,
|
||||
ArgumentParserSettings settings,
|
||||
bool hasVerb = false)
|
||||
{
|
||||
_args = args;
|
||||
_instance = instance;
|
||||
_settings = settings;
|
||||
_properties = properties;
|
||||
|
||||
PopulateInstance();
|
||||
if (!hasVerb) SetDefaultArgument();
|
||||
SetDefaultValues();
|
||||
GetRequiredList();
|
||||
}
|
||||
|
||||
public List<string> UnknownList { get; } = new List<string>();
|
||||
public List<string> RequiredList { get; } = new List<string>();
|
||||
|
||||
public bool IsValid() => (_settings.IgnoreUnknownArguments || !UnknownList.Any()) && !RequiredList.Any();
|
||||
|
||||
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
|
||||
=> _properties.Select(p => AttributeCache.DefaultCache.Value.RetrieveOne<ArgumentOptionAttribute>(p))
|
||||
.Where(x => x != null);
|
||||
|
||||
private void GetRequiredList()
|
||||
{
|
||||
foreach (var targetProperty in _properties)
|
||||
{
|
||||
var optionAttr = AttributeCache.DefaultCache.Value.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
||||
|
||||
if (optionAttr == null || optionAttr.Required == false)
|
||||
continue;
|
||||
|
||||
if (targetProperty.GetValue(_instance) == null)
|
||||
{
|
||||
RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDefaultValues()
|
||||
{
|
||||
foreach (var targetProperty in _properties.Except(_updatedList))
|
||||
{
|
||||
var optionAttr = AttributeCache.DefaultCache.Value.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
||||
|
||||
var defaultValue = optionAttr?.DefaultValue;
|
||||
|
||||
if (defaultValue == null)
|
||||
continue;
|
||||
|
||||
if (SetPropertyValue(targetProperty, defaultValue.ToString(), _instance, optionAttr))
|
||||
_updatedList.Add(targetProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDefaultArgument()
|
||||
{
|
||||
foreach (var targetProperty in _properties.Except(_updatedList))
|
||||
{
|
||||
var optionAttr = AttributeCache.DefaultCache.Value.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
||||
|
||||
if (!optionAttr.IsDefault)
|
||||
continue;
|
||||
|
||||
var defaultArgValue = _args.FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(defaultArgValue) || defaultArgValue[0] == OptionSwitchChar)
|
||||
continue;
|
||||
|
||||
if (SetPropertyValue(targetProperty, defaultArgValue, _instance, optionAttr))
|
||||
_updatedList.Add(targetProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateInstance()
|
||||
{
|
||||
var propertyName = string.Empty;
|
||||
|
||||
foreach (var arg in _args)
|
||||
{
|
||||
var ignoreSetValue = string.IsNullOrWhiteSpace(propertyName);
|
||||
|
||||
if (ignoreSetValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(arg) || arg[0] != OptionSwitchChar) continue;
|
||||
|
||||
propertyName = arg.Substring(1);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(propertyName) && propertyName[0] == OptionSwitchChar)
|
||||
propertyName = propertyName.Substring(1);
|
||||
}
|
||||
|
||||
var targetProperty = TryGetProperty(propertyName);
|
||||
|
||||
if (targetProperty == null)
|
||||
{
|
||||
// Skip if the property is not found
|
||||
UnknownList.Add(propertyName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ignoreSetValue && SetPropertyValue(targetProperty, arg, _instance))
|
||||
{
|
||||
_updatedList.Add(targetProperty);
|
||||
propertyName = string.Empty;
|
||||
}
|
||||
else if (targetProperty.PropertyType == typeof(bool))
|
||||
{
|
||||
// If the arg is a boolean property set it to true.
|
||||
targetProperty.SetValue(_instance, true);
|
||||
|
||||
_updatedList.Add(targetProperty);
|
||||
propertyName = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
UnknownList.Add(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetPropertyValue(
|
||||
PropertyInfo targetProperty,
|
||||
string propertyValueString,
|
||||
object result,
|
||||
ArgumentOptionAttribute? optionAttr = null)
|
||||
{
|
||||
if (!targetProperty.PropertyType.IsEnum)
|
||||
{
|
||||
return targetProperty.PropertyType.IsArray
|
||||
? targetProperty.TrySetArray(propertyValueString.Split(optionAttr?.Separator ?? ','), result)
|
||||
: targetProperty.TrySetBasicType(propertyValueString, result);
|
||||
}
|
||||
|
||||
var parsedValue = Enum.Parse(
|
||||
targetProperty.PropertyType,
|
||||
propertyValueString,
|
||||
_settings.CaseInsensitiveEnumValues);
|
||||
|
||||
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private PropertyInfo TryGetProperty(string propertyName)
|
||||
=> _properties.FirstOrDefault(p =>
|
||||
string.Equals(AttributeCache.DefaultCache.Value.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, _settings.NameComparer) ||
|
||||
string.Equals(AttributeCache.DefaultCache.Value.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, _settings.NameComparer));
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParser.TypeResolver.cs
vendored
Normal file
63
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParser.TypeResolver.cs
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Swan.Reflection;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to parse command line arguments.
|
||||
/// </summary>
|
||||
public partial class ArgumentParser
|
||||
{
|
||||
private sealed class TypeResolver<T>
|
||||
{
|
||||
public bool HasVerb { get; }
|
||||
|
||||
private bool _hasVerb = false;
|
||||
|
||||
private readonly string _selectedVerb;
|
||||
|
||||
private PropertyInfo[]? _properties;
|
||||
|
||||
public TypeResolver(string selectedVerb)
|
||||
{
|
||||
_selectedVerb = selectedVerb;
|
||||
}
|
||||
|
||||
public PropertyInfo[]? Properties => _properties?.Any() == true ? _properties : null;
|
||||
|
||||
public object? GetOptionsObject(T instance)
|
||||
{
|
||||
_properties = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties<T>(true).ToArray();
|
||||
|
||||
if (!_properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any()))
|
||||
return instance;
|
||||
|
||||
_hasVerb = true;
|
||||
|
||||
var selectedVerb = string.IsNullOrWhiteSpace(_selectedVerb)
|
||||
? null
|
||||
: _properties.FirstOrDefault(x =>
|
||||
AttributeCache.DefaultCache.Value.RetrieveOne<VerbOptionAttribute>(x).Name == _selectedVerb);
|
||||
|
||||
if (selectedVerb == null) return null;
|
||||
|
||||
var type = instance.GetType();
|
||||
|
||||
var verbProperty = type.GetProperty(selectedVerb.Name);
|
||||
|
||||
if (verbProperty?.GetValue(instance) == null)
|
||||
{
|
||||
var propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
|
||||
verbProperty?.SetValue(instance, propertyInstance);
|
||||
}
|
||||
|
||||
_properties = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(selectedVerb.PropertyType, true)
|
||||
.ToArray();
|
||||
|
||||
return verbProperty?.GetValue(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
253
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParser.cs
vendored
Normal file
253
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParser.cs
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
using Swan.Reflection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to parse command line arguments.
|
||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example shows how to parse CLI arguments into objects.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System;
|
||||
/// using Swan.Parsers;
|
||||
///
|
||||
/// static void Main(string[] args)
|
||||
/// {
|
||||
/// // parse the supplied command-line arguments into the options object
|
||||
/// var res = Runtime.ArgumentParser.ParseArguments(args, out var options);
|
||||
/// }
|
||||
///
|
||||
/// class Options
|
||||
/// {
|
||||
/// [ArgumentOption('v', "verbose", HelpText = "Set verbose mode.")]
|
||||
/// public bool Verbose { get; set; }
|
||||
///
|
||||
/// [ArgumentOption('u', Required = true, HelpText = "Set user name.")]
|
||||
/// public string Username { get; set; }
|
||||
///
|
||||
/// [ArgumentOption('n', "names", Separator = ',',
|
||||
/// Required = true, HelpText = "A list of files separated by a comma")]
|
||||
/// public string[] Files { get; set; }
|
||||
///
|
||||
/// [ArgumentOption('p', "port", DefaultValue = 22, HelpText = "Set port.")]
|
||||
/// public int Port { get; set; }
|
||||
///
|
||||
/// [ArgumentOption("color", DefaultValue = ConsoleColor.Red,
|
||||
/// HelpText = "Set a color.")]
|
||||
/// public ConsoleColor Color { get; set; }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// The following code describes how to parse CLI verbs.
|
||||
/// <code>
|
||||
/// class Example2
|
||||
/// {
|
||||
/// using Swan;
|
||||
/// using Swan.Parsers;
|
||||
///
|
||||
/// static void Main(string[] args)
|
||||
/// {
|
||||
/// // create an instance of the VerbOptions class
|
||||
/// var options = new VerbOptions();
|
||||
///
|
||||
/// // parse the supplied command-line arguments into the options object
|
||||
/// var res = Runtime.ArgumentParser.ParseArguments(args, options);
|
||||
///
|
||||
/// // if there were no errors parsing
|
||||
/// if (res)
|
||||
/// {
|
||||
/// if(options.Run != null)
|
||||
/// {
|
||||
/// // run verb was selected
|
||||
/// }
|
||||
///
|
||||
/// if(options.Print != null)
|
||||
/// {
|
||||
/// // print verb was selected
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // flush all error messages
|
||||
/// Terminal.Flush();
|
||||
/// }
|
||||
///
|
||||
/// class VerbOptions
|
||||
/// {
|
||||
/// [VerbOption("run", HelpText = "Run verb.")]
|
||||
/// public RunVerbOption Run { get; set; }
|
||||
///
|
||||
/// [VerbOption("print", HelpText = "Print verb.")]
|
||||
/// public PrintVerbOption Print { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// class RunVerbOption
|
||||
/// {
|
||||
/// [ArgumentOption('o', "outdir", HelpText = "Output directory",
|
||||
/// DefaultValue = "", Required = false)]
|
||||
/// public string OutDir { get; set; }
|
||||
/// }
|
||||
///
|
||||
/// class PrintVerbOption
|
||||
/// {
|
||||
/// [ArgumentOption('t', "text", HelpText = "Text to print",
|
||||
/// DefaultValue = "", Required = false)]
|
||||
/// public string Text { get; set; }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public partial class ArgumentParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentParser"/> class.
|
||||
/// </summary>
|
||||
public ArgumentParser()
|
||||
: this(new ArgumentParserSettings())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentParser" /> class,
|
||||
/// configurable with <see cref="ArgumentParserSettings" /> using a delegate.
|
||||
/// </summary>
|
||||
/// <param name="parseSettings">The parse settings.</param>
|
||||
public ArgumentParser(ArgumentParserSettings parseSettings)
|
||||
{
|
||||
Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The current.
|
||||
/// </value>
|
||||
public static ArgumentParser Current { get; } = new ArgumentParser();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The settings.
|
||||
/// </value>
|
||||
public ArgumentParserSettings Settings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the options.</typeparam>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if was converted successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// The exception that is thrown when a null reference (Nothing in Visual Basic)
|
||||
/// is passed to a method that does not accept it as a valid argument.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The exception that is thrown when a method call is invalid for the object's current state.
|
||||
/// </exception>
|
||||
public bool ParseArguments<T>(IEnumerable<string> args, out T instance)
|
||||
{
|
||||
instance = Activator.CreateInstance<T>();
|
||||
return ParseArguments(args, instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the options.</typeparam>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if was converted successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// The exception that is thrown when a null reference (Nothing in Visual Basic)
|
||||
/// is passed to a method that does not accept it as a valid argument.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The exception that is thrown when a method call is invalid for the object's current state.
|
||||
/// </exception>
|
||||
public bool ParseArguments<T>(IEnumerable<string> args, T instance)
|
||||
{
|
||||
if (args == null)
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
|
||||
if (Equals(instance, default(T)))
|
||||
throw new ArgumentNullException(nameof(instance));
|
||||
|
||||
var typeResolver = new TypeResolver<T>(args.FirstOrDefault());
|
||||
var options = typeResolver.GetOptionsObject(instance);
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
ReportUnknownVerb<T>();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeResolver.Properties == null)
|
||||
throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
|
||||
|
||||
var validator = new Validator(typeResolver.Properties, args, options, Settings, typeResolver.HasVerb);
|
||||
|
||||
if (validator.IsValid())
|
||||
return true;
|
||||
|
||||
ReportIssues(validator);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ReportUnknownVerb<T>()
|
||||
{
|
||||
Terminal.WriteLine("No verb was specified", ConsoleColor.Red);
|
||||
Terminal.WriteLine("Valid verbs:", ConsoleColor.Cyan);
|
||||
|
||||
PropertyTypeCache.DefaultCache.Value
|
||||
.RetrieveAllProperties<T>(true)
|
||||
.Select(x => AttributeCache.DefaultCache.Value.RetrieveOne<VerbOptionAttribute>(x))
|
||||
.Where(x => x != null)
|
||||
.ToList()
|
||||
.ForEach(x => Terminal.WriteLine(x.ToString(), ConsoleColor.Cyan));
|
||||
}
|
||||
|
||||
private void ReportIssues(Validator validator)
|
||||
{
|
||||
if (Settings.WriteBanner)
|
||||
Terminal.WriteWelcomeBanner();
|
||||
|
||||
var options = validator.GetPropertiesOptions();
|
||||
|
||||
foreach (var option in options)
|
||||
{
|
||||
Terminal.WriteLine(string.Empty);
|
||||
|
||||
// TODO: If Enum list values
|
||||
var shortName = string.IsNullOrWhiteSpace(option.ShortName) ? string.Empty : $"-{option.ShortName}";
|
||||
var longName = string.IsNullOrWhiteSpace(option.LongName) ? string.Empty : $"--{option.LongName}";
|
||||
var comma = string.IsNullOrWhiteSpace(shortName) || string.IsNullOrWhiteSpace(longName)
|
||||
? string.Empty
|
||||
: ", ";
|
||||
var defaultValue = option.DefaultValue == null ? string.Empty : $"(Default: {option.DefaultValue}) ";
|
||||
|
||||
Terminal.WriteLine($" {shortName}{comma}{longName}\t\t{defaultValue}{option.HelpText}", ConsoleColor.Cyan);
|
||||
}
|
||||
|
||||
Terminal.WriteLine(string.Empty);
|
||||
Terminal.WriteLine(" --help\t\tDisplay this help screen.", ConsoleColor.Cyan);
|
||||
|
||||
if (validator.UnknownList.Any())
|
||||
Terminal.WriteLine($"Unknown arguments: {string.Join(", ", validator.UnknownList)}", ConsoleColor.Red);
|
||||
|
||||
if (validator.RequiredList.Any())
|
||||
Terminal.WriteLine($"Required arguments: {string.Join(", ", validator.RequiredList)}", ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParserSettings.cs
vendored
Normal file
53
Vendor/Swan.Lite-3.1.0/Parsers/ArgumentParserSettings.cs
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides settings for <see cref="ArgumentParser"/>.
|
||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||
/// </summary>
|
||||
public class ArgumentParserSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [write banner].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [write banner]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool WriteBanner { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether perform case sensitive comparisons.
|
||||
/// Note that case insensitivity only applies to <i>parameters</i>, not the values
|
||||
/// assigned to them (for example, enum parsing).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [case sensitive]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool CaseSensitive { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
|
||||
/// Note that case insensitivity only applies to <i>values</i>, not the parameters.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool CaseInsensitiveEnumValues { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it
|
||||
/// encounter an unknown arguments.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> to allow parsing the arguments with different class options that do not have all the arguments.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but
|
||||
/// when these are unknown by the main program at build time.
|
||||
/// </remarks>
|
||||
public bool IgnoreUnknownArguments { get; set; } = true;
|
||||
|
||||
internal StringComparison NameComparer => CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
||||
}
|
||||
}
|
||||
117
Vendor/Swan.Lite-3.1.0/Parsers/ExpressionParser.cs
vendored
Normal file
117
Vendor/Swan.Lite-3.1.0/Parsers/ExpressionParser.cs
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic expression parser.
|
||||
/// </summary>
|
||||
public abstract class ExpressionParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the expression.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of expression result.</typeparam>
|
||||
/// <param name="tokens">The tokens.</param>
|
||||
/// <returns>The representation of the expression parsed.</returns>
|
||||
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) =>
|
||||
ResolveExpression<T>(tokens, System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the expression.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of expression result.</typeparam>
|
||||
/// <param name="tokens">The tokens.</param>
|
||||
/// <param name="formatProvider">The format provider.</param>
|
||||
/// <returns>The representation of the expression parsed.</returns>
|
||||
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens, IFormatProvider formatProvider)
|
||||
{
|
||||
var conversion = Expression.Convert(Parse(tokens,formatProvider), typeof(T));
|
||||
return Expression.Lambda<Func<T>>(conversion).Compile()();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified tokens.
|
||||
/// </summary>
|
||||
/// <param name="tokens">The tokens.</param>
|
||||
/// <returns>
|
||||
/// The final expression.
|
||||
/// </returns>
|
||||
public virtual Expression Parse(IEnumerable<Token> tokens) =>
|
||||
Parse(tokens, System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified tokens.
|
||||
/// </summary>
|
||||
/// <param name="tokens">The tokens.</param>
|
||||
/// <param name="formatProvider">The format provider.</param>
|
||||
/// <returns>
|
||||
/// The final expression.
|
||||
/// </returns>
|
||||
public virtual Expression Parse(IEnumerable<Token> tokens, IFormatProvider formatProvider)
|
||||
{
|
||||
var expressionStack = new List<Stack<Expression>>();
|
||||
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
if (expressionStack.Any() == false)
|
||||
expressionStack.Add(new Stack<Expression>());
|
||||
|
||||
switch (token.Type)
|
||||
{
|
||||
case TokenType.Wall:
|
||||
expressionStack.Add(new Stack<Expression>());
|
||||
break;
|
||||
case TokenType.Number:
|
||||
expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value, formatProvider)));
|
||||
break;
|
||||
case TokenType.Variable:
|
||||
ResolveVariable(token.Value, expressionStack.Last());
|
||||
break;
|
||||
case TokenType.String:
|
||||
expressionStack.Last().Push(Expression.Constant(token.Value));
|
||||
break;
|
||||
case TokenType.Operator:
|
||||
ResolveOperator(token.Value, expressionStack.Last());
|
||||
break;
|
||||
case TokenType.Function:
|
||||
ResolveFunction(token.Value, expressionStack.Last());
|
||||
|
||||
if (expressionStack.Count > 1 && expressionStack.Last().Count == 1)
|
||||
{
|
||||
var lastValue = expressionStack.Last().Pop();
|
||||
expressionStack.Remove(expressionStack.Last());
|
||||
expressionStack.Last().Push(lastValue);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return expressionStack.Last().Pop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the variable.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="expressionStack">The expression stack.</param>
|
||||
public abstract void ResolveVariable(string value, Stack<Expression> expressionStack);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the operator.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="expressionStack">The expression stack.</param>
|
||||
public abstract void ResolveOperator(string value, Stack<Expression> expressionStack);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the function.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="expressionStack">The expression stack.</param>
|
||||
public abstract void ResolveFunction(string value, Stack<Expression> expressionStack);
|
||||
}
|
||||
}
|
||||
32
Vendor/Swan.Lite-3.1.0/Parsers/Operator.cs
vendored
Normal file
32
Vendor/Swan.Lite-3.1.0/Parsers/Operator.cs
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an operator with precedence.
|
||||
/// </summary>
|
||||
public class Operator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the precedence.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The precedence.
|
||||
/// </value>
|
||||
public int Precedence { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [right associative].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [right associative]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool RightAssociative { get; set; }
|
||||
}
|
||||
}
|
||||
35
Vendor/Swan.Lite-3.1.0/Parsers/Token.cs
vendored
Normal file
35
Vendor/Swan.Lite-3.1.0/Parsers/Token.cs
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Token structure.
|
||||
/// </summary>
|
||||
public struct Token
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Token"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public Token(TokenType type, string value)
|
||||
{
|
||||
Type = type;
|
||||
Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
public TokenType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The value.
|
||||
/// </value>
|
||||
public string Value { get; }
|
||||
}
|
||||
}
|
||||
48
Vendor/Swan.Lite-3.1.0/Parsers/TokenType.cs
vendored
Normal file
48
Vendor/Swan.Lite-3.1.0/Parsers/TokenType.cs
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Enums the token types.
|
||||
/// </summary>
|
||||
public enum TokenType
|
||||
{
|
||||
/// <summary>
|
||||
/// The number
|
||||
/// </summary>
|
||||
Number,
|
||||
|
||||
/// <summary>
|
||||
/// The string
|
||||
/// </summary>
|
||||
String,
|
||||
|
||||
/// <summary>
|
||||
/// The variable
|
||||
/// </summary>
|
||||
Variable,
|
||||
|
||||
/// <summary>
|
||||
/// The function
|
||||
/// </summary>
|
||||
Function,
|
||||
|
||||
/// <summary>
|
||||
/// The parenthesis
|
||||
/// </summary>
|
||||
Parenthesis,
|
||||
|
||||
/// <summary>
|
||||
/// The operator
|
||||
/// </summary>
|
||||
Operator,
|
||||
|
||||
/// <summary>
|
||||
/// The comma
|
||||
/// </summary>
|
||||
Comma,
|
||||
|
||||
/// <summary>
|
||||
/// The wall, used to specified the end of argument list of the following function
|
||||
/// </summary>
|
||||
Wall,
|
||||
}
|
||||
}
|
||||
361
Vendor/Swan.Lite-3.1.0/Parsers/Tokenizer.cs
vendored
Normal file
361
Vendor/Swan.Lite-3.1.0/Parsers/Tokenizer.cs
vendored
Normal file
@@ -0,0 +1,361 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic tokenizer.
|
||||
/// </summary>
|
||||
public abstract class Tokenizer
|
||||
{
|
||||
private const char PeriodChar = '.';
|
||||
private const char CommaChar = ',';
|
||||
private const char StringQuotedChar = '"';
|
||||
private const char OpenFuncChar = '(';
|
||||
private const char CloseFuncChar = ')';
|
||||
private const char NegativeChar = '-';
|
||||
|
||||
private const string OpenFuncStr = "(";
|
||||
|
||||
private readonly List<Operator> _operators = new List<Operator>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Tokenizer"/> class.
|
||||
/// This constructor will use the following default operators:
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Operator</term>
|
||||
/// <description>Precedence</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>=</term>
|
||||
/// <description>1</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>!=</term>
|
||||
/// <description>1</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>></term>
|
||||
/// <description>2</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><</term>
|
||||
/// <description>2</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>>=</term>
|
||||
/// <description>2</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><=</term>
|
||||
/// <description>2</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>+</term>
|
||||
/// <description>3</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>&</term>
|
||||
/// <description>3</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>-</term>
|
||||
/// <description>3</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>*</term>
|
||||
/// <description>4</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>(backslash)</term>
|
||||
/// <description>4</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/</term>
|
||||
/// <description>4</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>^</term>
|
||||
/// <description>4</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
protected Tokenizer(string input)
|
||||
{
|
||||
_operators.AddRange(GetDefaultOperators());
|
||||
Tokenize(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Tokenizer" /> class.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <param name="operators">The operators to use.</param>
|
||||
protected Tokenizer(string input, IEnumerable<Operator> operators)
|
||||
{
|
||||
_operators.AddRange(operators);
|
||||
Tokenize(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tokens.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The tokens.
|
||||
/// </value>
|
||||
public List<Token> Tokens { get; } = new List<Token>();
|
||||
|
||||
/// <summary>
|
||||
/// Validates the input and return the start index for tokenizer.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns>
|
||||
public abstract bool ValidateInput(string input, out int startIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the type of the function or member.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>The token type.</returns>
|
||||
public abstract TokenType ResolveFunctionOrMemberType(string input);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the function or member.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <returns><c>true</c> if the input is a valid function or variable, otherwise <c>false</c>.</returns>
|
||||
public virtual bool EvaluateFunctionOrMember(string input, int position) => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default operators.
|
||||
/// </summary>
|
||||
/// <returns>An array with the operators to use for the tokenizer.</returns>
|
||||
public virtual Operator[] GetDefaultOperators() => new[]
|
||||
{
|
||||
new Operator {Name = "=", Precedence = 1},
|
||||
new Operator {Name = "!=", Precedence = 1},
|
||||
new Operator {Name = ">", Precedence = 2},
|
||||
new Operator {Name = "<", Precedence = 2},
|
||||
new Operator {Name = ">=", Precedence = 2},
|
||||
new Operator {Name = "<=", Precedence = 2},
|
||||
new Operator {Name = "+", Precedence = 3},
|
||||
new Operator {Name = "&", Precedence = 3},
|
||||
new Operator {Name = "-", Precedence = 3},
|
||||
new Operator {Name = "*", Precedence = 4},
|
||||
new Operator {Name = "/", Precedence = 4},
|
||||
new Operator {Name = "\\", Precedence = 4},
|
||||
new Operator {Name = "^", Precedence = 4},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Shunting the yard.
|
||||
/// </summary>
|
||||
/// <param name="includeFunctionStopper">if set to <c>true</c> [include function stopper] (Token type <c>Wall</c>).</param>
|
||||
/// <returns>
|
||||
/// Enumerable of the token in in.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Wrong token
|
||||
/// or
|
||||
/// Mismatched parenthesis.
|
||||
/// </exception>
|
||||
public virtual IEnumerable<Token> ShuntingYard(bool includeFunctionStopper = true)
|
||||
{
|
||||
var stack = new Stack<Token>();
|
||||
|
||||
foreach (var tok in Tokens)
|
||||
{
|
||||
switch (tok.Type)
|
||||
{
|
||||
case TokenType.Number:
|
||||
case TokenType.Variable:
|
||||
case TokenType.String:
|
||||
yield return tok;
|
||||
break;
|
||||
case TokenType.Function:
|
||||
stack.Push(tok);
|
||||
break;
|
||||
case TokenType.Operator:
|
||||
while (stack.Any() && stack.Peek().Type == TokenType.Operator &&
|
||||
CompareOperators(tok.Value, stack.Peek().Value))
|
||||
yield return stack.Pop();
|
||||
|
||||
stack.Push(tok);
|
||||
break;
|
||||
case TokenType.Comma:
|
||||
while (stack.Any() && (stack.Peek().Type != TokenType.Comma &&
|
||||
stack.Peek().Type != TokenType.Parenthesis))
|
||||
yield return stack.Pop();
|
||||
|
||||
break;
|
||||
case TokenType.Parenthesis:
|
||||
if (tok.Value == OpenFuncStr)
|
||||
{
|
||||
if (stack.Any() && stack.Peek().Type == TokenType.Function)
|
||||
{
|
||||
if (includeFunctionStopper)
|
||||
yield return new Token(TokenType.Wall, tok.Value);
|
||||
}
|
||||
|
||||
stack.Push(tok);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (stack.Peek().Value != OpenFuncStr)
|
||||
yield return stack.Pop();
|
||||
|
||||
stack.Pop();
|
||||
|
||||
if (stack.Any() && stack.Peek().Type == TokenType.Function)
|
||||
{
|
||||
yield return stack.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Wrong token");
|
||||
}
|
||||
}
|
||||
|
||||
while (stack.Any())
|
||||
{
|
||||
var tok = stack.Pop();
|
||||
if (tok.Type == TokenType.Parenthesis)
|
||||
throw new InvalidOperationException("Mismatched parenthesis");
|
||||
|
||||
yield return tok;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CompareOperators(Operator op1, Operator op2) => op1.RightAssociative
|
||||
? op1.Precedence < op2.Precedence
|
||||
: op1.Precedence <= op2.Precedence;
|
||||
|
||||
private void Tokenize(string input)
|
||||
{
|
||||
if (!ValidateInput(input, out var startIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = startIndex; i < input.Length; i++)
|
||||
{
|
||||
if (char.IsWhiteSpace(input, i)) continue;
|
||||
|
||||
if (input[i] == CommaChar)
|
||||
{
|
||||
Tokens.Add(new Token(TokenType.Comma, new string(new[] { input[i] })));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input[i] == StringQuotedChar)
|
||||
{
|
||||
i = ExtractString(input, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char.IsLetter(input, i) || EvaluateFunctionOrMember(input, i))
|
||||
{
|
||||
i = ExtractFunctionOrMember(input, i);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char.IsNumber(input, i) || (
|
||||
input[i] == NegativeChar &&
|
||||
((Tokens.Any() && Tokens.Last().Type != TokenType.Number) || !Tokens.Any())))
|
||||
{
|
||||
i = ExtractNumber(input, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input[i] == OpenFuncChar ||
|
||||
input[i] == CloseFuncChar)
|
||||
{
|
||||
Tokens.Add(new Token(TokenType.Parenthesis, new string(new[] { input[i] })));
|
||||
continue;
|
||||
}
|
||||
|
||||
i = ExtractOperator(input, i);
|
||||
}
|
||||
}
|
||||
|
||||
private int ExtractData(
|
||||
string input,
|
||||
int i,
|
||||
Func<string, TokenType> tokenTypeEvaluation,
|
||||
Func<char, bool> evaluation,
|
||||
int right = 0,
|
||||
int left = -1)
|
||||
{
|
||||
var charCount = 0;
|
||||
for (var j = i + right; j < input.Length; j++)
|
||||
{
|
||||
if (evaluation(input[j]))
|
||||
break;
|
||||
|
||||
charCount++;
|
||||
}
|
||||
|
||||
// Extract and set the value
|
||||
var value = input.SliceLength(i + right, charCount);
|
||||
Tokens.Add(new Token(tokenTypeEvaluation(value), value));
|
||||
|
||||
i += charCount + left;
|
||||
return i;
|
||||
}
|
||||
|
||||
private int ExtractOperator(string input, int i) =>
|
||||
ExtractData(
|
||||
input,
|
||||
i,
|
||||
x => TokenType.Operator,
|
||||
x => x == OpenFuncChar ||
|
||||
x == CommaChar ||
|
||||
x == PeriodChar ||
|
||||
x == StringQuotedChar ||
|
||||
char.IsWhiteSpace(x) ||
|
||||
char.IsNumber(x));
|
||||
|
||||
private int ExtractFunctionOrMember(string input, int i) =>
|
||||
ExtractData(
|
||||
input,
|
||||
i,
|
||||
ResolveFunctionOrMemberType,
|
||||
x => x == OpenFuncChar ||
|
||||
x == CloseFuncChar ||
|
||||
x == CommaChar ||
|
||||
char.IsWhiteSpace(x));
|
||||
|
||||
private int ExtractNumber(string input, int i) =>
|
||||
ExtractData(
|
||||
input,
|
||||
i,
|
||||
x => TokenType.Number,
|
||||
x => !char.IsNumber(x) && x != PeriodChar && x != NegativeChar);
|
||||
|
||||
private int ExtractString(string input, int i)
|
||||
{
|
||||
var length = ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
|
||||
|
||||
// open string, report issue
|
||||
if (length == input.Length && input[length - 1] != StringQuotedChar)
|
||||
throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'.");
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
private bool CompareOperators(string op1, string op2)
|
||||
=> CompareOperators(GetOperatorOrDefault(op1), GetOperatorOrDefault(op2));
|
||||
|
||||
private Operator GetOperatorOrDefault(string op)
|
||||
=> _operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
|
||||
}
|
||||
}
|
||||
40
Vendor/Swan.Lite-3.1.0/Parsers/VerbOptionAttribute.cs
vendored
Normal file
40
Vendor/Swan.Lite-3.1.0/Parsers/VerbOptionAttribute.cs
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Parsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Models a verb option.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class VerbOptionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <exception cref="ArgumentNullException">name.</exception>
|
||||
public VerbOptionAttribute(string name)
|
||||
{
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the verb option.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Name.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a short description of this command line verb. Usually a sentence summary.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The help text.
|
||||
/// </value>
|
||||
public string HelpText { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $" {Name}\t\t{HelpText}";
|
||||
}
|
||||
}
|
||||
188
Vendor/Swan.Lite-3.1.0/Reflection/AttributeCache.cs
vendored
Normal file
188
Vendor/Swan.Lite-3.1.0/Reflection/AttributeCache.cs
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
|
||||
///
|
||||
/// The Retrieve method is the most useful one in this class as it
|
||||
/// calls the retrieval process if the type is not contained
|
||||
/// in the cache.
|
||||
/// </summary>
|
||||
public class AttributeCache
|
||||
{
|
||||
private readonly Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>> _data =
|
||||
new Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>>(() =>
|
||||
new ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>(), true);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AttributeCache"/> class.
|
||||
/// </summary>
|
||||
/// <param name="propertyCache">The property cache object.</param>
|
||||
public AttributeCache(PropertyTypeCache? propertyCache = null)
|
||||
{
|
||||
PropertyTypeCache = propertyCache ?? PropertyTypeCache.DefaultCache.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default cache.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default cache.
|
||||
/// </value>
|
||||
public static Lazy<AttributeCache> DefaultCache { get; } = new Lazy<AttributeCache>(() => new AttributeCache());
|
||||
|
||||
/// <summary>
|
||||
/// A PropertyTypeCache object for caching properties and their attributes.
|
||||
/// </summary>
|
||||
public PropertyTypeCache PropertyTypeCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [contains] [the specified member].
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
|
||||
/// <param name="member">The member.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Contains<T>(MemberInfo member) => _data.Value.ContainsKey(new Tuple<object, Type>(member, typeof(T)));
|
||||
|
||||
/// <summary>
|
||||
/// Gets specific attributes from a member constrained to an attribute.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
|
||||
/// <param name="member">The member.</param>
|
||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An array of the attributes stored for the specified type.</returns>
|
||||
public IEnumerable<object> Retrieve<T>(MemberInfo member, bool inherit = false)
|
||||
where T : Attribute
|
||||
{
|
||||
if (member == null)
|
||||
throw new ArgumentNullException(nameof(member));
|
||||
|
||||
return Retrieve(new Tuple<object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all attributes of a specific type from a member.
|
||||
/// </summary>
|
||||
/// <param name="member">The member.</param>
|
||||
/// <param name="type">The attribute type.</param>
|
||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An array of the attributes stored for the specified type.</returns>
|
||||
public IEnumerable<object> Retrieve(MemberInfo member, Type type, bool inherit = false)
|
||||
{
|
||||
if (member == null)
|
||||
throw new ArgumentNullException(nameof(member));
|
||||
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
return Retrieve(
|
||||
new Tuple<object, Type>(member, type),
|
||||
t => member.GetCustomAttributes(type, inherit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets one attribute of a specific type from a member.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The attribute type.</typeparam>
|
||||
/// <param name="member">The member.</param>
|
||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An attribute stored for the specified type.</returns>
|
||||
public T RetrieveOne<T>(MemberInfo member, bool inherit = false)
|
||||
where T : Attribute
|
||||
{
|
||||
if (member == null)
|
||||
return default;
|
||||
|
||||
var attr = Retrieve(
|
||||
new Tuple<object, Type>(member, typeof(T)),
|
||||
t => member.GetCustomAttributes(typeof(T), inherit));
|
||||
|
||||
return ConvertToAttribute<T>(attr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets one attribute of a specific type from a generic type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
|
||||
/// <typeparam name="T">The type to retrieve the attribute.</typeparam>
|
||||
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
|
||||
/// <returns>An attribute stored for the specified type.</returns>
|
||||
public TAttribute RetrieveOne<TAttribute, T>(bool inherit = false)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
var attr = Retrieve(
|
||||
new Tuple<object, Type>(typeof(T), typeof(TAttribute)),
|
||||
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
|
||||
|
||||
return ConvertToAttribute<TAttribute>(attr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all properties an their attributes of a given type constrained to only attributes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attribute to retrieve.</typeparam>
|
||||
/// <param name="type">The type of the object.</param>
|
||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||
/// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns>
|
||||
public Dictionary<PropertyInfo, IEnumerable<object>> Retrieve<T>(Type type, bool inherit = false)
|
||||
where T : Attribute =>
|
||||
PropertyTypeCache.RetrieveAllProperties(type, true)
|
||||
.ToDictionary(x => x, x => Retrieve<T>(x, inherit));
|
||||
|
||||
/// <summary>
|
||||
/// Gets all properties and their attributes of a given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
|
||||
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
|
||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||
/// <returns>
|
||||
/// A dictionary of the properties and their attributes stored for the specified type.
|
||||
/// </returns>
|
||||
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T, TAttribute>(bool inherit = false)
|
||||
=> RetrieveFromType<T>(typeof(TAttribute), inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all properties and their attributes of a given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
|
||||
/// <param name="attributeType">Type of the attribute.</param>
|
||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||
/// <returns>
|
||||
/// A dictionary of the properties and their attributes stored for the specified type.
|
||||
/// </returns>
|
||||
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T>(Type attributeType, bool inherit = false)
|
||||
{
|
||||
if (attributeType == null)
|
||||
throw new ArgumentNullException(nameof(attributeType));
|
||||
|
||||
return PropertyTypeCache.RetrieveAllProperties<T>(true)
|
||||
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit));
|
||||
}
|
||||
|
||||
private static T ConvertToAttribute<T>(IEnumerable<object> attr)
|
||||
where T : Attribute
|
||||
{
|
||||
if (attr?.Any() != true)
|
||||
return default;
|
||||
|
||||
return attr.Count() == 1
|
||||
? (T) Convert.ChangeType(attr.First(), typeof(T))
|
||||
: throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
|
||||
}
|
||||
|
||||
private IEnumerable<object> Retrieve(Tuple<object, Type> key, Func<Tuple<object, Type>, IEnumerable<object>> factory)
|
||||
{
|
||||
if (factory == null)
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Vendor/Swan.Lite-3.1.0/Reflection/ConstructorTypeCache.cs
vendored
Normal file
50
Vendor/Swan.Lite-3.1.0/Reflection/ConstructorTypeCache.cs
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe cache of constructors belonging to a given type.
|
||||
/// </summary>
|
||||
public class ConstructorTypeCache : TypeCache<Tuple<ConstructorInfo, ParameterInfo[]>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default cache.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default cache.
|
||||
/// </value>
|
||||
public static Lazy<ConstructorTypeCache> DefaultCache { get; } =
|
||||
new Lazy<ConstructorTypeCache>(() => new ConstructorTypeCache());
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all constructors order by the number of parameters ascending.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to inspect.</typeparam>
|
||||
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
|
||||
/// <returns>
|
||||
/// A collection with all the constructors in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<Tuple<ConstructorInfo, ParameterInfo[]>> RetrieveAllConstructors<T>(bool includeNonPublic = false)
|
||||
=> Retrieve<T>(GetConstructors(includeNonPublic));
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all constructors order by the number of parameters ascending.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
|
||||
/// <returns>
|
||||
/// A collection with all the constructors in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<Tuple<ConstructorInfo, ParameterInfo[]>> RetrieveAllConstructors(Type type, bool includeNonPublic = false)
|
||||
=> Retrieve(type, GetConstructors(includeNonPublic));
|
||||
|
||||
private static Func<Type, IEnumerable<Tuple<ConstructorInfo, ParameterInfo[]>>> GetConstructors(bool includeNonPublic)
|
||||
=> t => t.GetConstructors(includeNonPublic ? BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance : BindingFlags.Public | BindingFlags.Instance)
|
||||
.Select(x => Tuple.Create(x, x.GetParameters()))
|
||||
.OrderBy(x => x.Item2.Length)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
267
Vendor/Swan.Lite-3.1.0/Reflection/ExtendedTypeInfo.cs
vendored
Normal file
267
Vendor/Swan.Lite-3.1.0/Reflection/ExtendedTypeInfo.cs
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extended information about a type.
|
||||
///
|
||||
/// This class is mainly used to define sets of types within the Definition class
|
||||
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
|
||||
/// </summary>
|
||||
public class ExtendedTypeInfo
|
||||
{
|
||||
private const string TryParseMethodName = nameof(byte.TryParse);
|
||||
private const string ToStringMethodName = nameof(ToString);
|
||||
|
||||
private static readonly Type[] NumericTypes =
|
||||
{
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(decimal),
|
||||
typeof(double),
|
||||
typeof(float),
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(long),
|
||||
typeof(ulong),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
};
|
||||
|
||||
private readonly ParameterInfo[]? _tryParseParameters;
|
||||
private readonly int _toStringArgumentLength;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="t">The t.</param>
|
||||
public ExtendedTypeInfo(Type t)
|
||||
{
|
||||
Type = t ?? throw new ArgumentNullException(nameof(t));
|
||||
IsNullableValueType = Type.IsGenericType
|
||||
&& Type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
|
||||
IsValueType = t.IsValueType;
|
||||
|
||||
UnderlyingType = IsNullableValueType ?
|
||||
new NullableConverter(Type).UnderlyingType :
|
||||
Type;
|
||||
|
||||
IsNumeric = NumericTypes.Contains(UnderlyingType);
|
||||
|
||||
// Extract the TryParse method info
|
||||
try
|
||||
{
|
||||
TryParseMethodInfo = UnderlyingType.GetMethod(TryParseMethodName,
|
||||
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), UnderlyingType.MakeByRefType() }) ??
|
||||
UnderlyingType.GetMethod(TryParseMethodName,
|
||||
new[] { typeof(string), UnderlyingType.MakeByRefType() });
|
||||
|
||||
_tryParseParameters = TryParseMethodInfo?.GetParameters();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
// Extract the ToString method Info
|
||||
try
|
||||
{
|
||||
ToStringMethodInfo = UnderlyingType.GetMethod(ToStringMethodName,
|
||||
new[] { typeof(IFormatProvider) }) ??
|
||||
UnderlyingType.GetMethod(ToStringMethodName,
|
||||
Array.Empty<Type>());
|
||||
|
||||
_toStringArgumentLength = ToStringMethodInfo?.GetParameters().Length ?? 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type this extended info class provides for.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
public Type Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a nullable value type.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsNullableValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type or underlying type is numeric.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsNumeric { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is value type.
|
||||
/// Nullable value types have this property set to False.
|
||||
/// </summary>
|
||||
public bool IsValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// When dealing with nullable value types, this property will
|
||||
/// return the underlying value type of the nullable,
|
||||
/// Otherwise it will return the same type as the Type property.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the underlying.
|
||||
/// </value>
|
||||
public Type UnderlyingType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the try parse method information. If the type does not contain
|
||||
/// a suitable TryParse static method, it will return null.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The try parse method information.
|
||||
/// </value>
|
||||
public MethodInfo TryParseMethodInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ToString method info
|
||||
/// It will prefer the overload containing the IFormatProvider argument.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// To string method information.
|
||||
/// </value>
|
||||
public MethodInfo ToStringMethodInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type contains a suitable TryParse method.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool CanParseNatively => TryParseMethodInfo != null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the string into an object of the type this instance represents.
|
||||
/// Returns false when no suitable TryParse methods exists for the type or when parsing fails
|
||||
/// for any reason. When possible, this method uses CultureInfo.InvariantCulture and NumberStyles.Any.
|
||||
/// </summary>
|
||||
/// <param name="s">The s.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <returns><c>true</c> if parse was converted successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool TryParse(string s, out object? result)
|
||||
{
|
||||
result = Type.GetDefault();
|
||||
|
||||
try
|
||||
{
|
||||
if (Type == typeof(string))
|
||||
{
|
||||
result = Convert.ChangeType(s, Type, CultureInfo.InvariantCulture);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((IsNullableValueType && string.IsNullOrEmpty(s)) || !CanParseNatively)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build the arguments of the TryParse method
|
||||
var dynamicArguments = new List<object?> { s };
|
||||
|
||||
for (var pi = 1; pi < _tryParseParameters.Length - 1; pi++)
|
||||
{
|
||||
var argInfo = _tryParseParameters[pi];
|
||||
if (argInfo.ParameterType == typeof(IFormatProvider))
|
||||
dynamicArguments.Add(CultureInfo.InvariantCulture);
|
||||
else if (argInfo.ParameterType == typeof(NumberStyles))
|
||||
dynamicArguments.Add(NumberStyles.Any);
|
||||
else
|
||||
dynamicArguments.Add(null);
|
||||
}
|
||||
|
||||
dynamicArguments.Add(null);
|
||||
var parseArguments = dynamicArguments.ToArray();
|
||||
|
||||
if ((bool) TryParseMethodInfo.Invoke(null, parseArguments))
|
||||
{
|
||||
result = parseArguments[parseArguments.Length - 1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this instance to its string representation,
|
||||
/// trying to use the CultureInfo.InvariantCulture
|
||||
/// IFormat provider if the overload is available.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||
public string ToStringInvariant(object instance)
|
||||
{
|
||||
if (instance == null)
|
||||
return string.Empty;
|
||||
|
||||
return _toStringArgumentLength != 1
|
||||
? instance.ToString()
|
||||
: ToStringMethodInfo.Invoke(instance, new object[] {CultureInfo.InvariantCulture}) as string ?? string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extended information about a type.
|
||||
///
|
||||
/// This class is mainly used to define sets of types within the Constants class
|
||||
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of extended type information.</typeparam>
|
||||
public class ExtendedTypeInfo<T> : ExtendedTypeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class.
|
||||
/// </summary>
|
||||
public ExtendedTypeInfo()
|
||||
: base(typeof(T))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this instance to its string representation,
|
||||
/// trying to use the CultureInfo.InvariantCulture
|
||||
/// IFormat provider if the overload is available.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||
public string ToStringInvariant(T instance) => base.ToStringInvariant(instance);
|
||||
}
|
||||
}
|
||||
45
Vendor/Swan.Lite-3.1.0/Reflection/IPropertyProxy.cs
vendored
Normal file
45
Vendor/Swan.Lite-3.1.0/Reflection/IPropertyProxy.cs
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic interface to store getters and setters for high speed access to properties.
|
||||
/// </summary>
|
||||
public interface IPropertyProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the property.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the property.
|
||||
/// </summary>
|
||||
Type PropertyType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated reflection property info.
|
||||
/// </summary>
|
||||
PropertyInfo Property { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type owning this property proxy.
|
||||
/// </summary>
|
||||
Type EnclosingType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value via a stored delegate.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>The property value.</returns>
|
||||
object? GetValue(object instance);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value via a stored delegate.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
void SetValue(object instance, object? value);
|
||||
}
|
||||
}
|
||||
117
Vendor/Swan.Lite-3.1.0/Reflection/MethodInfoCache.cs
vendored
Normal file
117
Vendor/Swan.Lite-3.1.0/Reflection/MethodInfoCache.cs
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Method Info Cache.
|
||||
/// </summary>
|
||||
public class MethodInfoCache : ConcurrentDictionary<string, MethodInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the properties stored for the specified type.
|
||||
/// If the properties are not available, it calls the factory method to retrieve them
|
||||
/// and returns them as an array of PropertyInfo.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of type.</typeparam>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="alias">The alias.</param>
|
||||
/// <param name="types">The types.</param>
|
||||
/// <returns>
|
||||
/// The cached MethodInfo.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">name
|
||||
/// or
|
||||
/// factory.</exception>
|
||||
public MethodInfo Retrieve<T>(string name, string alias, params Type[] types)
|
||||
=> Retrieve(typeof(T), name, alias, types);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the specified name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of type.</typeparam>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="types">The types.</param>
|
||||
/// <returns>
|
||||
/// The cached MethodInfo.
|
||||
/// </returns>
|
||||
public MethodInfo Retrieve<T>(string name, params Type[] types)
|
||||
=> Retrieve(typeof(T), name, name, types);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="types">The types.</param>
|
||||
/// <returns>
|
||||
/// An array of the properties stored for the specified type.
|
||||
/// </returns>
|
||||
public MethodInfo Retrieve(Type type, string name, params Type[] types)
|
||||
=> Retrieve(type, name, name, types);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="alias">The alias.</param>
|
||||
/// <param name="types">The types.</param>
|
||||
/// <returns>
|
||||
/// The cached MethodInfo.
|
||||
/// </returns>
|
||||
public MethodInfo Retrieve(Type type, string name, string alias, params Type[] types)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (alias == null)
|
||||
throw new ArgumentNullException(nameof(alias));
|
||||
|
||||
if (name == null)
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
|
||||
return GetOrAdd(
|
||||
alias,
|
||||
x => type.GetMethod(name, types ?? Array.Empty<Type>()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the specified name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of type.</typeparam>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>
|
||||
/// The cached MethodInfo.
|
||||
/// </returns>
|
||||
public MethodInfo Retrieve<T>(string name)
|
||||
=> Retrieve(typeof(T), name);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>
|
||||
/// The cached MethodInfo.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// type
|
||||
/// or
|
||||
/// name.
|
||||
/// </exception>
|
||||
public MethodInfo Retrieve(Type type, string name)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (name == null)
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
|
||||
return GetOrAdd(
|
||||
name,
|
||||
type.GetMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Vendor/Swan.Lite-3.1.0/Reflection/PropertyInfoProxy.cs
vendored
Normal file
79
Vendor/Swan.Lite-3.1.0/Reflection/PropertyInfoProxy.cs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// The concrete and hidden implementation of the <see cref="IPropertyProxy"/> implementation.
|
||||
/// </summary>
|
||||
/// <seealso cref="IPropertyProxy" />
|
||||
internal sealed class PropertyInfoProxy : IPropertyProxy
|
||||
{
|
||||
private readonly Func<object, object>? _getter;
|
||||
private readonly Action<object, object?>? _setter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyInfoProxy"/> class.
|
||||
/// </summary>
|
||||
/// <param name="declaringType">Type of the declaring.</param>
|
||||
/// <param name="propertyInfo">The property information.</param>
|
||||
public PropertyInfoProxy(Type declaringType, PropertyInfo propertyInfo)
|
||||
{
|
||||
Property = propertyInfo;
|
||||
EnclosingType = declaringType;
|
||||
_getter = CreateLambdaGetter(declaringType, propertyInfo);
|
||||
_setter = CreateLambdaSetter(declaringType, propertyInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PropertyInfo Property { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type EnclosingType { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Property.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type PropertyType => Property.PropertyType;
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? GetValue(object instance) => _getter?.Invoke(instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetValue(object instance, object? value) => _setter?.Invoke(instance, value);
|
||||
|
||||
private static Func<object, object>? CreateLambdaGetter(Type instanceType, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!propertyInfo.CanRead)
|
||||
return null;
|
||||
|
||||
var instanceParameter = Expression.Parameter(typeof(object), "instance");
|
||||
var typedInstance = Expression.Convert(instanceParameter, instanceType);
|
||||
var property = Expression.Property(typedInstance, propertyInfo);
|
||||
var convert = Expression.Convert(property, typeof(object));
|
||||
var dynamicGetter = (Func<object, object>)Expression.Lambda(convert, instanceParameter).Compile();
|
||||
|
||||
return dynamicGetter;
|
||||
}
|
||||
|
||||
private static Action<object, object?>? CreateLambdaSetter(Type instanceType, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!propertyInfo.CanWrite)
|
||||
return null;
|
||||
|
||||
var instanceParameter = Expression.Parameter(typeof(object), "instance");
|
||||
var valueParameter = Expression.Parameter(typeof(object), "value");
|
||||
|
||||
var typedInstance = Expression.Convert(instanceParameter, instanceType);
|
||||
var property = Expression.Property(typedInstance, propertyInfo);
|
||||
var propertyValue = Expression.Convert(valueParameter, propertyInfo.PropertyType);
|
||||
|
||||
var body = Expression.Assign(property, propertyValue);
|
||||
var dynamicSetter = Expression.Lambda<Action<object, object?>>(body, instanceParameter, valueParameter).Compile();
|
||||
|
||||
return dynamicSetter;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
Vendor/Swan.Lite-3.1.0/Reflection/PropertyTypeCache.cs
vendored
Normal file
74
Vendor/Swan.Lite-3.1.0/Reflection/PropertyTypeCache.cs
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe cache of properties belonging to a given type.
|
||||
/// </summary>
|
||||
public class PropertyTypeCache : TypeCache<PropertyInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default cache.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default cache.
|
||||
/// </value>
|
||||
public static Lazy<PropertyTypeCache> DefaultCache { get; } = new Lazy<PropertyTypeCache>(() => new PropertyTypeCache());
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all properties.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to inspect.</typeparam>
|
||||
/// <param name="onlyPublic">if set to <c>true</c> [only public].</param>
|
||||
/// <returns>
|
||||
/// A collection with all the properties in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<PropertyInfo> RetrieveAllProperties<T>(bool onlyPublic = false)
|
||||
=> Retrieve<T>(onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all properties.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="onlyPublic">if set to <c>true</c> [only public].</param>
|
||||
/// <returns>
|
||||
/// A collection with all the properties in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<PropertyInfo> RetrieveAllProperties(Type type, bool onlyPublic = false)
|
||||
=> Retrieve(type, onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the filtered properties.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="onlyPublic">if set to <c>true</c> [only public].</param>
|
||||
/// <param name="filter">The filter.</param>
|
||||
/// <returns>
|
||||
/// A collection with all the properties in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<PropertyInfo> RetrieveFilteredProperties(
|
||||
Type type,
|
||||
bool onlyPublic,
|
||||
Func<PropertyInfo, bool> filter)
|
||||
=> Retrieve(type,
|
||||
onlyPublic ? GetAllPublicPropertiesFunc(filter) : GetAllPropertiesFunc(filter));
|
||||
|
||||
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPropertiesFunc(
|
||||
Func<PropertyInfo, bool>? filter = null)
|
||||
=> GetPropertiesFunc(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
||||
filter);
|
||||
|
||||
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPublicPropertiesFunc(
|
||||
Func<PropertyInfo, bool>? filter = null)
|
||||
=> GetPropertiesFunc(BindingFlags.Public | BindingFlags.Instance, filter);
|
||||
|
||||
private static Func<Type, IEnumerable<PropertyInfo>> GetPropertiesFunc(BindingFlags flags,
|
||||
Func<PropertyInfo, bool>? filter = null)
|
||||
=> t => t.GetProperties(flags)
|
||||
.Where(filter ?? (p => p.CanRead || p.CanWrite));
|
||||
}
|
||||
}
|
||||
78
Vendor/Swan.Lite-3.1.0/Reflection/TypeCache.cs
vendored
Normal file
78
Vendor/Swan.Lite-3.1.0/Reflection/TypeCache.cs
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Swan.Collections;
|
||||
|
||||
namespace Swan.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe cache of members belonging to a given type.
|
||||
///
|
||||
/// The Retrieve method is the most useful one in this class as it
|
||||
/// calls the retrieval process if the type is not contained
|
||||
/// in the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Member to be cached.</typeparam>
|
||||
public abstract class TypeCache<T> : CollectionCacheRepository<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the cache contains the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TOut">The type of the out.</typeparam>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [contains]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Contains<TOut>() => ContainsKey(typeof(TOut));
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the properties stored for the specified type.
|
||||
/// If the properties are not available, it calls the factory method to retrieve them
|
||||
/// and returns them as an array of PropertyInfo.
|
||||
/// </summary>
|
||||
/// <typeparam name="TOut">The type of the out.</typeparam>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <returns>An array of the properties stored for the specified type.</returns>
|
||||
public IEnumerable<T> Retrieve<TOut>(Func<Type, IEnumerable<T>> factory)
|
||||
=> Retrieve(typeof(TOut), factory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A thread-safe cache of fields belonging to a given type
|
||||
/// The Retrieve method is the most useful one in this class as it
|
||||
/// calls the retrieval process if the type is not contained
|
||||
/// in the cache.
|
||||
/// </summary>
|
||||
public class FieldTypeCache : TypeCache<FieldInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default cache.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default cache.
|
||||
/// </value>
|
||||
public static Lazy<FieldTypeCache> DefaultCache { get; } = new Lazy<FieldTypeCache>(() => new FieldTypeCache());
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all fields.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to inspect.</typeparam>
|
||||
/// <returns>
|
||||
/// A collection with all the fields in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<FieldInfo> RetrieveAllFields<T>()
|
||||
=> Retrieve<T>(GetAllFieldsFunc());
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all fields.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>
|
||||
/// A collection with all the fields in the given type.
|
||||
/// </returns>
|
||||
public IEnumerable<FieldInfo> RetrieveAllFields(Type type)
|
||||
=> Retrieve(type, GetAllFieldsFunc());
|
||||
|
||||
private static Func<Type, IEnumerable<FieldInfo>> GetAllFieldsFunc()
|
||||
=> t => t.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||
}
|
||||
}
|
||||
62
Vendor/Swan.Lite-3.1.0/SelfCheck.cs
vendored
Normal file
62
Vendor/Swan.Lite-3.1.0/SelfCheck.cs
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to perform self-checks in library or application code.
|
||||
/// </summary>
|
||||
public static class SelfCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Creates and returns an exception telling that an internal self-check has failed.</para>
|
||||
/// <para>The returned exception will be of type <see cref="InternalErrorException"/>; its
|
||||
/// <see cref="Exception.Message">Message</see> property will contain the specified
|
||||
/// <paramref name="message"/>, preceded by an indication of the assembly, source file,
|
||||
/// and line number of the failed check.</para>
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message.</param>
|
||||
/// <param name="filePath">The path of the source file where this method is called.
|
||||
/// This parameter is automatically added by the compiler amd should never be provided explicitly.</param>
|
||||
/// <param name="lineNumber">The line number in source where this method is called.
|
||||
/// This parameter is automatically added by the compiler amd should never be provided explicitly.</param>
|
||||
/// <returns>
|
||||
/// A newly-created instance of <see cref="InternalErrorException"/>.
|
||||
/// </returns>
|
||||
public static InternalErrorException Failure(string message, [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0)
|
||||
=> new InternalErrorException(BuildMessage(message, filePath, lineNumber));
|
||||
|
||||
private static string BuildMessage(string message, string filePath, int lineNumber)
|
||||
{
|
||||
var frames = new StackTrace().GetFrames();
|
||||
if (frames == null)
|
||||
return message;
|
||||
|
||||
try
|
||||
{
|
||||
filePath = Path.GetFileName(filePath);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
|
||||
var frame = frames.FirstOrDefault(f => f.GetMethod().ReflectedType != typeof(SelfCheck));
|
||||
var sb = new StringBuilder()
|
||||
.Append('[')
|
||||
.Append(frame?.GetType().Assembly.GetName().Name ?? "<unknown>");
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
sb.Append(": ").Append(filePath);
|
||||
if (lineNumber > 0)
|
||||
sb.Append('(').Append(lineNumber).Append(')');
|
||||
}
|
||||
|
||||
return sb.Append("] ").Append(message).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Vendor/Swan.Lite-3.1.0/SingletonBase.cs
vendored
Normal file
61
Vendor/Swan.Lite-3.1.0/SingletonBase.cs
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a singleton pattern abstract class.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of class.</typeparam>
|
||||
public abstract class SingletonBase<T> : IDisposable
|
||||
where T : class
|
||||
{
|
||||
/// <summary>
|
||||
/// The static, singleton instance reference.
|
||||
/// </summary>
|
||||
protected static readonly Lazy<T> LazyInstance = new Lazy<T>(
|
||||
valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
|
||||
isThreadSafe: true);
|
||||
|
||||
private bool _isDisposing; // To detect redundant calls
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance that this singleton represents.
|
||||
/// If the instance is null, it is constructed and assigned when this member is accessed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The instance.
|
||||
/// </value>
|
||||
public static T Instance => LazyInstance.Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// Call the GC.SuppressFinalize if you override this method and use
|
||||
/// a non-default class finalizer (destructor).
|
||||
/// </summary>
|
||||
/// <param name="disposeManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposeManaged)
|
||||
{
|
||||
if (_isDisposing) return;
|
||||
|
||||
_isDisposing = true;
|
||||
|
||||
// free managed resources
|
||||
if (LazyInstance == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var disposableInstance = LazyInstance.Value as IDisposable;
|
||||
disposableInstance?.Dispose();
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Vendor/Swan.Lite-3.1.0/StringConversionException.cs
vendored
Normal file
76
Vendor/Swan.Lite-3.1.0/StringConversionException.cs
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when a conversion from a string to a
|
||||
/// specified type fails.
|
||||
/// </summary>
|
||||
/// <seealso cref="FromString" />
|
||||
[Serializable]
|
||||
public class StringConversionException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringConversionException"/> class.
|
||||
/// </summary>
|
||||
public StringConversionException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringConversionException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
public StringConversionException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringConversionException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception,
|
||||
/// or <see langword="null" /> if no inner exception is specified.</param>
|
||||
public StringConversionException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringConversionException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The desired resulting type of the attempted conversion.</param>
|
||||
public StringConversionException(Type type)
|
||||
: base(BuildStandardMessageForType(type))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringConversionException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The desired resulting type of the attempted conversion.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception,
|
||||
/// or <see langword="null" /> if no inner exception is specified.</param>
|
||||
public StringConversionException(Type type, Exception innerException)
|
||||
: base(BuildStandardMessageForType(type), innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringConversionException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="SerializationInfo" /> that holds the serialized object data
|
||||
/// about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="StreamingContext" /> that contains contextual information
|
||||
/// about the source or destination.</param>
|
||||
protected StringConversionException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
private static string BuildStandardMessageForType(Type type)
|
||||
=> $"Cannot convert a string to an instance of {type.FullName}";
|
||||
}
|
||||
}
|
||||
30
Vendor/Swan.Lite-3.1.0/StructEndiannessAttribute.cs
vendored
Normal file
30
Vendor/Swan.Lite-3.1.0/StructEndiannessAttribute.cs
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute used to help conversion structs back and forth into arrays of bytes via
|
||||
/// extension methods included in this library ToStruct and ToBytes.
|
||||
/// </summary>
|
||||
/// <seealso cref="Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)]
|
||||
public class StructEndiannessAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="endianness">The endianness.</param>
|
||||
public StructEndiannessAttribute(Endianness endianness)
|
||||
{
|
||||
Endianness = endianness;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endianness.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The endianness.
|
||||
/// </value>
|
||||
public Endianness Endianness { get; }
|
||||
}
|
||||
}
|
||||
237
Vendor/Swan.Lite-3.1.0/SwanRuntime.cs
vendored
Normal file
237
Vendor/Swan.Lite-3.1.0/SwanRuntime.cs
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
using Swan.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods to retrieve information about the current application.
|
||||
/// </summary>
|
||||
public static class SwanRuntime
|
||||
{
|
||||
private static readonly Lazy<Assembly> EntryAssemblyLazy = new Lazy<Assembly>(Assembly.GetEntryAssembly);
|
||||
|
||||
private static readonly Lazy<string> CompanyNameLazy = new Lazy<string>(() =>
|
||||
{
|
||||
var attribute =
|
||||
EntryAssembly.GetCustomAttribute(typeof(AssemblyCompanyAttribute)) as AssemblyCompanyAttribute;
|
||||
return attribute?.Company ?? string.Empty;
|
||||
});
|
||||
|
||||
private static readonly Lazy<string> ProductNameLazy = new Lazy<string>(() =>
|
||||
{
|
||||
var attribute =
|
||||
EntryAssembly.GetCustomAttribute(typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
|
||||
return attribute?.Product ?? string.Empty;
|
||||
});
|
||||
|
||||
private static readonly Lazy<string> ProductTrademarkLazy = new Lazy<string>(() =>
|
||||
{
|
||||
var attribute =
|
||||
EntryAssembly.GetCustomAttribute(typeof(AssemblyTrademarkAttribute)) as AssemblyTrademarkAttribute;
|
||||
return attribute?.Trademark ?? string.Empty;
|
||||
});
|
||||
|
||||
private static readonly string ApplicationMutexName = "Global\\{{" + EntryAssembly.FullName + "}}";
|
||||
|
||||
private static readonly object SyncLock = new object();
|
||||
|
||||
private static OperatingSystem? _oS;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Operating System.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The os.
|
||||
/// </value>
|
||||
public static OperatingSystem OS
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_oS.HasValue == false)
|
||||
{
|
||||
var windowsDirectory = Environment.GetEnvironmentVariable("windir");
|
||||
if (string.IsNullOrEmpty(windowsDirectory) == false
|
||||
&& windowsDirectory.Contains(@"\")
|
||||
&& Directory.Exists(windowsDirectory))
|
||||
{
|
||||
_oS = OperatingSystem.Windows;
|
||||
}
|
||||
else
|
||||
{
|
||||
_oS = File.Exists(@"/proc/sys/kernel/ostype") ? OperatingSystem.Unix : OperatingSystem.Osx;
|
||||
}
|
||||
}
|
||||
|
||||
return _oS ?? OperatingSystem.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this application (including version number) is the only instance currently running.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is the only instance; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool IsTheOnlyInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to open existing mutex.
|
||||
using var _ = Mutex.OpenExisting(ApplicationMutexName);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
try
|
||||
{
|
||||
// If exception occurred, there is no such mutex.
|
||||
using var appMutex = new Mutex(true, ApplicationMutexName);
|
||||
$"Application Mutex created {appMutex} named '{ApplicationMutexName}'".Debug(
|
||||
typeof(SwanRuntime));
|
||||
|
||||
// Only one instance.
|
||||
return true;
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
// Sometimes the user can't create the Global Mutex
|
||||
}
|
||||
}
|
||||
|
||||
// More than one instance.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this application instance is using the MONO runtime.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is using MONO runtime; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool IsUsingMonoRuntime => Type.GetType("Mono.Runtime") != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly that started the application.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The entry assembly.
|
||||
/// </value>
|
||||
public static Assembly EntryAssembly => EntryAssemblyLazy.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the entry assembly.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the entry assembly.
|
||||
/// </value>
|
||||
public static AssemblyName EntryAssemblyName => EntryAssemblyLazy.Value.GetName();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entry assembly version.
|
||||
/// </summary>
|
||||
public static Version EntryAssemblyVersion => EntryAssemblyName.Version;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the folder containing the assembly that started the application.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The entry assembly directory.
|
||||
/// </value>
|
||||
public static string EntryAssemblyDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
var uri = new UriBuilder(EntryAssembly.CodeBase);
|
||||
var path = Uri.UnescapeDataString(uri.Path);
|
||||
return Path.GetDirectoryName(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the company.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the company.
|
||||
/// </value>
|
||||
public static string CompanyName => CompanyNameLazy.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the product.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the product.
|
||||
/// </value>
|
||||
public static string ProductName => ProductNameLazy.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trademark.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The product trademark.
|
||||
/// </value>
|
||||
public static string ProductTrademark => ProductTrademarkLazy.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a local storage path with a version.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The local storage path.
|
||||
/// </value>
|
||||
public static string LocalStoragePath
|
||||
{
|
||||
get
|
||||
{
|
||||
var localAppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
EntryAssemblyName.Name);
|
||||
|
||||
var returnPath = Path.Combine(localAppDataPath, EntryAssemblyVersion.ToString());
|
||||
|
||||
if (!Directory.Exists(returnPath))
|
||||
{
|
||||
Directory.CreateDirectory(returnPath);
|
||||
}
|
||||
|
||||
return returnPath;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Build a full path pointing to the current user's desktop with the given filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>
|
||||
/// The fully qualified location of path, such as "C:\MyFile.txt".
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">filename.</exception>
|
||||
public static string GetDesktopFilePath(string filename)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
|
||||
var pathWithFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
|
||||
filename);
|
||||
|
||||
return Path.GetFullPath(pathWithFilename);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
37
Vendor/Swan.Lite-3.1.0/Terminal.Graphics.cs
vendored
Normal file
37
Vendor/Swan.Lite-3.1.0/Terminal.Graphics.cs
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// A console terminal helper to create nicer output and receive input from the user
|
||||
/// This class is thread-safe :).
|
||||
/// </summary>
|
||||
public static partial class Terminal
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Table to print in console.
|
||||
/// </summary>
|
||||
private static class Table
|
||||
{
|
||||
public static void Vertical() => Write('\u2502', Settings.BorderColor);
|
||||
|
||||
public static void RightTee() => Write('\u2524', Settings.BorderColor);
|
||||
|
||||
public static void TopRight() => Write('\u2510', Settings.BorderColor);
|
||||
|
||||
public static void BottomLeft() => Write('\u2514', Settings.BorderColor);
|
||||
|
||||
public static void BottomTee() => Write('\u2534', Settings.BorderColor);
|
||||
|
||||
public static void TopTee() => Write('\u252c', Settings.BorderColor);
|
||||
|
||||
public static void LeftTee() => Write('\u251c', Settings.BorderColor);
|
||||
|
||||
public static void Horizontal(int length) => Write(new string('\u2500', length), Settings.BorderColor);
|
||||
|
||||
public static void Tee() => Write('\u253c', Settings.BorderColor);
|
||||
|
||||
public static void BottomRight() => Write('\u2518', Settings.BorderColor);
|
||||
|
||||
public static void TopLeft() => Write('\u250C', Settings.BorderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
260
Vendor/Swan.Lite-3.1.0/Terminal.Interaction.cs
vendored
Normal file
260
Vendor/Swan.Lite-3.1.0/Terminal.Interaction.cs
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Swan.Logging;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// A console terminal helper to create nicer output and receive input from the user
|
||||
/// This class is thread-safe :).
|
||||
/// </summary>
|
||||
public static partial class Terminal
|
||||
{
|
||||
#region ReadKey
|
||||
|
||||
/// <summary>
|
||||
/// Reads a key from the Terminal. This is the closest equivalent to Console.ReadKey.
|
||||
/// </summary>
|
||||
/// <param name="intercept">if set to <c>true</c> the pressed key will not be rendered to the output.</param>
|
||||
/// <param name="disableLocking">if set to <c>true</c> the output will continue to be shown.
|
||||
/// This is useful for services and daemons that are running as console applications and wait for a key to exit the program.</param>
|
||||
/// <returns>The console key information.</returns>
|
||||
public static ConsoleKeyInfo ReadKey(bool intercept, bool disableLocking = false)
|
||||
{
|
||||
if (!IsConsolePresent) return default;
|
||||
if (disableLocking) return Console.ReadKey(intercept);
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
InputDone.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
Console.CursorVisible = true;
|
||||
return Console.ReadKey(intercept);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.CursorVisible = false;
|
||||
InputDone.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a key from the Terminal.
|
||||
/// </summary>
|
||||
/// <param name="prompt">The prompt.</param>
|
||||
/// <param name="preventEcho">if set to <c>true</c> [prevent echo].</param>
|
||||
/// <returns>The console key information.</returns>
|
||||
public static ConsoleKeyInfo ReadKey(string prompt, bool preventEcho = true)
|
||||
{
|
||||
if (!IsConsolePresent) return default;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
if (prompt != null)
|
||||
{
|
||||
Write($"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt} ", ConsoleColor.White);
|
||||
}
|
||||
|
||||
var input = ReadKey(true);
|
||||
var echo = preventEcho ? string.Empty : input.Key.ToString();
|
||||
WriteLine(echo);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other Terminal Read Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clears the screen.
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
Flush();
|
||||
Console.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line of text from the console.
|
||||
/// </summary>
|
||||
/// <returns>The read line.</returns>
|
||||
public static string? ReadLine()
|
||||
{
|
||||
if (IsConsolePresent == false) return default;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
InputDone.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
Console.CursorVisible = true;
|
||||
return Console.ReadLine();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.CursorVisible = false;
|
||||
InputDone.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a line from the input.
|
||||
/// </summary>
|
||||
/// <param name="prompt">The prompt.</param>
|
||||
/// <returns>The read line.</returns>
|
||||
public static string? ReadLine(string prompt)
|
||||
{
|
||||
if (!IsConsolePresent) return null;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Write($"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt}: ", ConsoleColor.White);
|
||||
|
||||
return ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a number from the input. If unable to parse, it returns the default number.
|
||||
/// </summary>
|
||||
/// <param name="prompt">The prompt.</param>
|
||||
/// <param name="defaultNumber">The default number.</param>
|
||||
/// <returns>
|
||||
/// Conversions of string representation of a number to its 32-bit signed integer equivalent.
|
||||
/// </returns>
|
||||
public static int ReadNumber(string prompt, int defaultNumber)
|
||||
{
|
||||
if (!IsConsolePresent) return defaultNumber;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Write($"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt} (default is {defaultNumber}): ",
|
||||
ConsoleColor.White);
|
||||
|
||||
var input = ReadLine();
|
||||
return int.TryParse(input, out var parsedInt) ? parsedInt : defaultNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a table prompt where the user can enter an option based on the options dictionary provided.
|
||||
/// </summary>
|
||||
/// <param name="title">The title.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="anyKeyOption">Any key option.</param>
|
||||
/// <returns>
|
||||
/// A value that identifies the console key that was pressed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">options.</exception>
|
||||
public static ConsoleKeyInfo ReadPrompt(
|
||||
string title,
|
||||
IDictionary<ConsoleKey, string> options,
|
||||
string anyKeyOption)
|
||||
{
|
||||
if (!IsConsolePresent) return default;
|
||||
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
||||
const ConsoleColor textColor = ConsoleColor.White;
|
||||
var lineLength = Console.WindowWidth;
|
||||
var lineAlign = -(lineLength - 2);
|
||||
var textFormat = "{0," + lineAlign + "}";
|
||||
|
||||
// lock the output as an atomic operation
|
||||
lock (SyncLock)
|
||||
{
|
||||
{
|
||||
// Top border
|
||||
Table.TopLeft();
|
||||
Table.Horizontal(-lineAlign);
|
||||
Table.TopRight();
|
||||
}
|
||||
|
||||
{
|
||||
// Title
|
||||
Table.Vertical();
|
||||
var titleText = string.Format(CultureInfo.CurrentCulture,
|
||||
textFormat,
|
||||
string.IsNullOrWhiteSpace(title) ? " Select an option from the list below." : $" {title}");
|
||||
Write(titleText, textColor);
|
||||
Table.Vertical();
|
||||
}
|
||||
|
||||
{
|
||||
// Title Bottom
|
||||
Table.LeftTee();
|
||||
Table.Horizontal(lineLength - 2);
|
||||
Table.RightTee();
|
||||
}
|
||||
|
||||
// Options
|
||||
foreach (var kvp in options)
|
||||
{
|
||||
Table.Vertical();
|
||||
Write(string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
textFormat,
|
||||
$" {"[ " + kvp.Key + " ]",-10} {kvp.Value}"),
|
||||
textColor);
|
||||
Table.Vertical();
|
||||
}
|
||||
|
||||
// Any Key Options
|
||||
if (string.IsNullOrWhiteSpace(anyKeyOption) == false)
|
||||
{
|
||||
Table.Vertical();
|
||||
Write(string.Format(CultureInfo.CurrentCulture, textFormat, " "), ConsoleColor.Gray);
|
||||
Table.Vertical();
|
||||
|
||||
Table.Vertical();
|
||||
Write(string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
textFormat,
|
||||
$" {" ",-10} {anyKeyOption}"),
|
||||
ConsoleColor.Gray);
|
||||
Table.Vertical();
|
||||
}
|
||||
|
||||
{
|
||||
// Input section
|
||||
Table.LeftTee();
|
||||
Table.Horizontal(lineLength - 2);
|
||||
Table.RightTee();
|
||||
|
||||
Table.Vertical();
|
||||
Write(string.Format(CultureInfo.CurrentCulture, textFormat, Settings.UserOptionText),
|
||||
ConsoleColor.Green);
|
||||
Table.Vertical();
|
||||
|
||||
Table.BottomLeft();
|
||||
Table.Horizontal(lineLength - 2);
|
||||
Table.BottomRight();
|
||||
}
|
||||
}
|
||||
|
||||
var inputLeft = Settings.UserOptionText.Length + 3;
|
||||
|
||||
SetCursorPosition(inputLeft, CursorTop - 1);
|
||||
var userInput = ReadKey(true);
|
||||
Write(userInput.Key.ToString(), ConsoleColor.Gray);
|
||||
|
||||
SetCursorPosition(0, CursorTop + 2);
|
||||
return userInput;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static string GetNowFormatted() =>
|
||||
$" {(string.IsNullOrWhiteSpace(TextLogger.LoggingTimeFormat) ? string.Empty : DateTime.Now.ToString(TextLogger.LoggingTimeFormat) + " ")}";
|
||||
}
|
||||
}
|
||||
97
Vendor/Swan.Lite-3.1.0/Terminal.Output.cs
vendored
Normal file
97
Vendor/Swan.Lite-3.1.0/Terminal.Output.cs
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// A console terminal helper to create nicer output and receive input from the user
|
||||
/// This class is thread-safe :).
|
||||
/// </summary>
|
||||
public static partial class Terminal
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a character a number of times, optionally adding a new line at the end.
|
||||
/// </summary>
|
||||
/// <param name="charCode">The character code.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="newLine">if set to <c>true</c> [new line].</param>
|
||||
/// <param name="writerFlags">The writer flags.</param>
|
||||
public static void Write(char charCode, ConsoleColor? color = null, int count = 1, bool newLine = false, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
var text = new string(charCode, count);
|
||||
|
||||
if (newLine)
|
||||
{
|
||||
text += Environment.NewLine;
|
||||
}
|
||||
|
||||
var buffer = OutputEncoding.GetBytes(text);
|
||||
var context = new OutputContext
|
||||
{
|
||||
OutputColor = color ?? Settings.DefaultColor,
|
||||
OutputText = OutputEncoding.GetChars(buffer),
|
||||
OutputWriters = writerFlags,
|
||||
};
|
||||
|
||||
EnqueueOutput(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified text in the given color.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="writerFlags">The writer flags.</param>
|
||||
public static void Write(string? text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
||||
{
|
||||
if (text == null) return;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
var buffer = OutputEncoding.GetBytes(text);
|
||||
var context = new OutputContext
|
||||
{
|
||||
OutputColor = color ?? Settings.DefaultColor,
|
||||
OutputText = OutputEncoding.GetChars(buffer),
|
||||
OutputWriters = writerFlags,
|
||||
};
|
||||
|
||||
EnqueueOutput(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a New Line Sequence to the standard output.
|
||||
/// </summary>
|
||||
/// <param name="writerFlags">The writer flags.</param>
|
||||
public static void WriteLine(TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
||||
=> Write(Environment.NewLine, Settings.DefaultColor, writerFlags);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line of text in the current console foreground color
|
||||
/// to the standard output.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="writerFlags">The writer flags.</param>
|
||||
public static void WriteLine(string text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
||||
=> Write($"{text ?? string.Empty}{Environment.NewLine}", color, writerFlags);
|
||||
|
||||
/// <summary>
|
||||
/// As opposed to WriteLine methods, it prepends a Carriage Return character to the text
|
||||
/// so that the console moves the cursor one position up after the text has been written out.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="writerFlags">The writer flags.</param>
|
||||
public static void OverwriteLine(string text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
||||
{
|
||||
Write($"\r{text ?? string.Empty}", color, writerFlags);
|
||||
Flush();
|
||||
CursorLeft = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
Vendor/Swan.Lite-3.1.0/Terminal.Settings.cs
vendored
Normal file
49
Vendor/Swan.Lite-3.1.0/Terminal.Settings.cs
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// A console terminal helper to create nicer output and receive input from the user
|
||||
/// This class is thread-safe :).
|
||||
/// </summary>
|
||||
public static partial class Terminal
|
||||
{
|
||||
/// <summary>
|
||||
/// Terminal global settings.
|
||||
/// </summary>
|
||||
public static class Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the default output color.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default color.
|
||||
/// </value>
|
||||
public static ConsoleColor DefaultColor { get; set; } = Console.ForegroundColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color of the border.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The color of the border.
|
||||
/// </value>
|
||||
public static ConsoleColor BorderColor { get; } = ConsoleColor.DarkGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user input prefix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The user input prefix.
|
||||
/// </value>
|
||||
public static string UserInputPrefix { get; set; } = "USR";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user option text.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The user option text.
|
||||
/// </value>
|
||||
public static string UserOptionText { get; set; } = " Option: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
339
Vendor/Swan.Lite-3.1.0/Terminal.cs
vendored
Normal file
339
Vendor/Swan.Lite-3.1.0/Terminal.cs
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Swan.Threading;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// A console terminal helper to create nicer output and receive input from the user.
|
||||
/// This class is thread-safe :).
|
||||
/// </summary>
|
||||
public static partial class Terminal
|
||||
{
|
||||
#region Private Declarations
|
||||
|
||||
private const int OutputFlushInterval = 15;
|
||||
private static readonly ExclusiveTimer DequeueOutputTimer;
|
||||
private static readonly object SyncLock = new object();
|
||||
private static readonly ConcurrentQueue<OutputContext> OutputQueue = new ConcurrentQueue<OutputContext>();
|
||||
|
||||
private static readonly ManualResetEventSlim OutputDone = new ManualResetEventSlim(false);
|
||||
private static readonly ManualResetEventSlim InputDone = new ManualResetEventSlim(true);
|
||||
|
||||
private static bool? _isConsolePresent;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes static members of the <see cref="Terminal"/> class.
|
||||
/// </summary>
|
||||
static Terminal()
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
if (DequeueOutputTimer != null) return;
|
||||
|
||||
if (IsConsolePresent)
|
||||
{
|
||||
Console.CursorVisible = false;
|
||||
}
|
||||
|
||||
// Here we start the output task, fire-and-forget
|
||||
DequeueOutputTimer = new ExclusiveTimer(DequeueOutputCycle);
|
||||
DequeueOutputTimer.Resume(OutputFlushInterval);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Synchronized Cursor Movement
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cursor left position.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The cursor left.
|
||||
/// </value>
|
||||
public static int CursorLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsConsolePresent == false) return -1;
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
return Console.CursorLeft;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsConsolePresent == false) return;
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
Console.CursorLeft = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cursor top position.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The cursor top.
|
||||
/// </value>
|
||||
public static int CursorTop
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsConsolePresent == false) return -1;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
return Console.CursorTop;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsConsolePresent == false) return;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
Console.CursorTop = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the Console is present.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is console present; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool IsConsolePresent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isConsolePresent == null)
|
||||
{
|
||||
_isConsolePresent = true;
|
||||
try
|
||||
{
|
||||
var windowHeight = Console.WindowHeight;
|
||||
_isConsolePresent = windowHeight >= 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_isConsolePresent = false;
|
||||
}
|
||||
}
|
||||
|
||||
return _isConsolePresent.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available output writers in a bitwise mask.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The available writers.
|
||||
/// </value>
|
||||
public static TerminalWriters AvailableWriters =>
|
||||
IsConsolePresent
|
||||
? TerminalWriters.StandardError | TerminalWriters.StandardOutput
|
||||
: TerminalWriters.None;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output encoding for the current console.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The output encoding.
|
||||
/// </value>
|
||||
public static Encoding OutputEncoding
|
||||
{
|
||||
get => Console.OutputEncoding;
|
||||
set => Console.OutputEncoding = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Waits for all of the queued output messages to be written out to the console.
|
||||
/// Call this method if it is important to display console text before
|
||||
/// quitting the application such as showing usage or help.
|
||||
/// Set the timeout to null or TimeSpan.Zero to wait indefinitely.
|
||||
/// </summary>
|
||||
/// <param name="timeout">The timeout. Set the amount of time to black before this method exits.</param>
|
||||
public static void Flush(TimeSpan? timeout = null)
|
||||
{
|
||||
if (timeout == null) timeout = TimeSpan.Zero;
|
||||
var startTime = DateTime.UtcNow;
|
||||
|
||||
while (OutputQueue.Count > 0)
|
||||
{
|
||||
// Manually trigger a timer cycle to run immediately
|
||||
DequeueOutputTimer.Change(0, OutputFlushInterval);
|
||||
|
||||
// Wait for the output to finish
|
||||
if (OutputDone.Wait(OutputFlushInterval))
|
||||
break;
|
||||
|
||||
// infinite timeout
|
||||
if (timeout.Value == TimeSpan.Zero)
|
||||
continue;
|
||||
|
||||
// break if we have reached a timeout condition
|
||||
if (DateTime.UtcNow.Subtract(startTime) >= timeout.Value)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cursor position.
|
||||
/// </summary>
|
||||
/// <param name="left">The left.</param>
|
||||
/// <param name="top">The top.</param>
|
||||
public static void SetCursorPosition(int left, int top)
|
||||
{
|
||||
if (!IsConsolePresent) return;
|
||||
|
||||
lock (SyncLock)
|
||||
{
|
||||
Flush();
|
||||
Console.SetCursorPosition(left.Clamp(0, left), top.Clamp(0, top));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the output cursor one line up starting at left position 0
|
||||
/// Please note that backlining the cursor does not clear the contents of the
|
||||
/// previous line so you might need to clear it by writing an empty string the
|
||||
/// length of the console width.
|
||||
/// </summary>
|
||||
public static void BacklineCursor() => SetCursorPosition(0, CursorTop - 1);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a standard banner to the standard output
|
||||
/// containing the company name, product name, assembly version and trademark.
|
||||
/// </summary>
|
||||
/// <param name="color">The color.</param>
|
||||
public static void WriteWelcomeBanner(ConsoleColor color = ConsoleColor.Gray)
|
||||
{
|
||||
WriteLine($"{SwanRuntime.CompanyName} {SwanRuntime.ProductName} [Version {SwanRuntime.EntryAssemblyVersion}]", color);
|
||||
WriteLine($"{SwanRuntime.ProductTrademark}", color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the output to be written to the console
|
||||
/// This is the only method that should enqueue to the output
|
||||
/// Please note that if AvailableWriters is None, then no output will be enqueued.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
private static void EnqueueOutput(OutputContext context)
|
||||
{
|
||||
lock (SyncLock)
|
||||
{
|
||||
var availableWriters = AvailableWriters;
|
||||
|
||||
if (availableWriters == TerminalWriters.None || context.OutputWriters == TerminalWriters.None)
|
||||
{
|
||||
OutputDone.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((context.OutputWriters & availableWriters) == TerminalWriters.None)
|
||||
return;
|
||||
|
||||
OutputDone.Reset();
|
||||
OutputQueue.Enqueue(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a Terminal I/O cycle in the <see cref="ThreadPool"/> thread.
|
||||
/// </summary>
|
||||
private static void DequeueOutputCycle()
|
||||
{
|
||||
if (AvailableWriters == TerminalWriters.None)
|
||||
{
|
||||
OutputDone.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
InputDone.Wait();
|
||||
|
||||
if (OutputQueue.Count <= 0)
|
||||
{
|
||||
OutputDone.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
OutputDone.Reset();
|
||||
|
||||
while (OutputQueue.Count > 0)
|
||||
{
|
||||
if (!OutputQueue.TryDequeue(out var context)) continue;
|
||||
|
||||
// Process Console output and Skip over stuff we can't display so we don't stress the output too much.
|
||||
if (!IsConsolePresent) continue;
|
||||
|
||||
Console.ForegroundColor = context.OutputColor;
|
||||
|
||||
// Output to the standard output
|
||||
if (context.OutputWriters.HasFlag(TerminalWriters.StandardOutput))
|
||||
{
|
||||
Console.Out.Write(context.OutputText);
|
||||
}
|
||||
|
||||
// output to the standard error
|
||||
if (context.OutputWriters.HasFlag(TerminalWriters.StandardError))
|
||||
{
|
||||
Console.Error.Write(context.OutputText);
|
||||
}
|
||||
|
||||
Console.ResetColor();
|
||||
Console.ForegroundColor = context.OriginalColor;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Output Context
|
||||
|
||||
/// <summary>
|
||||
/// Represents an asynchronous output context.
|
||||
/// </summary>
|
||||
private sealed class OutputContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OutputContext"/> class.
|
||||
/// </summary>
|
||||
public OutputContext()
|
||||
{
|
||||
OriginalColor = Settings.DefaultColor;
|
||||
OutputWriters = IsConsolePresent
|
||||
? TerminalWriters.StandardOutput
|
||||
: TerminalWriters.None;
|
||||
}
|
||||
|
||||
public ConsoleColor OriginalColor { get; }
|
||||
public ConsoleColor OutputColor { get; set; }
|
||||
public char[] OutputText { get; set; }
|
||||
public TerminalWriters OutputWriters { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
31
Vendor/Swan.Lite-3.1.0/TerminalWriters.Enums.cs
vendored
Normal file
31
Vendor/Swan.Lite-3.1.0/TerminalWriters.Enums.cs
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Swan
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a set of bitwise standard terminal writers.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum TerminalWriters
|
||||
{
|
||||
/// <summary>
|
||||
/// Prevents output
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the Console.Out
|
||||
/// </summary>
|
||||
StandardOutput = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the Console.Error
|
||||
/// </summary>
|
||||
StandardError = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Writes to all possible terminal writers
|
||||
/// </summary>
|
||||
All = StandardOutput | StandardError,
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicBoolean.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicBoolean.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
|
||||
/// </summary>
|
||||
public sealed class AtomicBoolean : AtomicTypeBase<bool>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicBoolean(bool initialValue = default)
|
||||
: base(initialValue ? 1 : 0)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool FromLong(long backingValue) => backingValue != 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long ToLong(bool value) => value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicDateTime.cs
vendored
Normal file
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicDateTime.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an atomic DateTime.
|
||||
/// </summary>
|
||||
public sealed class AtomicDateTime : AtomicTypeBase<DateTime>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicDateTime"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
public AtomicDateTime(DateTime initialValue)
|
||||
: base(initialValue.Ticks)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DateTime FromLong(long backingValue) => new DateTime(backingValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long ToLong(DateTime value) => value.Ticks;
|
||||
}
|
||||
}
|
||||
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicDouble.cs
vendored
Normal file
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicDouble.cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Fast, atomic double combining interlocked to write value and volatile to read values.
|
||||
/// </summary>
|
||||
public sealed class AtomicDouble : AtomicTypeBase<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicDouble"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicDouble(double initialValue = default)
|
||||
: base(BitConverter.DoubleToInt64Bits(initialValue))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override double FromLong(long backingValue) =>
|
||||
BitConverter.Int64BitsToDouble(backingValue);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long ToLong(double value) =>
|
||||
BitConverter.DoubleToInt64Bits(value);
|
||||
}
|
||||
}
|
||||
43
Vendor/Swan.Lite-3.1.0/Threading/AtomicEnum.cs
vendored
Normal file
43
Vendor/Swan.Lite-3.1.0/Threading/AtomicEnum.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an atomic generic Enum.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enum.</typeparam>
|
||||
public sealed class AtomicEnum<T>
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
private long _backingValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicEnum{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
/// <exception cref="ArgumentException">T must be an enumerated type.</exception>
|
||||
public AtomicEnum(T initialValue)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(T), initialValue))
|
||||
throw new ArgumentException("T must be an enumerated type");
|
||||
|
||||
Value = initialValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get => (T)Enum.ToObject(typeof(T), BackingValue);
|
||||
set => BackingValue = Convert.ToInt64(value);
|
||||
}
|
||||
|
||||
private long BackingValue
|
||||
{
|
||||
get => Interlocked.Read(ref _backingValue);
|
||||
set => Interlocked.Exchange(ref _backingValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicInteger.cs
vendored
Normal file
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicInteger.cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an atomically readable or writable integer.
|
||||
/// </summary>
|
||||
public class AtomicInteger : AtomicTypeBase<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicInteger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicInteger(int initialValue = default)
|
||||
: base(Convert.ToInt64(initialValue))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int FromLong(long backingValue) =>
|
||||
Convert.ToInt32(backingValue);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long ToLong(int value) =>
|
||||
Convert.ToInt64(value);
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicLong.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicLong.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Fast, atomic long combining interlocked to write value and volatile to read values.
|
||||
/// </summary>
|
||||
public sealed class AtomicLong : AtomicTypeBase<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicLong"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicLong(long initialValue = default)
|
||||
: base(initialValue)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long FromLong(long backingValue) => backingValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long ToLong(long value) => value;
|
||||
}
|
||||
}
|
||||
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicTimeSpan.cs
vendored
Normal file
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicTimeSpan.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an atomic TimeSpan type.
|
||||
/// </summary>
|
||||
public sealed class AtomicTimeSpan : AtomicTypeBase<TimeSpan>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicTimeSpan" /> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
public AtomicTimeSpan(TimeSpan initialValue)
|
||||
: base(initialValue.Ticks)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override TimeSpan FromLong(long backingValue) => TimeSpan.FromTicks(backingValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long ToLong(TimeSpan value) => value.Ticks < 0 ? 0 : value.Ticks;
|
||||
}
|
||||
}
|
||||
243
Vendor/Swan.Lite-3.1.0/Threading/AtomicTypeBase.cs
vendored
Normal file
243
Vendor/Swan.Lite-3.1.0/Threading/AtomicTypeBase.cs
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a generic implementation of an Atomic (interlocked) type
|
||||
///
|
||||
/// Idea taken from Memory model and .NET operations in article:
|
||||
/// http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam>
|
||||
public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>>
|
||||
where T : struct, IComparable, IComparable<T>, IEquatable<T>
|
||||
{
|
||||
private long _backingValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
protected AtomicTypeBase(long initialValue)
|
||||
{
|
||||
BackingValue = initialValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get => FromLong(BackingValue);
|
||||
set => BackingValue = ToLong(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the backing value.
|
||||
/// </summary>
|
||||
protected long BackingValue
|
||||
{
|
||||
get => Interlocked.Read(ref _backingValue);
|
||||
set => Interlocked.Exchange(ref _backingValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator ==.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator !=.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator >.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator <.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator >=.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator <=.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator ++.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance)
|
||||
{
|
||||
Interlocked.Increment(ref instance._backingValue);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator --.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance)
|
||||
{
|
||||
Interlocked.Decrement(ref instance._backingValue);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator -<.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <param name="operand">The operand.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, long operand)
|
||||
{
|
||||
instance.BackingValue = instance.BackingValue + operand;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator -.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <param name="operand">The operand.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, long operand)
|
||||
{
|
||||
instance.BackingValue = instance.BackingValue - operand;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the value to the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||
/// <exception cref="ArgumentException">When types are incompatible.</exception>
|
||||
public int CompareTo(object other)
|
||||
{
|
||||
switch (other)
|
||||
{
|
||||
case null:
|
||||
return 1;
|
||||
case AtomicTypeBase<T> atomic:
|
||||
return BackingValue.CompareTo(atomic.BackingValue);
|
||||
case T variable:
|
||||
return Value.CompareTo(variable);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Incompatible comparison types");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the value to the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||
public int CompareTo(T other) => Value.CompareTo(other);
|
||||
|
||||
/// <summary>
|
||||
/// Compares the value to the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||
public int CompareTo(AtomicTypeBase<T> other) => BackingValue.CompareTo(other?.BackingValue ?? default);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
switch (other)
|
||||
{
|
||||
case AtomicTypeBase<T> atomic:
|
||||
return Equals(atomic);
|
||||
case T variable:
|
||||
return Equals(variable);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode() => BackingValue.GetHashCode();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(AtomicTypeBase<T> other) =>
|
||||
BackingValue == (other?.BackingValue ?? default);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(T other) => Equals(Value, other);
|
||||
|
||||
/// <summary>
|
||||
/// Converts from a long value to the target type.
|
||||
/// </summary>
|
||||
/// <param name="backingValue">The backing value.</param>
|
||||
/// <returns>The value converted form a long value.</returns>
|
||||
protected abstract T FromLong(long backingValue);
|
||||
|
||||
/// <summary>
|
||||
/// Converts from the target type to a long value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The value converted to a long value.</returns>
|
||||
protected abstract long ToLong(T value);
|
||||
}
|
||||
}
|
||||
68
Vendor/Swan.Lite-3.1.0/Threading/CancellationTokenOwner.cs
vendored
Normal file
68
Vendor/Swan.Lite-3.1.0/Threading/CancellationTokenOwner.cs
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Acts as a <see cref="CancellationTokenSource"/> but with reusable tokens.
|
||||
/// </summary>
|
||||
public sealed class CancellationTokenOwner : IDisposable
|
||||
{
|
||||
private readonly object _syncLock = new object();
|
||||
private bool _isDisposed;
|
||||
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the token of the current.
|
||||
/// </summary>
|
||||
public CancellationToken Token
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
return _isDisposed
|
||||
? CancellationToken.None
|
||||
: _tokenSource.Token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the last referenced token and creates a new token source.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
232
Vendor/Swan.Lite-3.1.0/Threading/ExclusiveTimer.cs
vendored
Normal file
232
Vendor/Swan.Lite-3.1.0/Threading/ExclusiveTimer.cs
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time
|
||||
/// in a <see cref="ThreadPool"/> thread. Callback execution is NOT guaranteed to be carried out
|
||||
/// on the same <see cref="ThreadPool"/> thread every time the timer fires.
|
||||
/// </summary>
|
||||
public sealed class ExclusiveTimer : IDisposable
|
||||
{
|
||||
private readonly object _syncLock = new object();
|
||||
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true);
|
||||
private readonly Timer _backingTimer;
|
||||
private readonly TimerCallback _userCallback;
|
||||
private readonly AtomicBoolean _isDisposing = new AtomicBoolean();
|
||||
private readonly AtomicBoolean _isDisposed = new AtomicBoolean();
|
||||
private int _period;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(TimerCallback timerCallback, object? state, int dueTime, int period)
|
||||
{
|
||||
_period = period;
|
||||
_userCallback = timerCallback;
|
||||
_backingTimer = new Timer(InternalCallback, state ?? this, dueTime, Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(TimerCallback timerCallback, object? state, TimeSpan dueTime, TimeSpan period)
|
||||
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
public ExclusiveTimer(TimerCallback timerCallback)
|
||||
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(Action timerCallback, int dueTime, int period)
|
||||
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
|
||||
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
public ExclusiveTimer(Action timerCallback)
|
||||
: this(timerCallback, Timeout.Infinite, Timeout.Infinite)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposing.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsDisposing => _isDisposing.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsDisposed => _isDisposed.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the time is elapsed.
|
||||
/// </summary>
|
||||
/// <param name="untilDate">The until date.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public static void WaitUntil(DateTime untilDate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
static void Callback(IWaitEvent waitEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
waitEvent.Complete();
|
||||
waitEvent.Begin();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
using var delayLock = WaitEventFactory.Create(true);
|
||||
using var timer = new ExclusiveTimer(() => Callback(delayLock), 0, 15);
|
||||
while (!cancellationToken.IsCancellationRequested && DateTime.UtcNow < untilDate)
|
||||
delayLock.Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits the specified wait time.
|
||||
/// </summary>
|
||||
/// <param name="waitTime">The wait time.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public static void Wait(TimeSpan waitTime, CancellationToken cancellationToken = default) =>
|
||||
WaitUntil(DateTime.UtcNow.Add(waitTime), cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the start time and the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Change(int dueTime, int period)
|
||||
{
|
||||
_period = period;
|
||||
|
||||
_backingTimer.Change(dueTime, Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the start time and the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Change(TimeSpan dueTime, TimeSpan period)
|
||||
=> Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
|
||||
|
||||
/// <summary>
|
||||
/// Changes the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Resume(int period) => Change(0, period);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Resume(TimeSpan period) => Change(TimeSpan.Zero, period);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses this instance.
|
||||
/// </summary>
|
||||
public void Pause() => Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_isDisposed == true || _isDisposing == true)
|
||||
return;
|
||||
|
||||
_isDisposing.Value = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_cycleDoneEvent.Wait();
|
||||
_cycleDoneEvent.Dispose();
|
||||
Pause();
|
||||
_backingTimer.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDisposed.Value = true;
|
||||
_isDisposing.Value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logic that runs every time the timer hits the due time.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
private void InternalCallback(object state)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (IsDisposed || IsDisposing)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cycleDoneEvent.IsSet == false)
|
||||
return;
|
||||
|
||||
_cycleDoneEvent.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
_userCallback(state);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cycleDoneEvent?.Set();
|
||||
_backingTimer?.Change(_period, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Threading/ISyncLocker.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Threading/ISyncLocker.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a generic interface for synchronized locking mechanisms.
|
||||
/// </summary>
|
||||
public interface ISyncLocker : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Acquires a writer lock.
|
||||
/// The lock is released when the returned locking object is disposed.
|
||||
/// </summary>
|
||||
/// <returns>A disposable locking object.</returns>
|
||||
IDisposable AcquireWriterLock();
|
||||
|
||||
/// <summary>
|
||||
/// Acquires a reader lock.
|
||||
/// The lock is released when the returned locking object is disposed.
|
||||
/// </summary>
|
||||
/// <returns>A disposable locking object.</returns>
|
||||
IDisposable AcquireReaderLock();
|
||||
}
|
||||
}
|
||||
57
Vendor/Swan.Lite-3.1.0/Threading/IWaitEvent.cs
vendored
Normal file
57
Vendor/Swan.Lite-3.1.0/Threading/IWaitEvent.cs
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
|
||||
/// </summary>
|
||||
/// <seealso cref="IDisposable" />
|
||||
public interface IWaitEvent : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event is in the completed state.
|
||||
/// </summary>
|
||||
bool IsCompleted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the Begin method has been called.
|
||||
/// It returns false after the Complete method is called.
|
||||
/// </summary>
|
||||
bool IsInProgress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the underlying handle is not closed and it is still valid.
|
||||
/// </summary>
|
||||
bool IsValid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
bool IsDisposed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Enters the state in which waiters need to wait.
|
||||
/// All future waiters will block when they call the Wait method.
|
||||
/// </summary>
|
||||
void Begin();
|
||||
|
||||
/// <summary>
|
||||
/// Leaves the state in which waiters need to wait.
|
||||
/// All current waiters will continue.
|
||||
/// </summary>
|
||||
void Complete();
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the event to be completed.
|
||||
/// </summary>
|
||||
void Wait();
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the event to be completed.
|
||||
/// Returns <c>true</c> when there was no timeout. False if the timeout was reached.
|
||||
/// </summary>
|
||||
/// <param name="timeout">The maximum amount of time to wait for.</param>
|
||||
/// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns>
|
||||
bool Wait(TimeSpan timeout);
|
||||
}
|
||||
}
|
||||
69
Vendor/Swan.Lite-3.1.0/Threading/IWorker.cs
vendored
Normal file
69
Vendor/Swan.Lite-3.1.0/Threading/IWorker.cs
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a standard API to control background application workers.
|
||||
/// </summary>
|
||||
public interface IWorker
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current state of the worker.
|
||||
/// </summary>
|
||||
WorkerState WorkerState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisposed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is currently being disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisposing { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time interval used to execute cycles.
|
||||
/// </summary>
|
||||
TimeSpan Period { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name identifier of this worker.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts execution of worker cycles.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> StartAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Pauses execution of worker cycles.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> PauseAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Resumes execution of worker cycles.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> ResumeAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Permanently stops execution of worker cycles.
|
||||
/// An interrupt is always sent to the worker. If you wish to stop
|
||||
/// the worker without interrupting then call the <see cref="PauseAsync"/>
|
||||
/// method, await it, and finally call the <see cref="StopAsync"/> method.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> StopAsync();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user