Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String format compatibility #173

Merged
merged 30 commits into from
May 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
afd5b77
Update issue templates
axunonb Apr 17, 2021
1d22508
Inserted a link to merged version/v3 changes
axunonb Apr 27, 2021
11c3033
Updated README
axunonb Apr 27, 2021
d1779f9
Merge remote-tracking branch 'upstream/main' into version/v3.0
axunonb Apr 29, 2021
cb5559a
Make parser fully covered with unit tests
axunonb Apr 29, 2021
e327de5
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb Apr 29, 2021
cab97df
Updated CHANGES.md
axunonb Apr 29, 2021
e8d9c93
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb Apr 29, 2021
294f810
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb Apr 29, 2021
792c391
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 3, 2021
4f45a84
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 9, 2021
e49db1e
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 9, 2021
2c562b5
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 9, 2021
2ba8c5f
Updated .editorconfig
axunonb May 9, 2021
7c3a12e
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 25, 2021
bda63e0
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 25, 2021
ef58744
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 26, 2021
67b2119
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 27, 2021
974224b
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 27, 2021
3e6e8ae
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 27, 2021
6219b47
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 27, 2021
34f2821
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 27, 2021
6ae6516
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 29, 2021
1d7e6f3
Merge remote-tracking branch 'upstream/version/v3.0' into version/v3.0
axunonb May 29, 2021
60104e7
string.format and backward compatibility
axunonb May 29, 2021
4559f17
Parser.ParseFormat has the format string as the only argument
axunonb May 30, 2021
ec60567
Give the Parser a public CTOR
axunonb May 30, 2021
752491d
Updated Parser with Debug.Assert condition
axunonb May 30, 2021
babf794
Revered Parser CTOR back to internal
axunonb May 30, 2021
aa41f22
Moved ParserSettings.UseStringFormatCompatibility to Settings.UseStri…
axunonb May 30, 2021
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
2 changes: 1 addition & 1 deletion src/SmartFormat.Performance/SimpleSpanParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void Setup()
[Benchmark]
public void ParseSmartFormat()
{
var result = _sfParser.ParseFormat(_inputFormatString, new[] {"default"});
var result = _sfParser.ParseFormat(_inputFormatString);
}

/// <summary>
Expand Down
5 changes: 2 additions & 3 deletions src/SmartFormat.Performance/SourcePerformanceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,10 @@ public SourcePerformanceTests()
);

// Cache the parsing result, so we don't include parsing performance
var format = _jsonFormatter.Parser.ParseFormat(_format, _jsonFormatter.FormatterExtensions[0].Names);
var format = _jsonFormatter.Parser.ParseFormat(_format);
_formatCache = new FormatCache(format);

var formatForLiteral = _jsonFormatter.Parser.ParseFormat(_formatForLiteral,
_jsonFormatter.FormatterExtensions[0].Names);
var formatForLiteral = _jsonFormatter.Parser.ParseFormat(_formatForLiteral);
_formatCacheLiteral = new FormatCache(formatForLiteral);

}
Expand Down
4 changes: 2 additions & 2 deletions src/SmartFormat.Tests/Core/FormatCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void Format_WithCache()
var data = new {Name = "Joe", City = "Melbourne"};
var formatter = GetSimpleFormatter();
var formatString = "{Name}, {City}";
var format = formatter.Parser.ParseFormat(formatString, formatter.FormatterExtensions[0].Names);
var format = formatter.Parser.ParseFormat(formatString);
var cache = new FormatCache(format);
Assert.That(formatter.FormatWithCache(ref cache, formatString, data), Is.EqualTo($"{data.Name}, {data.City}"));
}
Expand All @@ -49,7 +49,7 @@ public void Format_WithCache_Into()
var data = new {Name = "Joe", City = "Melbourne"};
var formatter = GetSimpleFormatter();
var formatString = "{Name}, {City}";
var format = formatter.Parser.ParseFormat(formatString, formatter.FormatterExtensions[0].Names);
var format = formatter.Parser.ParseFormat(formatString);
var cache = new FormatCache(format);
var output = new StringOutput();
formatter.FormatWithCacheInto(ref cache, output, formatString, data);
Expand Down
21 changes: 14 additions & 7 deletions src/SmartFormat.Tests/Core/FormatterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,24 @@ public void Nested_Placeholders_Braces()
var data = new {Person = new {FirstName = "John", LastName = "Long"}, Address = new {City = "London"}};
var formatter = Smart.CreateDefaultSmartFormat();

