Skip to content

Commit a5f8606

Browse files
authored
Merge pull request #47 from altasoft/feature/DateAndTime
Feature/date and time
2 parents f92c3b4 + 362b5e0 commit a5f8606

57 files changed

Lines changed: 785 additions & 171 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ public readonly partial struct PositiveInteger : IEquatable<PositiveInteger>
282282
/// <param name="result">When this method returns, contains the created PositiveInteger if the conversion succeeded, or null if the conversion failed.</param>
283283
/// <param name="errorMessage">When this method returns, contains the error message if the conversion failed; otherwise, null.</param>
284284
/// <returns>true if the conversion succeeded; otherwise, false.</returns>
285-
public static bool TryCreate(int value,[NotNullWhen(true)] out PositiveInteger? result, [NotNullWhen(false)] out string? errorMessage)
285+
public static bool TryCreate(int value,[NotNullWhen(true)] out PositiveInteger? result, [NotNullWhen(false)] out string? errorMessage)
286286
{
287287
var validationResult = Validate(value);
288288
if (!validationResult.IsValid)

src/AltaSoft.DomainPrimitives.Generator/DomainPrimitiveGenerator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ public sealed class DomainPrimitiveGenerator : IIncrementalGenerator
2121
/// <param name="context">The generator initialization context.</param>
2222
public void Initialize(IncrementalGeneratorInitializationContext context)
2323
{
24-
//#if DEBUG
25-
// System.Diagnostics.Debugger.Launch();
26-
//#endif
24+
//#if DEBUG
25+
// System.Diagnostics.Debugger.Launch();
26+
//#endif
2727

2828
var domainPrimitivesToGenerate = context.SyntaxProvider.CreateSyntaxProvider(
2929
predicate: static (node, _) => IsSyntaxTargetForGeneration(node),

src/AltaSoft.DomainPrimitives.Generator/Executor.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,11 @@ internal static void Execute(
171171
return null;
172172
}
173173

174-
if (serializationAttribute is not null && serializationAttribute.ConstructorArguments.Length != 0)
174+
if (serializationAttribute is not null && serializationAttribute.ConstructorArguments.Length > 0)
175175
{
176-
var value = serializationAttribute.ConstructorArguments[0];
177-
generatorData.SerializationFormat = value.Value?.ToString();
176+
var format = serializationAttribute.ConstructorArguments[0].Value?.ToString() ?? string.Empty;
177+
var allowStandardFormats = (bool?)serializationAttribute.ConstructorArguments[1].Value != false;
178+
generatorData.SerializationInfo = (format, allowStandardFormats);
178179
}
179180

180181
if (isNumeric && globalOptions.GenerateNumericOperators)
@@ -385,7 +386,7 @@ private static void Process(GeneratorData data, string ctorCode, DomainPrimitive
385386
usings.Add("System.Xml.Serialization");
386387
}
387388

388-
if (data.SerializationFormat is not null)
389+
if (data.SerializationInfo is not null)
389390
{
390391
usings.Add("System.Globalization");
391392
}

src/AltaSoft.DomainPrimitives.Generator/Helpers/DiagnosticHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ internal static Diagnostic GeneralError(Location? location, Exception ex)
2222
new DiagnosticDescriptor(
2323
"AL1000",
2424
"An exception was thrown by the AltaSoft.DomainPrimitiveGenerator generator",
25-
"An exception was thrown by the AltaSoft.DomainPrimitiveGenerator generator: `{0}`{1}{2}",
25+
"An exception was thrown by the AltaSoft.DomainPrimitiveGenerator generator: `{0}`",
2626
Category,
2727
DiagnosticSeverity.Error,
2828
isEnabledByDefault: true),
29-
location, ex, SourceCodeBuilder.s_newLine, ex.StackTrace);
29+
location, ex.ToString());
3030
}
3131

3232
/// <summary>

src/AltaSoft.DomainPrimitives.Generator/Helpers/MethodGeneratorHelper.cs

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ void AddMapping()
8484
.Append("Type = ").Append(typeName).AppendLine(",");
8585

8686
if (!string.IsNullOrEmpty(format))
87-
builder.Append("Format = ").Append(QuoteAndEscape(data.SerializationFormat ?? format)).AppendLine(",");
87+
builder.Append("Format = ").Append(QuoteAndEscape(data.SerializationInfo?.serializationFormat ?? format)).AppendLine(",");
8888

