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

Add support for custom NodaTime patterns for serialization and deserialization #4801

Merged
27 changes: 21 additions & 6 deletions src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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 @@ public static bool TryParse<NodaTimeType>(
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 @@ public static bool TryParse<NodaTimeType>(
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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);
}
Loading