diff --git a/src/TotalMixVC.Tests/ConfigTests.cs b/src/TotalMixVC.Tests/ConfigTests.cs
index 0e45065..f59a3e7 100644
--- a/src/TotalMixVC.Tests/ConfigTests.cs
+++ b/src/TotalMixVC.Tests/ConfigTests.cs
@@ -1,5 +1,6 @@
using System.Net;
using System.Windows.Media;
+using Tomlyn.Syntax;
using TotalMixVC.Configuration;
using Xunit;
@@ -522,4 +523,114 @@ out var diagnostics
Assert.Empty(diagnostics);
Assert.Equal(expectedConfig, config);
}
+
+ [Fact]
+ public void CleanDiagnostics_InvalidProperties_ProvidesCleanMessages()
+ {
+ var diagnostics = new DiagnosticsBag(
+ [
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Error,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 0, line: 0, column: 0),
+ end: new TextPosition(offset: 27, line: 0, column: 27)
+ ),
+ "Exception while trying to convert System.String to type "
+ + "System.Windows.Media.Color. Reason: Token is not valid."
+ ),
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Error,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 0, line: 0, column: 0),
+ end: new TextPosition(offset: 27, line: 0, column: 27)
+ ),
+ "The property value of type System.String couldn't be converted to "
+ + "System.Windows.Media.Color for the property Config/background_color"
+ ),
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Warning,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 59, line: 2, column: 0),
+ end: new TextPosition(offset: 87, line: 2, column: 28)
+ ),
+ "The property `EndPoint` was not found, but `end_point` was. By default "
+ + "property names are lowered and split by _ by PascalCase letters. This "
+ + "behavior can be changed by passing a TomlModelOptions and specifying "
+ + "the TomlModelOptions.ConvertPropertyName delegate."
+ ),
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Error,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 59, line: 2, column: 0),
+ end: new TextPosition(offset: 87, line: 2, column: 28)
+ ),
+ "The property EndPoint was not found on object type Config"
+ ),
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Error,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 100, line: 5, column: 0),
+ end: new TextPosition(offset: 112, line: 5, column: 12)
+ ),
+ "Unsupported type to convert System.String to type System.Single."
+ ),
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Error,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 100, line: 5, column: 0),
+ end: new TextPosition(offset: 112, line: 5, column: 12)
+ ),
+ "The property value of type System.String couldn't be converted to "
+ + "System.Single for the property Volume/max"
+ ),
+ ]
+ );
+
+ var diagnosticMessages = Config.CleanDiagnostics(diagnostics);
+ Assert.Equal(
+ [
+ "(1,1) : error : The property value of type System.String couldn't be converted "
+ + "to System.Windows.Media.Color for the property Config/background_color. "
+ + "Token is not valid.",
+ "(3,1) : warning : The property `EndPoint` was not found, but `end_point` was. "
+ + "By default property names are lowered and split by _ by PascalCase "
+ + "letters. This behavior can be changed by passing a TomlModelOptions and "
+ + "specifying the TomlModelOptions.ConvertPropertyName delegate.",
+ "(3,1) : error : The property EndPoint was not found on object type Config.",
+ "(6,1) : error : The property value of type System.String couldn't be converted "
+ + "to System.Single for the property Volume/max.",
+ ],
+ diagnosticMessages
+ );
+ }
+
+ [Fact]
+ public void CleanDiagnostics_InvalidSyntax_DoesNotConsolidateMessages()
+ {
+ var diagnostics = new DiagnosticsBag(
+ [
+ new DiagnosticMessage(
+ DiagnosticMessageKind.Error,
+ new SourceSpan(
+ fileName: string.Empty,
+ start: new TextPosition(offset: 16, line: 0, column: 16),
+ end: new TextPosition(offset: 16, line: 0, column: 16)
+ ),
+ "Expecting `=` after a key instead of !"
+ ),
+ ]
+ );
+
+ var diagnosticMessages = Config.CleanDiagnostics(diagnostics);
+ Assert.Equal(
+ ["(1,17) : error : Expecting `=` after a key instead of !."],
+ diagnosticMessages
+ );
+ }
}
diff --git a/src/TotalMixVC/App.xaml.cs b/src/TotalMixVC/App.xaml.cs
index 57b000c..ef220e9 100644
--- a/src/TotalMixVC/App.xaml.cs
+++ b/src/TotalMixVC/App.xaml.cs
@@ -121,9 +121,9 @@ public bool LoadConfig(bool running = false)
);
}
- foreach (var diagnostic in diagnostics)
+ foreach (var diagnosticMessage in Config.CleanDiagnostics(diagnostics))
{
- message.Append(CultureInfo.InvariantCulture, $"- {diagnostic}\n");
+ message.Append(CultureInfo.InvariantCulture, $"- {diagnosticMessage}\n");
}
message.Append(
diff --git a/src/TotalMixVC/Configuration/Config.cs b/src/TotalMixVC/Configuration/Config.cs
index 20b5b95..ffbb0d1 100644
--- a/src/TotalMixVC/Configuration/Config.cs
+++ b/src/TotalMixVC/Configuration/Config.cs
@@ -1,4 +1,6 @@
using System.Net;
+using System.Text;
+using System.Text.RegularExpressions;
using System.Windows.Media;
using Tomlyn;
using Tomlyn.Syntax;
@@ -9,7 +11,7 @@ namespace TotalMixVC.Configuration;
///
/// Provides all configurable settings for the application along with suitable defaults.
///
-public record Config
+public partial record Config
{
/// Gets configuration related to OSC communication with the device.
public Osc Osc { get; init; } = new Osc();
@@ -75,4 +77,67 @@ public static bool TryFromToml(string text, out Config? config, out DiagnosticsB
return isValid;
}
+
+ ///
+ /// Consolidates diagnostics returned by Tomlyn in an attempt to have one clean message
+ /// for each property issue discovered.
+ ///
+ ///
+ /// The diagnostics bag returned by Tomlyn upon parsing a TOML file with errors or warnings.
+ ///
+ /// A consolidated iterable of formatted diagnostic messages.
+ public static IEnumerable CleanDiagnostics(DiagnosticsBag diagnostics)
+ {
+ var diagnosticsExceptionRegex = DiagnosticsExceptionRegex();
+
+ foreach (var group in diagnostics.GroupBy(diagnostic => diagnostic.Span))
+ {
+ var reason = (string?)null;
+ var span = group.Key;
+
+ foreach (var diagnostic in group)
+ {
+ if (
+ diagnostic.Message.StartsWith(
+ "Unsupported type to convert ",
+ StringComparison.Ordinal
+ )
+ )
+ {
+ continue;
+ }
+
+ var match = diagnosticsExceptionRegex.Match(diagnostic.Message);
+ if (match.Success)
+ {
+ reason = match.Groups[1].Value;
+ continue;
+ }
+
+ var message = new StringBuilder();
+
+ message
+ .Append(span.ToStringSimple())
+ .Append(" : ")
+ .Append(diagnostic.Kind == DiagnosticMessageKind.Warning ? "warning" : "error")
+ .Append(" : ")
+ .Append(diagnostic.Message);
+
+ if (!diagnostic.Message.EndsWith('.'))
+ {
+ message.Append('.');
+ }
+
+ if (reason is not null)
+ {
+ message.Append(' ').Append(reason);
+ }
+
+ yield return message.ToString();
+ }
+ }
+ }
+
+ [GeneratedRegex(@"Exception while trying to convert \S+ to type \S+. Reason: (.*)")]
+ private static partial Regex DiagnosticsExceptionRegex();
}
diff --git a/src/TotalMixVC/Configuration/Models/VolumeFineIncrementDecibels.cs b/src/TotalMixVC/Configuration/Models/VolumeFineIncrementDecibels.cs
index 64fe517..af74096 100644
--- a/src/TotalMixVC/Configuration/Models/VolumeFineIncrementDecibels.cs
+++ b/src/TotalMixVC/Configuration/Models/VolumeFineIncrementDecibels.cs
@@ -1,4 +1,4 @@
-namespace TotalMixVC.Configuration.Models;
+namespace TotalMixVC.Configuration.Models;
///
/// Provides the increment to use when finely increasing or decreasing the volume in decibels.
@@ -17,7 +17,7 @@ public VolumeFineIncrementDecibels(float value)
}
/// Gets or sets the volume decibel value.
- ///
+ ///
/// The fine increment specified is not in the supported range.
///
public float Value
@@ -27,10 +27,9 @@ public float Value
{
if (value <= 0.0 || value > 3.0 || value % 0.25f != 0.0f)
{
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "Must be a multiple of 0.25 while being greater than 0 and less than or equal "
- + "to 3.0."
+ throw new InvalidOperationException(
+ "The value must be a multiple of 0.25 while being greater than 0 and less "
+ + "than or equal to 3.0."
);
}
diff --git a/src/TotalMixVC/Configuration/Models/VolumeFineIncrementPercent.cs b/src/TotalMixVC/Configuration/Models/VolumeFineIncrementPercent.cs
index 15c98f1..3470011 100644
--- a/src/TotalMixVC/Configuration/Models/VolumeFineIncrementPercent.cs
+++ b/src/TotalMixVC/Configuration/Models/VolumeFineIncrementPercent.cs
@@ -1,4 +1,4 @@
-namespace TotalMixVC.Configuration.Models;
+namespace TotalMixVC.Configuration.Models;
///
/// Provides the increment to use when finely increasing or decreasing the volume in percent.
@@ -17,7 +17,7 @@ public VolumeFineIncrementPercent(float value)
}
/// Gets or sets the volume percentage value.
- ///
+ ///
/// The fine increment specified is not in the supported range.
///
public float Value
@@ -27,9 +27,8 @@ public float Value
{
if (value is <= 0.0f or > 0.05f)
{
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "Must be greater than 0 and less than or equal to 0.05."
+ throw new InvalidOperationException(
+ "The value must be greater than 0 and less than or equal to 0.05."
);
}
diff --git a/src/TotalMixVC/Configuration/Models/VolumeIncrementDecibels.cs b/src/TotalMixVC/Configuration/Models/VolumeIncrementDecibels.cs
index dd18c4d..6f265a5 100644
--- a/src/TotalMixVC/Configuration/Models/VolumeIncrementDecibels.cs
+++ b/src/TotalMixVC/Configuration/Models/VolumeIncrementDecibels.cs
@@ -1,4 +1,4 @@
-namespace TotalMixVC.Configuration.Models;
+namespace TotalMixVC.Configuration.Models;
///
/// Provides the increment to use when increasing or decreasing the volume in decibels.
@@ -17,7 +17,7 @@ public VolumeIncrementDecibels(float value)
}
/// Gets or sets the volume decibel value.
- ///
+ ///
/// The increment specified is not in the supported range.
///
public float Value
@@ -27,10 +27,9 @@ public float Value
{
if (value <= 0.0 || value > 6.0 || value % 0.5f != 0.0f)
{
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "Must be a multiple of 0.5 while being greater than 0 and less than or equal "
- + "to 6.0."
+ throw new InvalidOperationException(
+ "The value must be a multiple of 0.5 while being greater than 0 and less "
+ + "than or equal to 6.0."
);
}
diff --git a/src/TotalMixVC/Configuration/Models/VolumeIncrementPercent.cs b/src/TotalMixVC/Configuration/Models/VolumeIncrementPercent.cs
index f09d713..e7ede57 100644
--- a/src/TotalMixVC/Configuration/Models/VolumeIncrementPercent.cs
+++ b/src/TotalMixVC/Configuration/Models/VolumeIncrementPercent.cs
@@ -1,4 +1,4 @@
-namespace TotalMixVC.Configuration.Models;
+namespace TotalMixVC.Configuration.Models;
///
/// Provides the increment to use when increasing or decreasing the volume in percent.
@@ -17,7 +17,7 @@ public VolumeIncrementPercent(float value)
}
/// Gets or sets the volume percentage value.
- ///
+ ///
/// The increment specified is not in the supported range.
///
public float Value
@@ -27,9 +27,8 @@ public float Value
{
if (value is <= 0.0f or > 0.10f)
{
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "Must be greater than 0 and less than or equal to 0.1."
+ throw new InvalidOperationException(
+ "The value must be greater than 0 and less than or equal to 0.1."
);
}
diff --git a/src/TotalMixVC/Configuration/Models/VolumeMaxDecibels.cs b/src/TotalMixVC/Configuration/Models/VolumeMaxDecibels.cs
index ff53f3d..407d37c 100644
--- a/src/TotalMixVC/Configuration/Models/VolumeMaxDecibels.cs
+++ b/src/TotalMixVC/Configuration/Models/VolumeMaxDecibels.cs
@@ -1,4 +1,4 @@
-namespace TotalMixVC.Configuration.Models;
+namespace TotalMixVC.Configuration.Models;
///
/// Provides the maximum volume that should be allowed when increasing the volume in decibels.
@@ -17,7 +17,7 @@ public VolumeMaxDecibels(float value)
}
/// Gets or sets the volume decibel value.
- ///
+ ///
/// The max volume specified is not in the supported range.
///
public float Value
@@ -27,10 +27,7 @@ public float Value
{
if (value is > 6.0f)
{
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "Must be less than or equal to 6.0."
- );
+ throw new InvalidOperationException("The value must be less than or equal to 6.0.");
}
_value = value;
diff --git a/src/TotalMixVC/Configuration/Models/VolumeMaxPercent.cs b/src/TotalMixVC/Configuration/Models/VolumeMaxPercent.cs
index 8d8da76..7a9cd77 100644
--- a/src/TotalMixVC/Configuration/Models/VolumeMaxPercent.cs
+++ b/src/TotalMixVC/Configuration/Models/VolumeMaxPercent.cs
@@ -1,4 +1,4 @@
-namespace TotalMixVC.Configuration.Models;
+namespace TotalMixVC.Configuration.Models;
///
/// Provides the maximum volume that should be allowed when increasing the volume in percent.
@@ -17,7 +17,7 @@ public VolumeMaxPercent(float value)
}
/// Gets or sets the volume percentage value.
- ///
+ ///
/// The max volume specified is not in the supported range.
///
public float Value
@@ -27,9 +27,8 @@ public float Value
{
if (value is <= 0.0f or > 1.0f)
{
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "Must be greater than 0 and less than or equal to 1.0."
+ throw new InvalidOperationException(
+ "The value must be greater than 0 and less than or equal to 1.0."
);
}