Skip to content

Optional argument handling and added tests for ToString(x, f) #31

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

Merged
merged 1 commit into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ calling a function will be undefined if:
| `StartsWith(s, t)` | Tests whether the string `s` starts with substring `t`. |
| `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. |
| `TagOf(o)` | Returns the `TypeTag` field of a captured object (i.e. where `TypeOf(x)` is `'object'`). |
| `ToString(x, f)` | Applies the format string `f` to the formattable value `x`. |
| `ToString(x, [format])` | Convert `x` to a string, applying the format string `format` if `x` is `IFormattable`. |
| `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'`. |
| `Undefined()` | Explicitly mark an undefined value. |
| `UtcDateTime(x)` | Convert a `DateTime` or `DateTimeOffset` into a UTC `DateTime`. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Serilog.Expressions.Compilation.Variadics
{
// Now a bit of a misnomer - handles variadic `coalesce()`, as well as optional arguments for other functions.
class VariadicCallRewriter : IdentityTransformer
{
static readonly VariadicCallRewriter Instance = new VariadicCallRewriter();
Expand All @@ -19,26 +20,37 @@ protected override Expression Transform(CallExpression lx)
{
var operands = lx.Operands
.Select(Transform)
.Concat(new[] {new CallExpression(false, Operators.OpUndefined)})
.Concat(new[] {CallUndefined()})
.ToArray();
return new CallExpression(lx.IgnoreCase, lx.OperatorName, operands);
}

if (Operators.SameOperator(lx.OperatorName, Operators.OpCoalesce))
{
if (lx.Operands.Length == 0)
return new CallExpression(false, Operators.OpUndefined);
return CallUndefined();
if (lx.Operands.Length == 1)
return Transform(lx.Operands.Single());
if (lx.Operands.Length > 2)
{
var first = Transform(lx.Operands.First());
return new CallExpression(false, lx.OperatorName, first,
Transform(new CallExpression(false, lx.OperatorName, lx.Operands.Skip(1).ToArray())));
return new CallExpression(lx.IgnoreCase, lx.OperatorName, first,
Transform(new CallExpression(lx.IgnoreCase, lx.OperatorName, lx.Operands.Skip(1).ToArray())));
}
}

if (Operators.SameOperator(lx.OperatorName, Operators.OpToString) &&
lx.Operands.Length == 1)
{
return new CallExpression(lx.IgnoreCase, lx.OperatorName, lx.Operands[0], CallUndefined());
}

return base.Transform(lx);
}

static CallExpression CallUndefined()
{
return new CallExpression(false, Operators.OpUndefined);
}
}
}
}
21 changes: 16 additions & 5 deletions src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Serilog.Events;
using Serilog.Expressions.Compilation.Linq;

// ReSharper disable ForCanBeConvertedToForeach, InvertIf, MemberCanBePrivate.Global, UnusedMember.Global
// ReSharper disable ForCanBeConvertedToForeach, InvertIf, MemberCanBePrivate.Global, UnusedMember.Global, InconsistentNaming, ReturnTypeCanBeNotNullable

namespace Serilog.Expressions.Runtime
{
Expand Down Expand Up @@ -470,14 +470,25 @@ public static LogEventPropertyValue _Internal_IsNotNull(LogEventPropertyValue? v

public static LogEventPropertyValue? ToString(LogEventPropertyValue? value, LogEventPropertyValue? format)
{
if (!(value is ScalarValue sv && sv.Value is IFormattable formattable) ||
!Coerce.String(format, out var fmt))
if (!(value is ScalarValue sv) ||
sv.Value == null ||
!(Coerce.String(format, out var fmt) || format == null || format is ScalarValue { Value: null }))
{
return null;
}

// TODO #19: formatting is culture-specific.
return new ScalarValue(formattable.ToString(fmt, null));
string toString;
if (sv.Value is IFormattable formattable)
{
// TODO #19: formatting is culture-specific.
toString = formattable.ToString(fmt, null);
}
else
{
toString = sv.Value.ToString();
}

return new ScalarValue(toString);
}

public static LogEventPropertyValue? UtcDateTime(LogEventPropertyValue? dateTime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,18 @@ if 'string' then 1 else 2 ⇶ 2
if true then if false then 1 else 2 else 3 ⇶ 2

// ToString
tostring(16) ⇶ '16'
tostring('test') ⇶ 'test'
tostring({}) ⇶ undefined()
tostring([]) ⇶ undefined()
tostring(16, '000') ⇶ '016'
tostring('test', '000') ⇶ undefined()
tostring(16, undefined()) ⇶ undefined()
tostring(16, null) ⇶ undefined()
tostring(null) ⇶ undefined()
tostring(undefined()) ⇶ undefined()
tostring('test', '000') ⇶ 'test'
tostring('test', []) ⇶ undefined()
tostring('test', 42) ⇶ undefined()
tostring(16, undefined()) ⇶ '16'
tostring(16, null) ⇶ '16'

// TypeOf
typeof(undefined()) ⇶ 'undefined'
Expand Down
4 changes: 2 additions & 2 deletions test/Serilog.Expressions.Tests/FormatParityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class FormatParityTests
// ReSharper disable once UnusedMember.Global
public static LogEventPropertyValue? ClassicRender(LogEventPropertyValue? messageTemplate, LogEventPropertyValue? properties)
{
if (!(messageTemplate is ScalarValue svt && svt.Value is string smt) ||
if (!(messageTemplate is ScalarValue {Value: string smt}) ||
!(properties is StructureValue stp))
{
return null;
Expand All @@ -69,7 +69,7 @@ public class FormatParityTests
// ReSharper disable once UnusedMember.Global
public static LogEventPropertyValue? ClassicRenderings(LogEventPropertyValue? messageTemplate, LogEventPropertyValue? properties)
{
if (!(messageTemplate is ScalarValue svt && svt.Value is string smt) ||
if (!(messageTemplate is ScalarValue {Value: string smt}) ||
!(properties is StructureValue stp))
{
return null;
Expand Down