Skip to content
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 Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Product>Domain Primitives</Product>
<Company>ALTA Software llc.</Company>
<Copyright>Copyright © 2024 ALTA Software llc.</Copyright>
<Version>7.0.2</Version>
<Version>7.1.0</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
40 changes: 19 additions & 21 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
<Project>
<PropertyGroup>
<!-- Enable Central Package Management -->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- Enable Central Package Management -->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageFloatingVersionsEnabled>true</CentralPackageFloatingVersionsEnabled>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageVersion Include="System.Collections.Frozen" Version="7.0.0" />
<PackageVersion Include="Scalar.AspNetCore" Version="2.11.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Verify.SourceGenerators" Version="2.5.0" />
<PackageVersion Include="Verify.Xunit" Version="31.9.3" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.*" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.*" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.*" />
<PackageVersion Include="Verify.SourceGenerators" Version="2.5.*" />
<PackageVersion Include="Verify.Xunit" Version="31.9.3" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.22" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.6" />
<PackageVersion Include="Microsoft.OpenApi" Version="1.6.28" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.*" />
<PackageVersion Include="Microsoft.OpenApi" Version="1.6.*" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.11" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.6" />
<PackageVersion Include="Microsoft.OpenApi" Version="1.6.28" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.*" />
<PackageVersion Include="Microsoft.OpenApi" Version="1.6.*" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.1.0" />
<PackageVersion Include="Microsoft.OpenApi" Version="2.4.2" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.*" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.1.*" />
<PackageVersion Include="Microsoft.OpenApi" Version="2.6.*" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.11" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.12" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.0" />
</ItemGroup>

Expand Down
5 changes: 4 additions & 1 deletion Examples/AltaSoft.DomainPrimitives.Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => { options.AddAllDomainPrimitivesSwaggerMappings(); });
builder.Services.AddSwaggerGen(options =>
{
options.AddAllDomainPrimitivesSwaggerMappings();
});
builder.Services.AddSingleton<CustomerService>();
builder.Services.AddSingleton<TransferService>();
var app = builder.Build();
Expand Down
15 changes: 14 additions & 1 deletion src/AltaSoft.DomainPrimitives.Generator/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,15 @@ private static void Process(GeneratorData data, string ctorCode, DomainPrimitive
}

var builder = new SourceCodeBuilder();
var usings = new List<string>(3) { "System", "System.Numerics", "System.Diagnostics", "System.Runtime.CompilerServices", "AltaSoft.DomainPrimitives", "System.Diagnostics.CodeAnalysis" };
var usings = new List<string>(6)
{
"System",
"System.Numerics",
"System.Diagnostics",
"System.Runtime.CompilerServices",
"AltaSoft.DomainPrimitives",
"System.Diagnostics.CodeAnalysis"
};

