Преглед на файлове

misc: chore: [ci skip] generify Formatter Specs to be able to run formatters of different types at interleaving priorities

Evan Husted преди 1 година
родител
ревизия
30a534edcd
променени са 2 файла, в които са добавени 105 реда и са изтрити 69 реда
  1. 3 47
      src/Ryujinx/Utilities/PlayReport/Analyzer.cs
  2. 102 22
      src/Ryujinx/Utilities/PlayReport/Specs.cs

+ 3 - 47
src/Ryujinx/Utilities/PlayReport/Analyzer.cs

@@ -103,56 +103,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport
             if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
                 return FormattedValue.Unhandled;
 
-            foreach (FormatterSpec formatSpec in spec.SimpleValueFormatters.OrderBy(x => x.Priority))
+            foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority))
             {
-                if (!playReport.ReportData.AsDictionary().TryGetValue(formatSpec.ReportKey, out MessagePackObject valuePackObject))
+                if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
                     continue;
 
-                return formatSpec.Formatter(new SingleValue(valuePackObject)
-                {
-                    Application = appMeta,
-                    PlayReport = playReport
-                });
-            }
-
-            foreach (MultiFormatterSpec formatSpec in spec.MultiValueFormatters.OrderBy(x => x.Priority))
-            {
-                List<MessagePackObject> packedObjects = [];
-                foreach (var reportKey in formatSpec.ReportKeys)
-                {
-                    if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
-                        continue;
-
-                    packedObjects.Add(valuePackObject);
-                }
-
-                if (packedObjects.Count != formatSpec.ReportKeys.Length)
-                    return FormattedValue.Unhandled;
-
-                return formatSpec.Formatter(new MultiValue(packedObjects)
-                {
-                    Application = appMeta,
-                    PlayReport = playReport
-                });
-            }
-
-            foreach (SparseMultiFormatterSpec formatSpec in spec.SparseMultiValueFormatters.OrderBy(x => x.Priority))
-            {
-                Dictionary<string, MessagePackObject> packedObjects = [];
-                foreach (var reportKey in formatSpec.ReportKeys)
-                {
-                    if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
-                        continue;
-
-                    packedObjects.Add(reportKey, valuePackObject);
-                }
-
-                return formatSpec.Formatter(
-                    new SparseMultiValue(packedObjects)
-                    {
-                        Application = appMeta,
-                        PlayReport = playReport
-                    });
+                return value;
             }
 
             return FormattedValue.Unhandled;

+ 102 - 22
src/Ryujinx/Utilities/PlayReport/Specs.cs

@@ -1,4 +1,7 @@
 using FluentAvalonia.Core;
