Skip to content

Commit

Permalink
Obsolete elements, fix for Parser (axuno#246)
Browse files Browse the repository at this point in the history
* All obsolete element usage creates a compile time error
* A single escape character at the end of the input string will now throw an ArgumentException
* Updates for CHANGES.md
  • Loading branch information
axunonb authored Feb 15, 2022
1 parent b60b119 commit 377656f
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 27 deletions.
18 changes: 10 additions & 8 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Add unicode escape characters like `"\u1234"`. Thanks to [@karljj1](https://gith

The mode can be set with `SmartSettings.StringFormatCompatibility`. By default, `SmartSettings.StringFormatCompatibility` is `false`. ([#173](https://github.com/axuno/SmartFormat/pull/173), [#175](https://github.com/axuno/SmartFormat/pull/175))

Reasoning: The distinction was necessary because of syntax conflicts between SmartFormat extensions and `string.Format`. It brings a more concise and clear set of formatting rules and full `string.Format` compatibility even in "edge cases".

**a) *Smart.Format* features mode**
* Brings the full set of features implemented in *Smart.Format*
* Curly braces are escaped the *Smart.Format* way with `\{` and `\}`.
Expand Down Expand Up @@ -369,17 +371,17 @@ SmartFormat is not a fully-fledged HTML parser. If this is required, use [AngleS
SmartFormat makes heavy use of caching and object pooling for expensive operations, which both require `static` containers.

a) Instantiating `SmartFormatter`s from different threads:

`SmartSettings.IsThreadSafeMode=true` **must** be set, so that thread safe containers are used. This brings an inherent performance penalty.
#### a) Instantiating `SmartFormatter`s from different threads:
With `SmartSettings.IsThreadSafeMode=true` **must** be set, so that thread safe containers are used. This brings an inherent performance penalty.

**Note:** The simplified `Smart.Format(...)` API overloads use a static `SmartFormatter` instance which is **not** thread safe. Call `Smart.CreateDefaultSmartFormat()` to create a default `Formatter`.
**Note:** The simplified `Smart.Format(...)` API overloads use a static `SmartFormatter` instance which is **not** thread safe. Call `Smart.CreateDefaultSmartFormat()` to create a default `SmartFormatter`.

a) Instantiating `SmartFormatter`s from a single thread:
#### b) Instantiating `SmartFormatter`s from a single thread:

`SmartSettings.IsThreadSafeMode=false` **should** be set for avoiding the multithreading overhead and thus for best performance.
With `SmartSettings.IsThreadSafeMode=false` **should** be set for avoiding the multithreading overhead and thus for best performance.

The simplified `Smart.Format(...)` API overloads are allowed here.
The static `Smart.Format(...)` API overloads are allowed here.

<a id="ObjectPooling"></a>
### 22. How to benefit from object pooling ([#229](https://github.com/axuno/SmartFormat/pull/229))
Expand Down Expand Up @@ -451,7 +453,7 @@ SmartFormat is the core package. It comes with the most frequently used extensio

> **Breaking change:**
>
> Note that only extensions marked (✔️) are included when calling `Smart.CreateDefaultFormatter(...)`. These default extensions differ from previous versions.
> Note that only extensions marked (✔️) are included when calling `Smart.CreateDefaultFormatter(...)` amd also when using `Smart.Format(...)`. These default extensions differ from previously included extensions.
Some extensions (like `PersistentVariablesSource` and `TemplateFormatter`) require configuration to be useful.

Expand Down
22 changes: 22 additions & 0 deletions src/SmartFormat.Tests/Core/LiteralTextTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using NUnit.Framework;
using SmartFormat.Core.Settings;
using SmartFormat.Extensions;

namespace SmartFormat.Tests.Core
{
Expand Down Expand Up @@ -71,5 +72,26 @@ public void ConvertCharacterLiteralsToUnicodeWithListFormatter()

Assert.AreEqual("one\ntwo\nand three", result);
}

[Test, Description("Illegal escape sequence should always throw")]
public void IllegalEscapeSequenceThrowsException()
{
var smart = new SmartFormatter().AddExtensions(new DefaultSource()).AddExtensions(new DefaultFormatter());
Assert.That(code: () => {
smart.Format(@"\z Illegal escape sequence at the beginning of the text");
},Throws.ArgumentException.And.Message.Contains("escape sequence"));

Assert.That(code: () => {
smart.Format(@"Illegal escape sequence \z somewhere in text");
},Throws.ArgumentException.And.Message.Contains("escape sequence"));

Assert.That(code: () => {
smart.Format(@"Illegal escape sequence at end of line = \z");
}, Throws.ArgumentException.And.Message.Contains("escape sequence"));

Assert.That(code: () => {
smart.Format(@"Illegal escape sequence starts at end of line = \");
},Throws.ArgumentException.And.Message.Contains("escape sequence"));
}
}
}
9 changes: 4 additions & 5 deletions src/SmartFormat.Tests/Core/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private static Parser GetRegularParser(SmartSettings? settings = null)
[TestCase(" aaa {bbb.ccc: ddd {eee} fff } ggg ")]
[TestCase("{aaa} {bbb}")]
[TestCase("{}")]
[TestCase("{a:{b:{c:{d} } } }")]
[TestCase("{a:{b:{c:{d}}}}")]
[TestCase("{a}")]
[TestCase(" aaa {bbb_bbb.CCC} ddd ")]
public void Basic_Parser_Test(string format)
Expand Down Expand Up @@ -430,7 +430,6 @@ public void StringFormat_Escaping_In_Literal()
public void Nested_format_with_literal_escaping()
{
var parser = GetRegularParser();
// necessary because of the consecutive }}}, which would otherwise be escaped as }} and lead to "missing brace" exception:
var placeholders = parser.ParseFormat("{c1:{c2:{c3}}}");

var c1 = (Placeholder) placeholders.Items[0];
Expand Down Expand Up @@ -547,14 +546,14 @@ public void Selector_With_Nullable_Operator_Character(string formatString)
// Group Sel_1 is empty
Assert.That(placeholder.Selectors[1].ToString(), Is.EqualTo(reMatches[0].Groups["Sel_2"].Value));
// Concatenate because of regex simplification for 2 selectors
Assert.That(placeholder.Selectors[1].Operator.ToString(), Is.EqualTo(reMatches[0].Groups["Sel_1_Op"].Value + reMatches[0].Groups["Sel_2_Op"].Value));
Assert.That(placeholder.Selectors[1].Operator, Is.EqualTo(reMatches[0].Groups["Sel_1_Op"].Value + reMatches[0].Groups["Sel_2_Op"].Value));
}
else
{
Assert.That(placeholder.Selectors[0].ToString(), Is.EqualTo(reMatches[0].Groups["Sel_0"].Value));
Assert.That(placeholder.Selectors[1].Operator.ToString(), Is.EqualTo(reMatches[0].Groups["Sel_1_Op"].Value));
Assert.That(placeholder.Selectors[1].Operator, Is.EqualTo(reMatches[0].Groups["Sel_1_Op"].Value));
Assert.That(placeholder.Selectors[1].ToString(), Is.EqualTo(reMatches[0].Groups["Sel_1"].Value));
Assert.That(placeholder.Selectors[1].Operator.ToString(), Is.EqualTo(reMatches[0].Groups["Sel_1_Op"].Value));
Assert.That(placeholder.Selectors[1].Operator, Is.EqualTo(reMatches[0].Groups["Sel_1_Op"].Value));
Assert.That(placeholder.Selectors[2].ToString(), Is.EqualTo(reMatches[0].Groups["Sel_2"].Value));
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/SmartFormat.Tests/Core/StringFormatCompatibilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,13 @@ public void FormatterName_And_Options_Should_Be_Ignored2(string format, DateTime
var formatter = Smart.CreateDefaultSmartFormat(new SmartSettings {StringFormatCompatibility = true});
Assert.That(formatter.Format(format, arg0), Is.EqualTo(string.Format(format, arg0)));
}

[Test]
public void Escaped_Curly_Braces_At_Begin_And_End_Should_Work()
{
var formatter = Smart.CreateDefaultSmartFormat(new SmartSettings {StringFormatCompatibility = true});
var result = formatter.Format("{{{0}}}", 99999);
Assert.That(result, Is.EqualTo($"{{{99999}}}"));
}
}
}
}
1 change: 0 additions & 1 deletion src/SmartFormat.Tests/Extensions/ListFormatterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ public void List_of_anonymous_types_and_enumerables()
Parser = new ParserSettings {ErrorAction = ParseErrorAction.ThrowError}
});

