Skip to content

Commit 7f382ee

Browse files
committed
feat: Added more interfaces for the generic constraints of packets
1 parent 24ac7a9 commit 7f382ee

File tree

6 files changed

+132
-1
lines changed

6 files changed

+132
-1
lines changed

src/TrProtocol.SerializerGenerator/Internal/Serialization/ProtocolModelBuilder.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp;
23
using TrProtocol.Attributes;
34
using TrProtocol.Interfaces;
45
using TrProtocol.SerializerGenerator.Internal.Diagnostics;
@@ -81,6 +82,58 @@ public static ProtocolTypeData BuildProtocolTypeInfo(CompilationContext context,
8182
model.HasExtraData = true;
8283
}
8384

85+
if (modelSym.AllInterfaces.Any(i => i.Name == nameof(INonSideSpecific))) {
86+
var location = baseList!.GetLocation();
87+
var foundInterface = baseList!.Types.First(t => t.ToString() == nameof(INonSideSpecific));
88+
if (foundInterface is not null) {
89+
location = foundInterface.GetLocation();
90+
}
91+
throw new DiagnosticException(
92+
Diagnostic.Create(
93+
new DiagnosticDescriptor(
94+
"SCG32",
95+
"Do not manually implement INonSideSpecific",
96+
"The interface 'INonSideSpecific' is automatically provided by the source generator. Do not declare it explicitly in the inheritance list.",
97+
"SourceGeneration",
98+
DiagnosticSeverity.Error,
99+
true),
100+
location));
101+
}
102+
if (modelSym.AllInterfaces.Any(i => i.Name == nameof(INonLengthAware))) {
103+
var location = baseList!.GetLocation();
104+
var foundInterface = baseList!.Types.First(t => t.ToString() == nameof(INonLengthAware));
105+
if (foundInterface is not null) {
106+
location = foundInterface.GetLocation();
107+
}
108+
throw new DiagnosticException(
109+
Diagnostic.Create(
110+
new DiagnosticDescriptor(
111+
"SCG32",
112+
"Do not manually implement INonLengthAware",
113+
"The interface 'INonLengthAware' is automatically provided by the source generator. Do not declare it explicitly in the inheritance list.",
114+
"SourceGeneration",
115+
DiagnosticSeverity.Error,
116+
true),
117+
location));
118+
}
119+
if (modelSym.AllInterfaces.Any(i => i.Name == nameof(IManagedPacket))) {
120+
var location = baseList!.GetLocation();
121+
var foundInterface = baseList!.Types.First(t => t.ToString() == nameof(IManagedPacket));
122+
if (foundInterface is not null) {
123+
location = foundInterface.GetLocation();
124+
}
125+
throw new DiagnosticException(
126+
Diagnostic.Create(
127+
new DiagnosticDescriptor(
128+
"SCG32",
129+
"Do not manually implement IManagedPacket",
130+
"The interface 'IManagedPacket' is automatically provided by the source generator. Do not declare it explicitly in the inheritance list.",
131+
"SourceGeneration",
132+
DiagnosticSeverity.Error,
133+
true),
134+
location));
135+
}
136+
84137
if (modelSym.AllInterfaces.Any(i => i.Name == nameof(ISideSpecific))) {
85138
model.IsSideSpecific = true;
86139
}

src/TrProtocol.SerializerGenerator/Internal/SyntaxTemplates/TypeDefinitionWriter.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.CodeAnalysis;
22
using System.Runtime.InteropServices;
3+
using TrProtocol.Interfaces;
34
using TrProtocol.SerializerGenerator.Internal.Extensions;
45
using TrProtocol.SerializerGenerator.Internal.Models;
56
using TrProtocol.SerializerGenerator.Internal.Utilities;
@@ -17,7 +18,24 @@ public static BlockNode WriteTypeDefinition(this BlockNode namespaceBlock, Proto
1718
TypeKind.Interface => "interface",
1819
_ => "class",
1920
};
20-
namespaceBlock.Write($"public unsafe partial {typeKind} {typeData.TypeName} ");
21+
22+
string inheritance = "";
23+
List<string> interfaces = [];
24+
if (typeData.IsNetPacket) {
25+
if (!typeData.IsLengthAware) {
26+
interfaces.Add(nameof(INonLengthAware));
27+
}
28+
if (!typeData.IsSideSpecific) {
29+
interfaces.Add(nameof(INonSideSpecific));
30+
}
31+
if (!typeData.DefSymbol.IsUnmanagedType) {
32+
interfaces.Add(nameof(IManagedPacket));
33+
}
34+
}
35+
if (interfaces.Count > 0) {
36+
inheritance = $": {string.Join(", ", interfaces)} ";
37+
}
38+
namespaceBlock.Write($"public unsafe partial {typeKind} {typeData.TypeName} {inheritance}");
2139
return namespaceBlock.BlockWrite((classNode) => { });
2240
}
2341
}

