Skip to content

Commit

Permalink
Merge pull request #1 from gman-au/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
gubpalma authored Apr 18, 2024
2 parents ba5338a + 46f4dd2 commit 1cb97e4
Show file tree
Hide file tree
Showing 45 changed files with 1,278 additions and 516 deletions.
7 changes: 7 additions & 0 deletions src/6.0/Siren.Application/ISirenApplication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Siren.Application
{
public interface ISirenApplication
{
void Perform(string[] args);
}
}
17 changes: 17 additions & 0 deletions src/6.0/Siren.Application/Siren.Application.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Siren.Infrastructure.AssemblyLoad\Siren.Infrastructure.AssemblyLoad.csproj" />
<ProjectReference Include="..\Siren.Infrastructure.Io\Siren.Infrastructure.Io.csproj" />
<ProjectReference Include="..\Siren.Infrastructure.Parsing\Siren.Infrastructure.Parsing.csproj" />
</ItemGroup>

</Project>
72 changes: 72 additions & 0 deletions src/6.0/Siren.Application/SirenApplication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using Microsoft.Extensions.Logging;
using Siren.Infrastructure.AssemblyLoad;
using Siren.Infrastructure.Io;
using Siren.Infrastructure.Parsing;
using Siren.Infrastructure.Rendering;

namespace Siren.Application
{
public class SirenApplication : ISirenApplication
{
private readonly IAssemblyLoader _assemblyLoader;
private readonly IDomainRenderer _domainRenderer;
private readonly IFileWriter _fileWriter;
private readonly ILogger<SirenApplication> _logger;
private readonly IProgramArgumentsParser _programArgumentsParser;

public SirenApplication(
ILogger<SirenApplication> logger,
IProgramArgumentsParser programArgumentsParser,
IFileWriter fileWriter,
IDomainRenderer domainRenderer,
IAssemblyLoader assemblyLoader
)
{
_logger = logger;
_programArgumentsParser = programArgumentsParser;
_fileWriter = fileWriter;
_domainRenderer = domainRenderer;
_assemblyLoader = assemblyLoader;
}

public void Perform(string[] args)
{
try
{
_logger
.LogInformation("Starting Siren console...");

var arguments =
_programArgumentsParser
.Parse(args);

var outputPath = arguments.OutputFilePath;
var markdownAnchor = arguments.MarkdownAnchor;

var universe =
_assemblyLoader
.Perform(arguments);

var result =
_domainRenderer
.Perform(universe);

_fileWriter
.Perform(
outputPath,
result,
markdownAnchor
);

_logger
.LogInformation("Siren operation completed");
}
catch (Exception ex)
{
_logger
.LogError($"Error encountered: {ex.Message}");
}
}
}
}
19 changes: 19 additions & 0 deletions src/6.0/Siren.Domain/ProgramArguments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Siren.Domain
{
public class ProgramArguments
{
public string TestAssemblyPath { get; set; }

public string OutputFilePath { get; set; }

public string MarkdownAnchor { get; set; }

public override string ToString()
{
return
$"TestAssemblyFolder: '{TestAssemblyPath}'\r\n" +
$"OutputFilePath: '{OutputFilePath}'\r\n" +
$"MarkdownAnchor: '{MarkdownAnchor}'\r\n";
}
}
}
127 changes: 117 additions & 10 deletions src/6.0/Siren.Infrastructure.AssemblyLoad/AssemblyLoader.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,126 @@
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Logging;
using Mono.Cecil;
using Siren.Domain;
using Siren.Infrastructure.AssemblyLoad.Builders;
using Siren.Infrastructure.AssemblyLoad.Mapping;

