From 57928157b050069cc3acbef8405ac825f127c8ed Mon Sep 17 00:00:00 2001 From: Ryan Crosby Date: Wed, 18 Oct 2017 15:33:15 +1100 Subject: [PATCH 1/6] Added missing extension methods FormatWith and FormattableWith for custom handler function --- FormatWith/Internal/FormatToken.cs | 2 +- FormatWith/ReplacementResult.cs | 10 +++- FormatWith/StringExtensions.cs | 94 ++++++++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/FormatWith/Internal/FormatToken.cs b/FormatWith/Internal/FormatToken.cs index 3d5fddc..e3c764f 100644 --- a/FormatWith/Internal/FormatToken.cs +++ b/FormatWith/Internal/FormatToken.cs @@ -42,7 +42,7 @@ public string Raw { } /// - /// Gets the token intter text. + /// Gets the token inner text. /// This performs a substring operation and allocates a new string object. /// public string Value { diff --git a/FormatWith/ReplacementResult.cs b/FormatWith/ReplacementResult.cs index 34d3823..5580de5 100644 --- a/FormatWith/ReplacementResult.cs +++ b/FormatWith/ReplacementResult.cs @@ -6,7 +6,13 @@ namespace FormatWith { public struct ReplacementResult { - public bool Success { get; set; } - public object Value { get; set; } + public ReplacementResult(bool success, object value) + { + Success = success; + Value = value; + } + + public bool Success { get; } + public object Value { get; } } } diff --git a/FormatWith/StringExtensions.cs b/FormatWith/StringExtensions.cs index f8e8d23..4a3d8db 100644 --- a/FormatWith/StringExtensions.cs +++ b/FormatWith/StringExtensions.cs @@ -138,6 +138,48 @@ public static string FormatWith( closeBraceChar); } + /// + /// Formats a string, using a handler function to provide the value + /// of each parameter. + /// + /// The format string, containing keys like {foo} + /// A handler function that transforms each parameter into a + /// The formatted string + public static string FormatWith( + string formatString, + Func handler) + { + return FormatWithMethods.FormatWith(formatString, handler); + } + + /// + /// Formats a string, using a handler function to provide the value + /// of each parameter. + /// + /// The format string, containing keys like {foo} + /// A handler function that transforms each parameter into a + /// The behaviour to use when the format string contains a parameter that cannot be replaced by the handler + /// When the is specified, this object is used as a fallback replacement value. + /// The character used to begin parameters + /// The character used to end parameters + /// The formatted string + public static string FormatWith( + string formatString, + Func handler, + MissingKeyBehaviour missingKeyBehaviour = MissingKeyBehaviour.ThrowException, + object fallbackReplacementValue = null, + char openBraceChar = '{', + char closeBraceChar = '}') + { + return FormatWithMethods.FormatWith( + formatString, + handler, + missingKeyBehaviour, + fallbackReplacementValue, + openBraceChar, + closeBraceChar); + } + #endregion #region FormattableWith Overloads @@ -146,7 +188,7 @@ public static string FormatWith( /// /// The format string, containing keys like {foo} /// The object whose properties should be injected in the string - /// A FormattableString representing the string with dictionary keys replaced by (formatted) key values + /// The resultant public static FormattableString FormattableWith(this string formatString, object replacementObject) { // wrap the type object in a wrapper Dictionary class that exposes the properties as dictionary keys via reflection @@ -162,7 +204,7 @@ public static FormattableString FormattableWith(this string formatString, object /// When the is specified, this string is used as a fallback replacement value when the parameter is present in the lookup dictionary. /// The character used to begin parameters /// The character used to end parameters - /// A FormattableString representing the string with dictionary keys replaced by (formatted) key values + /// The resultant public static FormattableString FormattableWith( this string formatString, object replacementObject, @@ -185,7 +227,7 @@ public static FormattableString FormattableWith( /// /// The format string, containing keys like {foo} /// An with keys and values to inject into the string - /// A version of the formatString string with dictionary keys replaced by (formatted) key values + /// The resultant public static FormattableString FormattableWith(this string formatString, IDictionary replacements) { // wrap the IDictionary in a wrapper Dictionary class that casts the values to objects as needed @@ -201,7 +243,7 @@ public static FormattableString FormattableWith(this string formatString, IDicti /// When the is specified, this string is used as a fallback replacement value when the parameter is present in the lookup dictionary. /// The character used to begin parameters /// The character used to end parameters - /// A version of the formatString string with dictionary keys replaced by (formatted) key values + /// The resultant public static FormattableString FormattableWith( this string formatString, IDictionary replacements, @@ -225,7 +267,7 @@ public static FormattableString FormattableWith( /// /// The format string, containing keys like {foo} /// An with keys and values to inject into the string - /// A version of the formatString string with dictionary keys replaced by (formatted) key values + /// The resultant public static FormattableString FormattableWith(this string formatString, IDictionary replacements) { return FormatWithMethods.FormattableWith(formatString, replacements); @@ -240,7 +282,7 @@ public static FormattableString FormattableWith(this string formatString, IDicti /// When the is specified, this string is used as a fallback replacement value when the parameter is present in the lookup dictionary. /// The character used to begin parameters /// The character used to end parameters - /// A version of the formatString string with dictionary keys replaced by (formatted) key values + /// The resultant public static FormattableString FormattableWith( this string formatString, IDictionary replacements, @@ -258,6 +300,46 @@ public static FormattableString FormattableWith( closeBraceChar); } + /// + /// Produces a representing the input format string. + /// + /// The format string, containing keys like {foo} + /// A handler function that transforms each parameter into a + /// The resultant + public static FormattableString FormattableWith( + string formatString, + Func handler) + { + return FormatWithMethods.FormattableWith(formatString, handler); + } + + /// + /// Produces a representing the input format string. + /// + /// The format string, containing keys like {foo} + /// A handler function that transforms each parameter into a + /// The behaviour to use when the format string contains a parameter that cannot be replaced by the handler + /// When the is specified, this object is used as a fallback replacement value. + /// The character used to begin parameters + /// The character used to end parameters + /// The resultant + public static FormattableString FormattableWith( + string formatString, + Func handler, + MissingKeyBehaviour missingKeyBehaviour = MissingKeyBehaviour.ThrowException, + object fallbackReplacementValue = null, + char openBraceChar = '{', + char closeBraceChar = '}') + { + return FormatWithMethods.FormattableWith( + formatString, + handler, + missingKeyBehaviour, + fallbackReplacementValue, + openBraceChar, + closeBraceChar); + } + #endregion /// From 313cb93fdb2f55bd8dc273a394fdb0e9b6a1e410 Mon Sep 17 00:00:00 2001 From: Ryan Crosby Date: Wed, 18 Oct 2017 15:39:55 +1100 Subject: [PATCH 2/6] Bumped version to v2.2.0 --- FormatWith/FormatWith.csproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/FormatWith/FormatWith.csproj b/FormatWith/FormatWith.csproj index 1f9cadf..5e21b1a 100644 --- a/FormatWith/FormatWith.csproj +++ b/FormatWith/FormatWith.csproj @@ -7,7 +7,7 @@ FormatWith FormatWith FormatWith - 2.1.0 + 2.2.0 Ryan Crosby Copyright © Ryan Crosby 2017 named string formatter extension NetStandard 2.0 @@ -23,8 +23,6 @@ true https://github.com/crozone/FormatWith git - - Added a Func parameter handler overload to FormatWith and FormattableWith. -- Improved performance for the object based/anonymous class overloads. -- Internal code cleanup + Added handler overload to FormatWith and FormattableWith. From b383f6d842d009b36cf20eb8168db9111a8ee3a9 Mon Sep 17 00:00:00 2001 From: Ryan Crosby Date: Wed, 18 Oct 2017 15:54:34 +1100 Subject: [PATCH 3/6] Changed to constructor instead of property instantiation --- FormatWith/Internal/FormatWithMethods.cs | 88 ++++++++---------------- 1 file changed, 30 insertions(+), 58 deletions(-) diff --git a/FormatWith/Internal/FormatWithMethods.cs b/FormatWith/Internal/FormatWithMethods.cs index 878f9fe..b908b22 100644 --- a/FormatWith/Internal/FormatWithMethods.cs +++ b/FormatWith/Internal/FormatWithMethods.cs @@ -17,18 +17,13 @@ public static string FormatWith( char openBraceChar = '{', char closeBraceChar = '}') { - return FormatWith(formatString, key => - { - return new ReplacementResult - { - Success = replacements.TryGetValue(key, out string value), - Value = value - }; - }, - missingKeyBehaviour, - fallbackReplacementValue, - openBraceChar, - closeBraceChar); + return FormatWith( + formatString, + key => new ReplacementResult(replacements.TryGetValue(key, out string value), value), + missingKeyBehaviour, + fallbackReplacementValue, + openBraceChar, + closeBraceChar); } public static string FormatWith( @@ -39,18 +34,13 @@ public static string FormatWith( char openBraceChar = '{', char closeBraceChar = '}') { - return FormatWith(formatString, key => - { - return new ReplacementResult - { - Success = replacements.TryGetValue(key, out object value), - Value = value - }; - }, - missingKeyBehaviour, - fallbackReplacementValue, - openBraceChar, - closeBraceChar); + return FormatWith( + formatString, + key => new ReplacementResult(replacements.TryGetValue(key, out object value), value), + missingKeyBehaviour, + fallbackReplacementValue, + openBraceChar, + closeBraceChar); } private static BindingFlags propertyBindingFlags = BindingFlags.Instance | BindingFlags.Public; @@ -96,18 +86,13 @@ public static FormattableString FormattableWith( char openBraceChar = '{', char closeBraceChar = '}') { - return FormattableWith(formatString, key => - { - return new ReplacementResult - { - Success = replacements.TryGetValue(key, out string value), - Value = value - }; - }, - missingKeyBehaviour, - fallbackReplacementValue, - openBraceChar, - closeBraceChar); + return FormattableWith( + formatString, + key => new ReplacementResult(replacements.TryGetValue(key, out string value), value), + missingKeyBehaviour, + fallbackReplacementValue, + openBraceChar, + closeBraceChar); } public static FormattableString FormattableWith( @@ -118,18 +103,13 @@ public static FormattableString FormattableWith( char openBraceChar = '{', char closeBraceChar = '}') { - return FormattableWith(formatString, key => - { - return new ReplacementResult - { - Success = replacements.TryGetValue(key, out object value), - Value = value - }; - }, - missingKeyBehaviour, - fallbackReplacementValue, - openBraceChar, - closeBraceChar); + return FormattableWith( + formatString, + key => new ReplacementResult(replacements.TryGetValue(key, out object value), value), + missingKeyBehaviour, + fallbackReplacementValue, + openBraceChar, + closeBraceChar); } public static FormattableString FormattableWith( @@ -183,19 +163,11 @@ private static ReplacementResult FromReplacementObject(string key, object replac PropertyInfo propertyInfo = replacementObject.GetType().GetProperty(key, propertyBindingFlags); if (propertyInfo == null) { - return new ReplacementResult() - { - Success = false, - Value = null - }; + return new ReplacementResult(false, null); } else { - return new ReplacementResult - { - Success = true, - Value = propertyInfo.GetValue(replacementObject) - }; + return new ReplacementResult(true, propertyInfo.GetValue(replacementObject)); } } } From 3197564986c6bb8ad00900131088f9408d91f8dd Mon Sep 17 00:00:00 2001 From: Ryan Crosby Date: Wed, 18 Oct 2017 16:15:33 +1100 Subject: [PATCH 4/6] Made new extension methods actually extension methods --- FormatWith/StringExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FormatWith/StringExtensions.cs b/FormatWith/StringExtensions.cs index 4a3d8db..3413718 100644 --- a/FormatWith/StringExtensions.cs +++ b/FormatWith/StringExtensions.cs @@ -146,7 +146,7 @@ public static string FormatWith( /// A handler function that transforms each parameter into a /// The formatted string public static string FormatWith( - string formatString, + this string formatString, Func handler) { return FormatWithMethods.FormatWith(formatString, handler); @@ -164,7 +164,7 @@ public static string FormatWith( /// The character used to end parameters /// The formatted string public static string FormatWith( - string formatString, + this string formatString, Func handler, MissingKeyBehaviour missingKeyBehaviour = MissingKeyBehaviour.ThrowException, object fallbackReplacementValue = null, @@ -307,7 +307,7 @@ public static FormattableString FormattableWith( /// A handler function that transforms each parameter into a /// The resultant public static FormattableString FormattableWith( - string formatString, + this string formatString, Func handler) { return FormatWithMethods.FormattableWith(formatString, handler); @@ -324,7 +324,7 @@ public static FormattableString FormattableWith( /// The character used to end parameters /// The resultant public static FormattableString FormattableWith( - string formatString, + this string formatString, Func handler, MissingKeyBehaviour missingKeyBehaviour = MissingKeyBehaviour.ThrowException, object fallbackReplacementValue = null, From f85ed21af56ad9984a2fb5a399f67bd79a561500 Mon Sep 17 00:00:00 2001 From: Ryan Crosby Date: Wed, 18 Oct 2017 16:15:55 +1100 Subject: [PATCH 5/6] Added tests for new extension methods --- FormatWithTests/FormatWithTests.cs | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/FormatWithTests/FormatWithTests.cs b/FormatWithTests/FormatWithTests.cs index 09d1a5c..817db41 100644 --- a/FormatWithTests/FormatWithTests.cs +++ b/FormatWithTests/FormatWithTests.cs @@ -119,6 +119,55 @@ public void TestAsymmetricCustomBraces() Assert.Equal("abcReplacement1{DoesntExist>", replacement); } + [Fact] + public void TestCustomHandler1() + { + string replacement = "Hey, {make this uppercase!} Thanks.".FormatWith( + (parameter) => new ReplacementResult(true, parameter.ToUpper()) + ); + + Assert.Equal("Hey, MAKE THIS UPPERCASE! Thanks.", replacement); + } + + [Fact] + public void TestCustomHandler2() + { + string replacement = ", , .".FormatWith( + (parameter) => + { + int splitIndex = parameter.LastIndexOf(':'); + if (splitIndex < 0) + { + return new ReplacementResult(true, parameter); + } + else + { + string value = parameter.Substring(0, splitIndex); + string modifier = parameter.Length > splitIndex + 1 ? parameter.Substring(splitIndex + 1) : string.Empty; + + switch (modifier) + { + case "uppercase": + return new ReplacementResult(true, value.ToUpper()); + case "lowercase": + return new ReplacementResult(true, value.ToLower()); + case "reverse": + return new ReplacementResult(true, new string(value.Reverse().ToArray())); + default: + return new ReplacementResult(false, null); + } + } + + }, + MissingKeyBehaviour.ReplaceWithFallback, + "Fallback", + '<', + '>' + ); + + Assert.Equal("321FEDcba, ABCDEF123, abcdef123.", replacement); + } + [Fact] public void SpeedTest() { From 87441daf6ebba3c6492f58fca70dc2accd4cb119 Mon Sep 17 00:00:00 2001 From: Ryan Crosby Date: Wed, 18 Oct 2017 16:19:12 +1100 Subject: [PATCH 6/6] Update README.md --- README.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f097b67..af26d6d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # FormatWith -[![NuGet](https://img.shields.io/badge/nuget-2.1.0-green.svg)](https://www.nuget.org/packages/FormatWith/) +[![NuGet](https://img.shields.io/badge/nuget-2.2.0-green.svg)](https://www.nuget.org/packages/FormatWith/) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)]() A set of string extension methods for performing {named} {{parameterized}} string formatting, written for NetStandard 2.0. @@ -78,6 +78,46 @@ output: "abc Replacement1 " The first, second, and third overload of FormattableWith() function much the same way that the FormatWith() overloads do. However, FormattableWith returns a `FormattableString` instead of a `string`. This allows parameters and composite format string to be inspected, and allows a custom formatter to be used if desired. +### Handler overloads + +A custom handler can be passed to both FormatWith() and FormattableWith(). The handler is passed the value of each parameter key, and is responsible for providing a `ReplacementResult` in response. The `ReplacementResult` contains the `Value` which will be substituted, as well as a boolean `Success` parameter indicating whether the replacement was successful. If `Success` is false, the `MissingKeyBehaviour` is followed, as per the other overloads of FormatWith. + +This can allow for some neat tricks, and even complex behaviours. + +Example: + + "{abcDEF123:reverse}, {abcDEF123:uppercase}, {abcDEF123:lowercase}.".FormatWith( + (parameter) => + { + int splitIndex = parameter.LastIndexOf(':'); + if (splitIndex < 0) + { + return new ReplacementResult(true, parameter); + } + else + { + string value = parameter.Substring(0, splitIndex); + string modifier = parameter.Length > splitIndex + 1 ? parameter.Substring(splitIndex + 1) : string.Empty; + + switch (modifier) + { + case "uppercase": + return new ReplacementResult(true, value.ToUpper()); + case "lowercase": + return new ReplacementResult(true, value.ToLower()); + case "reverse": + return new ReplacementResult(true, new string(value.Reverse().ToArray())); + default: + return new ReplacementResult(false, null); + } + } + + }); + +Produces: + +"321FEDcba, ABCDEF123, abcdef123." + ### GetFormatParameters `GetFormatParameters()` can be used to get a list of parameter names out of a format string, which can be used for inspecting a format string before performing other actions on it.