Skip to content

Commit

Permalink
feature: Interface generation for data models (#427)
Browse files Browse the repository at this point in the history
* change interfacegenerationmodel to a record

* generate interface names on keyed models

* generate interface properties on unkeyedmodel

* pad out unit test plus nameable interface concept

* fix property generation not triggering

* remove unused method

* unkeyed properties always get\set

* Update UnkeyedModelClassGeneratorProcessor.cs
  • Loading branch information
dpvreony authored Dec 31, 2023
1 parent c73d73d commit 8c862ee
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<PackageReference Include="Whipstaff.AspNetCore" Version="7.1.15" />
<PackageReference Include="Whipstaff.Core" Version="7.1.15" />
<PackageReference Include="Whipstaff.EntityFramework" Version="7.1.15" />
<PackageReference Include="Whipstaff.Wpf" Version="7.1.15" />
<PackageReference Include="Whipstaff.Wpf.Mahapps" Version="7.1.15" />
Expand Down
13 changes: 13 additions & 0 deletions src/Dhgms.Nucleotide.Generators/CompilerServices/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.ComponentModel;

// ReSharper disable once CheckNamespace
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Enables support for C# 9/10 records on older frameworks.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DebugSymbols>True</DebugSymbols>
<LangVersion>9</LangVersion>
<LangVersion>12</LangVersion>
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Dhgms.Nucleotide.Generators.GeneratorProcessors;
using Dhgms.Nucleotide.Generators.Models;
using Dhgms.Nucleotide.Generators.PropertyInfo;
Expand All @@ -27,7 +28,85 @@ public class UnkeyedModelClassGeneratorProcessor : BaseClassLevelCodeGeneratorPr

protected override IEnumerable<PropertyDeclarationSyntax> GetPropertyDeclarations(IEntityGenerationModel entityGenerationModel)
{
return entityGenerationModel.Properties?.Select(GetPropertyDeclaration).ToArray();
var result = new List<PropertyDeclarationSyntax>();

return GetPropertyDeclarations(entityGenerationModel, result);
}

private IEnumerable<PropertyDeclarationSyntax> GetPropertyDeclarations(IEntityGenerationModel entityGenerationModel, List<PropertyDeclarationSyntax> result)
{
var properties = entityGenerationModel.Properties?.Select(GetPropertyDeclaration).ToArray();
if (properties != null)
{
result.AddRange(properties);
}

if (entityGenerationModel.InterfaceGenerationModels != null)
{
foreach (var interfaceGenerationModel in entityGenerationModel.InterfaceGenerationModels)
{
DoPropertyDeclarations(interfaceGenerationModel, result);
}
}

return result;
}

private void DoPropertyDeclarations(InterfaceGenerationModel interfaceGenerationModel, List<PropertyDeclarationSyntax> result)
{
PropertyDeclarationSyntax[] properties;
if (interfaceGenerationModel.Properties != null)
{
properties = interfaceGenerationModel.Properties?.Select(GetPropertyDeclaration).ToArray();
if (properties != null)
{
result.AddRange(properties);
}
}

if (interfaceGenerationModel.BaseInterfaces == null)
{
return;
}

foreach (var baseInterface in interfaceGenerationModel.BaseInterfaces)
{
DoPropertyDeclarations(baseInterface, result);
}
}

private PropertyDeclarationSyntax GetPropertyDeclaration(PropertyGenerationModel propertyGenerationModel)
{
var type = SyntaxFactory.ParseName(propertyGenerationModel.TypeName);
var identifier = propertyGenerationModel.Name;

var summary = new[]
{
SyntaxFactory.Comment($"/// <inheritdoc />"),
};

var accessorList = GetPropertyAccessorDeclarationSyntaxCollection(propertyGenerationModel.PropertyAccessorFlags);

var result = SyntaxFactory.PropertyDeclaration(type, identifier)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.WithAccessorList(
SyntaxFactory.AccessorList(
SyntaxFactory.List(accessorList)
))
.WithLeadingTrivia(summary);
return result;
}

private static AccessorDeclarationSyntax[] GetPropertyAccessorDeclarationSyntaxCollection(
PropertyAccessorFlags propertyAccessorFlags)
{
return
[
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
];
}

protected override PropertyDeclarationSyntax GetPropertyDeclaration(PropertyInfoBase propertyInfo, AccessorDeclarationSyntax[] accessorList, IEnumerable<SyntaxTrivia> summary)
Expand All @@ -37,7 +116,7 @@ protected override PropertyDeclarationSyntax GetPropertyDeclaration(PropertyInfo

var attributes = GetAttributesForProperty(propertyInfo);
var result = SyntaxFactory.PropertyDeclaration(type, identifier)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.VirtualKeyword))
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.WithAccessorList(
SyntaxFactory.AccessorList(
SyntaxFactory.List(accessorList)
Expand All @@ -59,29 +138,6 @@ protected override IList<string> GetUsings()
return null;
}

private MemberDeclarationSyntax[] GetUnkeyedClasses()
{
var name = "Test";

var leadingTrivia = new[]
{
SyntaxFactory.Comment($"/// <summary>Represents the Unkeyed {name} model. Typically used for adding a new record.</summary>"),
};

var baseTypes = new BaseTypeSyntax[]
{
SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"IUnkeyed{name}Model"))
};

