From 717931f11c9cd5e7c090d8bc4afc0620ec0db043 Mon Sep 17 00:00:00 2001 From: Patrik Svensson Date: Tue, 11 Aug 2020 07:54:01 +0200 Subject: [PATCH] Add support for RGB colors Closes #34 --- src/Spectre.Console.Tests/Unit/StyleTests.cs | 29 +++++++++++ .../Internal/Text/StyleParser.cs | 49 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/Spectre.Console.Tests/Unit/StyleTests.cs b/src/Spectre.Console.Tests/Unit/StyleTests.cs index 3280373..2ea7016 100644 --- a/src/Spectre.Console.Tests/Unit/StyleTests.cs +++ b/src/Spectre.Console.Tests/Unit/StyleTests.cs @@ -153,6 +153,35 @@ namespace Spectre.Console.Tests.Unit result.ShouldNotBeNull(); result.Message.ShouldBe(expected); } + + [Theory] + [InlineData("rgb(255,0,0) on rgb(0,0,255)")] + public void Should_Parse_Rgb_Colors_Correctly(string style) + { + // Given, When + var result = Style.Parse(style); + + // Then + result.Foreground.ShouldBe(Color.Red); + result.Background.ShouldBe(Color.Blue); + } + + [Theory] + [InlineData("rgb()", "Invalid RGB color 'rgb()'.")] + [InlineData("rgb(", "Invalid RGB color 'rgb('.")] + [InlineData("rgb(255)", "Invalid RGB color 'rgb(255)'.")] + [InlineData("rgb(255,255)", "Invalid RGB color 'rgb(255,255)'.")] + [InlineData("rgb(255,255,255", "Invalid RGB color 'rgb(255,255,255'.")] + [InlineData("rgb(A,B,C)", "Invalid RGB color 'rgb(A,B,C)'. Input string was not in a correct format.")] + public void Should_Return_Error_If_Rgb_Color_Is_Invalid(string style, string expected) + { + // Given, When + var result = Record.Exception(() => Style.Parse(style)); + + // Then + result.ShouldNotBeNull(); + result.Message.ShouldBe(expected); + } } public sealed class TheTryParseMethod diff --git a/src/Spectre.Console/Internal/Text/StyleParser.cs b/src/Spectre.Console/Internal/Text/StyleParser.cs index d432e91..1365c48 100644 --- a/src/Spectre.Console/Internal/Text/StyleParser.cs +++ b/src/Spectre.Console/Internal/Text/StyleParser.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; namespace Spectre.Console.Internal { @@ -65,6 +67,14 @@ namespace Spectre.Console.Internal return null; } } + else if (part.StartsWith("rgb", StringComparison.OrdinalIgnoreCase)) + { + color = ParseRgbColor(part, out error); + if (!string.IsNullOrWhiteSpace(error)) + { + return null; + } + } else { error = !foreground @@ -102,6 +112,7 @@ namespace Spectre.Console.Internal return new Style(effectiveForeground, effectiveBackground, effectiveDecoration); } + [SuppressMessage("Design", "CA1031:Do not catch general exception types")] private static Color? ParseHexColor(string hex, out string error) { error = null; @@ -138,5 +149,43 @@ namespace Spectre.Console.Internal error = $"Invalid hex color '#{hex}'."; return null; } + + [SuppressMessage("Design", "CA1031:Do not catch general exception types")] + private static Color? ParseRgbColor(string rgb, out string error) + { + try + { + error = null; + var normalized = rgb ?? string.Empty; + if (normalized.Length >= 3) + { + // Trim parenthesises + normalized = normalized.Substring(3).Trim(); + + if (normalized.StartsWith("(", StringComparison.OrdinalIgnoreCase) && + normalized.EndsWith(")", StringComparison.OrdinalIgnoreCase)) + { + normalized = normalized.Trim('(').Trim(')'); + + var parts = normalized.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 3) + { + return new Color( + (byte)Convert.ToInt32(parts[0], CultureInfo.InvariantCulture), + (byte)Convert.ToInt32(parts[1], CultureInfo.InvariantCulture), + (byte)Convert.ToInt32(parts[2], CultureInfo.InvariantCulture)); + } + } + } + } + catch (Exception ex) + { + error = $"Invalid RGB color '{rgb}'. {ex.Message}"; + return null; + } + + error = $"Invalid RGB color '{rgb}'."; + return null; + } } }