Added tool to add JsonExtensionData annotation to all model classes
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace JsonExtensionDataGenerator;
|
||||||
|
|
||||||
|
public class JsonExtensionDataGeneratorLauncher
|
||||||
|
{
|
||||||
|
private static readonly Regex _recordAndClassRegex = new("^(public record |public class )", RegexOptions.Multiline);
|
||||||
|
private static readonly Regex _endRecordClassRegex = new("^}", RegexOptions.Multiline);
|
||||||
|
private static readonly Regex _startRecordClassRegex = new("^{", RegexOptions.Multiline);
|
||||||
|
private const int StartRecordClassOffset = 3;
|
||||||
|
|
||||||
|
private static readonly Regex _extensionFinding =
|
||||||
|
new(
|
||||||
|
// https://regexr.com/8f5gf
|
||||||
|
"^(public){0,1} (record|class) (\\w+(<(\\w+(,){0,1})+>){0,1})(\\(.*\\)){0,1}[\r\n ]*:[\r\n ]*(\\w+(<(\\w+(,){0,1})+>){0,1}([\r\n ]*,[\r\n ]*)*)+"
|
||||||
|
, RegexOptions.Multiline);
|
||||||
|
|
||||||
|
private static readonly Regex _extensionCleanup = new(",.*");
|
||||||
|
|
||||||
|
private const string Insertion =
|
||||||
|
" [JsonExtensionData]\r\n public Dictionary<string, object> ExtensionData { get; set; }\r\n\r\n";
|
||||||
|
|
||||||
|
private const string Using = "using System.Text.Json.Serialization;\r\n";
|
||||||
|
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var modelFiles = LoadModelFiles();
|
||||||
|
foreach (var modelFile in modelFiles)
|
||||||
|
{
|
||||||
|
ProcessFile(modelFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessFile(string modelFile)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Processing file: {modelFile}...");
|
||||||
|
var fileName = Path.GetFileName(modelFile);
|
||||||
|
var content = File.ReadAllText(modelFile);
|
||||||
|
if (!content.Contains("public record ") && !content.Contains("public class "))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"File {fileName} doesnt contain any records or classes, skipping...");
|
||||||
|
// probably an enum or interface
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var classesAndRecordsToProcessCount = _recordAndClassRegex.Matches(content).Count;
|
||||||
|
Console.WriteLine($"Found {classesAndRecordsToProcessCount} records or classes for {fileName}");
|
||||||
|
var firstTimeFlag = false;
|
||||||
|
var currentIndex = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var i = 0; i < classesAndRecordsToProcessCount; i++)
|
||||||
|
{
|
||||||
|
var startIndex = FindNextClassStartIndex(content, currentIndex);
|
||||||
|
var endIndex = FindEndClassIndex(content, startIndex);
|
||||||
|
currentIndex = endIndex;
|
||||||
|
// Check if this class already has the tag anywhere
|
||||||
|
if (content.Substring(startIndex, endIndex - startIndex).Contains("[JsonExtensionData]"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Class index {i} for {fileName} already contains [JsonExtensionData], skipping class...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetExtensions(content, startIndex, endIndex, out var extensions))
|
||||||
|
{
|
||||||
|
if (extensions.Any(e => !e.StartsWith("I")))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Class index {i} for {fileName} extends a parent class, skipping...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we know for sure that we need to insert the [JsonExtensionData]
|
||||||
|
if (!firstTimeFlag)
|
||||||
|
{
|
||||||
|
if (!content.Contains("using System.Text.Json.Serialization;"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Class index {i} for {fileName} doesnt contain using for Json.Serialization. Adding.");
|
||||||
|
// insert the using and adjust the indexes
|
||||||
|
content = Using + content;
|
||||||
|
startIndex += Using.Length;
|
||||||
|
endIndex += Using.Length;
|
||||||
|
currentIndex = endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstTimeFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to add StartRecordClassOffset to offset the EOL
|
||||||
|
var insertionIndex = _startRecordClassRegex.Match(content, startIndex, endIndex - startIndex).Index +
|
||||||
|
StartRecordClassOffset;
|
||||||
|
content = content.Insert(insertionIndex, Insertion);
|
||||||
|
Console.WriteLine($"Class index {i} for {fileName} processed.");
|
||||||
|
currentIndex += Insertion.Length;
|
||||||
|
}
|
||||||
|
var stream = File.Open(modelFile, FileMode.Open);
|
||||||
|
stream.SetLength(0);
|
||||||
|
stream.Close();
|
||||||
|
File.WriteAllText(modelFile, content);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error caught processing {modelFile} file\n{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetExtensions(
|
||||||
|
string content,
|
||||||
|
int startIndex,
|
||||||
|
int endIndex,
|
||||||
|
out IEnumerable<string> extensions
|
||||||
|
)
|
||||||
|
{
|
||||||
|
extensions = null;
|
||||||
|
var match = _extensionFinding.Match(content, startIndex, endIndex - startIndex);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var extensionsGroup = match.Groups[8];
|
||||||
|
extensions = extensionsGroup.Captures.Select(c => _extensionCleanup.Replace(c.Value, ""));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindEndClassIndex(string content, int currentIndex)
|
||||||
|
{
|
||||||
|
// we do +3 cause thats the length of what we are searching for
|
||||||
|
return _endRecordClassRegex.Match(content, currentIndex).Index + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindNextClassStartIndex(string content, int currentIndex)
|
||||||
|
{
|
||||||
|
return _recordAndClassRegex.Match(content, currentIndex).Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> LoadModelFiles()
|
||||||
|
{
|
||||||
|
var projectDir = Directory.GetParent("./").Parent.Parent.Parent.Parent.Parent;
|
||||||
|
var modelsDir = Path.Combine(projectDir.FullName, "Libraries", "SPTarkov.Server.Core", "Models");
|
||||||
|
return Directory.GetFiles(modelsDir, "*.cs", SearchOption.AllDirectories);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JsonExtensionData", "JsonEx
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonExtensionData.Fody", "Libraries\FodyWeavers\JsonExtensionData.Fody\JsonExtensionData.Fody.csproj", "{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonExtensionData.Fody", "Libraries\FodyWeavers\JsonExtensionData.Fody\JsonExtensionData.Fody.csproj", "{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonExtensionDataGenerator", "Tools\JsonExtensionDataGenerator\JsonExtensionDataGenerator.csproj", "{6F4670CD-6861-47A8-9A02-2B63AD73A929}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -86,6 +88,10 @@ Global
|
|||||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Release|Any CPU.Build.0 = Release|Any CPU
|
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6F4670CD-6861-47A8-9A02-2B63AD73A929}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6F4670CD-6861-47A8-9A02-2B63AD73A929}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6F4670CD-6861-47A8-9A02-2B63AD73A929}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6F4670CD-6861-47A8-9A02-2B63AD73A929}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -101,5 +107,6 @@ Global
|
|||||||
{61D0AD50-5B86-41E6-8B19-F11944AC3EE4} = {F084DDFD-89F3-44F9-89C3-5CA11F4CDEEF}
|
{61D0AD50-5B86-41E6-8B19-F11944AC3EE4} = {F084DDFD-89F3-44F9-89C3-5CA11F4CDEEF}
|
||||||
{2938742B-34FA-47F2-A50B-E0470FB1D807} = {61D0AD50-5B86-41E6-8B19-F11944AC3EE4}
|
{2938742B-34FA-47F2-A50B-E0470FB1D807} = {61D0AD50-5B86-41E6-8B19-F11944AC3EE4}
|
||||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8} = {2938742B-34FA-47F2-A50B-E0470FB1D807}
|
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8} = {2938742B-34FA-47F2-A50B-E0470FB1D807}
|
||||||
|
{6F4670CD-6861-47A8-9A02-2B63AD73A929} = {587959C2-5AFA-4B77-B327-566610F9A289}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
Reference in New Issue
Block a user