Intermediate commit

This commit is contained in:
mdnapo 2024-05-29 14:20:18 +02:00
parent 8f3bd334e8
commit 78f3345419
9 changed files with 289 additions and 55 deletions

View File

@ -2,13 +2,13 @@
namespace MycroForge.CLI.CodeGen; namespace MycroForge.CLI.CodeGen;
public class DbEnvUpdater : PythonSourceModifier public class DbEnvModifier : PythonSourceModifier
{ {
private readonly string _importPath; private readonly string _importPath;
private readonly string _className; private readonly string _className;
private PythonParser.Import_fromContext? _lastImport; private PythonParser.Import_fromContext? _lastImport;
public DbEnvUpdater(string source, string importPath, string className) : base(source) public DbEnvModifier(string source, string importPath, string className) : base(source)
{ {
_importPath = importPath; _importPath = importPath;
_className = className; _className = className;

View File

@ -10,22 +10,22 @@ public partial class EntityLinker
private readonly string _className; private readonly string _className;
private readonly string _path; private readonly string _path;
private readonly List<PythonParser.Import_fromContext> _importCtxs; private readonly List<PythonParser.Import_fromContext> _importContexts;
private readonly List<string> _importsBuffer; private readonly List<string> _importsBuffer;
private PythonParser.Import_fromContext LastImport => _importCtxs.Last(); private PythonParser.Import_fromContext LastImport => _importContexts.Last();
private readonly List<PythonParser.Class_defContext> _classCtxs; private readonly List<PythonParser.Class_defContext> _classContexts;
private PythonParser.AssignmentContext _tableCtx; private PythonParser.AssignmentContext _tableContext;
private readonly List<PythonParser.AssignmentContext> _columnCtxs; private readonly List<PythonParser.AssignmentContext> _columnContexts;
private readonly List<string> _columnsBuffer; private readonly List<string> _columnsBuffer;
private PythonParser.AssignmentContext LastColumn => _columnCtxs.Last(); private PythonParser.AssignmentContext LastColumn => _columnContexts.Last();
public string ClassName => _className; public string ClassName => _className;
public string Path => _path; public string Path => _path;
public string FieldName => _className.Underscore().ToLower(); public string FieldName => _className.Underscore().ToLower();
public string TableName => GetOriginalText(_tableCtx) public string TableName => GetOriginalText(_tableContext)
.Replace("__tablename__", string.Empty) .Replace("__tablename__", string.Empty)
.Replace("=", string.Empty) .Replace("=", string.Empty)
.Replace("\"", string.Empty) .Replace("\"", string.Empty)
@ -35,11 +35,11 @@ public partial class EntityLinker
{ {
_className = className; _className = className;
_path = path; _path = path;
_importCtxs = new(); _importContexts = new();
_importsBuffer = new(); _importsBuffer = new();
_classCtxs = new(); _classContexts = new();
_tableCtx = default!; _tableContext = default!;
_columnCtxs = new(); _columnContexts = new();
_columnsBuffer = new(); _columnsBuffer = new();
} }
@ -49,10 +49,10 @@ public partial class EntityLinker
Visit(tree); Visit(tree);
if (!_classCtxs.Any(c => GetOriginalText(c).Contains(_className))) if (!_classContexts.Any(c => GetOriginalText(c).Contains(_className)))
throw new Exception($"Entity {_className} was not found in {_path}."); throw new Exception($"Entity {_className} was not found in {_path}.");
if (_columnCtxs.Count == 0) if (_columnContexts.Count == 0)
throw new Exception($"Entity {_className} has no columns."); throw new Exception($"Entity {_className} has no columns.");
_importsBuffer.Add(GetOriginalText(LastImport)); _importsBuffer.Add(GetOriginalText(LastImport));
@ -64,7 +64,7 @@ public partial class EntityLinker
private void InsertRelationshipImport() private void InsertRelationshipImport()
{ {
var relationship = _importCtxs.FirstOrDefault(import => var relationship = _importContexts.FirstOrDefault(import =>
{ {
var text = GetOriginalText(import); var text = GetOriginalText(import);
return text.Contains("sqlalchemy.orm") && text.Contains("relationship"); return text.Contains("sqlalchemy.orm") && text.Contains("relationship");
@ -76,7 +76,7 @@ public partial class EntityLinker
private void InsertForeignKeyImport() private void InsertForeignKeyImport()
{ {
var foreignKey = _importCtxs.FirstOrDefault(import => var foreignKey = _importContexts.FirstOrDefault(import =>
{ {
var text = GetOriginalText(import); var text = GetOriginalText(import);
return text.Contains("sqlalchemy") && text.Contains("ForeignKey"); return text.Contains("sqlalchemy") && text.Contains("ForeignKey");
@ -88,13 +88,13 @@ public partial class EntityLinker
public override object? VisitImport_from(PythonParser.Import_fromContext context) public override object? VisitImport_from(PythonParser.Import_fromContext context)
{ {
_importCtxs.Add(context); _importContexts.Add(context);
return base.VisitImport_from(context); return base.VisitImport_from(context);
} }
public override object? VisitClass_def(PythonParser.Class_defContext context) public override object? VisitClass_def(PythonParser.Class_defContext context)
{ {
_classCtxs.Add(context); _classContexts.Add(context);
return base.VisitClass_def(context); return base.VisitClass_def(context);
} }
@ -103,10 +103,10 @@ public partial class EntityLinker
var text = GetOriginalText(context); var text = GetOriginalText(context);
if (text.StartsWith("__tablename__")) if (text.StartsWith("__tablename__"))
_tableCtx = context; _tableContext = context;
if (text.Contains("Mapped[")) if (text.Contains("Mapped["))
_columnCtxs.Add(context); _columnContexts.Add(context);
return base.VisitAssignment(context); return base.VisitAssignment(context);
} }
@ -116,10 +116,10 @@ public partial class EntityLinker
public void Import(string from, string import) public void Import(string from, string import)
{ {
var isExisting = _importCtxs.Select(GetOriginalText).Any(ctx => ctx.Contains(from) && ctx.Contains(import)); var exists = _importContexts.Select(GetOriginalText).Any(ctx => ctx.Contains(from) && ctx.Contains(import));
var isBuffered = _importsBuffer.Any(txt => txt.Contains(from) && txt.Contains(import)); var buffered = _importsBuffer.Any(txt => txt.Contains(from) && txt.Contains(import));
if (!isExisting && !isBuffered) if (!exists && !buffered)
_importsBuffer.Add($"from {from} import {import}"); _importsBuffer.Add($"from {from} import {import}");
} }

View File

@ -0,0 +1,221 @@
using Antlr4.Runtime.Tree;
using MycroForge.Parsing;
namespace MycroForge.CLI.CodeGen;
public class MainModifier : PythonSourceModifier
{
private PythonParser.Import_fromContext? _lastEntityImport;
private PythonParser.Import_fromContext? _lastAssociationImport;
private PythonParser.Import_fromContext? _lastRouterImport;
private PythonParser.Import_fromContext? _lastRouterInclude;
private readonly List<string> _lastEntityImportBuffer;
private readonly List<string> _lastAssociationImportBuffer;
private readonly List<string> _lastRouterImportBuffer;
private readonly List<string> _lastRouterIncludeBuffer;
public MainModifier(string source) : base(source)
{
_lastEntityImportBuffer = new();
_lastAssociationImportBuffer = new();
_lastRouterImportBuffer = new();
_lastRouterIncludeBuffer = new();
}
public void Initialize()
{
var tree = Parser.file_input();
Visit(tree);
if (_lastEntityImport is not null)
_lastEntityImportBuffer.Add(GetOriginalText(_lastEntityImport));
if (_lastAssociationImport is not null)
_lastAssociationImportBuffer.Add(GetOriginalText(_lastAssociationImport));
if (_lastRouterImport is not null)
_lastRouterImportBuffer.Add(GetOriginalText(_lastRouterImport));
if (_lastRouterInclude is not null)
_lastRouterIncludeBuffer.Add(GetOriginalText(_lastRouterInclude));
}
private string ToImportString(string from, string import) => $"from {from} import {import}";
public void ImportEntity(string from, string import)
{
_lastEntityImportBuffer.Add(ToImportString(from, import));
}
public void ImportAssociation(string from, string import)
{
_lastAssociationImportBuffer.Add(ToImportString(from, import));
}
public void ImportRouter(string from, string import)
{
_lastRouterImportBuffer.Add(ToImportString(from, import));
}
public void IncludeRouter(string prefix, string router)
{
_lastRouterImportBuffer.Add($"app.include_router(prefix=\"/{prefix}\", router={router}.router)");
// _lastRouterImportBuffer.Add($"app.include_router(prefix=\"/{prefix}\", router={router}.router)");
}
public override string Rewrite()
{
if (_lastEntityImport is not null)
Rewrite(_lastEntityImport, _lastEntityImportBuffer.ToArray());
if (_lastAssociationImport is not null)
Rewrite(_lastAssociationImport, _lastAssociationImportBuffer.ToArray());
if (_lastRouterImport is not null)
Rewrite(_lastRouterImport, _lastRouterImportBuffer.ToArray());
return Rewriter.GetText();
}
// public override object? VisitPrimary(PythonParser.PrimaryContext context)
// {
// // Console.WriteLine(GetOriginalText(context));
// return base.VisitPrimary(context);
// }
//
// public override object? VisitName_or_attr(PythonParser.Name_or_attrContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitName_or_attr(context);
// }
//
// public override object? VisitStatement(PythonParser.StatementContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitStatement(context);
// }
// public override object? VisitDotted_name(PythonParser.Dotted_nameContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitDotted_name(context);
// }
//
// public override object? VisitDotted_name(PythonParser.Dotted_nameContext context)
// {
// Console.WriteLine(GetOriginalText(context));
//
// return base.VisitDotted_name(context);
// }
//
// public override object? VisitDotted_as_names(PythonParser.Dotted_as_namesContext context)
// {
// Console.WriteLine(GetOriginalText(context));
//
// return base.VisitDotted_as_names(context);
// }
//
public override object? VisitErrorNode(IErrorNode node)
{
Console.WriteLine(node.GetText());
return base.VisitErrorNode(node);
}
// public override object? VisitValue_pattern(PythonParser.Value_patternContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitValue_pattern(context);
// }
//
// public override object? VisitStar_atom(PythonParser.Star_atomContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitStar_atom(context);
// }
//
// public override object? VisitExpression(PythonParser.ExpressionContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitExpression(context);
// }
//
// public override object? VisitT_primary(PythonParser.T_primaryContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitT_primary(context);
// }
// public override object? VisitAttr(PythonParser.AttrContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitAttr(context);
// }
// public override object? VisitT_primary(PythonParser.T_primaryContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitT_primary(context);
// }
// public override object? VisitAwait_primary(PythonParser.Await_primaryContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitAwait_primary(context);
// }
// public override object? VisitTarget_with_star_atom(PythonParser.Target_with_star_atomContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitTarget_with_star_atom(context);
// }
// public override object? VisitSingle_subscript_attribute_target(
// PythonParser.Single_subscript_attribute_targetContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitSingle_subscript_attribute_target(context);
// }
//
// public override object? VisitSingle_target(PythonParser.Single_targetContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitSingle_target(context);
// }
// public override object? VisitName_or_attr(PythonParser.Name_or_attrContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitName_or_attr(context);
// }
// public override object? VisitNamed_expression(PythonParser.Named_expressionContext context)
// {
// Console.WriteLine(GetOriginalText(context));
//
// return base.VisitNamed_expression(context);
// }
// public override object? VisitPrimary(PythonParser.PrimaryContext context)
// {
// Console.WriteLine(GetOriginalText(context));
// return base.VisitPrimary(context);
// }
public override object? VisitImport_from(PythonParser.Import_fromContext context)
{
var text = GetOriginalText(context);
if (text.StartsWith($"from {Features.Db.FeatureName}.entities.associations"))
_lastAssociationImport = context;
if (text.StartsWith($"from {Features.Db.FeatureName}.entities"))
_lastEntityImport = context;
if (text.StartsWith($"from {Features.Api.FeatureName}.routers"))
_lastRouterImport = context;
return base.VisitImport_from(context);
}
}

View File

@ -30,7 +30,7 @@ public class RequestClassGenerator
]; ];
private static readonly Regex ImportInfoRegex = new(@"from\s+(.+)\s+import\s+(.+)"); private static readonly Regex ImportInfoRegex = new(@"from\s+(.+)\s+import\s+(.+)");
private static readonly Regex FieldInfoRegex = new(@"([_a-zA-Z-0-9]+)\s*:\s*Mapped\s*\[\s*(.+)\s*\]"); private static readonly Regex FieldInfoRegex = new(@"([_a-zA-Z-0-9]+)\s*:\s*Mapped\s*\[\s*(.+)\s*\]\s*=\s*.+");
private readonly ProjectContext _context; private readonly ProjectContext _context;
@ -113,7 +113,13 @@ public class RequestClassGenerator
foreach (Match match in matches) foreach (Match match in matches)
{ {
// Index 0 contains the whole Regex match, so we ignore this, since we're only interested in the captured groups. // Index 0 contains the full Regex match
var fullMatch = match.Groups[0].Value;
// Ignore relationship fields, these need to be done manually
if (fullMatch.IndexOf("=", StringComparison.Ordinal) <
fullMatch.IndexOf("relationship(", StringComparison.Ordinal)) continue;
var name = Clean(match.Groups[1].Value); var name = Clean(match.Groups[1].Value);
var type = Clean(match.Groups[2].Value); var type = Clean(match.Groups[2].Value);
fields.Add(new Field(name, type)); fields.Add(new Field(name, type));

View File

@ -104,7 +104,7 @@ public partial class MycroForge
.ToLower(); .ToLower();
var env = await _context.ReadFile($"{Features.Db.FeatureName}/env.py"); var env = await _context.ReadFile($"{Features.Db.FeatureName}/env.py");
env = new DbEnvUpdater(env, importPath, className).Rewrite(); env = new DbEnvModifier(env, importPath, className).Rewrite();
await _context.WriteFile($"{Features.Db.FeatureName}/env.py", env); await _context.WriteFile($"{Features.Db.FeatureName}/env.py", env);
} }

View File

@ -4,7 +4,7 @@ public sealed class Api : IFeature
{ {
#region Main #region Main
private static readonly string[] HelloRouter = private static readonly string[] RouterTemplate =
[ [
"from fastapi import APIRouter", "from fastapi import APIRouter",
"from fastapi.responses import JSONResponse", "from fastapi.responses import JSONResponse",
@ -13,16 +13,18 @@ public sealed class Api : IFeature
"router = APIRouter()", "router = APIRouter()",
"", "",
"@router.get(\"/{name}\")", "@router.get(\"/{name}\")",
"async def greet(name: str):", "async def hello(name: str):",
"\treturn JSONResponse(status_code=200, content=jsonable_encoder({'greeting': f\"Hello, {name}!\"}))" "\treturn JSONResponse(status_code=200, content=jsonable_encoder({'greeting': f\"Hello, {name}!\"}))"
]; ];
private static readonly string[] Main = private static readonly string[] MainTemplate =
[ [
"from fastapi import FastAPI", "from fastapi import FastAPI",
$"from {FeatureName}.routers import hello",
"",
"",
"app = FastAPI()", "app = FastAPI()",
"", "",
$"from {FeatureName}.routers import hello",
"app.include_router(prefix=\"/hello\", router=hello.router)" "app.include_router(prefix=\"/hello\", router=hello.router)"
]; ];
@ -42,10 +44,10 @@ public sealed class Api : IFeature
"python3 -m pip freeze > requirements.txt" "python3 -m pip freeze > requirements.txt"
); );
await context.CreateFile($"{FeatureName}/routers/hello.py", HelloRouter); await context.CreateFile($"{FeatureName}/routers/hello.py", RouterTemplate);
var main = await context.ReadFile("main.py"); var main = await context.ReadFile("main.py");
main = string.Join('\n', Main) + main; main = string.Join('\n', MainTemplate) + main;
await context.WriteFile("main.py", main); await context.WriteFile("main.py", main);
config.Api = new() config.Api = new()

View File

@ -70,7 +70,7 @@ public sealed class Db : IFeature
var env = await context.ReadFile($"{FeatureName}/env.py"); var env = await context.ReadFile($"{FeatureName}/env.py");
env = new DbEnvInitializer(env).Rewrite(); env = new DbEnvInitializer(env).Rewrite();
env = new DbEnvUpdater(env, "user", "User").Rewrite(); // env = new DbEnvUpdater(env, "user", "User").Rewrite();
await context.WriteFile($"{FeatureName}/env.py", env); await context.WriteFile($"{FeatureName}/env.py", env);
await context.CreateFile($"{FeatureName}/settings.py", Settings); await context.CreateFile($"{FeatureName}/settings.py", Settings);
@ -79,7 +79,7 @@ public sealed class Db : IFeature
await context.CreateFile($"{FeatureName}/entities/entity_base.py", EntityBase); await context.CreateFile($"{FeatureName}/entities/entity_base.py", EntityBase);
await context.CreateFile($"{FeatureName}/entities/user.py", User); // await context.CreateFile($"{FeatureName}/entities/user.py", User);
await context.SaveConfig(config); await context.SaveConfig(config);
} }

View File

@ -21,7 +21,6 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" /> <PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -4,25 +4,25 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using MycroForge.CLI.CodeGen; using MycroForge.CLI.CodeGen;
using var host = Host // using var host = Host
.CreateDefaultBuilder() // .CreateDefaultBuilder()
.ConfigureServices((_, services) => // .ConfigureServices((_, services) =>
{ // {
services // services
.AddServices() // .AddServices()
.AddCommands(); // .AddCommands();
}) // })
.Build(); // .Build();
//
try // try
{ // {
await host.Services.GetRequiredService<MycroForge.CLI.Commands.MycroForge>() // await host.Services.GetRequiredService<MycroForge.CLI.Commands.MycroForge>()
.InvokeAsync(args.Length == 0 ? ["--help"] : args); // .InvokeAsync(args.Length == 0 ? ["--help"] : args);
} // }
catch(Exception e) // catch(Exception e)
{ // {
Console.WriteLine(e.Message); // Console.WriteLine(e.Message);
} // }
// var rewrite = new EntityFieldReader(string.Join("\n", [ // var rewrite = new EntityFieldReader(string.Join("\n", [
@ -57,3 +57,9 @@ catch(Exception e)
// { // {
// Console.WriteLine($"name={f.Name}, type={f.Type}"); // Console.WriteLine($"name={f.Name}, type={f.Type}");
// }); // });
var main = new MainModifier(string.Join("\n", await File.ReadAllLinesAsync("scripts/user.py")));
main.Initialize();
Console.WriteLine(main.Rewrite());