8989
var title = data.ClassName;
9090
builder.Append("Title = ").Append(Quote(title)).AppendLine(",");
@@ -165,7 +165,7 @@ internal static void ProcessTypeConverter(GeneratorData data, SourceProductionCo
165165
.AppendLine("public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)")
166166
.OpenBracket();
167167

168-
if (data.SerializationFormat is not null)
168+
if (data.SerializationInfo is not null)
169169
{
170170
builder.AppendLine("if (value is string s)")
171171
.OpenBracket()
@@ -184,16 +184,16 @@ internal static void ProcessTypeConverter(GeneratorData data, SourceProductionCo
184184
else
185185
{
186186
builder.AppendLine("var result = base.ConvertFrom(context, culture, value);")
187-
.AppendLine("if (result is null)")
188-
.AppendIndentation().AppendLine("return null;")
189-
.AppendLine("try")
190-
.OpenBracket()
191-
.AppendLine($"return new {data.ClassName}(({data.PrimitiveTypeFriendlyName})result);")
192-
.CloseBracket()
193-
.AppendLine("catch (InvalidDomainValueException ex)")
194-
.OpenBracket()
195-
.Append("throw new FormatException(\"Cannot parse ").Append(data.ClassName).AppendLine("\", ex);")
196-
.CloseBracket();
187+
.AppendLine("if (result is null)")
188+
.AppendIndentation().AppendLine("return null;")
189+
.AppendLine("try")
190+
.OpenBracket()
191+
.AppendLine($"return new {data.ClassName}(({data.PrimitiveTypeFriendlyName})result);")
192+
.CloseBracket()
193+
.AppendLine("catch (InvalidDomainValueException ex)")
194+
.OpenBracket()
195+
.Append("throw new FormatException(\"Cannot parse ").Append(data.ClassName).AppendLine("\", ex);")
196+
.CloseBracket();
197197
}
198198
builder.CloseBracket().CloseBracket();
199199

@@ -306,7 +306,7 @@ internal static void ProcessJsonConverter(GeneratorData data, SourceProductionCo
306306
"System.Text.Json.Serialization",
307307
"System.Globalization",
308308
"System.Text.Json.Serialization.Metadata",
309-
"AltaSoft.DomainPrimitives",
309+
"AltaSoft.DomainPrimitives"
310310
};
311311

312312
var converterName = data.UnderlyingType.ToString();
@@ -321,7 +321,7 @@ internal static void ProcessJsonConverter(GeneratorData data, SourceProductionCo
321321
.Append("public override ").Append(data.ClassName).AppendLine(" Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)")
322322
.OpenBracket();
323323

324-
if (data.SerializationFormat is null)
324+
if (data.SerializationInfo is null)
325325
{
326326
var rawValueStr = $"JsonInternalConverters.{converterName}Converter.Read(ref reader, typeToConvert, options){(primitiveTypeIsValueType ? "" : "!")}";
327327

@@ -350,25 +350,25 @@ internal static void ProcessJsonConverter(GeneratorData data, SourceProductionCo
350350
.NewLine();
351351

352352
builder.AppendInheritDoc().AppendLine($"public override void Write(Utf8JsonWriter writer, {data.ClassName} value, JsonSerializerOptions options)")
353-
.OpenBracket()
354-
.AppendLineIf(data.SerializationFormat is null, $"JsonInternalConverters.{converterName}Converter.Write(writer, ({data.PrimitiveTypeFriendlyName})value, options);")
355-
.AppendLineIf(data.SerializationFormat is not null, $"writer.WriteStringValue(value.ToString({QuoteAndEscape(data.SerializationFormat)}, CultureInfo.InvariantCulture));")
356-
.CloseBracket()
357-
.NewLine();
353+
.OpenBracket();
354+
if (data.SerializationInfo is null)
355+
builder.AppendLine($"JsonInternalConverters.{converterName}Converter.Write(writer, ({data.PrimitiveTypeFriendlyName})value, options);");
356+
else
357+
builder.AppendLine($"writer.WriteStringValue(value.ToString({QuoteAndEscape(data.SerializationInfo.Value.serializationFormat)}, CultureInfo.InvariantCulture));");
358+
builder.CloseBracket().NewLine();
358359

359360
builder.AppendInheritDoc()
360361
.Append("public override ").Append(data.ClassName).AppendLine(" ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)")
361362
.OpenBracket();
362363

363-
if (data.SerializationFormat is null)
364+
if (data.SerializationInfo is null)
364365
{
365-
366366
var rawValueStr = $"JsonInternalConverters.{converterName}Converter.ReadAsPropertyName(ref reader, typeToConvert, options){(primitiveTypeIsValueType ? "" : "!")}";
367367

368368
builder.AppendLine("try")
369369
.OpenBracket()
370370
.AppendLineIf(data.GenerateImplicitOperators, $"return {rawValueStr};")
371-
.AppendLineIf(!data.GenerateImplicitOperators, $"return new({rawValueStr});")
371+
.AppendLineIf(!data.GenerateImplicitOperators, $"return new ({rawValueStr});")
372372
.CloseBracket();
373373
}
374374
else
@@ -392,10 +392,12 @@ internal static void ProcessJsonConverter(GeneratorData data, SourceProductionCo
392392

393393
builder.AppendInheritDoc()
394394
.Append("public override void WriteAsPropertyName(Utf8JsonWriter writer, ").Append(data.ClassName).AppendLine(" value, JsonSerializerOptions options)")
395-
.OpenBracket()
396-
.AppendLineIf(data.SerializationFormat is null, $"JsonInternalConverters.{converterName}Converter.WriteAsPropertyName(writer, ({data.PrimitiveTypeFriendlyName})value, options);")
397-
.AppendLineIf(data.SerializationFormat is not null, $"writer.WritePropertyName(value.ToString({QuoteAndEscape(data.SerializationFormat)}, CultureInfo.InvariantCulture));")
398-
.CloseBracket();
395+
.OpenBracket();
396+
if (data.SerializationInfo is null)
397+
builder.AppendLine($"JsonInternalConverters.{converterName}Converter.WriteAsPropertyName(writer, ({data.PrimitiveTypeFriendlyName})value, options);");
398+
else
399+
builder.AppendLine($"writer.WritePropertyName(value.ToString({QuoteAndEscape(data.SerializationInfo.Value.serializationFormat)}, CultureInfo.InvariantCulture));");
400+
builder.CloseBracket();
399401

400402
builder.CloseBracket();
401403

@@ -427,8 +429,8 @@ internal static void GenerateMandatoryMethods(GeneratorData data, SourceCodeBuil
427429
.AppendParamDescription("errorMessage", "When this method returns, contains the error message if the conversion failed; otherwise, null.")
428430
.AppendReturnsDescription("true if the conversion succeeded; otherwise, false.");
429431

430-
builder.Append("public static bool TryCreate(").Append(primitiveType).Append(" value, [NotNullWhen(true)] out ").Append(data.ClassName)
431-
.AppendLine("? result, [NotNullWhen(false)] out string? errorMessage)")
432+
builder.Append("public static bool TryCreate(").Append(primitiveType).Append(" value, [NotNullWhen(true)] out ").Append(data.ClassName)
433+
.AppendLine("? result, [NotNullWhen(false)] out string? errorMessage)")
432434
.OpenBracket();
433435

434436
if (data.UseTransformMethod)
@@ -796,7 +798,7 @@ public static void GenerateParsable(GeneratorData data, SourceCodeBuilder builde
796798
var underlyingType = data.ParentSymbols.Count == 0
797799
? data.PrimitiveTypeFriendlyName
798800
: data.ParentSymbols[0].Name;
799-
var format = data.SerializationFormat;
801+
var format = data.SerializationInfo?.serializationFormat;
800802

801803
builder.AppendInheritDoc()
802804
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
@@ -823,8 +825,17 @@ public static void GenerateParsable(GeneratorData data, SourceCodeBuilder builde
823825
}
824826
else
825827
{
826-
builder.Append($"{underlyingType}.")
827-
.AppendIfElse(format is null, "Parse(s, provider)", $"ParseExact(s, {QuoteAndEscape(format!)}, provider)");
828+
builder.Append($"{underlyingType}.");
829+
if (format is null && !data.UnderlyingType.IsDateOrTime())
830+
{
831+
builder.Append("Parse(s, provider)");
832+
}
833+
else
834+
{
835+
builder.Append(format is null
836+
? "ParseFlexible(s, provider)"
837+
: $"ParseFlexible(s, {QuoteAndEscape(format)}, {(data.SerializationInfo!.Value.allowStandardFormats ? "true" : "false")}, provider)");
838+
}
828839
}
829840

830841
builder.AppendLine(!data.GenerateImplicitOperators ? ");" : ";");
@@ -849,21 +860,24 @@ public static void GenerateParsable(GeneratorData data, SourceCodeBuilder builde
849860
}
850861
else
851862
{
852-
var style = "";
853-
if (format is not null)
863+
if (format is null && !data.UnderlyingType.IsDateOrTime())
854864
{
855-
style = data.UnderlyingType == DomainPrimitiveUnderlyingType.TimeSpan ? "TimeSpanStyles.None" : "DateTimeStyles.None";
865+
builder.Append($"if (!{underlyingType}.TryParse(s, provider, out var value))");
866+
}
867+
else
868+
{
869+
builder.Append(format is null
870+
? $"if (!{underlyingType}.TryParseFlexible(s, provider, out var value))"
871+
: $"if (!{underlyingType}.TryParseFlexible(s, {QuoteAndEscape(format)}, {(data.SerializationInfo!.Value.allowStandardFormats ? "true" : "false")}, provider, out var value))");
856872
}
857-
858-
builder.AppendIf(format is null, $"if (!{underlyingType}.TryParse(s, provider, out var value))")
859-
.AppendIf(format is not null, $"if (!{underlyingType}.TryParseExact(s, {QuoteAndEscape(format)}, provider, {style}, out var value))");
860873
}
861874

862-
builder.OpenBracket()
863-
.AppendLine("result = default;")
864-
.AppendLine("return false;")
865-
.CloseBracket()
866-
.NewLine();
875+
builder
876+
.OpenBracket()
877+
.AppendLine("result = default;")
878+
.AppendLine("return false;")
879+
.CloseBracket()
880+
.NewLine();
867881

868882
if (!data.TypeSymbol.IsValueType)
869883
{
@@ -1037,7 +1051,7 @@ public static void GenerateIXmlSerializableMethods(GeneratorData data, SourceCod
10371051

10381052
string method;
10391053

1040-
if (data.UnderlyingType.IsDateOrTime() && data.SerializationFormat is not null)
1054+
if (data.UnderlyingType.IsDateOrTime() && data.SerializationInfo is not null)
10411055
{
10421056
method = "ReadElementContentAs" + data.PrimitiveTypeFriendlyName;
10431057
}
@@ -1056,9 +1070,14 @@ public static void GenerateIXmlSerializableMethods(GeneratorData data, SourceCod
10561070
builder.AppendInheritDoc();
10571071
builder.AppendLine("public void ReadXml(XmlReader reader)")
10581072
.OpenBracket()
1059-
.Append("var value = reader.").Append(method)
1060-
.AppendLineIfElse(data.SerializationFormat is not null, $"({QuoteAndEscape(data.SerializationFormat)});", "();")
1061-
.AppendLine("ValidateOrThrow(value);")
1073+
.Append("var value = reader.").Append(method);
1074+
1075+
if (data.SerializationInfo is { } serInfo)
1076+
builder.AppendLine($"({QuoteAndEscape(serInfo.serializationFormat)}, {(serInfo.allowStandardFormats ? "true" : "false")});");
1077+
else
1078+
builder.AppendLine("();");
1079+
1080+
builder.AppendLine("ValidateOrThrow(value);")
10621081
.AppendLine("System.Runtime.CompilerServices.Unsafe.AsRef(in _value) = value;")
10631082
.AppendLine("System.Runtime.CompilerServices.Unsafe.AsRef(in _isInitialized) = true;")
10641083
.CloseBracket()
@@ -1068,10 +1087,10 @@ public static void GenerateIXmlSerializableMethods(GeneratorData data, SourceCod
10681087

10691088
if (string.Equals(data.PrimitiveTypeFriendlyName, "string", System.StringComparison.Ordinal))
10701089
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteString({data.FieldName});");
1071-
else if (data.SerializationFormat is null)
1090+
else if (data.SerializationInfo is null)
10721091
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteValue((({data.PrimitiveTypeFriendlyName}){data.FieldName}).ToXmlString());");
10731092
else
1074-
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteString({data.FieldName}.ToString({QuoteAndEscape(data.SerializationFormat)}));");
1093+
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteString({data.FieldName}.ToString({QuoteAndEscape(data.SerializationInfo?.serializationFormat)}));");
10751094
builder.NewLine();
10761095
}
10771096

src/AltaSoft.DomainPrimitives.Generator/Helpers/SourceCodeBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ public SourceCodeBuilder AppendUsings(IEnumerable<string> usings)
236236
/// <param name="condition">A Boolean value indicating whether to append the string.</param>
237237
/// <param name="line">The string to append if the condition is met.</param>
238238
/// <returns>A reference to this <see cref="SourceCodeBuilder"/> instance.</returns>
239+
[Obsolete("Use Append instead.")]
239240
public SourceCodeBuilder AppendIf(bool condition, string line) => !condition ? this : Append(line);
240241

241242
/// <summary>
@@ -245,6 +246,7 @@ public SourceCodeBuilder AppendUsings(IEnumerable<string> usings)
245246
/// <param name="ifLine">The string to append on a new line if the condition is true.</param>
246247
/// <param name="elseLine">The string to append on a new line if the condition is false.</param>
247248
/// <returns>A reference to this <see cref="SourceCodeBuilder"/> instance.</returns>
249+
[Obsolete("Use AppendIfElse instead.")]
248250
public SourceCodeBuilder AppendIfElse(bool condition, string ifLine, string elseLine) => Append(condition ? ifLine : elseLine);
249251

250252
/// <summary>
@@ -253,6 +255,7 @@ public SourceCodeBuilder AppendUsings(IEnumerable<string> usings)
253255
/// <param name="condition">A Boolean value indicating whether to append the string.</param>
254256
/// <param name="line">The string to append on a new line if the condition is met.</param>
255257
/// <returns>A reference to this <see cref="SourceCodeBuilder"/> instance.</returns>
258+
[Obsolete("Use AppendLine instead.")]
256259
public SourceCodeBuilder AppendLineIf(bool condition, string line) => !condition ? this : AppendLine(line);
257260

258261
/// <summary>
@@ -262,6 +265,7 @@ public SourceCodeBuilder AppendUsings(IEnumerable<string> usings)
262265
/// <param name="ifLine">The string to append on a new line if the condition is true.</param>
263266
/// <param name="elseLine">The string to append on a new line if the condition is false.</param>
264267
/// <returns>A reference to this <see cref="SourceCodeBuilder"/> instance.</returns>
268+
[Obsolete("Use AppendLineIfElse instead.")]
265269
public SourceCodeBuilder AppendLineIfElse(bool condition, string ifLine, string elseLine) => AppendLine(condition ? ifLine : elseLine);
266270

267271
/// <summary>
@@ -307,6 +311,7 @@ public SourceCodeBuilder AppendUsings(IEnumerable<string> usings)
307311
/// <param name="condition">A boolean indicating whether to append the line.</param>
308312
/// <param name="line">The line of text to be appended.</param>
309313
/// <returns>A reference to this <see cref="SourceCodeBuilder"/> instance.</returns>
314+
[Obsolete("Use Continue instead.")]
310315
public SourceCodeBuilder ContinueIf(bool condition, string line) => !condition ? this : Continue(line);
311316

312317
/// <summary>

src/AltaSoft.DomainPrimitives.Generator/Models/GeneratorData.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ internal sealed class GeneratorData
9494
public bool GenerateImplicitOperators { get; set; }
9595

9696
/// <summary>
97-
/// Gets or sets the serialization format (if applicable).
97+
/// Gets or sets the serialization information, including format and whether standard formats are allowed.
9898
/// </summary>
99-
public string? SerializationFormat { get; set; }
100-
99+
public (string serializationFormat, bool allowStandardFormats)? SerializationInfo { get; set; }
100+
101101
/// <summary>
102102
/// Gets or sets a value indicating whether to generate ISpanFormattable methods.
103103
/// </summary>

0 commit comments

Comments
 (0)