src/TrProtocol.Shared/Interfaces/ILengthAware.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,20 @@ public interface ILengthAware
2121
unsafe void ReadContent(ref void* ptr, void* end_ptr);
2222
unsafe void WriteContent(ref void* ptr);
2323
}
24+
/// <summary>
25+
/// Marker interface indicating that the type is not <see cref="ILengthAware"/>.
26+
/// </summary>
27+
/// <remarks>
28+
/// <para>
29+
/// This interface is automatically added by source generators to all types that do <b>not</b> implement
30+
/// <see cref="ILengthAware"/>. Developers do <b>not</b> need to manually implement this interface.
31+
/// </para>
32+
/// <para>
33+
/// Its primary purpose is to enable generic constraints that effectively express
34+
/// <c>not ILengthAware</c> in high-performance scenarios involving <c>struct</c>-based type specialization.
35+
/// This allows source generators or AOT-optimized code paths to distinguish between
36+
/// <see cref="ILengthAware"/> and non-specific types at compile time.
37+
/// </para>
38+
/// </remarks>
39+
public interface INonLengthAware { }
2440
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace TrProtocol.Interfaces
6+
{
7+
/// <summary>
8+
/// Marker interface indicating that the type is a managed packet type
9+
/// and therefore does <b>not</b> satisfy the <c>unmanaged</c> constraint.
10+
/// </summary>
11+
/// <remarks>
12+
/// <para>
13+
/// This interface is used to explicitly mark types that require garbage-collected (managed)
14+
/// memory and cannot be used in contexts constrained to <c>unmanaged</c> types.
15+
/// </para>
16+
/// <para>
17+
/// It is particularly useful in high-performance or source-generated serialization scenarios
18+
/// where type paths must distinguish between <c>unmanaged</c> types and their managed counterparts.
19+
/// </para>
20+
/// <para>
21+
/// Unlike typical marker interfaces, <see cref="IManagedPacket"/> does not have an explicit opposite like
22+
/// <c>IUnmanagedPacket</c>; instead, its semantic opposite is expressed via the C# <c>unmanaged</c> generic constraint.
23+
/// </para>
24+
/// </remarks>
25+
public interface IManagedPacket { }
26+
}

src/TrProtocol.Shared/Interfaces/ISideSpecific.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,21 @@ public interface ISideSpecific
1313
/// </summary>
1414
public bool IsServerSide { get; set; }
1515
}
16+
/// <summary>
17+
/// Marker interface indicating that the type is not <see cref="ISideSpecific"/>.
18+
/// </summary>
19+
/// <remarks>
20+
/// <para>
21+
/// This interface is automatically added by source generators to all types that do <b>not</b> implement
22+
/// <see cref="ISideSpecific"/>. Developers do <b>not</b> need to manually implement this interface.
23+
/// </para>
24+
/// <para>
25+
/// Its primary purpose is to enable generic constraints that effectively express
26+
/// <c>not ISideSpecific</c> in high-performance scenarios involving <c>struct</c>-based type specialization.
27+
/// This allows source generators or AOT-optimized code paths to distinguish between
28+
/// <see cref="ISideSpecific"/> and non-specific types at compile time.
29+
/// </para>
30+
/// </remarks>
31+
public interface INonSideSpecific { }
32+
1633
}

src/TrProtocol.Shared/TrProtocol.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IBinarySerializable.cs" />
3737
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IExtraData.cs" />
3838
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\ILengthAware.cs" />
39+
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IManagedStruct.cs" />
3940
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IPackedSerializable.cs" />
4041
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IRepeatElement.cs" />
4142
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\ISerializableView.cs" />

0 commit comments

Comments
 (0)