Skip to content

Commit 3ce8fc6

Browse files
authored
Merge pull request #31 from nblumhardt/more-runtime-functions
Optional argument handling and added tests for ToString(x, f)
2 parents dfe2fd6 + ef777ef commit 3ce8fc6

File tree

5 files changed

+47
-16
lines changed

5 files changed

+47
-16
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ calling a function will be undefined if:
187187
| `StartsWith(s, t)` | Tests whether the string `s` starts with substring `t`. |
188188
| `Substring(s, start, [length])` | Return the substring of string `s` from `start` to the end of the string, or of `length` characters, if this argument is supplied. |
189189
| `TagOf(o)` | Returns the `TypeTag` field of a captured object (i.e. where `TypeOf(x)` is `'object'`). |
190-
| `ToString(x, f)` | Applies the format string `f` to the formattable value `x`. |
190+
| `ToString(x, [format])` | Convert `x` to a string, applying the format string `format` if `x` is `IFormattable`. |
191191
| `TypeOf(x)` | Returns a string describing the type of expression `x`: a .NET type name if `x` is scalar and non-null, or, `'array'`, `'object'`, `'dictionary'`, `'null'`, or `'undefined'`. |
192192
| `Undefined()` | Explicitly mark an undefined value. |
193193
| `UtcDateTime(x)` | Convert a `DateTime` or `DateTimeOffset` into a UTC `DateTime`. |

src/Serilog.Expressions/Expressions/Compilation/Variadics/VariadicCallRewriter.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Serilog.Expressions.Compilation.Variadics
66
{
7+
// Now a bit of a misnomer - handles variadic `coalesce()`, as well as optional arguments for other functions.
78
class VariadicCallRewriter : IdentityTransformer
89
{
910
static readonly VariadicCallRewriter Instance = new VariadicCallRewriter();
@@ -19,26 +20,37 @@ protected override Expression Transform(CallExpression lx)
1920
{
2021
var operands = lx.Operands
2122
.Select(Transform)
22-
.Concat(new[] {new CallExpression(false, Operators.OpUndefined)})
23+
.Concat(new[] {CallUndefined()})
2324
.ToArray();
2425
return new CallExpression(lx.IgnoreCase, lx.OperatorName, operands);
2526
}
2627

2728
if (Operators.SameOperator(lx.OperatorName, Operators.OpCoalesce))
2829
{
2930
if (lx.Operands.Length == 0)
30-
return new CallExpression(false, Operators.OpUndefined);
31+
return CallUndefined();
3132
if (lx.Operands.Length == 1)
3233
return Transform(lx.Operands.Single());
3334
if (lx.Operands.Length > 2)
3435
{
3536
var first = Transform(lx.Operands.First());
36-
return new CallExpression(false, lx.OperatorName, first,
37-
Transform(new CallExpression(false, lx.OperatorName, lx.Operands.Skip(1).ToArray())));
37+
return new CallExpression(lx.IgnoreCase, lx.OperatorName, first,
38+
Transform(new CallExpression(lx.IgnoreCase, lx.OperatorName, lx.Operands.Skip(1).ToArray())));
3839
}
3940
}
4041

42+
if (Operators.SameOperator(lx.OperatorName, Operators.OpToString) &&
43+
lx.Operands.Length == 1)
44+
{
45+
return new CallExpression(lx.IgnoreCase, lx.OperatorName, lx.Operands[0], CallUndefined());
46+
}
47+
4148
return base.Transform(lx);
4249
}
50+
51+
static CallExpression CallUndefined()
52+
{
53+
return new CallExpression(false, Operators.OpUndefined);
54+
}
4355
}
44-
}
56+
}

src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using Serilog.Events;
55
using Serilog.Expressions.Compilation.Linq;
66

7-
// ReSharper disable ForCanBeConvertedToForeach, InvertIf, MemberCanBePrivate.Global, UnusedMember.Global
7+
// ReSharper disable ForCanBeConvertedToForeach, InvertIf, MemberCanBePrivate.Global, UnusedMember.Global, InconsistentNaming, ReturnTypeCanBeNotNullable
88

99
namespace Serilog.Expressions.Runtime
1010
{
@@ -470,14 +470,25 @@ public static LogEventPropertyValue _Internal_IsNotNull(LogEventPropertyValue? v
470470

471471
public static LogEventPropertyValue? ToString(LogEventPropertyValue? value, LogEventPropertyValue? format)
472472
{
473-
if (!(value is ScalarValue sv && sv.Value is IFormattable formattable) ||
474-
!Coerce.String(format, out var fmt))
473+
if (!(value is ScalarValue sv) ||
474+
sv.Value == null ||
475+
!(Coerce.String(format, out var fmt) || format == null || format is ScalarValue { Value: null }))
475476
{
476477
return null;
477478
}
478479

479-
// TODO #19: formatting is culture-specific.
480-
return new ScalarValue(formattable.ToString(fmt, null));
480+
string toString;
481+
if (sv.Value is IFormattable formattable)
482+
{
483+
// TODO #19: formatting is culture-specific.
484+
toString = formattable.ToString(fmt, null);
485+
}
486+
else
487+
{
488+
toString = sv.Value.ToString();
489+
}
490+
491+
return new ScalarValue(toString);
481492
}
482493

483494
public static LogEventPropertyValue? UtcDateTime(LogEventPropertyValue? dateTime)

test/Serilog.Expressions.Tests/Cases/expression-evaluation-cases.asv

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,18 @@ if 'string' then 1 else 2 ⇶ 2
218218
if true then if false then 1 else 2 else 3 ⇶ 2
219219

220220
// ToString
221+
tostring(16) ⇶ '16'
222+
tostring('test') ⇶ 'test'
223+
tostring({}) ⇶ undefined()
224+
tostring([]) ⇶ undefined()
221225
tostring(16, '000') ⇶ '016'
222-
tostring('test', '000') ⇶ undefined()
223-
tostring(16, undefined()) ⇶ undefined()
224-
tostring(16, null) ⇶ undefined()
226+
tostring(null) ⇶ undefined()
227+
tostring(undefined()) ⇶ undefined()
228+
tostring('test', '000') ⇶ 'test'
229+
tostring('test', []) ⇶ undefined()
230+
tostring('test', 42) ⇶ undefined()
231+
tostring(16, undefined()) ⇶ '16'
232+
tostring(16, null) ⇶ '16'
225233

226234
// TypeOf
227235
typeof(undefined()) ⇶ 'undefined'

test/Serilog.Expressions.Tests/FormatParityTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class FormatParityTests
5353
// ReSharper disable once UnusedMember.Global
5454
public static LogEventPropertyValue? ClassicRender(LogEventPropertyValue? messageTemplate, LogEventPropertyValue? properties)
5555
{
56-
if (!(messageTemplate is ScalarValue svt && svt.Value is string smt) ||
56+
if (!(messageTemplate is ScalarValue {Value: string smt}) ||
5757
!(properties is StructureValue stp))
5858
{
5959
return null;
@@ -69,7 +69,7 @@ public class FormatParityTests
6969
// ReSharper disable once UnusedMember.Global
7070
public static LogEventPropertyValue? ClassicRenderings(LogEventPropertyValue? messageTemplate, LogEventPropertyValue? properties)
7171
{
72-
if (!(messageTemplate is ScalarValue svt && svt.Value is string smt) ||
72+
if (!(messageTemplate is ScalarValue {Value: string smt}) ||
7373
!(properties is StructureValue stp))
7474
{
7575
return null;

0 commit comments

Comments
 (0)