// Note: it's faster to add the named formatter, than finding it implicitly by "trial and error".
var result = smart.Format("{0:list:{Name}|, |, and }", new object[] { data }); // Person A, Person B, and Person C
Assert.AreEqual("Person A, Person B, and Person C", result);
result = smart.Format("{0:list:{Name}|, |, and }", model.Persons); // Person A, and Person C
Expand Down
10 changes: 6 additions & 4 deletions src/SmartFormat/Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public Parser(SmartSettings? smartSettings = null)
/// <summary>
/// Includes a-z and A-Z in the list of allowed selector chars.
/// </summary>
[Obsolete("Alphanumeric selectors are always enabled")]
[Obsolete("Alphanumeric selectors are always enabled", true)]
public void AddAlphanumericSelectors()
{
// Do nothing - this is the standard behavior
Expand All @@ -96,7 +96,7 @@ public void AddAlphanumericSelectors()
/// Adds specific characters to the allowed selector chars.
/// </summary>
/// <param name="chars"></param>
[Obsolete("Use 'Settings.Parser.AddCustomSelectorChars' instead.")]
[Obsolete("Use 'Settings.Parser.AddCustomSelectorChars' instead.", true)]
public void AddAdditionalSelectorChars(string chars)
{
_parserSettings.AddCustomSelectorChars(chars.ToCharArray());
Expand All @@ -108,7 +108,7 @@ public void AddAdditionalSelectorChars(string chars)
/// that splits the selectors.
/// </summary>
/// <param name="chars"></param>
[Obsolete("Use 'Settings.Parser.AddCustomOperatorChars' instead.")]
[Obsolete("Use 'Settings.Parser.AddCustomOperatorChars' instead.", true)]
public void AddOperators(string chars)
{
_parserSettings.AddCustomOperatorChars(chars.ToCharArray());
Expand Down Expand Up @@ -433,9 +433,11 @@ private void ParseAlternativeEscaping()

// See what is the next character
var indexNextChar = _index.SafeAdd(_index.Current, 1);
if (indexNextChar >= _inputFormat.Length)
throw new ArgumentException($"Unrecognized escape sequence at the end of the literal");

// **** Alternative brace escaping with { or } following the escape character ****
if (indexNextChar < _inputFormat.Length && (_inputFormat[indexNextChar] == _parserSettings.PlaceholderBeginChar || _inputFormat[indexNextChar] == _parserSettings.PlaceholderEndChar))
if (_inputFormat[indexNextChar] == _parserSettings.PlaceholderBeginChar || _inputFormat[indexNextChar] == _parserSettings.PlaceholderEndChar)
{
// Finish the last text item:
if (_index.Current != _index.LastEnd) _resultFormat.Items.Add(LiteralTextPool.Instance.Get().Initialize(Settings, _resultFormat, _inputFormat, _index.LastEnd, _index.Current));
Expand Down
4 changes: 2 additions & 2 deletions src/SmartFormat/Core/Settings/ErrorAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace SmartFormat.Core.Settings
/// <summary>
/// Determines how format errors are handled.
/// </summary>
[Obsolete("Use 'ParseErrorAction' or 'FormatErrorAction' instead.", false)]
[Obsolete("Use 'ParseErrorAction' or 'FormatErrorAction' instead.", true)]
public enum ErrorAction
{
/// <summary>Throws an exception. This is only recommended for debugging, so that formatting errors can be easily found.</summary>
Expand All @@ -25,4 +25,4 @@ public enum ErrorAction
/// <summary>Leaves invalid tokens unmodified in the text.</summary>
MaintainTokens
}
}
}
8 changes: 4 additions & 4 deletions src/SmartFormat/Core/Settings/SmartSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public SmartSettings()
/// Gets the <see cref="ErrorAction" /> to apply for the <see cref="SmartFormatter" />.
/// The default is <see cref="ErrorAction.ThrowError"/>.
/// </summary>
[Obsolete("Use 'SmartSettings.Formatter.ErrorAction' instead.", false)]
[Obsolete("Use 'SmartSettings.Formatter.ErrorAction' instead.", true)]
public ErrorAction FormatErrorAction
{
get => (ErrorAction) Formatter.ErrorAction;
Expand All @@ -56,7 +56,7 @@ public ErrorAction FormatErrorAction
/// Gets the <see cref="ErrorAction" /> to apply for the <see cref="SmartFormat.Core.Parsing.Parser" />.
/// The default is <see cref="ErrorAction.ThrowError"/>.
/// </summary>
[Obsolete("Use 'SmartSettings.Parser.ErrorAction' instead.", false)]
[Obsolete("Use 'SmartSettings.Parser.ErrorAction' instead.", true)]
public ErrorAction ParseErrorAction
{
get => (ErrorAction) Parser.ErrorAction;
Expand All @@ -76,7 +76,7 @@ public ErrorAction ParseErrorAction
/// If false, character string literals are not converted, just like with this string.Format:
/// string.Format(@"\t") will return the 2 characters "\" and "t"
/// </summary>
[Obsolete("Use SmartSettings.Parser.ConvertCharacterStringLiterals instead", false)]
[Obsolete("Use SmartSettings.Parser.ConvertCharacterStringLiterals instead", true)]
public bool ConvertCharacterStringLiterals
{
get => Parser.ConvertCharacterStringLiterals;
Expand Down Expand Up @@ -128,4 +128,4 @@ public StringComparison GetCaseSensitivityComparison()
/// </summary>
public PoolSettings Pooling { get; set; }
}
}
}
4 changes: 2 additions & 2 deletions src/SmartFormat/Extensions/WellKnownExtensionTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public static class WellKnownExtensionTypes
{ "SmartFormat.Extensions.XmlSource", 9000 },
// sources for specific types must be in the list before ReflectionSource
{ "SmartFormat.Extensions.ReflectionSource", 10000 },
{ "SmartFormat.Extensions.DefaultSource", 11000 },
{ "SmartFormat.Extensions.KeyValuePairSource", 12000 }
{ "SmartFormat.Extensions.KeyValuePairSource", 11000 },
{ "SmartFormat.Extensions.DefaultSource", 12000 }
};

/// <summary>
Expand Down

0 comments on commit 377656f

Please sign in to comment.