return new MemberDeclarationSyntax[]
{
SyntaxFactory.ClassDeclaration($"Unkeyed{name}Model")
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddBaseListTypes(baseTypes)
.WithLeadingTrivia(leadingTrivia)
};
}

protected override string[] GetClassPrefixes() => new [] {"Unkeyed"};

protected override string GetClassSuffix() => "Model";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ protected override MethodDeclarationSyntax[] GetMethodDeclarations(string classN

protected override string[] GetBaseInterfaces(IEntityGenerationModel entityGenerationModel, string prefix)
{
return null;
return entityGenerationModel.InterfaceGenerationModels?.Select(x => x.ClassName)
.ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public abstract class EntityGenerationModel : IEntityGenerationModel

public abstract IEntityGenerationModel BaseTypeEntityGenerationModel { get; }

public abstract IInterfaceGenerationModel[] InterfaceGenerationModels { get; }
public abstract InterfaceGenerationModel[] InterfaceGenerationModels { get; }

/// <summary>
/// Gets the name of the information class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public interface IEntityGenerationModel : IObjectGenerationModel

IEntityGenerationModel BaseTypeEntityGenerationModel { get; }

IInterfaceGenerationModel[] InterfaceGenerationModels { get; }
InterfaceGenerationModel[] InterfaceGenerationModels { get; }

/// <summary>
/// Gets the class remarks.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@

namespace Dhgms.Nucleotide.Generators.Models
{
public interface IInterfaceGenerationModel : IObjectGenerationModel
/// <summary>
/// Represents the model for an interface.
/// </summary>
/// <param name="ClassName">Name of the interface.</param>
/// <param name="Properties">Properties directly defined on the interface.</param>
/// <param name="Methods">Methods directly defined on the interface.</param>
/// <param name="BaseInterfaces">Interfaces that this interface inherits from.</param>
public record InterfaceGenerationModel(
string ClassName,
IList<PropertyGenerationModel> Properties,
IList<IInterfaceMethodGenerationModel> Methods,
IList<InterfaceGenerationModel> BaseInterfaces) : IObjectGenerationModel
{
IList<IPropertyGenerationModel> Properties { get; }

IList<IInterfaceMethodGenerationModel> Methods { get; }

IList<IInterfaceGenerationModel> BaseInterfaces { get; }
}
}
16 changes: 10 additions & 6 deletions src/Dhgms.Nucleotide.Generators/Models/IPropertyGenerationModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@

namespace Dhgms.Nucleotide.Generators.Models
{
public interface IPropertyGenerationModel : INameable
{
string Type { get; }

PropertyAccessorFlags PropertyAccessorFlags { get; }
}
/// <summary>
/// Represents a property on an object.
/// </summary>
/// <param name="TypeName">The fully qualified name of the type.</param>
/// <param name="PropertyAccessorFlags">Accessor flags for the property.</param>
public record PropertyGenerationModel(
string TypeName,
string Name,
PropertyAccessorFlags PropertyAccessorFlags)
: INameable;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<DebugSymbols>True</DebugSymbols>
<LangVersion>12</LangVersion>
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Collections.Generic;
using Dhgms.Nucleotide.Generators.Models;

namespace Dhgms.Nucleotide.SampleGenerator.InterfaceGenerationModels.Whipstaff.Entities
{
public sealed record NameableInterfaceGenerationModel() : InterfaceGenerationModel(
"Whipstaff.Core.Entities.INameable",
new List<PropertyGenerationModel>
{
new ("string", "Name", PropertyAccessorFlags.Get)
},
new List<IInterfaceMethodGenerationModel>(),
new List<InterfaceGenerationModel>());
}
20 changes: 11 additions & 9 deletions src/Dhgms.Nucleotide.ModelTests/ModelGenerationDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// DHGMS Solutions and Contributors licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using Dhgms.Nucleotide.Generators.Features.Database;
using Dhgms.Nucleotide.Generators.Features.EntityFramework;
using Dhgms.Nucleotide.Generators.Features.ReactiveUI.Wpf;
using Dhgms.Nucleotide.Generators.Models;
using Dhgms.Nucleotide.Generators.PropertyInfo;
using Dhgms.Nucleotide.SampleGenerator.InterfaceGenerationModels.Whipstaff.Entities;

namespace Dhgms.Nucleotide.ModelTests
{
Expand All @@ -17,7 +19,7 @@ public class AddressEntityGenerationModel : EntityGenerationModel

public override KeyType KeyType => KeyType.Int32;
public override IEntityGenerationModel BaseTypeEntityGenerationModel => null;
public override IInterfaceGenerationModel[] InterfaceGenerationModels => null;
public override InterfaceGenerationModel[] InterfaceGenerationModels => null;

public override string ClassRemarks => "Represents an Address";

Expand All @@ -32,7 +34,7 @@ public class GenderEntityGenerationModel : EntityGenerationModel

public override KeyType KeyType => KeyType.Int32;
public override IEntityGenerationModel BaseTypeEntityGenerationModel => null;
public override IInterfaceGenerationModel[] InterfaceGenerationModels => null;
public override InterfaceGenerationModel[] InterfaceGenerationModels => null;

public override string ClassRemarks => "Represents a Gender";

Expand All @@ -48,7 +50,7 @@ public class PersonEntityGenerationModel : EntityGenerationModel

public override KeyType KeyType => KeyType.Int32;
public override IEntityGenerationModel BaseTypeEntityGenerationModel => null;
public override IInterfaceGenerationModel[] InterfaceGenerationModels => null;
public override InterfaceGenerationModel[] InterfaceGenerationModels => null;

public override string ClassRemarks => "Represents a Person";

Expand All @@ -63,14 +65,14 @@ public class SalutationEntityGenerationModel : EntityGenerationModel

public override KeyType KeyType => KeyType.Int32;
public override IEntityGenerationModel BaseTypeEntityGenerationModel => null;
public override IInterfaceGenerationModel[] InterfaceGenerationModels => null;
public override InterfaceGenerationModel[] InterfaceGenerationModels => new InterfaceGenerationModel[]
{
new NameableInterfaceGenerationModel()
};

public override string ClassRemarks => "Represents a Salutation";

public override PropertyInfoBase[] Properties => new PropertyInfoBase[]
{
new ClrStringPropertyInfo(CollectionType.None, "Name", "Name of the salutation", false, 3, 255, false, false, null),
};
public override PropertyInfoBase[] Properties => Array.Empty<PropertyInfoBase>();
}

public class UserEntityGenerationModel : EntityGenerationModel
Expand All @@ -79,7 +81,7 @@ public class UserEntityGenerationModel : EntityGenerationModel

public override KeyType KeyType => KeyType.Int32;
public override IEntityGenerationModel BaseTypeEntityGenerationModel => null;
public override IInterfaceGenerationModel[] InterfaceGenerationModels => null;
public override InterfaceGenerationModel[] InterfaceGenerationModels => null;

public override string ClassRemarks => "Represents a User";

Expand Down

0 comments on commit 8c862ee

Please sign in to comment.