Skip to content
Closed
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
33 changes: 29 additions & 4 deletions Modspec.Model/Generation/ModspecModelGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ namespace {schema.Name};
List<string> bufferInitialisers = [];
List<string> fieldInitialisers = [];
List<ConstructorParameter> constructorParams = [];
WriteGroups(schema.Groups, mainWriter, appendixWriter, bufferInitialisers, fieldInitialisers, constructorParams);
List<string> bitfieldPointsWithLevels = [];
WriteGroups(schema.Groups, mainWriter, appendixWriter, bufferInitialisers, fieldInitialisers, constructorParams, bitfieldPointsWithLevels: bitfieldPointsWithLevels);

foreach (RepeatingGroup repeatingGroup in schema.RepeatingGroups)
{
Expand Down Expand Up @@ -137,6 +138,11 @@ namespace {schema.Name};
mainWriter.WriteLine("}");
mainWriter.WriteLine();

if (schema.GenerateChangeDetectionFactory && bitfieldPointsWithLevels.Count > 0)
{
WriteChangeDetectionFactory(schema.Name, bitfieldPointsWithLevels, appendixWriter);
}

result = mainWriter.ToString() + appendixWriter.ToString();
return true;
}
Expand Down Expand Up @@ -168,7 +174,7 @@ private static void WriteFieldsAndConstructor(string name, StringWriter mainWrit
mainWriter.WriteLine($"{indent}\t}}");
}

private static void WriteGroups(IReadOnlyCollection<Group> groups, StringWriter mainWriter, StringWriter appendixWriter, List<string> bufferInitialisers, List<string> fieldInitialisers, List<ConstructorParameter> constructorParams, string indent = "", string readOffsetField = "")
private static void WriteGroups(IReadOnlyCollection<Group> groups, StringWriter mainWriter, StringWriter appendixWriter, List<string> bufferInitialisers, List<string> fieldInitialisers, List<ConstructorParameter> constructorParams, string indent = "", string readOffsetField = "", List<string>? bitfieldPointsWithLevels = null)
{
foreach (Group group in groups)
{
Expand All @@ -185,7 +191,7 @@ private static void WriteGroups(IReadOnlyCollection<Group> groups, StringWriter
// supplied count of elements, rather than max size of array)
throw new InvalidOperationException($"An array must be the last (or only) element in a group.");
}
WritePoint(point, bufferName, group.Table, mainWriter, appendixWriter, fieldInitialisers, constructorParams, ref maxOffset, ref bufferSize, indent);
WritePoint(point, bufferName, group.Table, mainWriter, appendixWriter, fieldInitialisers, constructorParams, ref maxOffset, ref bufferSize, indent, bitfieldPointsWithLevels);
}
if (String.IsNullOrEmpty(bufferSize))
{
Expand All @@ -209,7 +215,7 @@ private static void WriteGroups(IReadOnlyCollection<Group> groups, StringWriter
}
}

private static void WritePoint(Point point, string bufferName, Table table, StringWriter mainWriter, StringWriter appendixWriter, List<string> fieldInitialisers, List<ConstructorParameter> constructorParams, ref int maxOffset, ref string bufferSize, string indent = "")
private static void WritePoint(Point point, string bufferName, Table table, StringWriter mainWriter, StringWriter appendixWriter, List<string> fieldInitialisers, List<ConstructorParameter> constructorParams, ref int maxOffset, ref string bufferSize, string indent = "", List<string>? bitfieldPointsWithLevels = null)
{
string type;
string readMethod;
Expand Down Expand Up @@ -337,6 +343,7 @@ private static void WritePoint(Point point, string bufferName, Table table, Stri
appendixWriter.WriteLine();
if (isFlags && masksByLevel.Count > 0)
{
bitfieldPointsWithLevels?.Add(point.Name);
appendixWriter.WriteLine($"public static class {point.Name}Extensions");
appendixWriter.WriteLine("{");
appendixWriter.WriteLine($"\tpublic static Level GetLevel(this {point.Name} self)");
Expand Down Expand Up @@ -421,6 +428,24 @@ private static void WritePoint(Point point, string bufferName, Table table, Stri
maxOffset += point.SizeInBytes * (point.Count?.MaxValue ?? 1);
}

private static void WriteChangeDetectionFactory(string schemaName, List<string> points, StringWriter writer)
{
string clientName = $"{schemaName}Client";
writer.WriteLine($"public static class {schemaName}ChangeDetection");
writer.WriteLine("{");
writer.WriteLine($"\tpublic static BitfieldChangeDetector<{clientName}> CreateDetector()");
writer.WriteLine("\t{");
writer.WriteLine($"\t\treturn new BitfieldChangeDetector<{clientName}>()");
for (int i = 0; i < points.Count; i++)
{
string terminator = i < points.Count - 1 ? "" : ";";
writer.WriteLine($"\t\t\t.Track(c => c.{points[i]}, v => v.GetLevel()){terminator}");
}
writer.WriteLine("\t}");
writer.WriteLine("}");
writer.WriteLine();
}

private static string ToFieldName(string name)
{
Span<char> result = stackalloc char[name.Length + 1];
Expand Down
13 changes: 13 additions & 0 deletions Modspec.Model/Schema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ public class Schema
/// </summary>
public List<RepeatingGroup> RepeatingGroups { get; set; } = [];

/// <summary>
/// If true, the generator will emit a static factory class for creating a
/// <c>BitfieldChangeDetector</c> that tracks all bitfield points with level annotations.
/// The consuming project must provide a <c>BitfieldChangeDetector&lt;TClient&gt;</c> class
/// in the <c>Modspec.Model</c> namespace with the following method:
/// <code>
/// BitfieldChangeDetector&lt;TClient&gt; Track&lt;T&gt;(Func&lt;TClient, T&gt; getter, Func&lt;T, Level&gt; getLevel)
/// where T : struct, Enum
/// </code>
/// The <c>Track</c> method must return the detector instance to support fluent chaining.
/// </summary>
public bool GenerateChangeDetectionFactory { get; set; }

public void Serialise(Stream stream)
{
JsonSerializer.Serialize(stream, this, Options);
Expand Down
Loading