diff --git a/FormatWith/Internal/FormatHelpers.cs b/FormatWith/Internal/FormatHelpers.cs
index ed55fb6..2d8e72d 100644
--- a/FormatWith/Internal/FormatHelpers.cs
+++ b/FormatWith/Internal/FormatHelpers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -43,29 +43,22 @@ public static string ProcessTokens(
{
// token is a parameter token
// perform parameter logic now.
- var tokenKey = thisToken.Value;
- string format = null;
- var separatorIdx = tokenKey.IndexOf(":", StringComparison.Ordinal);
- if (separatorIdx > -1)
- {
- tokenKey = thisToken.Value.Substring(0, separatorIdx);
- format = thisToken.Value.Substring(separatorIdx + 1);
- }
+ TokenInformation tokenInfo = new TokenInformation(thisToken.Value);
// append the replacement for this parameter
- ReplacementResult replacementResult = handler(tokenKey, format);
-
+ ReplacementResult replacementResult = handler(tokenInfo.TokenKey, tokenInfo.Format);
+
if (replacementResult.Success)
{
// the key exists, add the replacement value
// this does nothing if replacement value is null
- if (string.IsNullOrWhiteSpace(format))
+ if (string.IsNullOrWhiteSpace(tokenInfo.FormatString))
{
resultBuilder.Append(replacementResult.Value);
}
else
{
- resultBuilder.AppendFormat("{0:" + format + "}", replacementResult.Value);
+ resultBuilder.AppendFormat("{0" + tokenInfo.FormatString + "}", replacementResult.Value);
}
}
else
@@ -133,26 +126,19 @@ public static FormattableString ProcessTokensIntoFormattableString(
{
// token is a parameter token
// perform parameter logic now.
- var tokenKey = thisToken.Value;
- string format = null;
- var separatorIdx = tokenKey.IndexOf(":", StringComparison.Ordinal);
- if (separatorIdx > -1)
- {
- tokenKey = thisToken.Value.Substring(0, separatorIdx);
- format = thisToken.Value.Substring(separatorIdx + 1);
- }
+ TokenInformation tokenInfo = new TokenInformation(thisToken.Value);
// append the replacement for this parameter
- ReplacementResult replacementResult = handler(tokenKey);
+ ReplacementResult replacementResult = handler(tokenInfo.TokenKey);
string IndexAndFormat()
{
- if (string.IsNullOrWhiteSpace(format))
+ if (string.IsNullOrWhiteSpace(tokenInfo.FormatString))
{
return "{" + placeholderIndex + "}";
}
- return "{" + placeholderIndex + ":" + format + "}";
+ return "{" + placeholderIndex + tokenInfo.FormatString + "}";
}
// append the replacement for this parameter
diff --git a/FormatWith/TokenInformation.cs b/FormatWith/TokenInformation.cs
new file mode 100644
index 0000000..b02a539
--- /dev/null
+++ b/FormatWith/TokenInformation.cs
@@ -0,0 +1,192 @@
+namespace FormatWith
+{
+
+ ///
+ /// Encapsulates the token-parsing logic and values, ensuring consistent behavior and allowing them to be passed to handlers more concisely.
+ ///
+ public class TokenInformation
+ {
+
+ #region Static & Const
+
+ /////
+ ///// A regular expression to parse the token
+ /////
+ //private static readonly System.Text.RegularExpressions.Regex _parseRegEx = new System.Text.RegularExpressions.Regex(@"(?([^,;:\s]+))(,(?-?\d+))?(:(?.*))?", System.Text.RegularExpressions.RegexOptions.Compiled);
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// The raw token parsed.
+ ///
+ public string RawToken { get; private set; }
+
+ ///
+ /// The token key.
+ ///
+ public string TokenKey { get; private set; }
+
+ ///
+ /// The alignment indicator
+ ///
+ public string Alignment { get; private set; }
+
+ ///
+ /// The base format string. (no alignment)
+ ///
+ public string Format { get; private set; }
+
+ private string _formatString = null;
+ ///
+ /// The full composite formatting string. [,align][:format]
+ ///
+ public string FormatString
+ {
+ get
+ {
+ if (null == this._formatString)
+ {
+ this._formatString = string.Concat((!string.IsNullOrEmpty(this.Alignment) ? $",{this.Alignment}" : string.Empty), (!string.IsNullOrEmpty(this.Format) ? $":{this.Format}" : string.Empty));
+ }
+ return (this._formatString);
+ }
+ }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Parses token information from a raw token string value.
+ ///
+ /// The raw token string.
+ /// IndexOf/Substring version of parser based on existing code
+ public TokenInformation(string rawToken)
+ {
+ this.RawToken = rawToken;
+ this.Format = null;
+ this.Alignment = null;
+
+ // Parse out the format
+ var separatorIdx = rawToken.IndexOf(":", System.StringComparison.Ordinal);
+ if (separatorIdx > -1)
+ {
+ this.Format = rawToken.Substring(separatorIdx + 1);
+ rawToken = rawToken.Substring(0, separatorIdx);
+ }
+
+ // Parse out alignment
+ var alignmentIdx = rawToken.LastIndexOf(",", System.StringComparison.Ordinal);
+ if (alignmentIdx > -1)
+ {
+ this.Alignment = rawToken.Substring(alignmentIdx + 1);
+ rawToken = rawToken.Substring(0, alignmentIdx);
+ }
+
+ // Grab the token key
+ this.TokenKey = rawToken;
+ }
+
+ /////
+ ///// Parses token information from a raw token string value.
+ /////
+ ///// The raw token string.
+ ///// RegEx version of parser, for performance comparison, and in case someone else knows how to optimize it more
+ //public TokenInformation(string rawToken, bool ignoreThisParameterItIsHereForPerformanceComparisons)
+ //{
+ // this.RawToken = rawToken;
+ // this.Alignment = null;
+ // this.Format = null;
+ //
+ // System.Text.RegularExpressions.Match results = _parseRegEx.Match(rawToken);
+ // if(results.Success)
+ // {
+ // this.TokenKey = results.Groups["token"].Value;
+ // this.Alignment = results.Groups["alignment"].Value;
+ // this.Format = results.Groups["format"].Value;
+ // }
+ //}
+
+ #endregion
+
+ #region Methods
+
+ private static string BuildString(string tokenKey, string alignment, string format)
+ {
+ string rawToken = string.Concat(tokenKey, (!string.IsNullOrEmpty(alignment) ? $",{alignment}" : string.Empty), (!string.IsNullOrEmpty(format) ? $":{format}" : string.Empty));
+ return (rawToken);
+ }
+
+ ///
+ /// Builds a raw token string given the desired tokenKey and format
+ ///
+ /// The key value for the token.
+ /// The left- or right-padding indicator desired.
+ /// The format string.
+ ///
+ public static string BuildString(string tokenKey, int? alignment, string format)
+ {
+ return (TokenInformation.BuildString(tokenKey, alignment.HasValue ? $"{alignment}" : string.Empty, format));
+ }
+
+ ///
+ /// Builds a raw token string given the desired tokenKey and format
+ ///
+ /// The key value for the token.
+ /// The format string.
+ ///
+ public static string BuildString(string tokenKey, string format)
+ {
+ return (TokenInformation.BuildString(tokenKey, (int?)null, format));
+ }
+
+ ///
+ /// Builds a raw token string given the desired tokenKey and format
+ ///
+ /// The key value for the token.
+ /// The left- or right-padding indicator desired.
+ ///
+ public static string BuildString(string tokenKey, int? alignment)
+ {
+ return (TokenInformation.BuildString(tokenKey, alignment, null));
+ }
+
+ ///
+ /// Builds a raw token string given the desired tokenKey and format
+ ///
+ /// The key value for the token.
+ ///
+ public static string BuildString(string tokenKey)
+ {
+ return(TokenInformation.BuildString(tokenKey, (int?)null, null));
+ }
+
+ ///
+ /// Builds a TokenInfo object given the desired tokenKey and format
+ ///
+ /// The key value for the token.
+ /// The left- or right-padding indicator desired.
+ /// The format string.
+ ///
+ public static TokenInformation Build(string tokenKey, int? alignment, string format)
+ {
+ return (new TokenInformation(TokenInformation.BuildString(tokenKey, alignment, format)));
+ }
+
+ ///
+ /// Builds a TokenInfo object given the desired tokenKey and format
+ ///
+ /// The key value for the token.
+ /// The format string.
+ ///
+ public static TokenInformation Build(string tokenKey, string format)
+ {
+ return (TokenInformation.Build(tokenKey, null, format));
+ }
+
+ #endregion
+
+ } // end class
+} // end namespace
diff --git a/FormatWithTests/TestStrings.cs b/FormatWithTests/TestStrings.cs
index a16ce7f..ce76ce6 100644
--- a/FormatWithTests/TestStrings.cs
+++ b/FormatWithTests/TestStrings.cs
@@ -28,7 +28,7 @@ public static class TestStrings
public static readonly string TestFormat6 = "abc{Replacement1:upper}";
public static readonly string TestFormat6Composite = "abc{0:upper}";
public static readonly string TestFormat6Solution = $"abc{Replacement1.ToUpper()}";
-
+
public static readonly string TestFormat7 = "Today is {Today:YYYYMMDD HH:mm}";
public static readonly string TestFormat7Composite = "Today is {0:YYYYMMDD HH:mm}";
public static readonly DateTime TestFormat7Date = new DateTime(2018, 10, 30, 17, 25, 0);
diff --git a/FormatWithTests/TokenInformationTests.cs b/FormatWithTests/TokenInformationTests.cs
new file mode 100644
index 0000000..9f8aa96
--- /dev/null
+++ b/FormatWithTests/TokenInformationTests.cs
@@ -0,0 +1,162 @@
+using Xunit;
+using FormatWith;
+using Xunit.Sdk;
+
+namespace FormatWithTests
+{
+ public class TokenInformationTests
+ {
+
+ #region Test Values
+
+ private static string testTokenKey = "token";
+ private static int? testLeftPad = 15;
+ private static int? testRightPad = -15;
+ private static string testFormat = "yyyy-MM-dd";
+ private static System.DateTime testDate = new System.DateTime(2024, 8, 29);
+
+ public class Parse_TestData : TheoryData
+ {
+ public Parse_TestData()
+ {
+ }
+ };
+
+ #endregion
+
+ #region BuildString
+
+ [Fact]
+ public void BuildString_TokenOnly()
+ {
+ string expected = "token";
+
+ string result = TokenInformation.BuildString(testTokenKey);
+ Assert.Equal(result, expected);
+ }
+
+ [Fact]
+ public void BuildString_TokenAndLeftPad()
+ {
+ string expected = "token,15";
+
+ string result = TokenInformation.BuildString(testTokenKey, testLeftPad);
+ Assert.Equal(result, expected);
+ }
+
+ [Fact]
+ public void BuildString_TokenAndRightPad()
+ {
+ string expected = "token,-15";
+
+ string result = TokenInformation.BuildString(testTokenKey, testRightPad);
+ Assert.Equal(result, expected);
+ }
+
+ [Fact]
+ public void BuildString_TokenAndFormat()
+ {
+ string expected = "token:yyyy-MM-dd";
+
+ string result = TokenInformation.BuildString(testTokenKey, testFormat);
+ Assert.Equal(result, expected);
+ }
+
+ [Fact]
+ public void BuildString_TokenLeftPadAndFormat()
+ {
+ string expected = "token,15:yyyy-MM-dd";
+
+ string result = TokenInformation.BuildString(testTokenKey, testLeftPad, testFormat);
+ Assert.Equal(result, expected);
+ }
+
+ [Fact]
+ public void BuildString_TokenAndRightPadAndFormat()
+ {
+ string expected = "token,-15:yyyy-MM-dd";
+
+ string result = TokenInformation.BuildString(testTokenKey, testRightPad, testFormat);
+ Assert.Equal(result, expected);
+ }
+
+ #endregion
+
+ #region Parse
+
+ [Theory]
+ [InlineData("token", "token", null, null, "")]
+ [InlineData("token,15", "token", "15", null, ",15")]
+ [InlineData("token,-15", "token", "-15", null, ",-15")]
+ [InlineData("token:yyyy-MM-dd", "token", null, "yyyy-MM-dd", ":yyyy-MM-dd")]
+ [InlineData("token,15:yyyy-MM-dd", "token", "15", "yyyy-MM-dd", ",15:yyyy-MM-dd")]
+ [InlineData("token,-15:yyyy-MM-dd", "token", "-15", "yyyy-MM-dd", ",-15:yyyy-MM-dd")]
+ public void Parse(string rawToken, string expectedTokenKey, string expectedAlignment, string expectedFormat, string expectedFormatString)
+ {
+ TokenInformation result = new TokenInformation(rawToken);
+
+ Assert.Equal(result.RawToken, rawToken);
+ Assert.Equal(result.TokenKey, expectedTokenKey);
+ if (null == expectedAlignment)
+ {
+ Assert.Null(result.Alignment);
+ }
+ else
+ {
+ Assert.Equal(result.Alignment, expectedAlignment);
+ }
+ if (null == expectedFormat)
+ {
+ Assert.Null(result.Format);
+ }
+ else
+ {
+ Assert.Equal(result.Format, expectedFormat);
+ }
+ Assert.Equal(result.FormatString, expectedFormatString);
+ }
+
+ #endregion
+
+ #region FormatWith
+
+ [Fact]
+ public void FormatWith_DateTest_FormatOnly()
+ {
+ System.Collections.Generic.Dictionary replacmentValues = new System.Collections.Generic.Dictionary();
+ replacmentValues.Add(testTokenKey, testDate);
+
+ string expectedResult = "'2024-08-29'";
+ string result = "'{token:yyyy-MM-dd}'".FormatWith(replacmentValues);
+
+ Assert.Equal(result, expectedResult);
+ }
+
+ [Fact]
+ public void FormatWith_DateTest_FormatAndLeftPad()
+ {
+ System.Collections.Generic.Dictionary replacmentValues = new System.Collections.Generic.Dictionary();
+ replacmentValues.Add(testTokenKey, testDate);
+
+ string expectedResult = "' 2024-08-29'";
+ string result = "'{token,15:yyyy-MM-dd}'".FormatWith(replacmentValues);
+
+ Assert.Equal(result, expectedResult);
+ }
+
+ [Fact]
+ public void FormatWith_DateTest_FormatAndRightPad()
+ {
+ System.Collections.Generic.Dictionary replacmentValues = new System.Collections.Generic.Dictionary();
+ replacmentValues.Add(testTokenKey, testDate);
+
+ string expectedResult = "'2024-08-29 '";
+ string result = "'{token,-15:yyyy-MM-dd}'".FormatWith(replacmentValues);
+
+ Assert.Equal(result, expectedResult);
+ }
+
+ #endregion
+
+ } // end class
+} // end namespace