164 lines
7.2 KiB
C#
164 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Microsoft.CodeAnalysis;
|
|
|
|
namespace AscNet.Table
|
|
{
|
|
[Generator]
|
|
public class TableGenerator : ISourceGenerator
|
|
{
|
|
public void Execute(GeneratorExecutionContext context)
|
|
{
|
|
List<object> fails = new();
|
|
foreach (var table in context.AdditionalFiles.Where(x => x.Path.EndsWith(".tsv")))
|
|
{
|
|
try
|
|
{
|
|
string ns = string.Join("", Path.GetDirectoryName(table.Path)?.Split(new string[] { "table" }, StringSplitOptions.None).Skip(1)!)
|
|
.Replace('\\', '/').Replace('/', '.');
|
|
|
|
string fileTsv = table.GetText()?.ToString() ?? string.Empty;
|
|
|
|
IEnumerable<string> tsvLines = fileTsv.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
string[] nameList = tsvLines.First().Split('\t');
|
|
List<string[]> resolvedTypes = new();
|
|
|
|
foreach (var items in tsvLines.Skip(1).Select(x => x.Split('\t')))
|
|
{
|
|
List<string> types = new();
|
|
foreach (var item in items)
|
|
{
|
|
if (string.IsNullOrEmpty(item))
|
|
{
|
|
types.Add("global::System.String");
|
|
continue;
|
|
}
|
|
else if (int.TryParse(item, out var res))
|
|
{
|
|
types.Add("global::System.Int32");
|
|
continue;
|
|
}
|
|
types.Add("global::System.String");
|
|
}
|
|
resolvedTypes.Add(types.ToArray());
|
|
}
|
|
|
|
Dictionary<string, string> properties = new();
|
|
Dictionary<string, int> listCount = new();
|
|
|
|
for (int i = 0; i < nameList.Length; i++)
|
|
{
|
|
string headName = nameList[i];
|
|
if (string.IsNullOrEmpty(headName))
|
|
continue;
|
|
|
|
string[] types = resolvedTypes.Select(x => x[i]).ToArray();
|
|
string propName = headName.Split('[').First();
|
|
|
|
|
|
string resolvedType = types.All(x => x.Equals(types.First())) ? types.First() : "global::System.String";
|
|
if (headName.Contains('['))
|
|
{
|
|
if (listCount.ContainsKey(propName))
|
|
listCount[propName] += 1;
|
|
else
|
|
listCount.Add(propName, 1);
|
|
resolvedType = $"global::System.Collections.Generic.List<{resolvedType}>";
|
|
}
|
|
|
|
if (!properties.ContainsKey(propName))
|
|
properties.Add(propName, $"public {resolvedType} {propName} {{ get; set; }}");
|
|
}
|
|
|
|
string file = $@"// <auto-generated/>
|
|
namespace AscNet.Table{ns}
|
|
{{
|
|
#nullable enable
|
|
#pragma warning disable CS8618, CS8602 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
public class {Path.GetFileName(table.Path).Split('.').First()}Table
|
|
{{
|
|
{string.Join("\r\n\t\t", properties.Values)}
|
|
}}
|
|
|
|
public class {Path.GetFileName(table.Path).Split('.').First()}TableReader : global::AscNet.Common.Util.TableReader<{Path.GetFileName(table.Path).Split('.').First()}TableReader, {Path.GetFileName(table.Path).Split('.').First()}Table>
|
|
{{
|
|
protected override string FilePath {{ get {{ return ""{string.Join("", table.Path.Replace("\\", "/").Split(new string[] {"/Resources/"}, StringSplitOptions.None).Skip(1))}""; }} }}
|
|
|
|
public override void Load()
|
|
{{
|
|
string tsvStr = global::System.IO.File.ReadAllText(FilePath);
|
|
string[] tsvValues = tsvStr.Split('\t');
|
|
string[] csvValues = new string[tsvValues.Length];
|
|
for (int i = 0; i < tsvValues.Length; i++)
|
|
{{
|
|
if (tsvValues[i].Contains("",""))
|
|
{{
|
|
csvValues[i] = ""\"""" + tsvValues[i].Replace(""\"""", ""\""\"""") + ""\"""";
|
|
}}
|
|
else
|
|
{{
|
|
csvValues[i] = tsvValues[i];
|
|
}}
|
|
}}
|
|
using var reader = new global::System.IO.StringReader(string.Join("","", csvValues));
|
|
using var csv = new global::CsvHelper.CsvReader(reader, new global::CsvHelper.Configuration.CsvConfiguration(global::System.Globalization.CultureInfo.InvariantCulture) {{ BadDataFound = null, HeaderValidated = null, MissingFieldFound = null }});
|
|
csv.Context.RegisterClassMap<{Path.GetFileName(table.Path).Split('.').First()}TableMap>();
|
|
All = csv.GetRecords<{Path.GetFileName(table.Path).Split('.').First()}Table>().ToList();
|
|
}}
|
|
{string.Join("\r\n", properties.Values.Select(x => x.Split(' ')).Where(x => !x[1].Contains("<")).Select(property => $@"
|
|
public {Path.GetFileName(table.Path).Split('.').First()}Table? From{property[2]}({property[1]} val)
|
|
{{
|
|
return All.FirstOrDefault(x => x.{property[2]} == val);
|
|
}}
|
|
"))}
|
|
}}
|
|
|
|
public sealed class {Path.GetFileName(table.Path).Split('.').First()}TableMap : global::CsvHelper.Configuration.ClassMap<{Path.GetFileName(table.Path).Split('.').First()}Table>
|
|
{{
|
|
public {Path.GetFileName(table.Path).Split('.').First()}TableMap()
|
|
{{
|
|
{string.Join("\r\n\t\t\t", properties.Keys.Where(x => !listCount.ContainsKey(x)).Select(x => $"Map(m => m.{x}).Name(\"{x}\");"))}
|
|
{string.Join("\r\n\t\t\t", listCount.Keys.Select(x => $@"
|
|
Map(m => m.{x}).Convert(args =>
|
|
{{
|
|
{properties[x].Split(' ')[1]} tags = new {properties[x].Split(' ')[1]}();
|
|
{(nameList.Contains($"{x}[0]") ? $"for (int i = 0; i < {listCount[x]}; i++)" : $"for (int i = 1; i <= {listCount[x]}; i++)")}
|
|
{{
|
|
string? tagValue = args.Row.GetField<string>($""{x}[{{i}}]"");
|
|
if (!string.IsNullOrEmpty(tagValue))
|
|
{{
|
|
{(properties[x].Split('<')[1].StartsWith("global::System.Int32") ? @"
|
|
if (int.TryParse(tagValue, out int tag))
|
|
{
|
|
tags.Add(tag);
|
|
}": @"
|
|
tags.Add(tagValue);
|
|
")}
|
|
}}
|
|
}}
|
|
|
|
return tags;
|
|
}});
|
|
"))}
|
|
}}
|
|
}}
|
|
}}";
|
|
context.AddSource($"AscNet.Table{ns}.{Path.GetFileName(table.Path).Split('.').First()}.g.cs", file);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fails.Add(new
|
|
{
|
|
msg = ex.Message,
|
|
file = table.Path
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Initialize(GeneratorInitializationContext context) { }
|
|
}
|
|
} |