namespace Siren.Infrastructure.AssemblyLoad
{
public static class AssemblyLoader
public class AssemblyLoader : IAssemblyLoader
{
public static bool IsASnapshotAssembly(this Assembly assembly)
private const string ModelSnapshotBaseType = "ModelSnapshot";
private const string BuildModelMethod = "BuildModel";

private readonly IEntityBuilder _entityBuilder;
private readonly IAssemblyMapper _assemblyMapper;
private readonly ILogger<AssemblyLoader> _logger;
private readonly IRelationshipBuilder _relationshipBuilder;

public AssemblyLoader(
ILogger<AssemblyLoader> logger,
IEntityBuilder entityBuilder,
IRelationshipBuilder relationshipBuilder,
IAssemblyMapper assemblyMapper
)
{
_logger = logger;
_entityBuilder = entityBuilder;
_relationshipBuilder = relationshipBuilder;
_assemblyMapper = assemblyMapper;
}

public Universe Perform(ProgramArguments arguments)
{
return
assembly
.GetTypes()
.Any(
o => o.BaseType == typeof(ModelSnapshot)
);
var filePath =
arguments
.TestAssemblyPath;

var assembly =
AssemblyDefinition
.ReadAssembly(filePath);

foreach (var module in assembly.Modules)
{
foreach (var type in module.Types)
{
if (type.BaseType?.Name == ModelSnapshotBaseType)
{
_logger
.LogInformation($"Located snapshot type {type.Name}");

foreach (var method in type.Methods)
{
if (method.Name != BuildModelMethod) continue;

_logger
.LogInformation($"Located build model method");

var entityInstructions =
method
.Body
.Instructions
.Where(
o =>
_entityBuilder
.IsApplicable(o)
)
.ToList();

var entities =
entityInstructions
.Select(
o =>
_entityBuilder
.Process(o)
)
.Where(o => o != null)
.ToList();

_logger
.LogInformation($"Extracted {entities.Count} entities");

var relationshipInstructions =
method
.Body
.Instructions
.Where(
o =>
_relationshipBuilder
.IsApplicable(o)
)
.ToList();

var relationships =
relationshipInstructions
.SelectMany(
o =>
_relationshipBuilder
.Process(
o,
entities
)
)
.Where(o => o != null)
.ToList();

_logger
.LogInformation($"Extracted {relationships.Count} relationships");

var result =
_assemblyMapper
.Map(
entities,
relationships
);

return result;
}
}
}
}

return null;
}
}
}
129 changes: 129 additions & 0 deletions src/6.0/Siren.Infrastructure.AssemblyLoad/Builders/EntityBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Siren.Infrastructure.AssemblyLoad.Configuration;
using Siren.Infrastructure.AssemblyLoad.Domain;
using Siren.Infrastructure.AssemblyLoad.Extensions;

namespace Siren.Infrastructure.AssemblyLoad.Builders
{
public class EntityBuilder : IEntityBuilder
{
private const string EntityMethodName = "Entity";
private readonly IBuildConfigurationProvider _buildConfigurationProvider;
private readonly IPropertyBuilder _propertyBuilder;
private readonly ITableBuilder _tableBuilder;

public EntityBuilder(
IBuildConfigurationProvider buildConfigurationProvider,
IPropertyBuilder propertyBuilder,
ITableBuilder tableBuilder
)
{
_buildConfigurationProvider = buildConfigurationProvider;
_propertyBuilder = propertyBuilder;
_tableBuilder = tableBuilder;
}

public bool IsApplicable(Instruction instr)
{
if (instr.OpCode != OpCodes.Brtrue_S) return false;

if (instr.Operand is not Instruction opInstr) return false;

if (opInstr.OpCode != OpCodes.Callvirt) return false;

if (opInstr.Operand is not MethodReference mr) return false;

return mr.Name == EntityMethodName;
}

public ExtractedEntity Process(Instruction instr)
{
var result =
new ExtractedEntity
{
ReferenceInstruction = instr,
Properties = new List<ExtractedProperty>()
};

var configuration =
_buildConfigurationProvider
.Get()
.First();

// Extract name
var currInstr =
instr
.StepPrevious(configuration.StepsBackToEntityName);

if (currInstr.OpCode == OpCodes.Ldstr)
{
var splitName =
currInstr
.Operand
.ToString()
.SplitNamespace();

result.EntityName = splitName.Item2;
result.Namespace = splitName.Item1;
}

// Get property builder instruction
currInstr =
instr
.StepNext(configuration.StepsForwardToPropertyBuilder);

if (currInstr.OpCode != OpCodes.Ldftn) return result;

if (currInstr.Operand is not MethodDefinition methodReference) return result;

// Extract properties
var propertyInstructions =
methodReference
.Body
.Instructions
.Where(o =>
_propertyBuilder
.IsApplicable(o))
.ToList();

var extractedProperties =
propertyInstructions
.Select(o => _propertyBuilder.Process(o))
.Where(o => o != null)
.ToList();

// Extract table information
var tableInstr =
methodReference
.Body
.Instructions
.FirstOrDefault(
o =>
_tableBuilder
.IsApplicable(o)
);

if (tableInstr != null)
{
var extractedTable =
_tableBuilder
.Process(tableInstr);

if (extractedTable != null)
{
result.SchemaName = extractedTable.SchemaName;
result.TableName = extractedTable.TableName;
}
}

if (!extractedProperties.Any()) return null;

result.Properties = extractedProperties;

return result;
}
}
}
Loading

0 comments on commit 1cb97e4

Please sign in to comment.