Skip to content

Commit

Permalink
Add support for custom NodaTime patterns for serialization and deseri…
Browse files Browse the repository at this point in the history
…alization (#4801)
  • Loading branch information
david-driscoll authored and michaelstaib committed Mar 11, 2022
1 parent dc16e86 commit ee6184e
Show file tree
Hide file tree
Showing 45 changed files with 2,314 additions and 155 deletions.
27 changes: 21 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs
@@ -1,3 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using HotChocolate.Types.NodaTime.Properties;
Expand All @@ -11,25 +12,39 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class DurationType : StringToStructBaseType<Duration>
{
private readonly IPattern<Duration>[] _allowedPatterns;
private readonly IPattern<Duration> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="DurationType"/>.
/// </summary>
public DurationType() : this(DurationPattern.Roundtrip)
{
}

/// <summary>
/// Initializes a new instance of <see cref="DurationType"/>.
/// </summary>
public DurationType() : base("Duration")
public DurationType(params IPattern<Duration>[] allowedPatterns) : base("Duration")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = allowedPatterns[0];
Description = NodaTimeResources.DurationType_Description;
}

/// <inheritdoc />
protected override string Serialize(Duration runtimeValue)
=> DurationPattern.Roundtrip
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out Duration? runtimeValue)
=> DurationPattern.Roundtrip
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using NodaTime.Text;

namespace HotChocolate.Types.NodaTime;
Expand All @@ -7,7 +8,7 @@ internal static class PatternExtensions
public static bool TryParse<NodaTimeType>(
this IPattern<NodaTimeType> pattern,
string text,
out NodaTimeType? output)
[NotNullWhen(true)] out NodaTimeType? output)
where NodaTimeType : struct
{
ParseResult<NodaTimeType> result = pattern.Parse(text);
Expand All @@ -25,7 +26,7 @@ internal static class PatternExtensions
public static bool TryParse<NodaTimeType>(
this IPattern<NodaTimeType> pattern,
string text,
out NodaTimeType? output)
[NotNullWhen(true)] out NodaTimeType? output)
where NodaTimeType : class
{
ParseResult<NodaTimeType> result = pattern.Parse(text);
Expand All @@ -39,4 +40,40 @@ internal static class PatternExtensions
output = null;
return false;
}

public static bool TryParse<NodaTimeType>(
this IPattern<NodaTimeType>[] patterns,
string text,
[NotNullWhen(true)] out NodaTimeType? output)
where NodaTimeType : struct
{
foreach (IPattern<NodaTimeType> pattern in patterns)
{
if (pattern.TryParse(text, out output))
{
return true;
}
}

output = default;
return false;
}

public static bool TryParse<NodaTimeType>(
this IPattern<NodaTimeType>[] patterns,
string text,
[NotNullWhen(true)] out NodaTimeType? output)
where NodaTimeType : class
{
foreach (IPattern<NodaTimeType> pattern in patterns)
{
if (pattern.TryParse(text, out output))
{
return true;
}
}

output = default;
return false;
}
}
26 changes: 20 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs
Expand Up @@ -11,25 +11,39 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class InstantType : StringToStructBaseType<Instant>
{
private readonly IPattern<Instant>[] _allowedPatterns;
private readonly IPattern<Instant> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="InstantType"/>.
/// </summary>
public InstantType() : this(InstantPattern.ExtendedIso)
{
}

/// <summary>
/// Initializes a new instance of <see cref="InstantType"/>.
/// </summary>
public InstantType() : base("Instant")
public InstantType(params IPattern<Instant>[] allowedPatterns) : base("Instant")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = allowedPatterns[0];
Description = NodaTimeResources.InstantType_Description;
}

/// <inheritdoc />
protected override string Serialize(Instant runtimeValue)
=> InstantPattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out Instant? runtimeValue)
=> InstantPattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}
26 changes: 20 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs
Expand Up @@ -11,25 +11,39 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class LocalDateTimeType : StringToStructBaseType<LocalDateTime>
{
private readonly IPattern<LocalDateTime>[] _allowedPatterns;
private readonly IPattern<LocalDateTime> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="LocalDateTimeType"/>.
/// </summary>
public LocalDateTimeType() : this(LocalDateTimePattern.ExtendedIso)
{
}

/// <summary>
/// Initializes a new instance of <see cref="LocalDateTimeType"/>.
/// </summary>
public LocalDateTimeType() : base("LocalDateTime")
public LocalDateTimeType(params IPattern<LocalDateTime>[] allowedPatterns) : base("LocalDateTime")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = allowedPatterns[0];
Description = NodaTimeResources.LocalDateTimeType_Description;
}

