-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from gman-au/refactor
Refactor
- Loading branch information
Showing
45 changed files
with
1,278 additions
and
516 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Siren.Application | ||
{ | ||
public interface ISirenApplication | ||
{ | ||
void Perform(string[] args); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
127
src/6.0/Siren.Infrastructure.AssemblyLoad/AssemblyLoader.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
129
src/6.0/Siren.Infrastructure.AssemblyLoad/Builders/EntityBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.