diff --git a/CapyKit/.editorconfig b/CapyKit/.editorconfig
new file mode 100644
index 0000000..0e1204c
--- /dev/null
+++ b/CapyKit/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# CS8625: Cannot convert null literal to non-nullable reference type.
+dotnet_diagnostic.CS8625.severity = none
diff --git a/CapyKit/Attributes/EnumerationAttribute.cs b/CapyKit/Attributes/EnumerationAttribute.cs
new file mode 100644
index 0000000..47b6eaa
--- /dev/null
+++ b/CapyKit/Attributes/EnumerationAttribute.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapyKit.Attributes
+{
+ ///
+ /// Custom attribute class for decorating enumeration fields with additional data.
+ ///
+ ///
+ /// Generic type parameter allowing for arbitrary declarations and assignments of meaning.
+ ///
+ [AttributeUsage(AttributeTargets.Field)]
+ public abstract class EnumerationAttribute : Attribute
+ {
+ #region Properties
+
+ ///
+ /// Initializes a new instance of the class with a
+ /// specified value.
+ ///
+ /// The value.
+ public T Value { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ /// Gets the value of the enumeration represented by this attribute.
+ ///
+ /// Initializes a new instance of the class with a
+ /// specified value.
+ ///
+ protected EnumerationAttribute(T value)
+ {
+ this.Value = value;
+ }
+
+ #endregion
+ }
+}
diff --git a/CapyKit/Attributes/EnumerationDescriptionAttribute.cs b/CapyKit/Attributes/EnumerationDescriptionAttribute.cs
new file mode 100644
index 0000000..4b626fc
--- /dev/null
+++ b/CapyKit/Attributes/EnumerationDescriptionAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapyKit.Attributes
+{
+ /// An attribute class for decorating enumeration fields with a description.
+ ///
+ [AttributeUsage(AttributeTargets.Field)]
+ public class EnumerationDescriptionAttribute : EnumerationAttribute
+ {
+ ///
+ /// Initializes a new instance of the class with
+ /// the specified description.
+ ///
+ /// The description of the enumeration value.
+ public EnumerationDescriptionAttribute(string description)
+ : base(description)
+ {
+ //
+ }
+ }
+}
diff --git a/CapyKit/CapyEvent.cs b/CapyKit/CapyEvent.cs
new file mode 100644
index 0000000..8745bfe
--- /dev/null
+++ b/CapyKit/CapyEvent.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using CapyKit.Extensions;
+
+namespace CapyKit
+{
+ ///
+ /// The CapyEventReporter class is responsible for managing event subscriptions and emissions within CapyKit.
+ ///
+ ///
+ /// Because consumers of CapyKit may have varied ways of handling logging, the provides
+ /// a way for subscribers to recieve events for various "events" within the library. These can be thought of as
+ /// a logging solution for CapyKit.
+ ///
+ public static class CapyEventReporter
+ {
+ #region Members
+
+ ///
+ /// A dictionary storing event handlers and their corresponding origins for each subscription level.
+ ///
+ private static Dictionary> subscribers = new Dictionary>();
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Subscribes the specified event handler to the event with the given subscription level and
+ /// origin.
+ ///
+ ///
+ /// If there is no existing list for the given subscription level, a new list is created and
+ /// added to the dictionary.
+ ///
+ /// The event handler to subscribe.
+ /// The severity level of the event to subscribe to.
+ ///
+ /// (Optional) The name of the method or class where the subscription is made.
+ ///
+ public static void Subscribe(CapyEventHandler callback, EventLevel subscriptionLevel, [CallerMemberName] string origin = null)
+ {
+ if (!subscribers[subscriptionLevel].Any())
+ {
+ subscribers.Add(subscriptionLevel, new List<(CapyEventHandler Handler, string origin)>());
+ }
+
+ subscribers[subscriptionLevel].Add((callback, origin ?? "[Unknown]"));
+ }
+
+ ///
+ /// Unsubscribes the specified event handler from the event with the given origin.
+ ///
+ /// The event handler to unsubscribe.
+ ///
+ /// The name of the method or class where the subscription was made.
+ ///
+ public static void Unsubscribe(CapyEventHandler callback, string origin)
+ {
+ foreach (var value in Enum.GetValues(typeof(EventLevel)))
+ {
+ if (value is EventLevel)
+ {
+ subscribers[(EventLevel)value].RemoveAll(c => c.Handler == callback && c.origin == origin);
+ }
+ }
+ }
+
+ /// Emits an event with the given severity level, message, and method name.
+ ///
+ /// In order to allow for efficient calling member access via
+ /// , it is suggested that is defined explicitly for formatted messages.
+ ///
+ /// The severity level of the event.
+ /// The message describing the reason for the event.
+ ///
+ /// (Optional) The name of the method where the event was raised. String formatting for
+ /// is accepted.
+ ///
+ ///
+ /// A variable-length parameters list containing arguments for formatting the message.
+ ///
+ ///
+ /// CapyEventReporter.EmitEvent(EventLevel.Error, "Could not find the description for {0}.",
+ /// args: new[] { enumeration });
+ ///
+ internal static void EmitEvent(EventLevel eventLevel, string message, [CallerMemberName] string method = null, params object[] args)
+ {
+ if (!subscribers.ContainsKey(eventLevel))
+ {
+ return;
+ }
+
+ var formattedMessage = string.Format(message, args);
+
+ var capyEventArgs = new CapyEventArgs(eventLevel, formattedMessage, method);
+
+ foreach (var subscriber in subscribers[eventLevel])
+ {
+ subscriber.Handler(capyEventArgs);
+ }
+ }
+
+ #endregion
+ }
+
+ ///
+ /// A delegate representing an event handler that accepts a instance.
+ ///
+ /// The CapyEventArgs instance containing event data.
+ public delegate void CapyEventHandler(CapyEventArgs e);
+
+ ///
+ /// The CapyEventArgs class represents an event argument instance with event level, message, and
+ /// method name information.
+ ///
+ public class CapyEventArgs : EventArgs
+ {
+ #region Properties
+
+ ///
+ /// Gets the severity level of the event.
+ ///
+ public EventLevel Level { get; private set; }
+
+ ///
+ /// Gets the message describing the reason for the event.
+ ///
+ public string Message { get; private set; }
+
+ ///
+ /// Gets the name of the method where the event was raised.
+ ///
+ public string MethodName { get; private set; }
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// Initializes a new instance of the CapyEventArgs class with the specified event level, message, and method name.
+ ///
+ /// The severity level of the event.
+ /// A descriptive message explaining the reason for the event.
+ /// The name of the method where the event was raised.
+ public CapyEventArgs(EventLevel level, string message, string method = null)
+ {
+ this.Level = level;
+ this.Message = message;
+ this.MethodName = method ?? "[Unknown]";
+ }
+
+ #endregion
+ }
+
+
+ /// Enumeration representing different event level severity values.
+ public enum EventLevel
+ {
+ /// Represents a critical error that requires immediate attention.
+ Critical = 0,
+ /// Represents an error that prevents the normal execution of the application.
+ Error = 1,
+ /// Represents informational messages that provide useful context to the consumer.
+ Information = 2,
+ /// Represents detailed messages that are typically used for debugging purposes.
+ Debug = 3
+ }
+}
diff --git a/CapyKit/CapyKit.csproj b/CapyKit/CapyKit.csproj
new file mode 100644
index 0000000..fa71b7a
--- /dev/null
+++ b/CapyKit/CapyKit.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/CapyKit/CapyKit.sln b/CapyKit/CapyKit.sln
new file mode 100644
index 0000000..486de70
--- /dev/null
+++ b/CapyKit/CapyKit.sln
@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34408.163
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapyKit", "CapyKit.csproj", "{D1ACE10F-CBC8-4BA8-BB85-11DB4EEE5912}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE8D96CA-FC33-4F28-AF49-0E4AEC6D3FD9}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D1ACE10F-CBC8-4BA8-BB85-11DB4EEE5912}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1ACE10F-CBC8-4BA8-BB85-11DB4EEE5912}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1ACE10F-CBC8-4BA8-BB85-11DB4EEE5912}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1ACE10F-CBC8-4BA8-BB85-11DB4EEE5912}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AF075174-308F-4D69-9160-C3CCE89EAD68}
+ EndGlobalSection
+EndGlobal
diff --git a/CapyKit/Extensions/EnumerationExtensions.cs b/CapyKit/Extensions/EnumerationExtensions.cs
new file mode 100644
index 0000000..f7afccf
--- /dev/null
+++ b/CapyKit/Extensions/EnumerationExtensions.cs
@@ -0,0 +1,93 @@
+using CapyKit.Attributes;
+using CapyKit.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapyKit.Extensions
+{
+ public static class EnumerationExtensions
+ {
+ #region Methods
+
+ ///
+ /// A extension method that parses a string into an enumeration.
+ ///
+ /// Generic type parameter.
+ /// The enumeration to act on.
+ /// The value.
+ /// A T.
+ public static T Parse(this T enumeration, string value) where T : Enum
+ {
+ return (T)Enum.Parse(typeof(T), value);
+ }
+
+ ///
+ /// A extension method that parses a string into an enumeration.
+ ///
+ /// Generic type parameter.
+ /// The enumeration to act on.
+ /// The string value of the .
+ /// True to ignore case.
+ /// A T.
+ public static T Parse(this T enumeration, string value, bool ignoreCase) where T : Enum
+ {
+ return (T)Enum.Parse(typeof(T), value, ignoreCase);
+ }
+
+ ///
+ /// An extension method that gets an integer value representing the enumation.
+ ///
+ /// The enumeration to act on.
+ /// The integer value of the enumeration.
+ public static int GetValue(this Enum enumeration)
+ {
+ return (int)Convert.ChangeType(enumeration, TypeCode.Int32);
+ }
+
+ /// An extension method that gets a name.
+ /// The enumeration to act on.
+ /// The name of the enumeration.
+ public static string GetName(this Enum enumeration)
+ {
+ return Enum.GetName(enumeration.GetType(), enumeration) ?? "[Unknown]";
+ }
+
+ /// An extension method that gets a human readable name.
+ /// The enumeration to act on.
+ /// The human readable name of the enumeration.
+ public static string GetPrettyName(this Enum enumeration)
+ {
+ return LanguageHelper.CamelCaseToHumanReadable(GetName(enumeration));
+ }
+
+ /// An extension method that gets a description.
+ /// The enumeration to act on.
+ ///
+ /// The description if available, otherwise the string representation of the enumeration.
+ ///
+ public static string GetDescription(this Enum enumeration)
+ {
+ var memInfo = enumeration.GetType().GetMember(enumeration.GetName());
+ if (memInfo.Any())
+ {
+ var attribute = memInfo.First().GetCustomAttribute(typeof(EnumerationDescriptionAttribute)) as EnumerationDescriptionAttribute;
+ if (attribute == null)
+ {
+ CapyEventReporter.EmitEvent(EventLevel.Error, "Could not find the description for {0}.", args: new[] { enumeration });
+ }
+ else
+ {
+ return attribute.Value;
+ }
+ }
+
+ return enumeration.ToString();
+ }
+
+ #endregion Methods
+ }
+}
diff --git a/CapyKit/Helpers/LanguageHelper.cs b/CapyKit/Helpers/LanguageHelper.cs
new file mode 100644
index 0000000..ec68d91
--- /dev/null
+++ b/CapyKit/Helpers/LanguageHelper.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace CapyKit.Helpers
+{
+ public class LanguageHelper
+ {
+ #region Methods
+
+ /// Converts camel case text to human readable text.
+ /// The value.
+ /// A string in human readable format.
+ public static string CamelCaseToHumanReadable(string value)
+ {
+ var regex = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
+
+ return regex.Replace(value, " ");
+ }
+
+ #endregion
+ }
+}
diff --git a/CapyKit/Pool.cs b/CapyKit/Pool.cs
new file mode 100644
index 0000000..f7eeceb
--- /dev/null
+++ b/CapyKit/Pool.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace CapyKit
+{
+ ///
+ /// A managed pool of resources. This class provides a thread-safe way to manage a collection of
+ /// objects of type .
+ ///
+ /// The type of objects to be managed by the pool.
+ public class Pool
+ {
+ #region Members
+
+ /// The collection of pooled items.
+ private readonly ConcurrentBag> poolItemCollection;
+
+ /// (Immutable) The number of items in the pool.
+ private readonly int poolSize;
+
+ #endregion Members
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class with the specified pool size.
+ ///
+ /// The size of the pool.
+ public Pool(int poolSize)
+ {
+ this.poolSize = poolSize;
+ this.poolItemCollection = new ConcurrentBag>();
+ FillPoolItemCollection(poolSize);
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified pool size
+ /// and constructor selector.
+ ///
+ /// The size of the pool.
+ ///
+ /// The constructor selector used to create new instances of .
+ ///
+ public Pool(int poolSize, Func constructorSelector)
+ {
+ this.poolSize = poolSize;
+ this.poolItemCollection = new ConcurrentBag>();
+ FillPoolItemCollection(poolSize, constructorSelector);
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified collection
+ /// of items.
+ ///
+ ///
+ /// The collection of items with which to seed the pool.
+ ///
+ public Pool(IEnumerable collection)
+ {
+ this.poolSize = collection.Count();
+ this.poolItemCollection = new ConcurrentBag>();
+ FillPoolItemCollection(collection);
+ }
+
+ #endregion Constructors
+
+ #region Methods
+
+ ///
+ /// Initializes the pool with the specified number of items using the default constructor.
+ ///
+ /// The size of the pool.
+ private void FillPoolItemCollection(int poolSize)
+ {
+ FillPoolItemCollection(poolSize, () => default(T));
+ }
+
+ ///
+ /// Initializes the pool with the specified number of items using the specified constructor
+ /// selector.
+ ///
+ /// The size of the pool.
+ /// The constructor selector.
+ private void FillPoolItemCollection(int poolSize, Func constructorSelector)
+ {
+ for (int i = 0; i < poolSize; i++)
+ {
+ this.poolItemCollection.Add(new PoolItem(constructorSelector(), i));
+ }
+ }
+
+ /// Fill the pool item collection from an existing collection.
+ /// The collection.
+ private void FillPoolItemCollection(IEnumerable collection)
+ {
+ int index = 0;
+ foreach (var item in collection)
+ {
+ this.poolItemCollection.Add(new PoolItem(item, index++));
+ }
+ }
+
+ /// Gets the first available item from the pool and sets its lock.
+ /// The first available item from the pool.
+ public PoolItem GetAvailableItem()
+ {
+ lock (this.poolItemCollection)
+ {
+ if (this.poolItemCollection.Any(item => !item.Locked))
+ {
+ var firstAvailableItem = this.poolItemCollection.First(item => !item.Locked);
+ firstAvailableItem.SetLock();
+
+ CapyEventReporter.EmitEvent(EventLevel.Debug, "Accessed ppol and retrieved {0}", args: new[] { firstAvailableItem });
+
+ return firstAvailableItem;
+ }
+ }
+
+ CapyEventReporter.EmitEvent(EventLevel.Error, "Could not return an available item.");
+ return null;
+ }
+
+ /// Releases the lock on the specified item and returns it to the pool.
+ ///
+ /// This method sets the flag to so that
+ /// it can be retrieved by .
+ ///
+ /// The item to release.
+ public void ReleaseItem(PoolItem item)
+ {
+ item.ReleaseLock();
+ }
+
+ #endregion Methods
+ }
+
+ /// A pool item. This class cannot be inherited.
+ /// The type of the pooled item.
+ public sealed class PoolItem
+ {
+ #region Members
+
+ /// The pooled item.
+ private readonly T item;
+
+ /// A flag indicating whether the item is locked or not.
+ private bool locked;
+
+ /// The zero-based index of the pooled item.
+ private readonly int index;
+
+ /// The name of the pooled item .
+ private readonly string typeName;
+
+ #endregion Members
+
+ #region Properties
+
+ /// Gets the pooled resource.
+ /// The pooled resource.
+ public T Item
+ {
+ get
+ {
+ return this.item;
+ }
+ }
+
+ /// Gets a value indicating whether this object is locked or not.
+ /// A value indicating whether this object is locked or not.
+ public bool Locked
+ {
+ get
+ {
+ return this.locked;
+ }
+ }
+
+ /// Gets the zero-based index of the pooled item.
+ /// The index.
+ public int Index
+ {
+ get
+ {
+ return this.index;
+ }
+ }
+
+ /// Gets the name of the of the pooled item.
+ /// The name of the of the pooled item.
+ public string TypeName
+ {
+ get
+ {
+ return this.typeName;
+ }
+ }
+
+ #endregion Properties
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class with the specified item and
+ /// index.
+ ///
+ /// The pooled item.
+ /// The zero-based index of the pooled item.
+ internal PoolItem(T item, int index)
+ {
+ this.item = item;
+ this.index = index;
+ this.locked = false;
+ this.typeName = typeof(T).Name;
+ }
+
+ #endregion Constructors
+
+ #region Methods
+
+ /// Sets the lock on the item indicating that it is in use.
+ /// If the item is already locked, an error event is emitted.
+ ///
+ /// if the item is locked successfully, if it
+ /// fails.
+ ///
+ public bool SetLock()
+ {
+ if (this.locked)
+ {
+ CapyEventReporter.EmitEvent(EventLevel.Error, "Lock requested for {0}, but the lock request failed.", args: new[] { this });
+ return false;
+ }
+
+ this.locked = true;
+
+ return true;
+ }
+
+ /// Releases the lock on the item.
+ /// If the item is not locked, an error event is emitted.
+ public void ReleaseLock()
+ {
+ if (!this.locked)
+ {
+ CapyEventReporter.EmitEvent(EventLevel.Error, "Lock release requested for {0}, but the lock was already released.", args: new[] { this } );
+ }
+
+ this.locked = false;
+ }
+
+ #endregion Methods
+
+ #region Overrides
+
+ /// Returns a string that represents the current object and its lock state.
+ /// A string that represents the current object and its lock state.
+ public override string ToString()
+ {
+ return string.Format("{0} {1} ({2})", this.typeName, this.index, this.locked ? "Locked" : "Unlocked");
+ }
+
+ #endregion Overrides
+ }
+}