/// <inheritdoc />
protected override string Serialize(LocalDateTime runtimeValue)
=> LocalDateTimePattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out LocalDateTime? runtimeValue)
=> LocalDateTimePattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}
26 changes: 20 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs
Expand Up @@ -12,25 +12,39 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class LocalDateType : StringToStructBaseType<LocalDate>
{
private readonly IPattern<LocalDate>[] _allowedPatterns;
private readonly IPattern<LocalDate> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="LocalDateType"/>.
/// </summary>
public LocalDateType() : this(LocalDatePattern.Iso)
{
}

/// <summary>
/// Initializes a new instance of <see cref="LocalDateType"/>.
/// </summary>
public LocalDateType() : base("LocalDate")
public LocalDateType(params IPattern<LocalDate>[] allowedPatterns) : base("LocalDate")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = allowedPatterns[0];
Description = NodaTimeResources.LocalDateType_Description;
}

/// <inheritdoc />
protected override string Serialize(LocalDate runtimeValue)
=> LocalDatePattern.Iso
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out LocalDate? runtimeValue)
=> LocalDatePattern.Iso
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}
26 changes: 20 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs
Expand Up @@ -12,25 +12,39 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class LocalTimeType : StringToStructBaseType<LocalTime>
{
private readonly IPattern<LocalTime>[] _allowedPatterns;
private readonly IPattern<LocalTime> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="LocalTimeType"/>.
/// </summary>
public LocalTimeType() : this(LocalTimePattern.ExtendedIso)
{
}

/// <summary>
/// Initializes a new instance of <see cref="LocalTimeType"/>.
/// </summary>
public LocalTimeType() : base("LocalTime")
public LocalTimeType(params IPattern<LocalTime>[] allowedPatterns) : base("LocalTime")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = allowedPatterns[0];
Description = NodaTimeResources.LocalTimeType_Description;
}

/// <inheritdoc />
protected override string Serialize(LocalTime runtimeValue)
=> LocalTimePattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out LocalTime? runtimeValue)
=> LocalTimePattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}
30 changes: 24 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs
Expand Up @@ -11,25 +11,43 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class OffsetDateTimeType : StringToStructBaseType<OffsetDateTime>
{
private readonly IPattern<OffsetDateTime>[] _allowedPatterns;
private readonly IPattern<OffsetDateTime> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="OffsetDateTimeType"/>.
/// </summary>
public OffsetDateTimeType() : this(OffsetDateTimePattern.ExtendedIso)
{
// Backwards compatibility with the original code's behavior
_serializationPattern = OffsetDateTimePattern.GeneralIso;
_allowedPatterns = new IPattern<OffsetDateTime>[] { OffsetDateTimePattern.ExtendedIso };
}

/// <summary>
/// Initializes a new instance of <see cref="OffsetDateTimeType"/>.
/// </summary>
public OffsetDateTimeType() : base("OffsetDateTime")
public OffsetDateTimeType(params IPattern<OffsetDateTime>[] allowedPatterns)
: base("OffsetDateTime")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = _allowedPatterns[0];
Description = NodaTimeResources.OffsetDateTimeType_Description;
}

/// <inheritdoc />
protected override string Serialize(OffsetDateTime runtimeValue)
=> OffsetDateTimePattern.GeneralIso
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out OffsetDateTime? runtimeValue)
=> OffsetDateTimePattern.ExtendedIso
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}
26 changes: 20 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs
Expand Up @@ -13,25 +13,39 @@ namespace HotChocolate.Types.NodaTime;
/// </summary>
public class OffsetDateType : StringToStructBaseType<OffsetDate>
{
private readonly IPattern<OffsetDate>[] _allowedPatterns;
private readonly IPattern<OffsetDate> _serializationPattern;

/// <summary>
/// Initializes a new instance of <see cref="OffsetDateType"/>.
/// </summary>
public OffsetDateType() : this(OffsetDatePattern.GeneralIso)
{
}

/// <summary>
/// Initializes a new instance of <see cref="OffsetDateType"/>.
/// </summary>
public OffsetDateType() : base("OffsetDate")
public OffsetDateType(params IPattern<OffsetDate>[] allowedPatterns) : base("OffsetDate")
{
if (allowedPatterns.Length == 0)
{
throw ThrowHelper.PatternCannotBeEmpty(this);
}

_allowedPatterns = allowedPatterns;
_serializationPattern = allowedPatterns[0];
Description = NodaTimeResources.OffsetDateType_Description;
}

/// <inheritdoc />
protected override string Serialize(OffsetDate runtimeValue)
=> OffsetDatePattern.GeneralIso
.WithCulture(CultureInfo.InvariantCulture)
=> _serializationPattern
.Format(runtimeValue);

/// <inheritdoc />
protected override bool TryDeserialize(
string resultValue,
[NotNullWhen(true)] out OffsetDate? runtimeValue)
=> OffsetDatePattern.GeneralIso
.WithCulture(CultureInfo.InvariantCulture)
.TryParse(resultValue, out runtimeValue);
=> _allowedPatterns.TryParse(resultValue, out runtimeValue);
}

0 comments on commit ee6184e

Please sign in to comment.