// This is necessary to avoid undesired trailing blanks:
// }}} are now considered as 3 different closing braces
formatter.Parser.UseAlternativeEscapeChar('\\');

// This allows a nested template to access outer scopes.
// Here, {City} will come from Address, but {FirstName} will come from Person:
var result = formatter.Format("{Person:{Address:City: {City}, Name: {FirstName}}}", data);
var result = formatter.Format("{Person:{Address:City\\: {City}, Name\\: {FirstName}}}", data);

Assert.That(result, Is.EqualTo("City: London, Name: John"));
}

[TestCase("({.Joe.})", ":{Joe}:")]
[TestCase("Kate", ":{(.Not:Joe.)}:")]
public void Any_Character_Anywhere_If_Escaped(string name, string expected)
{
var smart = Smart.CreateDefaultSmartFormat();
var arg = new {Name = name};
// {} and () must and can only be escaped inside options
var format = @":\{{Name:choose(\(\{.Joe.\}\)):Joe|(.Not\:Joe.)}\}:";
Assert.That(smart.Format(format, arg), Is.EqualTo(expected));
}

[TestCase(1)]
[TestCase(2)]
public void Nested_PlaceHolders_Conditional(int numOfPeople)
Expand Down Expand Up @@ -153,7 +160,7 @@ public void LeadingBackslashMustNotEscapeBraces()
{
var smart = Smart.CreateDefaultSmartFormat();
smart.Settings.Parser.ConvertCharacterStringLiterals = false;
smart.Settings.Parser.UseStringFormatCompatibility = true;
smart.Settings.UseStringFormatCompatibility = true;

var expected = "\\Hello";
var actual = smart.Format("\\{Test}", new { Test = "Hello" });
Expand Down Expand Up @@ -187,7 +194,7 @@ public void SmartFormatter_FormatDetails()
formatter.Settings.Formatter.ErrorAction = FormatErrorAction.OutputErrorInResult;
formatter.Settings.Parser.ErrorAction = ParseErrorAction.OutputErrorInResult;
formatter.Parser.AddAlphanumericSelectors(); // required for this test
var formatParsed = formatter.Parser.ParseFormat(format, new []{string.Empty});
var formatParsed = formatter.Parser.ParseFormat(format);
var formatDetails = new FormatDetails(formatter, formatParsed, args, null, null, output);

Assert.AreEqual(args, formatDetails.OriginalArgs);
Expand Down
48 changes: 23 additions & 25 deletions src/SmartFormat.Tests/Core/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void TestParser()
"{a}",
" aaa {bbb_bbb.CCC} ddd ",
};
var results = formats.Select(f => new { format = f, parsed = parser.ParseFormat(f, new[] { Guid.NewGuid().ToString("N") }) }).ToArray();
var results = formats.Select(f => new { format = f, parsed = parser.ParseFormat(f) }).ToArray();

// Verify that the reconstructed formats
// match the original ones:
Expand Down Expand Up @@ -106,7 +106,7 @@ public void Parser_Ignores_Exceptions()
};
foreach (var format in invalidFormats)
{
_ = parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") });
_ = parser.ParseFormat(format);
}
}

Expand All @@ -121,7 +121,7 @@ public void Parser_Error_Action_Ignore()

var parser = GetRegularParser();
parser.Settings.Parser.ErrorAction = ParseErrorAction.Ignore;
var parsed = parser.ParseFormat(invalidTemplate, new[] { Guid.NewGuid().ToString("N") });
var parsed = parser.ParseFormat(invalidTemplate);

