diff --git a/R4Utils/Messaging/Exceptions/AlreadySetUpException.cs b/R4Utils/Messaging/Exceptions/AlreadySetUpException.cs new file mode 100644 index 0000000..37fd3c2 --- /dev/null +++ b/R4Utils/Messaging/Exceptions/AlreadySetUpException.cs @@ -0,0 +1,15 @@ +using System; + +namespace R4Utils.Messaging.Exceptions +{ + /// + /// Thrown when a set up method has been called more than once. + /// + public class AlreadySetUpException : Exception + { + public AlreadySetUpException(string methodName) + : base($"The method ${methodName} may not be called more than once.") + { + } + } +} diff --git a/R4Utils/Messaging/Exceptions/MessageException.cs b/R4Utils/Messaging/Exceptions/MessageException.cs new file mode 100644 index 0000000..b61cb21 --- /dev/null +++ b/R4Utils/Messaging/Exceptions/MessageException.cs @@ -0,0 +1,39 @@ +using System; + +namespace R4Utils.Messaging.Exceptions +{ + /// + /// This may be created from + /// a or a . + /// + public class MessageException : Exception + { + /// + /// Creates an instance from just the without any data. + /// + /// A containing information + /// about the used to create this instance. + public MessageException(string messageInformation) + : base($"The following message was thrown: {messageInformation}") + { + } + + /// + /// Creates an instance from a and some . + /// + /// A containing information + /// about the used to create this instance. + /// The data enclosed in the used + /// to create this instance. + public MessageException(string messageInformation, object data) : base(messageInformation) + { + MessageData = data; + } + + /// + /// The data that was enclosed in the used to create this instance. + /// Is null when just a was used to create this instance. + /// + public object? MessageData { get; init; } = null; + } +} diff --git a/R4Utils/Messaging/Exceptions/NotSetUpException.cs b/R4Utils/Messaging/Exceptions/NotSetUpException.cs new file mode 100644 index 0000000..b20e5e6 --- /dev/null +++ b/R4Utils/Messaging/Exceptions/NotSetUpException.cs @@ -0,0 +1,14 @@ +using System; + +namespace R4Utils.Messaging.Exceptions +{ + /// + /// Thrown when using a method that requires set up that was not performed. + /// + public class NotSetUpException : Exception + { + public NotSetUpException(string name) : base($"Calling ${name} requires setting it up first.") + { + } + } +} diff --git a/R4Utils/Messaging/Message.cs b/R4Utils/Messaging/Message.cs new file mode 100644 index 0000000..c49b24c --- /dev/null +++ b/R4Utils/Messaging/Message.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.CompilerServices; +using R4Utils.Messaging.Exceptions; + +namespace R4Utils.Messaging +{ + /// + /// Represents a message sent between methods + /// + /// The type of the value being returned + /// The type of the that is + /// used to infer the + public class Message/* : MessageBase*/ where TEnum : Enum + { + /// + /// The actual value being returned + /// + public TData Data { get; init; } + + /// + /// The message context containing more information about this instance + /// + public MessageContext MessageContext { get; init; } + + /// + /// The function returning the message and severity for an element + /// of , given some . + /// + // protected static Func> ContextGetter { get; set; } = (enumEntry, data) + // => BasicContextGetter(Convert.ToInt32(enumEntry), typeof(TEnum), data); + protected static Func ContextGetter { get; set; } = (_, _) => + throw new NotImplementedException(); + + /// + /// Whether has already been called without failing + /// + // ReSharper disable once StaticMemberInGenericType + protected static bool AlreadySetUp { get; private set; } = false; + + /// + /// Call this method to set up the message handling for + /// this specific combination of and . + /// MUST be called once before the first can be created. + /// MAY NOT be called thereafter. + /// + /// The function returning + /// the message and severity for an element + /// of , given some . + /// Thrown when this method was already called. + public static void SetUp(Func contextGetter) + { + if (AlreadySetUp) + throw new AlreadySetUpException(nameof(SetUp)); + + ContextGetter = contextGetter; + AlreadySetUp = true; + } + + public static Message Create(TData data, TEnum enumEntry, + [CallerMemberName] string memberName = "", + [CallerFilePath] string sourceFilePath = "", + [CallerLineNumber] int sourceLineNumber = 0) + { + if (AlreadySetUp == false/* && AlreadyBasicSetUp == false*/) + throw new NotSetUpException(nameof(Create)); + + if (data is null) + throw new ArgumentNullException(nameof(data), + $"A ${nameof(Message)} may not be created with null ${nameof(data)}."); + + (string message, int severity) = ContextGetter(enumEntry, data); + MessageContext context = MessageContext.Create(enumEntry, message, severity, + sourceFilePath, memberName, sourceLineNumber); + return new(data, context); + } + + protected Message(TData data, MessageContext messageContext) + { + Data = data; + MessageContext = messageContext; + } + } +} diff --git a/R4Utils/Messaging/MessageBase.cs b/R4Utils/Messaging/MessageBase.cs new file mode 100644 index 0000000..096c49a --- /dev/null +++ b/R4Utils/Messaging/MessageBase.cs @@ -0,0 +1,55 @@ +using System; +using R4Utils.Messaging.Exceptions; + +namespace R4Utils.Messaging +{ + #if FALSE + /// + /// Contains information shared for all types + /// + public abstract class MessageBase + { + /// + /// The containing the message context indices + /// + protected static Type MessageContextIndex { get; private set; } = typeof(object); + + /// + /// The function returning the for the index in the enum, provided some data + /// + protected static Func BasicContextGetter { get; private set; } = (_, _, _) + => throw new NotImplementedException(nameof(BasicContextGetter)); + + /// + /// Whether has already been called without failing + /// + protected static bool AlreadyBasicSetUp { get; private set; } = false; + + /// + /// Call this method to set up the message handling. + /// MUST be called once before the first can be created. + /// MAY NOT be called thereafter. + /// + /// The that identifies the + /// instances that may be used. + /// The function returning the for an index + /// in , given some . + /// Thrown when this method was already called. + /// Thrown when is + /// not an . + public static void BasicSetUp(Type enumType, Func basicContextGetter) + { + if (AlreadyBasicSetUp) + throw new AlreadySetUpException(nameof(BasicSetUp)); + + if (enumType.IsEnum == false) + throw new ArgumentException($"The provided ${nameof(Type)} is no ${nameof(Enum)}.", + nameof(enumType)); + + AlreadyBasicSetUp = true; + MessageContextIndex = enumType; + BasicContextGetter = basicContextGetter; + } + } +#endif +} diff --git a/R4Utils/Messaging/MessageContext.cs b/R4Utils/Messaging/MessageContext.cs new file mode 100644 index 0000000..96a29b9 --- /dev/null +++ b/R4Utils/Messaging/MessageContext.cs @@ -0,0 +1,85 @@ +using System; +using System.Text; +using R4Utils.Messaging.Exceptions; + +namespace R4Utils.Messaging +{ + /// + /// Represents the context of a + /// + public class MessageContext where TEnum : Enum + { + /// + /// The entry of the this in the + /// used to create this instance. + /// + public TEnum ContextIdentifier { get; init; } + + /// + /// The message that can get displayed to the user. + /// + public string MessageText { get; init; } + + /// + /// The severity assigned to this instance. + /// The interpretation of this value is not fixed. + /// + public int Severity { get; init; } + + /// + /// The file containing the caller creating + /// the for which this instance has been created. + /// + public string SourceCompilationUnit { get; init; } + /// + /// The method creating + /// the for which this instance has been created. + /// + public string SourceMethod { get; init; } + /// + /// The line in the in which + /// the for which this instance has been created, + /// was created. + /// + public int SourceLine { get; init; } + + public static MessageContext Create(TEnum enumEntry, string messageText, int severity, + string sourceCompilationUnit, string sourceMethod, int sourceLine) + => new(enumEntry, messageText, severity, sourceCompilationUnit, sourceMethod, sourceLine); + + protected MessageContext(TEnum contextIdentifier, string messageText, int severity, string sourceCompilationUnit, + string sourceMethod, int sourceLine) + { + ContextIdentifier = contextIdentifier; + MessageText = messageText; + Severity = severity; + SourceCompilationUnit = sourceCompilationUnit; + SourceMethod = sourceMethod; + SourceLine = sourceLine; + } + + /// + /// Get all data from this instance as a . + /// + /// A in the form 'Message: ...\n Identifier: ...\n Severity: + /// ...\n Message occurred at ... in line ... in ...'. + public override string ToString() + { + StringBuilder sb = new(); + sb.Append($"Message: \"{MessageText}\""); + sb.Append(Environment.NewLine); + sb.Append($"Identifier: {ContextIdentifier}"); + sb.Append(Environment.NewLine); + sb.Append($"Severity: {Severity}"); + sb.Append(Environment.NewLine); + sb.Append($"Message occurred at '{SourceCompilationUnit} in line {SourceLine} in {SourceMethod}"); + return sb.ToString(); + } + + /// + /// Get a from this instance. + /// + /// The containing the information about this instance. + public MessageException AsException() => new(ToString()); + } +}