Intermediate commit
This commit is contained in:
parent
8f3bd334e8
commit
78f3345419
@ -2,13 +2,13 @@
|
||||
|
||||
namespace MycroForge.CLI.CodeGen;
|
||||
|
||||
public class DbEnvUpdater : PythonSourceModifier
|
||||
public class DbEnvModifier : PythonSourceModifier
|
||||
{
|
||||
private readonly string _importPath;
|
||||
private readonly string _className;
|
||||
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;
|
||||
_className = className;
|
@ -10,22 +10,22 @@ public partial class EntityLinker
|
||||
private readonly string _className;
|
||||
private readonly string _path;
|
||||
|
||||
private readonly List<PythonParser.Import_fromContext> _importCtxs;
|
||||
private readonly List<PythonParser.Import_fromContext> _importContexts;
|
||||
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 PythonParser.AssignmentContext _tableCtx;
|
||||
private readonly List<PythonParser.Class_defContext> _classContexts;
|
||||
private PythonParser.AssignmentContext _tableContext;
|
||||
|
||||
private readonly List<PythonParser.AssignmentContext> _columnCtxs;
|
||||
private readonly List<PythonParser.AssignmentContext> _columnContexts;
|
||||
private readonly List<string> _columnsBuffer;
|
||||
private PythonParser.AssignmentContext LastColumn => _columnCtxs.Last();
|
||||
private PythonParser.AssignmentContext LastColumn => _columnContexts.Last();
|
||||
|
||||
public string ClassName => _className;
|
||||
public string Path => _path;
|
||||
public string FieldName => _className.Underscore().ToLower();
|
||||
|
||||
public string TableName => GetOriginalText(_tableCtx)
|
||||
public string TableName => GetOriginalText(_tableContext)
|
||||
.Replace("__tablename__", string.Empty)
|
||||
.Replace("=", string.Empty)
|
||||
.Replace("\"", string.Empty)
|
||||
@ -35,11 +35,11 @@ public partial class EntityLinker
|
||||
{
|
||||
_className = className;
|
||||
_path = path;
|
||||
_importCtxs = new();
|
||||
_importContexts = new();
|
||||
_importsBuffer = new();
|
||||
_classCtxs = new();
|
||||
_tableCtx = default!;
|
||||
_columnCtxs = new();
|
||||
_classContexts = new();
|
||||
_tableContext = default!;
|
||||
_columnContexts = new();
|
||||
_columnsBuffer = new();
|
||||
}
|
||||
|
||||
@ -49,10 +49,10 @@ public partial class EntityLinker
|
||||
|
||||
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}.");
|
||||
|
||||
if (_columnCtxs.Count == 0)
|
||||
if (_columnContexts.Count == 0)
|
||||
throw new Exception($"Entity {_className} has no columns.");
|
||||
|
||||
_importsBuffer.Add(GetOriginalText(LastImport));
|
||||
@ -64,7 +64,7 @@ public partial class EntityLinker
|
||||
|
||||
private void InsertRelationshipImport()
|
||||
{
|
||||
var relationship = _importCtxs.FirstOrDefault(import =>
|
||||
var relationship = _importContexts.FirstOrDefault(import =>
|
||||
{
|
||||
var text = GetOriginalText(import);
|
||||
return text.Contains("sqlalchemy.orm") && text.Contains("relationship");
|
||||
@ -76,7 +76,7 @@ public partial class EntityLinker
|
||||
|
||||
private void InsertForeignKeyImport()
|
||||
{
|
||||
var foreignKey = _importCtxs.FirstOrDefault(import =>
|
||||
var foreignKey = _importContexts.FirstOrDefault(import =>
|
||||
{
|
||||
var text = GetOriginalText(import);
|
||||
return text.Contains("sqlalchemy") && text.Contains("ForeignKey");
|
||||
@ -88,13 +88,13 @@ public partial class EntityLinker
|
||||
|
||||
public override object? VisitImport_from(PythonParser.Import_fromContext context)
|
||||
{
|
||||
_importCtxs.Add(context);
|
||||
_importContexts.Add(context);
|
||||
return base.VisitImport_from(context);
|
||||
}
|
||||
|
||||
public override object? VisitClass_def(PythonParser.Class_defContext context)
|
||||
{
|
||||
_classCtxs.Add(context);
|
||||
_classContexts.Add(context);
|
||||
return base.VisitClass_def(context);
|
||||
}
|
||||
|
||||
@ -103,10 +103,10 @@ public partial class EntityLinker
|
||||
var text = GetOriginalText(context);
|
||||
|
||||
if (text.StartsWith("__tablename__"))
|
||||
_tableCtx = context;
|
||||
_tableContext = context;
|
||||
|
||||
if (text.Contains("Mapped["))
|
||||
_columnCtxs.Add(context);
|
||||
_columnContexts.Add(context);
|
||||
|
||||
return base.VisitAssignment(context);
|
||||
}
|
||||
@ -116,10 +116,10 @@ public partial class EntityLinker
|
||||
|
||||
public void Import(string from, string import)
|
||||
{
|
||||
var isExisting = _importCtxs.Select(GetOriginalText).Any(ctx => ctx.Contains(from) && ctx.Contains(import));
|
||||
var isBuffered = _importsBuffer.Any(txt => txt.Contains(from) && txt.Contains(import));
|
||||
var exists = _importContexts.Select(GetOriginalText).Any(ctx => ctx.Contains(from) && ctx.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}");
|
||||
}
|
||||
|
||||
|
221
MycroForge.CLI/CodeGen/MainModifier.cs
Normal file
221
MycroForge.CLI/CodeGen/MainModifier.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ public class RequestClassGenerator
|
||||
];
|
||||
|
||||
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;
|
||||
|
||||
@ -113,7 +113,13 @@ public class RequestClassGenerator
|
||||
|
||||
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 type = Clean(match.Groups[2].Value);
|
||||
fields.Add(new Field(name, type));
|
||||
|
@ -104,7 +104,7 @@ public partial class MycroForge
|
||||
.ToLower();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ public sealed class Api : IFeature
|
||||
{
|
||||
#region Main
|
||||
|
||||
private static readonly string[] HelloRouter =
|
||||
private static readonly string[] RouterTemplate =
|
||||
[
|
||||
"from fastapi import APIRouter",
|
||||
"from fastapi.responses import JSONResponse",
|
||||
@ -13,16 +13,18 @@ public sealed class Api : IFeature
|
||||
"router = APIRouter()",
|
||||
"",
|
||||
"@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}!\"}))"
|
||||
];
|
||||
|
||||
private static readonly string[] Main =
|
||||
private static readonly string[] MainTemplate =
|
||||
[
|
||||
"from fastapi import FastAPI",
|
||||
$"from {FeatureName}.routers import hello",
|
||||
"",
|
||||
"",
|
||||
"app = FastAPI()",
|
||||
"",
|
||||
$"from {FeatureName}.routers import hello",
|
||||
"app.include_router(prefix=\"/hello\", router=hello.router)"
|
||||
];
|
||||
|
||||
@ -42,10 +44,10 @@ public sealed class Api : IFeature
|
||||
"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");
|
||||
main = string.Join('\n', Main) + main;
|
||||
main = string.Join('\n', MainTemplate) + main;
|
||||
await context.WriteFile("main.py", main);
|
||||
|
||||
config.Api = new()
|
||||
|
@ -70,7 +70,7 @@ public sealed class Db : IFeature
|
||||
|
||||
var env = await context.ReadFile($"{FeatureName}/env.py");
|
||||
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.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/user.py", User);
|
||||
// await context.CreateFile($"{FeatureName}/entities/user.py", User);
|
||||
|
||||
await context.SaveConfig(config);
|
||||
}
|
||||
|
@ -21,7 +21,6 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" 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.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,25 +4,25 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MycroForge.CLI.CodeGen;
|
||||
|
||||
using var host = Host
|
||||
.CreateDefaultBuilder()
|
||||
.ConfigureServices((_, services) =>
|
||||
{
|
||||
services
|
||||
.AddServices()
|
||||
.AddCommands();
|
||||
})
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
await host.Services.GetRequiredService<MycroForge.CLI.Commands.MycroForge>()
|
||||
.InvokeAsync(args.Length == 0 ? ["--help"] : args);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
// using var host = Host
|
||||
// .CreateDefaultBuilder()
|
||||
// .ConfigureServices((_, services) =>
|
||||
// {
|
||||
// services
|
||||
// .AddServices()
|
||||
// .AddCommands();
|
||||
// })
|
||||
// .Build();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// await host.Services.GetRequiredService<MycroForge.CLI.Commands.MycroForge>()
|
||||
// .InvokeAsync(args.Length == 0 ? ["--help"] : args);
|
||||
// }
|
||||
// catch(Exception e)
|
||||
// {
|
||||
// Console.WriteLine(e.Message);
|
||||
// }
|
||||
|
||||
|
||||
// var rewrite = new EntityFieldReader(string.Join("\n", [
|
||||
@ -57,3 +57,9 @@ catch(Exception e)
|
||||
// {
|
||||
// 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());
|
Loading…
Reference in New Issue
Block a user