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 --- ArcaneLibs | 2 +- .../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 ++++++ .../LibMatrix.EventTypes.Spec.csproj | 26 +++ .../RoomMembershipEventContent.cs | 14 ++ .../RoomMessageEventContent.cs | 57 +++++ .../Events/RoomMembershipEventContent.cs | 12 - .../Events/RoomMessageEventContent.cs | 57 ----- LibMatrix.EventTypes/LibMatrix.EventTypes.csproj | 14 -- LibMatrix.EventTypes/MatrixEvent.cs | 9 - LibMatrix.EventTypes/MatrixEventCollection.cs | 71 ------ LibMatrix.EventTypes/MatrixEventContent.cs | 60 ----- LibMatrix.EventTypes/temp/Program.cs | 30 --- .../LegacyMatrixEvent.cs | 251 +++++++++++++++++++++ LibMatrix/LegacyMatrixEvent.cs | 227 ------------------- LibMatrix/LibMatrix.csproj | 2 +- .../LibMatrix.EventTypes.BasicTests.csproj | 24 ++ .../LibMatrix.EventTypes.BasicTests/Program.cs | 37 +++ 23 files changed, 690 insertions(+), 482 deletions(-) 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 create mode 100644 LibMatrix.EventTypes.Spec/LibMatrix.EventTypes.Spec.csproj create mode 100644 LibMatrix.EventTypes.Spec/RoomMembershipEventContent.cs create mode 100644 LibMatrix.EventTypes.Spec/RoomMessageEventContent.cs delete mode 100644 LibMatrix.EventTypes/Events/RoomMembershipEventContent.cs delete mode 100644 LibMatrix.EventTypes/Events/RoomMessageEventContent.cs delete mode 100644 LibMatrix.EventTypes/LibMatrix.EventTypes.csproj delete mode 100644 LibMatrix.EventTypes/MatrixEvent.cs delete mode 100644 LibMatrix.EventTypes/MatrixEventCollection.cs delete mode 100644 LibMatrix.EventTypes/MatrixEventContent.cs delete mode 100644 LibMatrix.EventTypes/temp/Program.cs create mode 100644 LibMatrix.LegacyEvents.EventTypes/LegacyMatrixEvent.cs delete mode 100644 LibMatrix/LegacyMatrixEvent.cs create mode 100644 Utilities/LibMatrix.EventTypes.BasicTests/LibMatrix.EventTypes.BasicTests.csproj create mode 100644 Utilities/LibMatrix.EventTypes.BasicTests/Program.cs diff --git a/ArcaneLibs b/ArcaneLibs index 68eca20..65d6fa9 160000 --- a/ArcaneLibs +++ b/ArcaneLibs @@ -1 +1 @@ -Subproject commit 68eca20fbf4d5c08b6960fb2362ba3733d2df9e5 +Subproject commit 65d6fa98cc05ed3c37e71797145e8eb7f2c072a1 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 diff --git a/LibMatrix.EventTypes.Spec/LibMatrix.EventTypes.Spec.csproj b/LibMatrix.EventTypes.Spec/LibMatrix.EventTypes.Spec.csproj new file mode 100644 index 0000000..5bfaedf --- /dev/null +++ b/LibMatrix.EventTypes.Spec/LibMatrix.EventTypes.Spec.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + true + true + full + + + true + true + true + true + true + true + true + true + + + + + + + diff --git a/LibMatrix.EventTypes.Spec/RoomMembershipEventContent.cs b/LibMatrix.EventTypes.Spec/RoomMembershipEventContent.cs new file mode 100644 index 0000000..370a192 --- /dev/null +++ b/LibMatrix.EventTypes.Spec/RoomMembershipEventContent.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace LibMatrix.EventTypes.Events; + +[MatrixEvent("m.room.member")] +[JsonConverter(typeof(MatrixEventContentConverter))] +public class RoomMembershipEventContent : BaseMatrixEventContent { + public string Membership { + get => InternalJson["membership"]!.GetValue(); + set => InternalJson["membership"] = value; + } + + public string? Something { get; set; } +} \ No newline at end of file diff --git a/LibMatrix.EventTypes.Spec/RoomMessageEventContent.cs b/LibMatrix.EventTypes.Spec/RoomMessageEventContent.cs new file mode 100644 index 0000000..f2a5483 --- /dev/null +++ b/LibMatrix.EventTypes.Spec/RoomMessageEventContent.cs @@ -0,0 +1,57 @@ +// using System.Text.Json.Serialization; +// using LibMatrix.EventTypes; +// +// namespace LibMatrix.LegacyEvents.EventTypes.Spec; +// +// [MatrixEvent(EventId)] +// public class RoomMessageEventContent : MatrixEventContent { +// public const string EventId = "m.room.message"; +// +// public RoomMessageEventContent(string messageType = "m.notice", string? body = null) { +// MessageType = messageType; +// Body = body ?? ""; +// } +// +// [JsonPropertyName("body")] +// public string Body { get; set; } +// +// [JsonPropertyName("msgtype")] +// public string MessageType { get; set; } = "m.notice"; +// +// [JsonPropertyName("formatted_body")] +// public string? FormattedBody { get; set; } +// +// [JsonPropertyName("format")] +// public string? Format { get; set; } +// +// /// +// /// Media URI for this message, if any +// /// +// [JsonPropertyName("url")] +// public string? Url { get; set; } +// +// public string? FileName { get; set; } +// +// [JsonPropertyName("info")] +// public FileInfoStruct? FileInfo { get; set; } +// +// [JsonIgnore] +// public string BodyWithoutReplyFallback => Body.Split('\n').SkipWhile(x => x.StartsWith(">")).SkipWhile(x=>x.Trim().Length == 0).Aggregate((x, y) => $"{x}\n{y}"); +// +// public class FileInfoStruct { +// [JsonPropertyName("mimetype")] +// public string? MimeType { get; set; } +// +// [JsonPropertyName("size")] +// public long Size { get; set; } +// +// [JsonPropertyName("thumbnail_url")] +// public string? ThumbnailUrl { get; set; } +// +// [JsonPropertyName("w")] +// public int? Width { get; set; } +// +// [JsonPropertyName("h")] +// public int? Height { get; set; } +// } +// } \ No newline at end of file diff --git a/LibMatrix.EventTypes/Events/RoomMembershipEventContent.cs b/LibMatrix.EventTypes/Events/RoomMembershipEventContent.cs deleted file mode 100644 index fe50a2e..0000000 --- a/LibMatrix.EventTypes/Events/RoomMembershipEventContent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LibMatrix.EventTypes.Events; - -[MatrixEvent("m.room.member")] -[JsonConverter(typeof(MatrixEventContentConverter))] -public class RoomMembershipEventContent : MatrixEventContent { - public string Membership { - get => InternalJson["membership"]!.GetValue(); - set => InternalJson["membership"] = value; - } -} \ No newline at end of file diff --git a/LibMatrix.EventTypes/Events/RoomMessageEventContent.cs b/LibMatrix.EventTypes/Events/RoomMessageEventContent.cs deleted file mode 100644 index 55c2b6c..0000000 --- a/LibMatrix.EventTypes/Events/RoomMessageEventContent.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.EventTypes; - -namespace LibMatrix.LegacyEvents.EventTypes.Spec; - -[MatrixEvent(EventId)] -public class RoomMessageEventContent : MatrixEventContent { - public const string EventId = "m.room.message"; - - public RoomMessageEventContent(string messageType = "m.notice", string? body = null) { - MessageType = messageType; - Body = body ?? ""; - } - - [JsonPropertyName("body")] - public string Body { get; set; } - - [JsonPropertyName("msgtype")] - public string MessageType { get; set; } = "m.notice"; - - [JsonPropertyName("formatted_body")] - public string? FormattedBody { get; set; } - - [JsonPropertyName("format")] - public string? Format { get; set; } - - /// - /// Media URI for this message, if any - /// - [JsonPropertyName("url")] - public string? Url { get; set; } - - public string? FileName { get; set; } - - [JsonPropertyName("info")] - public FileInfoStruct? FileInfo { get; set; } - - [JsonIgnore] - public string BodyWithoutReplyFallback => Body.Split('\n').SkipWhile(x => x.StartsWith(">")).SkipWhile(x=>x.Trim().Length == 0).Aggregate((x, y) => $"{x}\n{y}"); - - public class FileInfoStruct { - [JsonPropertyName("mimetype")] - public string? MimeType { get; set; } - - [JsonPropertyName("size")] - public long Size { get; set; } - - [JsonPropertyName("thumbnail_url")] - public string? ThumbnailUrl { get; set; } - - [JsonPropertyName("w")] - public int? Width { get; set; } - - [JsonPropertyName("h")] - public int? Height { get; set; } - } -} \ No newline at end of file diff --git a/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj b/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj deleted file mode 100644 index bd33993..0000000 --- a/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net8.0 - enable - enable - Exe - - - - - - - diff --git a/LibMatrix.EventTypes/MatrixEvent.cs b/LibMatrix.EventTypes/MatrixEvent.cs deleted file mode 100644 index 63f1e75..0000000 --- a/LibMatrix.EventTypes/MatrixEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LibMatrix.EventTypes; - -public interface IMatrixEvent where T : MatrixEventContent; -public class MatrixEvent : IMatrixEvent where T : MatrixEventContent { - [JsonPropertyName("content")] - public T? Content { get; set; } -} \ No newline at end of file diff --git a/LibMatrix.EventTypes/MatrixEventCollection.cs b/LibMatrix.EventTypes/MatrixEventCollection.cs deleted file mode 100644 index 78886d9..0000000 --- a/LibMatrix.EventTypes/MatrixEventCollection.cs +++ /dev/null @@ -1,71 +0,0 @@ -// 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 diff --git a/LibMatrix.EventTypes/MatrixEventContent.cs b/LibMatrix.EventTypes/MatrixEventContent.cs deleted file mode 100644 index 81b8c52..0000000 --- a/LibMatrix.EventTypes/MatrixEventContent.cs +++ /dev/null @@ -1,60 +0,0 @@ -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))] -public class MatrixEventContent { - - [JsonExtensionData, JsonInclude] - public JsonObject InternalJson { get; set; } = new(); - - - - public MatrixEventContent() { } - - public MatrixEventContent(JsonNode json) { - InternalJson = json.AsObject(); - } - - public static implicit operator MatrixEventContent(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 : MatrixEventContent, new() { - public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - // read entire object into a JsonObject - var json = JsonNode.Parse(ref reader); - return new T { InternalJson = json.AsObject() }; - } - - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - value.InternalJson.WriteTo(writer); - } - } -} - -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/temp/Program.cs b/LibMatrix.EventTypes/temp/Program.cs deleted file mode 100644 index 22a65d4..0000000 --- a/LibMatrix.EventTypes/temp/Program.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Events; - -namespace LibMatrix.EventTypes.temp; - -public class Program { - // public MatrixEventCollection Members = [ - // new MatrixEvent() { - // Content = new() { - // Membership = "join" - // } - // } - // ]; - - public static void Main(string[] args) { - var evt = new RoomMembershipEventContent() { - Membership = "join" - }; - Console.WriteLine(evt.ToJson()); - - var eventJson = File.ReadAllText("test-event.json"); - var evt2 = JsonSerializer.Deserialize>(eventJson); - evt2.Content.Membership = "meow"; - Console.WriteLine(evt2.Content.ToJson()); - Console.WriteLine(ObjectExtensions.ToJson(evt2)); - - } -} \ No newline at end of file diff --git a/LibMatrix.LegacyEvents.EventTypes/LegacyMatrixEvent.cs b/LibMatrix.LegacyEvents.EventTypes/LegacyMatrixEvent.cs new file mode 100644 index 0000000..3cb9ecb --- /dev/null +++ b/LibMatrix.LegacyEvents.EventTypes/LegacyMatrixEvent.cs @@ -0,0 +1,251 @@ +#if !DISABLE_LEGACY_EVENTS +using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using ArcaneLibs; +using ArcaneLibs.Attributes; +using ArcaneLibs.Extensions; +using LibMatrix.LegacyEvents.EventTypes; + +namespace LibMatrix; + +public class LegacyMatrixEvent { + public static FrozenSet KnownStateEventTypes { get; } = new ClassCollector().ResolveFromAllAccessibleAssemblies().ToFrozenSet(); + + public static FrozenDictionary KnownStateEventTypesByName { get; } = KnownStateEventTypes.Aggregate( + new Dictionary(), + (dict, type) => { + var attrs = type.GetCustomAttributes(); + foreach (var attr in attrs) { + if (dict.TryGetValue(attr.EventName, out var existing)) + Console.WriteLine($"Duplicate event type '{attr.EventName}' registered for types '{existing.Name}' and '{type.Name}'"); + dict[attr.EventName] = type; + } + + return dict; + }).OrderBy(x => x.Key).ToFrozenDictionary(); + + public static Type GetStateEventType(string? type) => + string.IsNullOrWhiteSpace(type) ? typeof(UnknownLegacyEventContent) : KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownLegacyEventContent); + + [JsonIgnore] + public Type MappedType => GetStateEventType(Type); + + [JsonIgnore] + public bool IsLegacyType => MappedType.GetCustomAttributes().FirstOrDefault(x => x.EventName == Type)?.Legacy ?? false; + + [JsonIgnore] + public string FriendlyTypeName => MappedType.GetFriendlyNameOrNull() ?? Type; + + [JsonIgnore] + public string FriendlyTypeNamePlural => MappedType.GetFriendlyNamePluralOrNull() ?? Type; + + private static readonly JsonSerializerOptions TypedContentSerializerOptions = new() { + Converters = { + new JsonFloatStringConverter(), + new JsonDoubleStringConverter(), + new JsonDecimalStringConverter() + } + }; + + [JsonIgnore] + [SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")] + public LegacyEventContent? TypedContent { + get { + // if (Type == "m.receipt") { + // return null; + // } + try { + var mappedType = GetStateEventType(Type); + if (mappedType == typeof(UnknownLegacyEventContent)) + Console.WriteLine($"Warning: unknown event type '{Type}'"); + var deserialisedContent = (LegacyEventContent)RawContent.Deserialize(mappedType, TypedContentSerializerOptions)!; + return deserialisedContent; + } + catch (JsonException e) { + Console.WriteLine(e); + Console.WriteLine("Content:\n" + (RawContent?.ToJson() ?? "null")); + } + + return null; + } + set { + if (value is null) + RawContent?.Clear(); + else + RawContent = JsonSerializer.Deserialize(JsonSerializer.Serialize(value, value.GetType(), + new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); + } + } + + [JsonPropertyName("state_key")] + public string? StateKey { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("replaces_state")] + public string? ReplacesState { get; set; } + + private JsonObject? _rawContent; + + [JsonPropertyName("content")] + public JsonObject? RawContent { + get => _rawContent; + set => _rawContent = value; + } + + [JsonIgnore] + public string InternalSelfTypeName { + get { + var res = GetType().Name switch { + "StateEvent`1" => "StateEvent", + _ => GetType().Name + }; + return res; + } + } + + [JsonIgnore] + public string InternalContentTypeName => TypedContent?.GetType().Name ?? "null"; +} + +public class LegacyMatrixEventResponse : LegacyMatrixEvent { + [JsonPropertyName("origin_server_ts")] + public long? OriginServerTs { get; set; } + + [JsonPropertyName("room_id")] + public string? RoomId { get; set; } + + [JsonPropertyName("sender")] + public string? Sender { get; set; } + + [JsonPropertyName("unsigned")] + public UnsignedData? Unsigned { get; set; } + + [JsonPropertyName("event_id")] + public string? EventId { get; set; } + + public class UnsignedData { + [JsonPropertyName("age")] + public ulong? Age { get; set; } + + [JsonPropertyName("redacted_because")] + public object? RedactedBecause { get; set; } + + [JsonPropertyName("transaction_id")] + public string? TransactionId { get; set; } + + [JsonPropertyName("replaces_state")] + public string? ReplacesState { get; set; } + + [JsonPropertyName("prev_sender")] + public string? PrevSender { get; set; } + + [JsonPropertyName("prev_content")] + public JsonObject? PrevContent { get; set; } + } +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(ChunkedStateEventResponse))] +internal partial class ChunkedStateEventResponseSerializerContext : JsonSerializerContext; + +public class EventList { + public EventList() { } + + public EventList(List? events) { + Events = events; + } + + [JsonPropertyName("events")] + public List? Events { get; set; } = new(); +} + +public class ChunkedStateEventResponse { + [JsonPropertyName("chunk")] + public List? Chunk { get; set; } = new(); +} + +public class PaginatedChunkedStateEventResponse : ChunkedStateEventResponse { + [JsonPropertyName("start")] + public string? Start { get; set; } + + [JsonPropertyName("end")] + public string? End { get; set; } +} + +public class BatchedChunkedStateEventResponse : ChunkedStateEventResponse { + [JsonPropertyName("next_batch")] + public string? NextBatch { get; set; } + + [JsonPropertyName("prev_batch")] + public string? PrevBatch { get; set; } +} + +public class RecursedBatchedChunkedStateEventResponse : BatchedChunkedStateEventResponse { + [JsonPropertyName("recursion_depth")] + public int? RecursionDepth { get; set; } +} + +#region Unused code + +/* +public class StateEventContentPolymorphicTypeInfoResolver : DefaultJsonTypeInfoResolver +{ + public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) + { + JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); + + Type baseType = typeof(EventContent); + if (jsonTypeInfo.Type == baseType) { + jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions { + TypeDiscriminatorPropertyName = "type", + IgnoreUnrecognizedTypeDiscriminators = true, + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType, + + DerivedTypes = StateEvent.KnownStateEventTypesByName.Select(x => new JsonDerivedType(x.Value, x.Key)).ToList() + + // DerivedTypes = new ClassCollector() + // .ResolveFromAllAccessibleAssemblies() + // .SelectMany(t => t.GetCustomAttributes() + // .Select(a => new JsonDerivedType(t, attr.EventName)); + + }; + } + + return jsonTypeInfo; + } +} +*/ + +#endregion +#endif + +public class JsonFloatStringConverter : JsonConverter { + public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => float.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); +} + +public class JsonDoubleStringConverter : JsonConverter { + public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => double.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); +} + +public class JsonDecimalStringConverter : JsonConverter { + public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => decimal.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); +} \ No newline at end of file diff --git a/LibMatrix/LegacyMatrixEvent.cs b/LibMatrix/LegacyMatrixEvent.cs deleted file mode 100644 index a1ac5db..0000000 --- a/LibMatrix/LegacyMatrixEvent.cs +++ /dev/null @@ -1,227 +0,0 @@ -#if !DISABLE_LEGACY_EVENTS -using System.Collections.Frozen; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; -using ArcaneLibs; -using ArcaneLibs.Attributes; -using ArcaneLibs.Extensions; -using LibMatrix.LegacyEvents.EventTypes; -using LibMatrix.Extensions; - -namespace LibMatrix; - -public class LegacyMatrixEvent { - public static FrozenSet KnownStateEventTypes { get; } = new ClassCollector().ResolveFromAllAccessibleAssemblies().ToFrozenSet(); - - public static FrozenDictionary KnownStateEventTypesByName { get; } = KnownStateEventTypes.Aggregate( - new Dictionary(), - (dict, type) => { - var attrs = type.GetCustomAttributes(); - foreach (var attr in attrs) { - if (dict.TryGetValue(attr.EventName, out var existing)) - Console.WriteLine($"Duplicate event type '{attr.EventName}' registered for types '{existing.Name}' and '{type.Name}'"); - dict[attr.EventName] = type; - } - - return dict; - }).OrderBy(x => x.Key).ToFrozenDictionary(); - - public static Type GetStateEventType(string? type) => - string.IsNullOrWhiteSpace(type) ? typeof(UnknownLegacyEventContent) : KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownLegacyEventContent); - - [JsonIgnore] - public Type MappedType => GetStateEventType(Type); - - [JsonIgnore] - public bool IsLegacyType => MappedType.GetCustomAttributes().FirstOrDefault(x => x.EventName == Type)?.Legacy ?? false; - - [JsonIgnore] - public string FriendlyTypeName => MappedType.GetFriendlyNameOrNull() ?? Type; - - [JsonIgnore] - public string FriendlyTypeNamePlural => MappedType.GetFriendlyNamePluralOrNull() ?? Type; - - private static readonly JsonSerializerOptions TypedContentSerializerOptions = new() { - Converters = { - new JsonFloatStringConverter(), - new JsonDoubleStringConverter(), - new JsonDecimalStringConverter() - } - }; - - [JsonIgnore] - [SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")] - public LegacyEventContent? TypedContent { - get { - // if (Type == "m.receipt") { - // return null; - // } - try { - var mappedType = GetStateEventType(Type); - if (mappedType == typeof(UnknownLegacyEventContent)) - Console.WriteLine($"Warning: unknown event type '{Type}'"); - var deserialisedContent = (LegacyEventContent)RawContent.Deserialize(mappedType, TypedContentSerializerOptions)!; - return deserialisedContent; - } - catch (JsonException e) { - Console.WriteLine(e); - Console.WriteLine("Content:\n" + (RawContent?.ToJson() ?? "null")); - } - - return null; - } - set { - if (value is null) - RawContent?.Clear(); - else - RawContent = JsonSerializer.Deserialize(JsonSerializer.Serialize(value, value.GetType(), - new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); - } - } - - [JsonPropertyName("state_key")] - public string? StateKey { get; set; } - - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("replaces_state")] - public string? ReplacesState { get; set; } - - private JsonObject? _rawContent; - - [JsonPropertyName("content")] - public JsonObject? RawContent { - get => _rawContent; - set => _rawContent = value; - } - - [JsonIgnore] - public string InternalSelfTypeName { - get { - var res = GetType().Name switch { - "StateEvent`1" => "StateEvent", - _ => GetType().Name - }; - return res; - } - } - - [JsonIgnore] - public string InternalContentTypeName => TypedContent?.GetType().Name ?? "null"; -} - -public class LegacyMatrixEventResponse : LegacyMatrixEvent { - [JsonPropertyName("origin_server_ts")] - public long? OriginServerTs { get; set; } - - [JsonPropertyName("room_id")] - public string? RoomId { get; set; } - - [JsonPropertyName("sender")] - public string? Sender { get; set; } - - [JsonPropertyName("unsigned")] - public UnsignedData? Unsigned { get; set; } - - [JsonPropertyName("event_id")] - public string? EventId { get; set; } - - public class UnsignedData { - [JsonPropertyName("age")] - public ulong? Age { get; set; } - - [JsonPropertyName("redacted_because")] - public object? RedactedBecause { get; set; } - - [JsonPropertyName("transaction_id")] - public string? TransactionId { get; set; } - - [JsonPropertyName("replaces_state")] - public string? ReplacesState { get; set; } - - [JsonPropertyName("prev_sender")] - public string? PrevSender { get; set; } - - [JsonPropertyName("prev_content")] - public JsonObject? PrevContent { get; set; } - } -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(ChunkedStateEventResponse))] -internal partial class ChunkedStateEventResponseSerializerContext : JsonSerializerContext; - -public class EventList { - public EventList() { } - - public EventList(List? events) { - Events = events; - } - - [JsonPropertyName("events")] - public List? Events { get; set; } = new(); -} - -public class ChunkedStateEventResponse { - [JsonPropertyName("chunk")] - public List? Chunk { get; set; } = new(); -} - -public class PaginatedChunkedStateEventResponse : ChunkedStateEventResponse { - [JsonPropertyName("start")] - public string? Start { get; set; } - - [JsonPropertyName("end")] - public string? End { get; set; } -} - -public class BatchedChunkedStateEventResponse : ChunkedStateEventResponse { - [JsonPropertyName("next_batch")] - public string? NextBatch { get; set; } - - [JsonPropertyName("prev_batch")] - public string? PrevBatch { get; set; } -} - -public class RecursedBatchedChunkedStateEventResponse : BatchedChunkedStateEventResponse { - [JsonPropertyName("recursion_depth")] - public int? RecursionDepth { get; set; } -} - -#region Unused code - -/* -public class StateEventContentPolymorphicTypeInfoResolver : DefaultJsonTypeInfoResolver -{ - public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) - { - JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); - - Type baseType = typeof(EventContent); - if (jsonTypeInfo.Type == baseType) { - jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions { - TypeDiscriminatorPropertyName = "type", - IgnoreUnrecognizedTypeDiscriminators = true, - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType, - - DerivedTypes = StateEvent.KnownStateEventTypesByName.Select(x => new JsonDerivedType(x.Value, x.Key)).ToList() - - // DerivedTypes = new ClassCollector() - // .ResolveFromAllAccessibleAssemblies() - // .SelectMany(t => t.GetCustomAttributes() - // .Select(a => new JsonDerivedType(t, attr.EventName)); - - }; - } - - return jsonTypeInfo; - } -} -*/ - -#endregion -#endif \ No newline at end of file diff --git a/LibMatrix/LibMatrix.csproj b/LibMatrix/LibMatrix.csproj index 69f2bf4..d6c9155 100644 --- a/LibMatrix/LibMatrix.csproj +++ b/LibMatrix/LibMatrix.csproj @@ -10,7 +10,7 @@ true - $(DefineConstants);DISABLE_LEGACY_EVENTS + $(DefineConstants);$(ExtraDefineConstants) diff --git a/Utilities/LibMatrix.EventTypes.BasicTests/LibMatrix.EventTypes.BasicTests.csproj b/Utilities/LibMatrix.EventTypes.BasicTests/LibMatrix.EventTypes.BasicTests.csproj new file mode 100644 index 0000000..b769cb7 --- /dev/null +++ b/Utilities/LibMatrix.EventTypes.BasicTests/LibMatrix.EventTypes.BasicTests.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + + Always + + + diff --git a/Utilities/LibMatrix.EventTypes.BasicTests/Program.cs b/Utilities/LibMatrix.EventTypes.BasicTests/Program.cs new file mode 100644 index 0000000..8c1e15a --- /dev/null +++ b/Utilities/LibMatrix.EventTypes.BasicTests/Program.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using ArcaneLibs.Extensions; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Running; +using LibMatrix.EventTypes; +using LibMatrix.EventTypes.Events; + +BenchmarkRunner.Run(); + +[ShortRunJob] +[MemoryDiagnoser] +public class Tests { + // public MatrixEventCollection Members = [ + // new MatrixEvent() { + // Content = new() { + // Membership = "join" + // } + // } + // ]; + + private static string eventJson = File.ReadAllText("test-event.json"); + private static MatrixEvent evt2 = JsonSerializer.Deserialize>(eventJson); + [Benchmark] + public void Deserialise() { + JsonSerializer.Deserialize>(eventJson); + } + [Benchmark] + public void Serialise() { + evt2.ToJson(); + } + + [Benchmark] + public void Modify() { + evt2.Content.Membership = "meow"; + } +} \ No newline at end of file -- cgit 1.4.1