Skip to content

Commit

Permalink
Added error table support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Irwin committed Mar 14, 2024
1 parent 35f08ea commit b8cb73d
Show file tree
Hide file tree
Showing 35 changed files with 2,206 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public static class SourceGeneratorHelper

public static string TableTypeAttributeName { get; } = $"{NamespaceName}TableAttribute";

public static string ResultTypeAttributeName { get; } = $"{NamespaceName}ResultType";

public static string BaseDataContextClassName { get; } = $"{NamespaceName}BaseDataContext";

public static string DefaultConnectionStringAppSettingName { get; } = $"{NamespaceName}Database";
Expand Down
66 changes: 65 additions & 1 deletion SQuiL.SourceGenerator/SQuiL/Generator/SQuiLGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,20 @@ public class SQuiLGenerator(bool ShowDebugMessages) : IIncrementalGenerator
{
public static string Debug { get; } = nameof(Debug);

public static string Error { get; } = nameof(Error);

public static string EnvironmentName { get; } = nameof(EnvironmentName);

public static bool IsError(string value)
=> Error.Equals(value) || $"{Error}s".Equals(value);

public static bool IsSpecial(string value)
{
if (Debug.Equals(value)) return true;
if (EnvironmentName.Equals(value)) return true;
return IsError(value);
}

public SQuiLGenerator() : this(false) { }

public void Initialize(IncrementalGeneratorInitializationContext context)
Expand Down Expand Up @@ -225,7 +237,7 @@ private void Execute(Compilation _, ImmutableArray<SQuiLDependency> dependencies
.Where(p => p is not null))
{
var table = attribute!.ToString();

if (!table.StartsWith("TableType."))
continue;

Expand Down Expand Up @@ -313,6 +325,11 @@ private void Execute(Compilation _, ImmutableArray<SQuiLDependency> dependencies
generation.FilePath = file.Path;
}

if (tableMap.TableNames
.Where(p => p.StartsWith(NamespaceName))
.Select(p => p[NamespaceName.Length..])
.Any(IsError))
GenerateResultType();
GenerateDependencyInjectionCode(contexts);
GenerateTablesEnum(context, tableMap);

Expand All @@ -326,6 +343,51 @@ private void Execute(Compilation _, ImmutableArray<SQuiLDependency> dependencies
_ => ("", default(Location))
};

void GenerateResultType()
{
context.AddSource($"{ResultTypeAttributeName}.g.cs", SourceText.From($$""""
{{FileHeader}}
namespace {{NamespaceName}};

using System.Collections.Generic;
using System;

public sealed record {{ResultTypeAttributeName}}<T>
{
public bool IsValue { get; }
private T Value { get; } = default!;
public SQuiLResultType(T value)
{
Value = value;
IsValue = true;
}

public bool HasErrors { get; }
private IReadOnlyList<SQuiLError> Errors { get; } = default!;
public SQuiLResultType(IReadOnlyList<SQuiLError> errors)
{
Errors = errors;
HasErrors = true;
}

public bool TryGetValue(out T value, out IReadOnlyList<SQuiLError> errors)
{
value = default!;
errors = default!;

if (IsValue)
{
value = Value;
return true;
}

errors = Errors;
return false;
}
}
"""", Encoding.UTF8));
}

void GenerateBaseDataContextClass()
{
if (missingConfiguration)
Expand Down Expand Up @@ -458,6 +520,8 @@ public enum TableType
var comma = "";
foreach (var table in tableMap.TableNames)
{
if (IsError(table)) continue;

sb.AppendLine(comma);
sb.Append($"\t{table}");
comma = ",";
Expand Down
115 changes: 81 additions & 34 deletions SQuiL.SourceGenerator/SQuiL/Models/SQuiLDataContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using SQuiL.SourceGenerator.Parser;

using System.CodeDom.Compiler;
using System.Diagnostics;

namespace SQuiL.Models;

Expand Down Expand Up @@ -38,12 +39,18 @@ public ExceptionOrValue<string> GenerateCode(SQuiLFileGeneration generation)
.ToList();

var outputs = Blocks
.Where(p => (p.CodeType & CodeType.OUTPUT) == CodeType.OUTPUT)
.Where(p => (p.CodeType & CodeType.OUTPUT) == CodeType.OUTPUT
&& !SQuiLGenerator.IsError(p.Name))
.Select(p => (CodeBlock: p, Query: p.CodeType == CodeType.OUTPUT_VARIABLE
? $"Declare {p.DatabaseType.Original};"
: TableDeclaration(p.DatabaseType.Original!, p)))
.ToList();

var errors = Blocks
.Where(p => SQuiLGenerator.IsError(p.Name))
.Select(p => (CodeBlock: p, Query: TableDeclaration(p.DatabaseType.Original!, p)))
.ToList();

writer.WriteLine($$"""
{{SourceGeneratorHelper.FileHeader}}
using Microsoft.Data.SqlClient;
Expand All @@ -61,8 +68,12 @@ void Process()
{
writer.Block($$"""partial class {{ClassName}} : {{SourceGeneratorHelper.BaseDataContextClassName}}""", () =>
{
var returnType = generation.Response.ModelName;
if (errors.Count > 0)
returnType = $"{SourceGeneratorHelper.ResultTypeAttributeName}<{returnType}>";
writer.Block($$"""
public async Task<{{generation.Response.ModelName}}> Process{{Method}}Async(
public async Task<{{returnType}}> Process{{Method}}Async(
{{generation.Request.ModelName}} request,
CancellationToken cancellationToken = default!)
""", () =>
Expand Down Expand Up @@ -93,6 +104,11 @@ void Process()
writer.WriteLine($"{generation.Response.ModelName} response = new();");
writer.WriteLine();
VariableSetTracking();
if (errors.Count > 0)
{
writer.WriteLine();
writer.WriteLine($"List<SQuiLError> errors = [];");
}
writer.Block("""
using var reader = await command.ExecuteReaderAsync(cancellationToken);
Expand Down Expand Up @@ -124,7 +140,12 @@ void Process()
writer.WriteLine();
}
writer.WriteLine("return response;");
writer.WriteLine("if(errors.Count == 0)");
writer.Indent++;
writer.WriteLine("return new(response);");
writer.Indent--;
writer.WriteLine();
writer.WriteLine("return new(errors);");
}
InsertQueries();
writer.Block($""""
Expand Down Expand Up @@ -156,6 +177,12 @@ void QueryDeclareStatements()
writer.Block(output.Query);
writer.WriteLine();
}
foreach (var error in errors)
{
writer.Block(error.Query);
writer.WriteLine();
}
}
void VariableSetTracking()
Expand All @@ -169,6 +196,15 @@ void VariableSetTracking()
void SwitchStatements()
{
writer.Block($"""case "{SQuiLTableTypeDatabaseTagName}Error__":""", () =>
{
writer.WriteLine("if (!await reader.ReadAsync(cancellationToken)) break;");
foreach (var error in errors)
LoopProperties("Error", "errors", error.CodeBlock.Properties);
writer.WriteLine();
writer.WriteLine("break;");
});
try
{
foreach (var (block, query) in outputs)
Expand All @@ -192,29 +228,7 @@ void SwitchStatements()
writer.WriteLine("if (!await reader.ReadAsync(cancellationToken)) break;");
if (block.IsTable)
{
writer.WriteLine();
foreach (var item in block.Properties)
writer.WriteLine($"""var index{item.Identifier.Value} = reader.GetOrdinal("{item.Identifier.Value}");""");
writer.WriteLine();
writer.Block("do", () =>
{
writer.Block($"""if (reader.GetString(0) == "{switchCase}")""", () =>
{
writer.Write($"response.{block.Name}.Add(new(");
writer.Indent++;
var comma = "";
foreach (var item in block.Properties)
{
writer.WriteLine(comma);
writer.Write($"""{item.DataReader()}(index{item.Identifier.Value})""");
comma = ",";
}
writer.Indent--;
writer.WriteLine("));");
});
}, $"""while (await reader.ReadAsync(cancellationToken));""");
LoopProperties(switchCase, $"response.{block.Name}", block.Properties);
}
else if (block.IsObject)
{
Expand Down Expand Up @@ -269,6 +283,33 @@ void SwitchStatements()
{
//writer.WriteLine("""//default: throw new Exception($"Invalid Table `{reader.GetString(0)}`");""");
}
void LoopProperties(string switchCase, string model, List<CodeItem> properties)
{
writer.WriteLine();
foreach (var item in properties)
writer.WriteLine($"""var index{item.Identifier.Value} = reader.GetOrdinal("{item.Identifier.Value}");""");
writer.WriteLine();
writer.Block("do", () =>
{
writer.Block($"""if (reader.GetString(0) == "{switchCase}")""", () =>
{
writer.Write($"{model}.Add(new(");
writer.Indent++;
var comma = "";
foreach (var item in properties)
{
writer.WriteLine(comma);
writer.Write($"""{item.DataReader()}(index{item.Identifier.Value})""");
comma = ",";
}
writer.Indent--;
writer.WriteLine("));");
});
}, $"""while (await reader.ReadAsync(cancellationToken));""");
}
}
void InsertQueries()
Expand Down Expand Up @@ -367,12 +408,19 @@ void AddParams(string param, string index, string item)
}
}
string TableDeclaration(string name, CodeBlock block) => $"""
Declare {block.DatabaseType.Original}(
[{SQuiLTableTypeDatabaseTagName}{name[1..^6]}__] varchar(max) default('{name[1..^6]}'),
{string.Join($",{writer.NewLine}\t", block.Properties.Select(p
=> $"{p.Identifier.Value} {p.Type.Original}"))});
""";
string TableDeclaration(string name, CodeBlock block)
{
name = name[1..^6].Trim();
if (name == "Errors")
name = "Error";
return $"""
Declare {block.DatabaseType.Original}(
[{SQuiLTableTypeDatabaseTagName}{name}__] varchar(max) default('{name}'),
{string.Join($",{writer.NewLine}\t", block.Properties.Select(p
=> $"[{p.Identifier.Value}] {p.Type.Original}"))});
""";
}
void CommandParameters()
{
Expand All @@ -398,8 +446,7 @@ void CommandParameters()
var comma = $"";
foreach (var parameter in parameters)
{
if (parameter.Name.Equals(SQuiLGenerator.EnvironmentName)) continue;
if (parameter.Name.Equals(SQuiLGenerator.Debug)) continue;
if (SQuiLGenerator.IsSpecial(parameter.Name)) continue;
writer.WriteLine(comma);
writer.Write($$"""new("@Param_{{parameter.Name}}", {{parameter.SqlDbType()}}) """);
Expand Down
50 changes: 30 additions & 20 deletions SQuiL.SourceGenerator/SQuiL/Models/SQuiLModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

using System.CodeDom.Compiler;
using System.Collections.Immutable;
using System.Diagnostics;

namespace SQuiL.Models;

Expand Down Expand Up @@ -78,43 +79,52 @@ private SQuiLModel Build(IEnumerable<CodeBlock> blocks)
{
foreach (var block in blocks)
{
if (block.Name == SQuiLGenerator.Debug
|| block.Name == SQuiLGenerator.EnvironmentName
if (SQuiLGenerator.IsSpecial(block.Name)
|| InheritsProperty(ModelName, block.Name))
continue;

if (block.IsTable)
Create<SQuiLTable>(block, (p, q) => new(NameSpace, Modifier(p), p, block, TableMap, Records)
{
HasParameterizedConstructor = q
});
else if (block.IsObject)
Create<SQuiLObject>(block, (p, q) => new(NameSpace, Modifier(p), p, block, TableMap, Records)
{
HasParameterizedConstructor = q
});
CreateSpecial(block);
else if (block.IsTable || block.IsObject)
CreateTableObject(block);
else
Properties.Add(new(block, TableMap));
}

return this;
}

private void Create<T>(CodeBlock block, Func<string, bool, T> callback) where T : SQuiLTable
private void CreateSpecial(CodeBlock block)
{
if (!SQuiLGenerator.IsError(block.Name)) return;
CreateTableObject(block, false);
}

private void CreateTableObject(CodeBlock block, bool addProperty = true)
{
var type = typeof(T).Name[5..];
var type = (addProperty
? (block.IsTable ? typeof(SQuiLTable) : typeof(SQuiLObject))
: typeof(SQuiLProperty)).Name[5..];

if (block.Properties is null)
throw new DiagnosticException(
$"Cannot generate {type} `{block.Name}` for {ModelName}");

//type = $"{Scope}{block.Name}{type}";

var hasParameterizedConstructor = !Records.TryGetValue(type, out var partial)
|| partial.Syntax.ParameterList?.Parameters.Count == 0;

var table = callback(type, hasParameterizedConstructor);
Properties.Add(table);
var name = $"{block.Name}{type}";

if (addProperty) name = $"{NameSpace}{block.Name}"; else type = "";

SQuiLTable table = block.IsTable
? new SQuiLTable(NameSpace, Modifier(name), type, block, TableMap, Records)
{
HasParameterizedConstructor = hasParameterizedConstructor
}
: new SQuiLObject(NameSpace, Modifier(name), type, block, TableMap, Records)
{
HasParameterizedConstructor = hasParameterizedConstructor
};

if (addProperty) Properties.Add(table);
TableMap.Add(table);

if (hasParameterizedConstructor)
Expand Down

0 comments on commit b8cb73d

Please sign in to comment.