Assert.That(parsed.Items.Count, Is.EqualTo(4), "Number of parsed items");
Assert.That(parsed.Items[0].RawText, Is.EqualTo("Hello, I'm "), "Literal text");
Expand All @@ -141,7 +141,7 @@ public void Parser_Error_Action_MaintainTokens(string invalidTemplate, bool last
{
var parser = GetRegularParser();
parser.Settings.Parser.ErrorAction = ParseErrorAction.MaintainTokens;
var parsed = parser.ParseFormat(invalidTemplate, new[] { Guid.NewGuid().ToString("N") });
var parsed = parser.ParseFormat(invalidTemplate);

Assert.That(parsed.Items.Count, Is.EqualTo(4), "Number of parsed items");
Assert.That(parsed.Items[0].RawText, Is.EqualTo("Hello, I'm "));
Expand All @@ -167,7 +167,7 @@ public void Parser_Error_Action_OutputErrorInResult()

var parser = GetRegularParser();
parser.Settings.Parser.ErrorAction = ParseErrorAction.OutputErrorInResult;
var parsed = parser.ParseFormat(invalidTemplate, new[] { Guid.NewGuid().ToString("N") });
var parsed = parser.ParseFormat(invalidTemplate);

Assert.That(parsed.Items.Count, Is.EqualTo(1));
Assert.That(parsed.Items[0].RawText, Does.StartWith("The format string has 3 issues"));
Expand Down Expand Up @@ -241,7 +241,7 @@ function interpolationSearch(sortedArray, seekIndex) {
";
var parser = GetRegularParser();
parser.Settings.Parser.ErrorAction = ParseErrorAction.MaintainTokens;
var parsed = parser.ParseFormat(js, new[] { Guid.NewGuid().ToString("N") });
var parsed = parser.ParseFormat(js);

// No characters should get lost compared to the format string,
// no matter if a Placeholder can be identified or not
Expand Down Expand Up @@ -290,7 +290,7 @@ .comment img {
";
var parser = GetRegularParser();
parser.Settings.Parser.ErrorAction = ParseErrorAction.MaintainTokens;
var parsed = parser.ParseFormat(css, new[] { Guid.NewGuid().ToString("N") });
var parsed = parser.ParseFormat(css);

// No characters should get lost compared to the format string,
// no matter if a Placeholder can be identified or not
Expand All @@ -313,8 +313,6 @@ .comment img {
private static Parser GetRegularParser()
{
var parser = new SmartFormatter() { Settings = { Parser = {ErrorAction = ParseErrorAction.ThrowError }}}.Parser;
parser.AddAlphanumericSelectors();
parser.AddOperators(".,");
return parser;
}

Expand All @@ -324,7 +322,7 @@ public void Test_Format_Substring()
var parser = GetRegularParser();
var formatString = " a|aa {bbb: ccc dd|d {:|||} {eee} ff|f } gg|g ";

var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") });
var format = parser.ParseFormat(formatString);

// Extract the substrings of literal text:
Assert.That(format.Substring( 1, 3).ToString(), Is.EqualTo("a|a"));
Expand Down Expand Up @@ -364,7 +362,7 @@ public void Test_Format_Set_Alignment_Property()
var parser = GetRegularParser();
var formatString = "{0}";

var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") });
var format = parser.ParseFormat(formatString);
var placeholder = (Placeholder) format.Items[0];
Assert.AreEqual(formatString, placeholder.ToString());
placeholder.Alignment = 10;
Expand All @@ -377,7 +375,7 @@ public void Test_Format_With_Alignment()
var parser = GetRegularParser();
var formatString = "{0,-10}";

var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") });
var format = parser.ParseFormat(formatString);
var placeholder = (Placeholder) format.Items[0];
Assert.That(placeholder.ToString(), Is.EqualTo(formatString));
Assert.That(placeholder.Selectors.Count, Is.EqualTo(2));
Expand All @@ -391,7 +389,7 @@ public void Test_Formatter_Name_And_Options()
var parser = GetRegularParser();
var formatString = "{0}";

var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") });
var format = parser.ParseFormat(formatString);
var placeholder = (Placeholder)format.Items[0];
placeholder.FormatterName = "test";
Assert.AreEqual($"{{0:{placeholder.FormatterName}}}", placeholder.ToString());
Expand All @@ -404,7 +402,7 @@ public void Test_Format_IndexOf()
{
var parser = GetRegularParser();
var format = " a|aa {bbb: ccc dd|d {:|||} {eee} ff|f } gg|g ";
var Format = parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") });
var Format = parser.ParseFormat(format);

Assert.That(Format.IndexOf('|'), Is.EqualTo(2));
Assert.That(Format.IndexOf('|', 3), Is.EqualTo(43));
Expand All @@ -427,7 +425,7 @@ public void Test_Format_Split()
{
var parser = GetRegularParser();
var format = " a|aa {bbb: ccc dd|d {:|||} {eee} ff|f } gg|g ";
var parsedFormat = parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") });
var parsedFormat = parser.ParseFormat(format);
var splits = parsedFormat.Split('|');

Assert.That(splits.Count, Is.EqualTo(3));
Expand All @@ -449,7 +447,7 @@ public void Test_Format_Split()

private Format Parse(string format, string[] formatterExentionNames )
{
return GetRegularParser().ParseFormat(format, formatterExentionNames);
return GetRegularParser().ParseFormat(format);
}

[TestCase("{0:name:}", "name", "", "")]
Expand Down Expand Up @@ -478,7 +476,7 @@ public void Name_of_unregistered_NamedFormatter_will_not_be_parsed()
{
// find formatter formattername, which does not exist in the (empty) list of formatter extensions
var placeholderWithNonExistingName = (Placeholder)Parse("{0:formattername:}", new string[] {} ).Items[0];
Assert.AreEqual("formattername:", placeholderWithNonExistingName.Format?.ToString()); // name is only treaded as a literal
Assert.AreEqual("formattername", placeholderWithNonExistingName.FormatterName); // name is only treaded as a literal
}

// Incomplete:
Expand Down Expand Up @@ -528,7 +526,7 @@ private void AssertNoNamedFormatter(string format, string expectedLiteralText)
parser.UseAlternativeEscapeChar('\\');
parser.Settings.ConvertCharacterStringLiterals = false;

var placeholder = (Placeholder) parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") }).Items[0];
var placeholder = (Placeholder) parser.ParseFormat(format).Items[0];
Assert.IsEmpty(placeholder.FormatterName);
Assert.IsEmpty(placeholder.FormatterOptions);
var literalText = placeholder.Format?.GetLiteralText();
Expand All @@ -553,7 +551,7 @@ public void Alternative_Escaping_In_Literal()
{
var parser = GetRegularParser();
parser.UseAlternativeEscapeChar('\\');
Assert.That(parser.ParseFormat("\\{\\}", Array.Empty<string>()).ToString(), Is.EqualTo("{}"));
Assert.That(parser.ParseFormat("\\{\\}").ToString(), Is.EqualTo("{}"));
}

[Test]
Expand All @@ -562,7 +560,7 @@ public void Nested_format_with_alternative_escaping()
var parser = GetRegularParser();
// necessary because of the consecutive }}}, which would otherwise be escaped as }} and lead to "missing brace" exception:
parser.UseAlternativeEscapeChar('\\');
var placeholders = parser.ParseFormat("{c1:{c2:{c3}}}", new[] {Guid.NewGuid().ToString("N")});
var placeholders = parser.ParseFormat("{c1:{c2:{c3}}}");

var c1 = (Placeholder) placeholders.Items[0];
var c2 = (Placeholder) c1.Format?.Items[0]!;
Expand All @@ -585,7 +583,7 @@ public void Parse_Options()
// The literal may also contain escaped characters
var literal = "one|two|th\\} \\{ree|other";

var format = parser.ParseFormat($"{{{selector}:{formatterName}({options}):{literal}}}", new[] {formatterName});
var format = parser.ParseFormat($"{{{selector}:{formatterName}({options}):{literal}}}");
var placeholder = (Placeholder) format.Items[0];

Assert.That(format.Items.Count, Is.EqualTo(1));
Expand All @@ -606,7 +604,7 @@ public void Parse_Options()
public void Parse_Unicode(string formatString, string unicodeLiteral, int itemIndex, bool isLegal)
{
var parser = GetRegularParser();
var result = parser.ParseFormat(formatString, new[] {"d"});
var result = parser.ParseFormat(formatString);

var literal = result.Items[itemIndex];
Assert.That(literal, Is.TypeOf(typeof(LiteralText)));
Expand All @@ -625,7 +623,7 @@ public void Selector_With_Custom_Selector_Character(string formatString, char cu
{
var parser = GetRegularParser();
parser.Settings.Parser.AddCustomSelectorChars(new[]{customChar});
var result = parser.ParseFormat(formatString, new[] {"d"});
var result = parser.ParseFormat(formatString);

var placeholder = result.Items[0] as Placeholder;
Assert.That(placeholder, Is.Not.Null);
Expand All @@ -639,7 +637,7 @@ public void Selectors_With_Custom_Operator_Character(string formatString, char c
{
var parser = GetRegularParser();
parser.Settings.Parser.AddCustomOperatorChars(new[]{customChar});
var result = parser.ParseFormat(formatString, new[] {"d"});
var result = parser.ParseFormat(formatString);

var placeholder = result.Items[0] as Placeholder;
Assert.That(placeholder, Is.Not.Null);
Expand All @@ -658,7 +656,7 @@ public void Selector_With_Contiguous_Operator_Characters(string formatString, ch
var parser = GetRegularParser();
// adding '.' is ignored, as it's the standard operator
parser.Settings.Parser.AddCustomOperatorChars(new[]{customChar});
var result = parser.ParseFormat(formatString, new[] {"d"});
var result = parser.ParseFormat(formatString);

var placeholder = result.Items[0] as Placeholder;
Assert.That(placeholder, Is.Not.Null);
Expand Down
Loading