+using MsgPack;
+using Ryujinx.Ava.Utilities.AppLibrary;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 
@@ -11,10 +14,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport
     /// </summary>
     public class GameSpec
     {
+        private int _lastPriority;
+        
         public required string[] TitleIds { get; init; }
-        public List<FormatterSpec> SimpleValueFormatters { get; } = [];
-        public List<MultiFormatterSpec> MultiValueFormatters { get; } = [];
-        public List<SparseMultiFormatterSpec> SparseMultiValueFormatters { get; } = [];
+
+        public List<FormatterSpecBase> ValueFormatters { get; } = [];
 
 
         /// <summary>
@@ -25,7 +29,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         /// <param name="valueFormatter">The function which can return a potential formatted value.</param>
         /// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
         public GameSpec AddValueFormatter(string reportKey, ValueFormatter valueFormatter)
-            => AddValueFormatter(SimpleValueFormatters.Count, reportKey, valueFormatter);
+            => AddValueFormatter(_lastPriority++, reportKey, valueFormatter);
 
         /// <summary>
         /// Add a value formatter at a specific priority to the current <see cref="GameSpec"/>
@@ -38,9 +42,9 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         public GameSpec AddValueFormatter(int priority, string reportKey,
             ValueFormatter valueFormatter)
         {
-            SimpleValueFormatters.Add(new FormatterSpec
+            ValueFormatters.Add(new FormatterSpec
             {
-                Priority = priority, ReportKey = reportKey, Formatter = valueFormatter
+                Priority = priority, ReportKeys = [reportKey], Formatter = valueFormatter
             });
             return this;
         }
@@ -53,7 +57,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         /// <param name="valueFormatter">The function which can format the values.</param>
         /// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
         public GameSpec AddMultiValueFormatter(string[] reportKeys, MultiValueFormatter valueFormatter)
-            => AddMultiValueFormatter(MultiValueFormatters.Count, reportKeys, valueFormatter);
+            => AddMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
 
         /// <summary>
         /// Add a multi-value formatter at a specific priority to the current <see cref="GameSpec"/>
@@ -66,7 +70,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         public GameSpec AddMultiValueFormatter(int priority, string[] reportKeys,
             MultiValueFormatter valueFormatter)
         {
-            MultiValueFormatters.Add(new MultiFormatterSpec
+            ValueFormatters.Add(new MultiFormatterSpec
             {
                 Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
             });
@@ -84,7 +88,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         /// <param name="valueFormatter">The function which can format the values.</param>
         /// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
         public GameSpec AddSparseMultiValueFormatter(string[] reportKeys, SparseMultiValueFormatter valueFormatter)
-            => AddSparseMultiValueFormatter(SparseMultiValueFormatters.Count, reportKeys, valueFormatter);
+            => AddSparseMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
 
         /// <summary>
         /// Add a multi-value formatter at a specific priority to the current <see cref="GameSpec"/>
@@ -100,7 +104,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         public GameSpec AddSparseMultiValueFormatter(int priority, string[] reportKeys,
             SparseMultiValueFormatter valueFormatter)
         {
-            SparseMultiValueFormatters.Add(new SparseMultiFormatterSpec
+            ValueFormatters.Add(new SparseMultiFormatterSpec
             {
                 Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
             });
@@ -111,30 +115,106 @@ namespace Ryujinx.Ava.Utilities.PlayReport
     /// <summary>
     /// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value.
     /// </summary>
-    public struct FormatterSpec
+    public class FormatterSpec : FormatterSpecBase
     {
-        public required int Priority { get; init; }
-        public required string ReportKey { get; init; }
-        public ValueFormatter Formatter { get; init; }
+        public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
+        {
+            if (!playReport.ReportData.AsDictionary().TryGetValue(ReportKeys[0], out MessagePackObject valuePackObject))
+            {
+                result = null;
+                return false;
+            }
+
+            result = valuePackObject;
+            return true;
+        }
     }
 
     /// <summary>
     /// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their potential values.
     /// </summary>
-    public struct MultiFormatterSpec
+    public class MultiFormatterSpec : FormatterSpecBase
     {
-        public required int Priority { get; init; }
-        public required string[] ReportKeys { get; init; }
-        public MultiValueFormatter Formatter { get; init; }
+        public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
+        {
+            List<MessagePackObject> packedObjects = [];
+            foreach (var reportKey in ReportKeys)
+            {
+                if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
+                {
+                    result = null;
+                    return false;
+                }
+
+                packedObjects.Add(valuePackObject);
+            }
+
+            result = packedObjects;
+            return true;
+        }
     }
 
     /// <summary>
     /// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their sparsely populated potential values.
     /// </summary>
-    public struct SparseMultiFormatterSpec
+    public class SparseMultiFormatterSpec : FormatterSpecBase
     {
-        public required int Priority { get; init; }
-        public required string[] ReportKeys { get; init; }
-        public SparseMultiValueFormatter Formatter { get; init; }
+        public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
+        {
+            Dictionary<string, MessagePackObject> packedObjects = [];
+            foreach (var reportKey in ReportKeys)
+            {
+                if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
+                    continue;
+
+                packedObjects.Add(reportKey, valuePackObject);
+            }
+
+            result = packedObjects;
+            return true;
+        }
+    }
+    
+    public abstract class FormatterSpecBase
+    {
+        public abstract bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object data);
+        
+        public int Priority { get; init; }
+        public string[] ReportKeys { get; init; }
+        public Delegate Formatter { get; init; }
+
+        public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport, out FormattedValue formattedValue)
+        {
+            formattedValue = default;
+            if (!GetData(playReport, out object data))
+                return false;
+
+            if (data is FormattedValue fv)
+            {
+                formattedValue = fv;
+                return true;
+            }
+
+            if (Formatter is ValueFormatter vf && data is MessagePackObject mpo)
+            {
+                formattedValue = vf(new SingleValue(mpo) { Application = appMeta, PlayReport = playReport });
+                return true;
+            }
+
+            if (Formatter is MultiValueFormatter mvf && data is List<MessagePackObject> messagePackObjects)
+            {
+                formattedValue = mvf(new MultiValue(messagePackObjects) { Application = appMeta, PlayReport = playReport });
+                return true;
+            }
+
+            if (Formatter is SparseMultiValueFormatter smvf &&
+                data is Dictionary<string, MessagePackObject> sparseMessagePackObjects)
+            {
+                formattedValue = smvf(new SparseMultiValue(sparseMessagePackObjects) { Application = appMeta, PlayReport = playReport });
+                return true;
+            }
+
+            throw new InvalidOperationException("Formatter delegate is not of a known type!");
+        }
     }
 }