131 lines
5.8 KiB
C#
131 lines
5.8 KiB
C#
using System.CommandLine;
|
|
using Humanizer;
|
|
using MycroForge.CLI.CodeGen;
|
|
using MycroForge.CLI.Commands.Interfaces;
|
|
|
|
namespace MycroForge.CLI.Commands;
|
|
|
|
public partial class MycroForge
|
|
{
|
|
public partial class Db
|
|
{
|
|
public partial class Generate
|
|
{
|
|
public class Entity : Command, ISubCommandOf<Generate>
|
|
{
|
|
private record ColumnDefinition(string Name, string NativeType, string OrmType);
|
|
|
|
private static readonly string[] Template =
|
|
[
|
|
"from sqlalchemy import %type_imports%",
|
|
"from sqlalchemy.orm import Mapped, mapped_column",
|
|
"from db.entities.entity_base import EntityBase",
|
|
"",
|
|
"class %class_name%(EntityBase):",
|
|
"\t__tablename__ = \"%table_name%\"",
|
|
"\tid: Mapped[int] = mapped_column(primary_key=True)",
|
|
"\t%column_definitions%",
|
|
"",
|
|
"\tdef __repr__(self) -> str:",
|
|
"\t\treturn f\"%class_name%(id={self.id!r})\""
|
|
];
|
|
|
|
private static readonly Argument<string> NameArgument =
|
|
new(name: "name", description: string.Join('\n', [
|
|
"The name of the database entity",
|
|
"",
|
|
"Supported formats:",
|
|
"\tEntity",
|
|
"\tpath/relative/to/entities:Entity",
|
|
]));
|
|
|
|
private static readonly Option<IEnumerable<string>> ColumnsOption =
|
|
new(aliases: ["--column", "-c"], description: string.Join('\n', [
|
|
"Specify the fields to add.",
|
|
"",
|
|
"Format:",
|
|
"\t<name>:<native_type>:<orm_type>",
|
|
"\t",
|
|
"\t<name> = Name of the column",
|
|
"\t<native_type> = The native Python type",
|
|
"\t<orm_type> = The SQLAlchemy type",
|
|
"",
|
|
"Example:",
|
|
"\tfirst_name:str:String(255)",
|
|
])) { AllowMultipleArgumentsPerToken = true };
|
|
|
|
private readonly ProjectContext _context;
|
|
|
|
public Entity(ProjectContext context) : base("entity", "Generate and database entity")
|
|
{
|
|
_context = context;
|
|
AddAlias("e");
|
|
AddArgument(NameArgument);
|
|
AddOption(ColumnsOption);
|
|
this.SetHandler(ExecuteAsync, NameArgument, ColumnsOption);
|
|
}
|
|
|
|
private async Task ExecuteAsync(string name, IEnumerable<string> columns)
|
|
{
|
|
_context.AssertDirectoryExists(Features.Db.FeatureName);
|
|
|
|
var path = string.Empty;
|
|
if (name.Split(':').Select(s => s.Trim()).ToArray() is { Length: 2 } fullName)
|
|
{
|
|
path = fullName[0];
|
|
name = fullName[1];
|
|
}
|
|
|
|
var _columns = GetColumnDefinitions(columns.ToArray());
|
|
var className = name.Underscore().Pascalize();
|
|
var typeImports = string.Join(", ", _columns.Select(c => c.OrmType.Split('(').First()).Distinct());
|
|
var columnDefinitions = string.Join("\n\t", _columns.Select(ColumnToString));
|
|
|
|
var code = string.Join('\n', Template);
|
|
code = code.Replace("%type_imports%", typeImports);
|
|
code = code.Replace("%class_name%", className);
|
|
code = code.Replace("%table_name%", name.Underscore().ToLower().Pluralize());
|
|
code = code.Replace("%column_definitions%", columnDefinitions);
|
|
|
|
var folderPath = Path.Join($"{Features.Db.FeatureName}/entities", path);
|
|
var fileName = $"{name.ToLower()}.py";
|
|
var filePath = Path.Join(folderPath, fileName);
|
|
await _context.CreateFile(filePath, code);
|
|
|
|
var importPathParts = new[] { path, fileName.Replace(".py", "") }
|
|
.Where(s => !string.IsNullOrEmpty(s));
|
|
|
|
var importPath = string.Join('.', importPathParts)
|
|
.Replace('/', '.')
|
|
.Replace('\\', '.')
|
|
.Underscore()
|
|
.ToLower();
|
|
|
|
var env = await _context.ReadFile($"{Features.Db.FeatureName}/env.py");
|
|
env = new DbEnvUpdater(env, importPath, className).Rewrite();
|
|
await _context.WriteFile($"{Features.Db.FeatureName}/env.py", env);
|
|
}
|
|
|
|
private List<ColumnDefinition> GetColumnDefinitions(string[] fields)
|
|
{
|
|
var definitions = new List<ColumnDefinition>();
|
|
|
|
foreach (var field in fields)
|
|
{
|
|
if (field.Split(':') is not { Length: 3 } definition)
|
|
throw new Exception($"Field definition {field} is invalid.");
|
|
|
|
definitions.Add(new ColumnDefinition(definition[0], definition[1], definition[2]));
|
|
}
|
|
|
|
return definitions;
|
|
}
|
|
|
|
private static string ColumnToString(ColumnDefinition definition)
|
|
{
|
|
return $"{definition.Name}: Mapped[{definition.NativeType}] = mapped_column({definition.OrmType})";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |