Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/Discord.Net.Core/Entities/Roles/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,94 @@ public static implicit operator Color(uint rawValue)
public static implicit operator uint(Color color)
=> color.RawValue;

/// <summary>
/// Converts the string representation of a color to a Color object.
/// </summary>
/// <param name="rawValue">String to be parsed to a color</param>
/// <param name="colorType">Color format of the string</param>
/// <returns>A Color object with a color value parsed from a string</returns>
/// <exception cref="ArgumentOutOfRangeException">The argument is not a number or of incorrect length</exception>
public static Color Parse(string rawValue, ColorType colorType = ColorType.CssHexColor)
{
if (TryParse(rawValue, out var color, colorType))
return color;

throw new ArgumentOutOfRangeException(nameof(rawValue), "Value must be a number of valid length - matching the specified ColorType");
}

/// <summary>
/// Converts the string representation of a color to a Color object. The return value indicates whether the conversion succeeded.
/// </summary>
/// <param name="rawValue">String to be parsed to a color</param>
/// <param name="color">When this method returns true, contains a Color that represents the parsed string.</param>
/// <param name="colorType">Color format of the string</param>
/// <returns>true if the conversion succeeded; false otherwise.</returns>
public static bool TryParse(string rawValue, out Color color, ColorType colorType = ColorType.CssHexColor)
{
color = new Color();

if (string.IsNullOrWhiteSpace(rawValue))
return false;

rawValue = rawValue.TrimStart('#');

if (rawValue.StartsWith("0x"))
rawValue = rawValue.Substring(2);

if (!uint.TryParse(rawValue, System.Globalization.NumberStyles.HexNumber, null, out var parsedValue))
return false;

uint r;
uint g;
uint b;
uint fullHex;

switch (rawValue.Length, colorType)
{
case (3, ColorType.CssHexColor):
r = parsedValue >> 8;
g = (parsedValue & 0xF0) >> 4;
b = parsedValue & 0xF;

r = (r << 4) | r;
g = (g << 4) | g;
b = (b << 4) | b;

fullHex = (r << 16) | (g << 8) | b;

break;

case (4, ColorType.CssHexColor):
r = (parsedValue & 0xF00) >> 8;
g = (parsedValue & 0xF0) >> 4;
b = parsedValue & 0xF;

r = (r << 4) | r;
g = (g << 4) | g;
b = (b << 4) | b;

fullHex = (r << 16) | (g << 8) | b;

break;

case (6, ColorType.CssHexColor):
color = new Color(parsedValue);
return true;

case (8, ColorType.CssHexColor):
parsedValue &= 0xFFFFFF;
color = new Color(parsedValue);
return true;

default:
return false;
}

color = new Color(fullHex);

return true;
}

public override bool Equals(object obj)
=> obj is Color c && RawValue == c.RawValue;

Expand Down
21 changes: 21 additions & 0 deletions src/Discord.Net.Core/Entities/Roles/ColorType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Discord;

public enum ColorType
{
/// <summary>
/// Color in one of the following formats.
/// <para>
/// #RGB The three-value syntax
/// </para>
/// <para>
/// #RGBA The four-value syntax
/// </para>
/// <para>
/// #RRGGBB The six-value syntax
/// </para>
/// #RRGGBBAA The eight-value syntax
/// </summary>
CssHexColor = 0
}
20 changes: 20 additions & 0 deletions test/Discord.Net.Tests.Unit/ColorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ public void Color_FromRgb_Float_OutOfRange()
Assert.Throws<ArgumentOutOfRangeException>(() => new Color(2.0f, 2.0f, 2.0f));
}
[Fact]
public void Color_FromRgb_String_CssHexColor()
{
Assert.Equal(0xFF0000u, Color.Parse("#F00", ColorType.CssHexColor).RawValue);
Assert.Equal(0x22BB44u, Color.Parse("#2B4", ColorType.CssHexColor).RawValue);
Assert.Equal(0xAABBAAu, Color.Parse("FABA", ColorType.CssHexColor).RawValue);
Assert.Equal(0x00F672u, Color.Parse("00F672", ColorType.CssHexColor).RawValue);
Assert.Equal(0x257777u, Color.Parse("0xFF257777", ColorType.CssHexColor).RawValue);
}
[Fact]
public void Color_FromRgb_String_Invalid()
{
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse(" ", ColorType.CssHexColor));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse(null, ColorType.CssHexColor));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("#F"));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("F0", ColorType.CssHexColor));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("FF000"));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("FF00000"));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("FF0000000"));
}
[Fact]
public void Color_Red()
{
Assert.Equal(0xAF, new Color(0xAF1390).R);
Expand Down