From a129b321998614b20e4ebb8a7c1632553ebee981 Mon Sep 17 00:00:00 2001 From: "Emma [it/its]@Rory&" Date: Sat, 1 Jun 2024 19:02:28 +0200 Subject: Split event abstractions --- .../BaseMatrixEventContent.cs | 54 ++++++++++ .../Converters/EventConverterFactory.cs | 112 +++++++++++++++++++++ .../Converters/EventJsonSerializerContext.cs | 10 ++ .../LibMatrix.EventTypes.Abstractions.csproj | 14 +++ LibMatrix.EventTypes.Abstractions/MatrixEvent.cs | 9 ++ .../MatrixEventAttribute.cs | 9 ++ .../MatrixEventCollection.cs | 71 +++++++++++++ 7 files changed, 279 insertions(+) create mode 100644 LibMatrix.EventTypes.Abstractions/BaseMatrixEventContent.cs create mode 100644 LibMatrix.EventTypes.Abstractions/Converters/EventConverterFactory.cs create mode 100644 LibMatrix.EventTypes.Abstractions/Converters/EventJsonSerializerContext.cs create mode 100644 LibMatrix.EventTypes.Abstractions/LibMatrix.EventTypes.Abstractions.csproj create mode 100644 LibMatrix.EventTypes.Abstractions/MatrixEvent.cs create mode 100644 LibMatrix.EventTypes.Abstractions/MatrixEventAttribute.cs create mode 100644 LibMatrix.EventTypes.Abstractions/MatrixEventCollection.cs (limited to 'LibMatrix.EventTypes.Abstractions') diff --git a/LibMatrix.EventTypes.Abstractions/BaseMatrixEventContent.cs b/LibMatrix.EventTypes.Abstractions/BaseMatrixEventContent.cs new file mode 100644 index 0000000..eba50a5 --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/BaseMatrixEventContent.cs @@ -0,0 +1,54 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using ArcaneLibs.Extensions; + +namespace LibMatrix.EventTypes; + +// : MatrixEventContent where T : MatrixEventContent, new() { +/// +/// Extensible Event Content, aims to provide an API similar to JsonNode/JsonObject +/// +/// +/// +[JsonConverter(typeof(MatrixEventContentConverter))] +// [JsonSerializable(typeof(MatrixEventContent))] +public class BaseMatrixEventContent { + public JsonObject InternalJson { get; set; } = new(); + + public BaseMatrixEventContent() { } + + public BaseMatrixEventContent(JsonNode json) { + InternalJson = json.AsObject(); + } + + public static implicit operator BaseMatrixEventContent(JsonNode json) => new(json); + + // public static implicit operator JsonNode(MatrixEventContent content) => content.InternalJson; + + [JsonIgnore] + public IEnumerable EventTypes => this.GetType().GetCustomAttributes().Select(x => x.EventType); + + [JsonIgnore] + public string EventType => EventTypes.First(); + + public JsonNode? this[string key] => InternalJson[key]; + + public string ToJson() => InternalJson.ToJson(); + + + public class MatrixEventContentConverter : JsonConverter where T : BaseMatrixEventContent, new() { + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + // read entire object into a JsonObject + // Console.WriteLine($"MatrixEventContentConverter: Reading {typeToConvert}"); + var json = JsonNode.Parse(ref reader); + return new T { InternalJson = json.AsObject() }; + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { + // Console.WriteLine($"MatrixEventContentConverter: Writing {value.GetType()}"); + value.InternalJson.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/LibMatrix.EventTypes.Abstractions/Converters/EventConverterFactory.cs b/LibMatrix.EventTypes.Abstractions/Converters/EventConverterFactory.cs new file mode 100644 index 0000000..3b4c493 --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/Converters/EventConverterFactory.cs @@ -0,0 +1,112 @@ +/*namespace LibMatrix.EventTypes.Converters; + +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; + +public class MatrixEventConverter : JsonConverterFactory { + public override bool CanConvert(Type typeToConvert) { + Console.WriteLine(typeToConvert); + if (!typeToConvert.IsGenericType) { + return false; + } + + if (typeToConvert.GetGenericTypeDefinition() != typeof(MatrixEvent<>)) { + return false; + } + + return typeToConvert.GetGenericArguments()[0].IsAssignableTo(typeof(MatrixEventContent)); + } + + public override JsonConverter CreateConverter( + Type type, + JsonSerializerOptions options) { + Type[] typeArguments = type.GetGenericArguments(); + Type keyType = typeArguments[0]; + Type valueType = typeArguments[1]; + + JsonConverter converter = (JsonConverter)Activator.CreateInstance( + typeof(DictionaryEnumConverterInner<,>).MakeGenericType( + [keyType, valueType]), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: [options], + culture: null)!; + + return converter; + } + + private class DictionaryEnumConverterInner : + JsonConverter> where TKey : struct, Enum { + private readonly JsonConverter _valueConverter; + private readonly Type _keyType; + private readonly Type _valueType; + + public DictionaryEnumConverterInner(JsonSerializerOptions options) { + // For performance, use the existing converter. + _valueConverter = (JsonConverter)options + .GetConverter(typeof(TValue)); + + // Cache the key and value types. + _keyType = typeof(TKey); + _valueType = typeof(TValue); + } + + public override Dictionary Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { + if (reader.TokenType != JsonTokenType.StartObject) { + throw new JsonException(); + } + + var dictionary = new Dictionary(); + + while (reader.Read()) { + if (reader.TokenType == JsonTokenType.EndObject) { + return dictionary; + } + + // Get the key. + if (reader.TokenType != JsonTokenType.PropertyName) { + throw new JsonException(); + } + + string? propertyName = reader.GetString(); + + // For performance, parse with ignoreCase:false first. + if (!Enum.TryParse(propertyName, ignoreCase: false, out TKey key) && + !Enum.TryParse(propertyName, ignoreCase: true, out key)) { + throw new JsonException( + $"Unable to convert \"{propertyName}\" to Enum \"{_keyType}\"."); + } + + // Get the value. + reader.Read(); + TValue value = _valueConverter.Read(ref reader, _valueType, options)!; + + // Add to dictionary. + dictionary.Add(key, value); + } + + throw new JsonException(); + } + + public override void Write( + Utf8JsonWriter writer, + Dictionary dictionary, + JsonSerializerOptions options) { + writer.WriteStartObject(); + + foreach ((TKey key, TValue value) in dictionary) { + string propertyName = key.ToString(); + writer.WritePropertyName + (options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName); + + _valueConverter.Write(writer, value, options); + } + + writer.WriteEndObject(); + } + } +}*/ \ No newline at end of file diff --git a/LibMatrix.EventTypes.Abstractions/Converters/EventJsonSerializerContext.cs b/LibMatrix.EventTypes.Abstractions/Converters/EventJsonSerializerContext.cs new file mode 100644 index 0000000..5b8e2dc --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/Converters/EventJsonSerializerContext.cs @@ -0,0 +1,10 @@ +// using System.Text.Json; +// using System.Text.Json.Serialization; +// using System.Text.Json.Serialization.Metadata; +// +// namespace LibMatrix.EventTypes.Converters; +// +// [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] +// [JsonSerializable(typeof(MatrixEvent<>))] +// internal partial class EventJsonSerializerContext : JsonSerializerContext { +// } \ No newline at end of file diff --git a/LibMatrix.EventTypes.Abstractions/LibMatrix.EventTypes.Abstractions.csproj b/LibMatrix.EventTypes.Abstractions/LibMatrix.EventTypes.Abstractions.csproj new file mode 100644 index 0000000..dce51ea --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/LibMatrix.EventTypes.Abstractions.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + preview + + + + + + + diff --git a/LibMatrix.EventTypes.Abstractions/MatrixEvent.cs b/LibMatrix.EventTypes.Abstractions/MatrixEvent.cs new file mode 100644 index 0000000..0e548c6 --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/MatrixEvent.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace LibMatrix.EventTypes; + +public interface IMatrixEvent where T : BaseMatrixEventContent; +public class MatrixEvent : IMatrixEvent where T : BaseMatrixEventContent { + [JsonPropertyName("content")] + public T? Content { get; set; } +} \ No newline at end of file diff --git a/LibMatrix.EventTypes.Abstractions/MatrixEventAttribute.cs b/LibMatrix.EventTypes.Abstractions/MatrixEventAttribute.cs new file mode 100644 index 0000000..aface6d --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/MatrixEventAttribute.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace LibMatrix.EventTypes; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public class MatrixEventAttribute(string eventType, bool deprecated = false) : Attribute { + public string EventType { get; } = eventType; + public bool Deprecated { get; } = deprecated; +} \ No newline at end of file diff --git a/LibMatrix.EventTypes.Abstractions/MatrixEventCollection.cs b/LibMatrix.EventTypes.Abstractions/MatrixEventCollection.cs new file mode 100644 index 0000000..78886d9 --- /dev/null +++ b/LibMatrix.EventTypes.Abstractions/MatrixEventCollection.cs @@ -0,0 +1,71 @@ +// using System.Collections; +// +// namespace LibMatrix.EventTypes; +// +// public interface IMatrixEventCollection : IEnumerable> where T : MatrixEventContent { +// +// } +// public class MatrixEventCollection : IMatrixEventCollection, IList { +// private IList> _listImplementation; +// public IEnumerator> GetEnumerator() => _listImplementation.GetEnumerator(); +// +// IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_listImplementation).GetEnumerator(); +// +// public void Add(MatrixEvent item) => _listImplementation.Add(item); +// +// public void Clear() => _listImplementation.Clear(); +// +// public bool Contains(MatrixEvent item) => _listImplementation.Contains(item); +// +// public void CopyTo(MatrixEvent[] array, int arrayIndex) => _listImplementation.CopyTo(array, arrayIndex); +// +// public bool Remove(MatrixEvent item) => _listImplementation.Remove(item); +// +// public int Count => _listImplementation.Count; +// +// public bool IsReadOnly => _listImplementation.IsReadOnly; +// +// public int IndexOf(MatrixEvent item) => _listImplementation.IndexOf(item); +// +// public void Insert(int index, MatrixEvent item) => _listImplementation.Insert(index, item); +// +// public void RemoveAt(int index) => _listImplementation.RemoveAt(index); +// +// public MatrixEvent this[int index] { +// get => _listImplementation[index]; +// set => _listImplementation[index] = value; +// } +// } +// public class MatrixEventCollection : IMatrixEventCollection, IList> where T : MatrixEventContent { +// //TODO: implement +// +// private IList> _listImplementation = new List>(); +// public IEnumerator> GetEnumerator() => _listImplementation.GetEnumerator(); +// +// IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_listImplementation).GetEnumerator(); +// +// public void Add(MatrixEvent item) => _listImplementation.Add(item); +// +// public void Clear() => _listImplementation.Clear(); +// +// public bool Contains(MatrixEvent item) => _listImplementation.Contains(item); +// +// public void CopyTo(MatrixEvent[] array, int arrayIndex) => _listImplementation.CopyTo(array, arrayIndex); +// +// public bool Remove(MatrixEvent item) => _listImplementation.Remove(item); +// +// public int Count => _listImplementation.Count; +// +// public bool IsReadOnly => _listImplementation.IsReadOnly; +// +// public int IndexOf(MatrixEvent item) => _listImplementation.IndexOf(item); +// +// public void Insert(int index, MatrixEvent item) => _listImplementation.Insert(index, item); +// +// public void RemoveAt(int index) => _listImplementation.RemoveAt(index); +// +// public MatrixEvent this[int index] { +// get => _listImplementation[index]; +// set => _listImplementation[index] = value; +// } +// }a \ No newline at end of file -- cgit 1.4.1