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 committed Dec 31, 2023
1 parent c73d73d commit 8c862ee
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 50 deletions.
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
@@ -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
{
}
}
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
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
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();
}
}
}
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
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
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
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;
}
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
@@ -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
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.