Skip to content

Commit 5811737

Browse files
oformaniukOleh Formaniukm31coding
authored
feat: Inheritance (#32)
* Inheritance implementation for Fluent API * refactor(ClassInfoFactory): minor changes * test: InheritedClass * fix(ClassInfoFactory): compare to SpecialType in GetMembers * test: InheritedClassProtectedMembers and InheritedClassProtectedSetters * docs: mention inheritance in features * fix: inheritance tests * test: inherited record * improve(ExampleProject): add ExchangeStudent example * test: add failing test CanExecuteInheritedClassPrivateSetters * fix: make test InheritedClassPrivateSetters work * fix: InnerBodyForMethodGenerator * refactor(ClassInfoFactory): use List instead of HashSet * fix: commit generated file * chore: bump nuget version --------- Co-authored-by: Oleh Formaniuk <[email protected]> Co-authored-by: Kevin Schaal <[email protected]>
1 parent 85188ca commit 5811737

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1620
-59
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Accompanying blog post: [www.m31coding.com>blog>fluent-api](https://www.m31codin
2424
- Optional (skippable) builder methods
2525
- Forking and branching capabilities
2626
- Support for returning arbitrary types
27-
- Support for generics and partial classes
27+
- Support for inheritance, generics, and partial classes
2828

2929
## Installing via NuGet
3030

@@ -37,7 +37,7 @@ PM> Install-Package M31.FluentApi
3737
A package reference will be added to your `csproj` file. Moreover, since this library provides code via source code generation, consumers of your project don't need the reference to `M31.FluentApi`. Therefore, it is recommended to use the `PrivateAssets` metadata tag:
3838

3939
```xml
40-
<PackageReference Include="M31.FluentApi" Version="1.8.0" PrivateAssets="all"/>
40+
<PackageReference Include="M31.FluentApi" Version="1.9.0" PrivateAssets="all"/>
4141
```
4242

4343
If you would like to examine the generated code, you may emit it by adding the following lines to your `csproj` file:

src/ExampleProject/ExchangeStudent.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Non-nullable member is uninitialized
2+
#pragma warning disable CS8618
3+
// ReSharper disable All
4+
5+
using M31.FluentApi.Attributes;
6+
7+
namespace ExampleProject;
8+
9+
[FluentApi]
10+
public class ExchangeStudent : Student
11+
{
12+
[FluentMember(6)]
13+
public string HomeCountry { get; private set; }
14+
}

src/ExampleProject/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
Console.WriteLine(JsonSerializer.Serialize(student1));
1313
Console.WriteLine(JsonSerializer.Serialize(student2));
1414

15+
// ExchangeStudent (inherited from Student)
16+
//
17+
18+
ExchangeStudent exchangeStudent = CreateExchangeStudent.Named("Bob", "Bishop").BornOn(new DateOnly(2002, 8, 3))
19+
.InSemester(2).LivingInBoston().WithUnknownMood().WhoseFriendIs("Alice").WithHomeCountry("United States");
20+
21+
Console.WriteLine(JsonSerializer.Serialize(exchangeStudent));
22+
1523
// Person
1624
//
1725

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMemberGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ protected override void InitializeInfoField(string fieldName, MemberSymbolInfo s
4343
// semesterPropertyInfo = typeof(Student<T1, T2>)
4444
// .GetProperty("Semester", BindingFlags.Instance | BindingFlags.NonPublic););
4545
string code = $"{fieldName} =" +
46-
$" typeof({CodeBoard.Info.FluentApiClassNameWithTypeParameters})" +
46+
$" typeof({symbolInfo.DeclaringClassNameWithTypeParameters})" +
4747
$".Get{SymbolType(symbolInfo)}(\"{symbolInfo.Name}\", " +
4848
$"{InfoFieldBindingFlagsArgument(symbolInfo)})!;";
4949

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMethodGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ protected override void InitializeInfoField(string fieldName, MethodSymbolInfo s
194194
// Generic types are created via Type.MakeGenericMethodParameter(int position). In addition, a ref type is
195195
// specified via MakeByRefType().
196196
staticConstructor.AppendBodyLine($"{fieldName} = " +
197-
$"typeof({CodeBoard.Info.FluentApiClassNameWithTypeParameters}).GetMethod(");
197+
$"typeof({symbolInfo.DeclaringClassNameWithTypeParameters}).GetMethod(");
198198
staticConstructor.AppendBodyLine($"{indentation}\"{symbolInfo.Name}\",");
199199
staticConstructor.AppendBodyLine($"{indentation}{GetGenericParameterCount(symbolInfo.GenericInfo)},");
200200
staticConstructor.AppendBodyLine($"{indentation}{InfoFieldBindingFlagsArgument(symbolInfo)},");

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/BuilderAndTargetInfo.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ internal BuilderAndTargetInfo(
1717
{
1818
Namespace = @namespace;
1919
FluentApiClassName = fluentApiClassName;
20-
FluentApiClassNameWithTypeParameters = WithTypeParameters(fluentApiClassName, genericInfo);
20+
FluentApiClassNameWithTypeParameters =
21+
ClassInfoFactory.AugmentTypeNameWithGenericParameters(fluentApiClassName, genericInfo);
2122
GenericInfo = genericInfo;
2223
FluentApiTypeIsStruct = fluentApiTypeIsStruct;
2324
FluentApiTypeIsInternal = fluentApiTypeIsInternal;
2425
DefaultAccessModifier = fluentApiTypeIsInternal ? "internal" : "public";
2526
FluentApiTypeConstructorInfo = fluentApiTypeConstructorInfo;
2627
BuilderClassName = builderClassName;
27-
BuilderClassNameWithTypeParameters = WithTypeParameters(builderClassName, genericInfo);
28+
BuilderClassNameWithTypeParameters =
29+
ClassInfoFactory.AugmentTypeNameWithGenericParameters(builderClassName, genericInfo);
2830
BuilderInstanceName = builderClassName.FirstCharToLower();
2931
ClassInstanceName = fluentApiClassName.FirstCharToLower();
3032
InitialStepInterfaceName = $"I{builderClassName}";
@@ -43,10 +45,4 @@ internal BuilderAndTargetInfo(
4345
internal string BuilderInstanceName { get; }
4446
internal string ClassInstanceName { get; }
4547
internal string InitialStepInterfaceName { get; }
46-
47-
private static string WithTypeParameters(string typeName, GenericInfo? genericInfo)
48-
{
49-
string parameterListInAngleBrackets = genericInfo?.ParameterListInAngleBrackets ?? string.Empty;
50-
return $"{typeName}{parameterListInAngleBrackets}";
51-
}
5248
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,29 @@ namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
55

66
internal abstract class FluentApiSymbolInfo
77
{
8-
internal FluentApiSymbolInfo(string name, Accessibility accessibility, bool requiresReflection)
8+
internal FluentApiSymbolInfo(
9+
string name,
10+
string declaringClassNameWithTypeParameters,
11+
Accessibility accessibility,
12+
bool requiresReflection)
913
{
1014
Name = name;
1115
NameInCamelCase = Name.TrimStart('_').FirstCharToLower();
16+
DeclaringClassNameWithTypeParameters = declaringClassNameWithTypeParameters;
1217
Accessibility = accessibility;
1318
RequiresReflection = requiresReflection;
1419
}
1520

1621
internal string Name { get; }
1722
internal string NameInCamelCase { get; }
23+
internal string DeclaringClassNameWithTypeParameters { get; }
1824
internal Accessibility Accessibility { get; }
1925
internal bool RequiresReflection { get; }
2026

2127
protected bool Equals(FluentApiSymbolInfo other)
2228
{
2329
return Name == other.Name &&
30+
DeclaringClassNameWithTypeParameters == other.DeclaringClassNameWithTypeParameters &&
2431
Accessibility == other.Accessibility &&
2532
RequiresReflection == other.RequiresReflection;
2633
}
@@ -35,6 +42,6 @@ public override bool Equals(object? obj)
3542

3643
public override int GetHashCode()
3744
{
38-
return new HashCode().Add(Name, Accessibility, RequiresReflection);
45+
return new HashCode().Add(Name, DeclaringClassNameWithTypeParameters, Accessibility, RequiresReflection);
3946
}
4047
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ internal class MemberSymbolInfo : FluentApiSymbolInfo
99
internal MemberSymbolInfo(
1010
string name,
1111
string type,
12+
string declaringClassNameWithTypeParameters,
1213
Accessibility accessibility,
1314
bool requiresReflection,
1415
string typeForCodeGeneration,
1516
bool isNullable,
1617
bool isProperty,
1718
CollectionType? collectionType)
18-
: base(name, accessibility, requiresReflection)
19+
: base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection)
1920
{
2021
Type = type;
2122
TypeForCodeGeneration = typeForCodeGeneration;

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ internal class MethodSymbolInfo : FluentApiSymbolInfo
88
{
99
internal MethodSymbolInfo(
1010
string name,
11+
string declaringClassNameWithTypeParameters,
1112
Accessibility accessibility,
1213
bool requiresReflection,
1314
GenericInfo? genericInfo,
1415
IReadOnlyCollection<ParameterSymbolInfo> parameterInfos,
1516
string returnType)
16-
: base(name, accessibility, requiresReflection)
17+
: base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection)
1718
{
1819
GenericInfo = genericInfo;
1920
ParameterInfos = parameterInfos;

src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1212
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1313
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
14-
<PackageVersion>1.8.0</PackageVersion>
14+
<PackageVersion>1.9.0</PackageVersion>
1515
<Authors>Kevin Schaal</Authors>
1616
<Description>The generator package for M31.FluentAPI. Don't install this package explicitly, install M31.FluentAPI instead.</Description>
1717
<PackageTags>fluentapi fluentbuilder fluentinterface fluentdesign fluent codegeneration</PackageTags>

0 commit comments

Comments
 (0)