if (data.ParentSymbols.Count > 0)
{
Expand All @@ -376,6 +384,11 @@ private static void Process(GeneratorData data, string ctorCode, DomainPrimitive
usings.Add("System.Xml.Serialization");
}

if (data.SerializationFormat is not null)
{
usings.Add("System.Globalization");
}

var needsMathOperators = data.GenerateAdditionOperators || data.GenerateDivisionOperators ||
data.GenerateMultiplyOperators || data.GenerateSubtractionOperators || data.GenerateModulusOperator;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using AltaSoft.DomainPrimitives.Generator.Extensions;
using AltaSoft.DomainPrimitives.Generator.Models;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace AltaSoft.DomainPrimitives.Generator.Helpers;

Expand Down Expand Up @@ -90,7 +91,7 @@ void AddMapping()
.Append("Type = ").Append(typeName).AppendLine(",");

if (!string.IsNullOrEmpty(format))
builder.Append("Format = ").Append(Quote(data.SerializationFormat ?? format)).AppendLine(",");
builder.Append("Format = ").Append(QuoteAndEscape(data.SerializationFormat ?? format)).AppendLine(",");

Comment thread
temonk marked this conversation as resolved.
var title = data.ClassName;
builder.Append("Title = ").Append(Quote(title)).AppendLine(",");
Expand Down Expand Up @@ -145,8 +146,6 @@ void AddMapping()

return;

static string Quote(string? value) => '\"' + value?.Replace("\"", "\"\"") + '\"';

static void ProcessOldVersionOpenApi(List<GeneratorData> types, SourceCodeBuilder builder, CancellationToken cancellationToken)
{
foreach (var data in types)
Comment thread
temonk marked this conversation as resolved.
Expand Down Expand Up @@ -177,7 +176,7 @@ void AddOldMapping(bool isNullable)
.Append("Type = ").Append(Quote(typeName)).AppendLine(",");

if (!string.IsNullOrEmpty(format))
builder.Append("Format = ").Append(Quote(data.SerializationFormat ?? format)).AppendLine(",");
builder.Append("Format = ").Append(QuoteAndEscape(data.SerializationFormat ?? format)).AppendLine(",");

if (isNullable)
builder.AppendLine("Nullable = true,");
Expand Down Expand Up @@ -253,7 +252,7 @@ internal static void ProcessTypeConverter(GeneratorData data, SourceProductionCo
.CloseBracket()
.AppendLine("catch (InvalidDomainValueException ex)")
.OpenBracket()
.Append("throw new FormatException(\"Cannot parse ").AppendLine("\", ex);")
.Append("throw new FormatException(\"Cannot parse ").Append(data.ClassName).AppendLine("\", ex);")
.CloseBracket()
.CloseBracket()
.NewLine()
Expand Down Expand Up @@ -430,7 +429,7 @@ internal static void ProcessJsonConverter(GeneratorData data, SourceProductionCo
builder.AppendInheritDoc().AppendLine($"public override void Write(Utf8JsonWriter writer, {data.ClassName} value, JsonSerializerOptions options)")
.OpenBracket()
.AppendLineIf(data.SerializationFormat is null, $"JsonInternalConverters.{converterName}Converter.Write(writer, ({data.PrimitiveTypeFriendlyName})value, options);")
.AppendLineIf(data.SerializationFormat is not null, $"writer.WriteStringValue(value.ToString(\"{data.SerializationFormat}\", CultureInfo.InvariantCulture));")
.AppendLineIf(data.SerializationFormat is not null, $"writer.WriteStringValue(value.ToString({QuoteAndEscape(data.SerializationFormat)}, CultureInfo.InvariantCulture));")
.CloseBracket()
.NewLine();

Expand Down Expand Up @@ -472,7 +471,7 @@ internal static void ProcessJsonConverter(GeneratorData data, SourceProductionCo
.Append("public override void WriteAsPropertyName(Utf8JsonWriter writer, ").Append(data.ClassName).AppendLine(" value, JsonSerializerOptions options)")
.OpenBracket()
.AppendLineIf(data.SerializationFormat is null, $"JsonInternalConverters.{converterName}Converter.WriteAsPropertyName(writer, ({data.PrimitiveTypeFriendlyName})value, options);")
.AppendLineIf(data.SerializationFormat is not null, $"writer.WritePropertyName(value.ToString(\"{data.SerializationFormat}\", CultureInfo.InvariantCulture));")
.AppendLineIf(data.SerializationFormat is not null, $"writer.WritePropertyName(value.ToString({QuoteAndEscape(data.SerializationFormat)}, CultureInfo.InvariantCulture));")
.CloseBracket();

builder.CloseBracket();
Expand Down Expand Up @@ -885,7 +884,7 @@ public static void GenerateParsable(GeneratorData data, SourceCodeBuilder builde
else
{
builder.Append($"{underlyingType}.")
.AppendIfElse(format is null, "Parse(s, provider)", $"ParseExact(s, \"{format}\", provider)");
.AppendIfElse(format is null, "Parse(s, provider)", $"ParseExact(s, {QuoteAndEscape(format!)}, provider)");
}

builder.AppendLine(!data.GenerateImplicitOperators ? ");" : ";");
Expand All @@ -912,8 +911,14 @@ public static void GenerateParsable(GeneratorData data, SourceCodeBuilder builde
}
else
{
var style = "";
if (format is not null)
{
style = data.UnderlyingType == DomainPrimitiveUnderlyingType.TimeSpan ? "TimeSpanStyles.None" : "DateTimeStyles.None";
}

builder.AppendIf(format is null, $"if (!{underlyingType}.TryParse(s, provider, out var value))")
.AppendIf(format is not null, $"if (!{underlyingType}.TryParseExact(s, \"{format}\", out var value))");
.AppendIf(format is not null, $"if (!{underlyingType}.TryParseExact(s, {QuoteAndEscape(format)}, provider, {style}, out var value))");
}

builder.OpenBracket()
Expand Down Expand Up @@ -1092,18 +1097,28 @@ public static void GenerateIXmlSerializableMethods(GeneratorData data, SourceCod
builder.AppendLine("public XmlSchema? GetSchema() => null;")
.NewLine();

var method = data.PrimitiveTypeFriendlyName switch
string method;

if (data.UnderlyingType.IsDateOrTime() && data.SerializationFormat is not null)
{
"string" => "ReadElementContentAsString",
"bool" => "ReadElementContentAsBoolean",
"DateOnly" => "ReadElementContentAsDateOnly",
_ => $"ReadElementContentAs<{data.PrimitiveTypeFriendlyName}>"
};
method = "ReadElementContentAs" + data.PrimitiveTypeFriendlyName;
}
else
{
method = data.PrimitiveTypeFriendlyName switch
{
"string" => "ReadElementContentAsString",
"bool" => "ReadElementContentAsBoolean",
"DateOnly" => "ReadElementContentAsDateOnly",
_ => $"ReadElementContentAs<{data.PrimitiveTypeFriendlyName}>"
};
}
Comment thread
temonk marked this conversation as resolved.

builder.AppendInheritDoc();
builder.AppendLine("public void ReadXml(XmlReader reader)")
.OpenBracket()
.Append("var value = reader.").Append(method).AppendLine("();")
.Append("var value = reader.").Append(method)
.AppendLineIfElse(data.SerializationFormat is not null, $"({QuoteAndEscape(data.SerializationFormat)});", "();")
.AppendLine("ValidateOrThrow(value);")
.AppendLine("System.Runtime.CompilerServices.Unsafe.AsRef(in _value) = value;")
.AppendLine("System.Runtime.CompilerServices.Unsafe.AsRef(in _isInitialized) = true;")
Expand All @@ -1118,7 +1133,10 @@ public static void GenerateIXmlSerializableMethods(GeneratorData data, SourceCod
if (data.SerializationFormat is null)
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteValue((({data.PrimitiveTypeFriendlyName}){data.FieldName}).ToXmlString());");
else
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteString({data.FieldName}.ToString(\"{data.SerializationFormat}\"));");
builder.AppendLine($"public void WriteXml(XmlWriter writer) => writer.WriteString({data.FieldName}.ToString({QuoteAndEscape(data.SerializationFormat)}));");
builder.NewLine();
}

private static string Quote(string? value) => '\"' + value?.Replace("\"", "\"\"") + '\"';
private static string QuoteAndEscape(string? value) => value is null ? "\"\"" : SymbolDisplay.FormatLiteral(value, quote: true);
}
98 changes: 98 additions & 0 deletions src/AltaSoft.DomainPrimitives/XmlReaderExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,44 @@ public static T ReadElementContentAs<T>(this XmlReader reader) where T : IParsab
return T.Parse(reader.ReadElementContentAsString(), CultureInfo.InvariantCulture);
}

/// <summary>
/// Reads the content of the current XML element as a <see cref="DateTime"/> value.
/// </summary>
/// <param name="reader">The <see cref="XmlReader"/> instance.</param>
/// <param name="serializationFormat">serialization format to be used</param>
/// <returns>
/// A <see cref="DateTime"/> value parsed from the current element's content.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DateTime ReadElementContentAsDateTime(this XmlReader reader, string serializationFormat)
{
var str = reader.ReadElementContentAsString();

if (DateTime.TryParseExact(str, serializationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
return result;

return DateTime.Parse(str, CultureInfo.InvariantCulture);
Comment thread
temonk marked this conversation as resolved.
}

/// <summary>
/// Reads the content of the current XML element as a <see cref="TimeOnly"/> value.
/// </summary>
/// <param name="reader">The <see cref="XmlReader"/> instance.</param>
/// <param name="serializationFormat">serialization format to be used</param>
/// <returns>
/// A <see cref="TimeOnly"/> value parsed from the current element's content.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TimeOnly ReadElementContentAsTimeOnly(this XmlReader reader, string serializationFormat)
{
var str = reader.ReadElementContentAsString();

if (TimeOnly.TryParseExact(str, serializationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
return result;

return TimeOnly.Parse(str, CultureInfo.InvariantCulture);
Comment thread
temonk marked this conversation as resolved.
}

/// <summary>
/// Reads the content of the current XML element as a <see cref="DateOnly"/> value.
/// </summary>
Expand All @@ -42,4 +80,64 @@ public static DateOnly ReadElementContentAsDateOnly(this XmlReader reader)

return DateOnly.FromDateTime(DateTime.Parse(str, CultureInfo.InvariantCulture));
}

/// <summary>
/// Reads the content of the current XML element as a <see cref="DateOnly"/> value.
/// </summary>
/// <param name="reader">The <see cref="XmlReader"/> instance.</param>
/// <param name="serializationFormat">serialization format to be used</param>
/// <returns>
/// A <see cref="DateOnly"/> value parsed from the current element's content.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DateOnly ReadElementContentAsDateOnly(this XmlReader reader, string serializationFormat)
{
var str = reader.ReadElementContentAsString();

if (DateOnly.TryParseExact(str, serializationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
return result;

if (DateOnly.TryParse(str, CultureInfo.InvariantCulture, out result))
return result;

return DateOnly.FromDateTime(DateTime.Parse(str, CultureInfo.InvariantCulture));
Comment thread
temonk marked this conversation as resolved.
}

/// <summary>
/// Reads the content of the current XML element as a <see cref="DateTimeOffset" /> value.
/// </summary>
/// <param name="reader">The <see cref="XmlReader"/> instance.</param>
/// <param name="serializationFormat">serialization format to be used</param>
/// <returns>
/// A <see cref="DateTimeOffset"/> value parsed from the current element's content.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DateTimeOffset ReadElementContentAsDateTimeOffset(this XmlReader reader, string serializationFormat)
{
var str = reader.ReadElementContentAsString();

if (DateTimeOffset.TryParseExact(str, serializationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
return result;

return DateTimeOffset.Parse(str, CultureInfo.InvariantCulture);
}

/// <summary>
/// Reads the content of the current XML element as a <see cref="TimeSpan" /> value.
/// </summary>
/// <param name="reader">The <see cref="XmlReader"/> instance.</param>
/// <param name="serializationFormat">serialization format to be used</param>
/// <returns>
/// A <see cref="TimeSpan"/> value parsed from the current element's content.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TimeSpan ReadElementContentAsTimeSpan(this XmlReader reader, string serializationFormat)
{
var str = reader.ReadElementContentAsString();

if (TimeSpan.TryParseExact(str, serializationFormat, CultureInfo.InvariantCulture, TimeSpanStyles.None, out var result))
return result;

return TimeSpan.Parse(str, CultureInfo.InvariantCulture);
Comment thread
temonk marked this conversation as resolved.
}
}
Loading