Skip to content

Commit f5f7808

Browse files
authored
feat: FluentResult (#13)
* fix(Readme): typo * fix(Readme): typos in code example * feat(FluentReturn): add FluentReturnAttribute * feat(FluentReturn): bookkeep return type and respectReturnType values * refactor(InnerBodyCreation): rename classes * refactor(InnerBodyGeneration): rename methods * refactor(InnerBodyGeneration): rename method * test: add FluentReturnSingleStepClass * feat(TestDataProvider): add filter method for running a single test * feat(Generics): make FluentReturnSingleStepClass test work * refactor(BuilderStepMethod): add CreateMethod and CreateInterfaceMethod methods * fix(InnerBodyForMethodGenerator): use parameter returnType * test: FluentReturnSingleStepPrivateMethodsClass * feat(FluentReturn): adjust BuildReflectionCodeWithRefAndOutParameters method * test: FluentReturn method with ref parameter * fix(InnerBodyForMethodGenerator): suppress nullability for methods that return a result * test: FluentReturnMultiStepClass and FluentReturnMultiStepPrivateMethodsClass * feat(CodeGenerationTests): write generated code only if it changed * fix(FluentReturnMultiStepClass): remove unused using * test: CanExecuteFluentReturnSingleStepPrivateMethodsClass and CanExecuteFluentReturnMultiStepPrivateMethodsClass * feat: detect conflicting control attributes FluentBreak and FluentReturn * docs(Readme): fix typo * test: ContinueWithOfOverloadedMethodClass * feat(Storybook): add FluentReturn example * docs(Readme): add FluentReturn description and example * chore: increase nuget versions to 1.3.0 * chore(Nuget): add readme
1 parent 5b1ddb1 commit f5f7808

File tree

60 files changed

+1588
-216
lines changed

Some content is hidden

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

60 files changed

+1588
-216
lines changed

README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ PM> Install-Package M31.FluentApi
2525
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:
2626

2727
```xml
28-
<PackageReference Include="M31.FluentApi" Version="1.2.0" PrivateAssets="all"/>
28+
<PackageReference Include="M31.FluentApi" Version="1.3.0" PrivateAssets="all"/>
2929
```
3030

3131
If you would like to examine the generated code, you may emit it by adding the following lines to your `csproj` file:
@@ -103,7 +103,7 @@ The attributes `FluentPredicate` and `FluentCollection` can be used instead of a
103103

104104
The `FluentMethod` attribute is used for custom builder method implementations.
105105

106-
The control attribute `FluentContinueWith` indicates a jump to the specified builder step, and `FluentBreak` stops the builder.
106+
The control attribute `FluentContinueWith` indicates a jump to the specified builder step, and `FluentBreak` stops the builder. `FluentReturn` allows returning arbitrary types and values within the generated API.
107107

108108

109109
### FluentApi
@@ -310,6 +310,24 @@ private void WhoseAddressIsUnknown()
310310
...WhoseAddressIsUnknown();
311311
```
312312

313+
### FluentReturn
314+
315+
Allows the builder to respect the return value of the decorated method, enabling the return of arbitrary types and values within the generated API. If a void method is decorated with this attribute, the builder method will also return void.
316+
317+
```cs
318+
[FluentMethod(1)]
319+
[FluentReturn]
320+
public string ToJson()
321+
{
322+
return JsonSerializer.Serialize(this);
323+
}
324+
```
325+
326+
```cs
327+
string serialized = ...ToJson();
328+
```
329+
330+
313331
### Forks
314332

315333
To create forks specify builder methods at the same builder step. The resulting API offers all specified methods at this step but only one can be called:
@@ -319,11 +337,11 @@ To create forks specify builder methods at the same builder step. The resulting
319337
public int Age { get; private set; }
320338

321339
[FluentMethod(1)]
322-
private void BornOn(DateOnldateOfBirth)
340+
private void BornOn(DateOnly dateOfBirth)
323341
{
324-
DateOnly today = DateOnlFromDateTime(DateTime.Today);
325-
int age = today.Year dateOfBirth.Year;
326-
if (dateOfBirth > today.AddYea(-age)) age--;
342+
DateOnly today = DateOnly.FromDateTime(DateTime.Today);
343+
int age = today.Year - dateOfBirth.Year;
344+
if (dateOfBirth > today.AddYears(-age)) age--;
327345
Age = age;
328346
}
329347
```

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderStepsGeneration/BuilderStepMethod.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,22 @@ protected BuilderStepMethod(BuilderMethod builderMethod)
1414

1515
internal abstract Method BuildMethodCode(BuilderAndTargetInfo info);
1616

17-
protected MethodSignature CreateMethodSignature(string returnType, params string[] modifiers)
17+
protected Method CreateMethod(string defaultReturnType, params string[] modifiers)
1818
{
19+
MethodSignature methodSignature = CreateMethodSignature(defaultReturnType, modifiers);
20+
return new Method(methodSignature);
21+
}
22+
23+
protected Method CreateInterfaceMethod(string interfaceName, string defaultReturnType, params string[] modifiers)
24+
{
25+
MethodSignature methodSignature = CreateMethodSignature(defaultReturnType, modifiers);
26+
return new InterfaceMethod(methodSignature, interfaceName);
27+
}
28+
29+
private MethodSignature CreateMethodSignature(string defaultReturnType, params string[] modifiers)
30+
{
31+
string returnType = ReturnTypeToRespect ?? defaultReturnType;
32+
1933
MethodSignature signature = MethodSignature.Create(returnType, MethodName, false);
2034
signature.AddModifiers(modifiers);
2135

@@ -39,10 +53,18 @@ protected MethodSignature CreateMethodSignature(string returnType, params string
3953

4054
protected void CreateBody(Method method, string instancePrefix)
4155
{
42-
List<string> bodyCode = BuildBodyCode(instancePrefix);
56+
List<string> bodyCode = BuildBodyCode(instancePrefix, ReturnTypeToRespect);
4357
foreach (string bodyLine in bodyCode)
4458
{
4559
method.AppendBodyLine(bodyLine);
4660
}
4761
}
62+
63+
protected void CreateReturnStatement(Method method, string defaultReturnStatement)
64+
{
65+
if (ReturnTypeToRespect == null)
66+
{
67+
method.AppendBodyLine(defaultReturnStatement);
68+
}
69+
}
4870
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderStepsGeneration/FirstStepBuilderMethod.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ internal FirstStepBuilderMethod(BuilderMethod builderMethod, string returnType)
1818
internal override Method BuildMethodCode(BuilderAndTargetInfo info)
1919
{
2020
// public static IBornOn WithName(string name)
21-
MethodSignature methodSignature = CreateMethodSignature(ReturnType, "public", "static");
22-
Method method = new Method(methodSignature);
21+
Method method = CreateMethod(ReturnType, "public", "static");
2322

2423
// CreateStudent<T1, T2> createStudent = new CreateStudent<T1, T2>();
2524
method.AppendBodyLine(
@@ -30,7 +29,7 @@ internal override Method BuildMethodCode(BuilderAndTargetInfo info)
3029
CreateBody(method, $"{info.BuilderInstanceName}.");
3130

3231
// return createStudent;
33-
method.AppendBodyLine($"return {info.BuilderInstanceName};");
32+
CreateReturnStatement(method, $"return {info.BuilderInstanceName};");
3433

3534
return method;
3635
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderStepsGeneration/InterjacentBuilderMethod.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,13 @@ internal InterjacentBuilderMethod(BuilderMethod builderMethod, string returnType
2020
internal override Method BuildMethodCode(BuilderAndTargetInfo info)
2121
{
2222
// public IInSemester BornOn(System.DateOnly dateOfBirth)
23-
MethodSignature methodSignature = CreateMethodSignature(ReturnType, "public");
24-
Method method = new InterfaceMethod(methodSignature, InterfaceName);
23+
Method method = CreateInterfaceMethod(InterfaceName, ReturnType, "public");
2524

2625
// student.DateOfBirth = dateOfBirth;
2726
CreateBody(method, string.Empty);
2827

2928
// return this;
30-
method.AppendBodyLine($"return this;");
29+
CreateReturnStatement(method, "return this;");
3130

3231
return method;
3332
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderStepsGeneration/LastStepBuilderMethod.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ internal LastStepBuilderMethod(BuilderMethod builderMethod, string interfaceName
1818
internal override Method BuildMethodCode(BuilderAndTargetInfo info)
1919
{
2020
// public Student<T1, T2> InSemester(int semester)
21-
MethodSignature methodSignature = CreateMethodSignature(info.FluentApiClassNameWithTypeParameters, "public");
22-
Method method = new InterfaceMethod(methodSignature, InterfaceName);
21+
Method method = CreateInterfaceMethod(InterfaceName, info.FluentApiClassNameWithTypeParameters, "public");
2322

2423
// student.Semester = semester;
2524
CreateBody(method, string.Empty);
2625

2726
// return student;
28-
method.AppendBodyLine($"return {info.ClassInstanceName};");
27+
CreateReturnStatement(method, $"return {info.ClassInstanceName};");
2928

3029
return method;
3130
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderStepsGeneration/SingleStepBuilderMethod.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ internal SingleStepBuilderMethod(BuilderMethod builderMethod)
1515
internal override Method BuildMethodCode(BuilderAndTargetInfo info)
1616
{
1717
// public static Student<T1, T2> InSemester(int semester)
18-
MethodSignature methodSignature =
19-
CreateMethodSignature(info.FluentApiClassNameWithTypeParameters, "public", "static");
20-
Method method = new Method(methodSignature);
18+
Method method = CreateMethod(info.FluentApiClassNameWithTypeParameters, "public", "static");
2119

2220
// CreateStudent<T1, T2> createStudent = new CreateStudent<T1, T2>();
2321
method.AppendBodyLine(
@@ -28,7 +26,7 @@ internal override Method BuildMethodCode(BuilderAndTargetInfo info)
2826
CreateBody(method, $"{info.BuilderInstanceName}.");
2927

3028
// return createStudent.student;
31-
method.AppendBodyLine($"return {info.BuilderInstanceName}.{info.ClassInstanceName};");
29+
CreateReturnStatement(method, $"return {info.BuilderInstanceName}.{info.ClassInstanceName};");
3230

3331
return method;
3432
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.Commons;
22

3-
internal delegate List<string> BuildBodyCode(string instancePrefix);
3+
internal delegate List<string> BuildBodyCode(string instancePrefix, string? returnType);

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethod.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ internal class BuilderMethod
88
internal string MethodName { get; }
99
internal GenericInfo? GenericInfo { get; }
1010
internal IReadOnlyCollection<Parameter> Parameters { get; }
11+
internal string? ReturnTypeToRespect { get; }
1112
internal BuildBodyCode BuildBodyCode { get; }
1213

1314
internal BuilderMethod(
1415
string methodName,
1516
GenericInfo? genericInfo,
1617
IReadOnlyCollection<Parameter> parameters,
18+
string? returnTypeToRespect,
1719
BuildBodyCode buildBodyCode)
1820
{
1921
MethodName = methodName;
2022
GenericInfo = genericInfo;
2123
Parameters = parameters;
24+
ReturnTypeToRespect = returnTypeToRespect;
2225
BuildBodyCode = buildBodyCode;
2326
}
2427

@@ -27,6 +30,7 @@ internal BuilderMethod(BuilderMethod builderMethod)
2730
MethodName = builderMethod.MethodName;
2831
GenericInfo = builderMethod.GenericInfo;
2932
Parameters = builderMethod.Parameters;
33+
ReturnTypeToRespect = builderMethod.ReturnTypeToRespect;
3034
BuildBodyCode = builderMethod.BuildBodyCode;
3135
}
3236
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethodFactory.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal BuilderMethodFactory(InnerBodyCreationDelegates innerBodyCreationDelega
1414

1515
internal BuilderMethod CreateBuilderMethod(string methodName)
1616
{
17-
return new BuilderMethod(methodName, null, new List<Parameter>(), _ => new List<string>());
17+
return new BuilderMethod(methodName, null, new List<Parameter>(), null, (_, _) => new List<string>());
1818
}
1919

2020
internal BuilderMethod CreateBuilderMethod(string methodName, ComputeValueCode computeValue)
@@ -23,7 +23,7 @@ internal BuilderMethod CreateBuilderMethod(string methodName, ComputeValueCode c
2323
? new List<Parameter>() { computeValue.Parameter }
2424
: new List<Parameter>();
2525

26-
List<string> BuildBodyCode(string instancePrefix)
26+
List<string> BuildBodyCode(string instancePrefix, string? returnType)
2727
{
2828
return new List<string>()
2929
{
@@ -32,25 +32,28 @@ List<string> BuildBodyCode(string instancePrefix)
3232
};
3333
}
3434

35-
return new BuilderMethod(methodName, null, parameters, BuildBodyCode);
35+
return new BuilderMethod(methodName, null, parameters, null, BuildBodyCode);
3636
}
3737

3838
internal BuilderMethod CreateBuilderMethod(string methodName, List<ComputeValueCode> computeValues)
3939
{
4040
List<Parameter> parameters = computeValues.Select(v => v.Parameter).OfType<Parameter>().ToList();
4141

42-
List<string> BuildBodyCode(string instancePrefix)
42+
List<string> BuildBodyCode(string instancePrefix, string? returnType)
4343
{
4444
return computeValues
4545
.Select(v =>
4646
innerBodyCreationDelegates.GetSetMemberCode(v.TargetMember).BuildCode(instancePrefix, v.Code))
4747
.ToList();
4848
}
4949

50-
return new BuilderMethod(methodName, null, parameters, BuildBodyCode);
50+
return new BuilderMethod(methodName, null, parameters, null, BuildBodyCode);
5151
}
5252

53-
internal BuilderMethod CreateBuilderMethod(MethodSymbolInfo methodSymbolInfo, string methodName)
53+
internal BuilderMethod CreateBuilderMethod(
54+
MethodSymbolInfo methodSymbolInfo,
55+
string methodName,
56+
bool respectReturnType)
5457
{
5558
List<Parameter> parameters = methodSymbolInfo.ParameterInfos
5659
.Select(i => new Parameter(
@@ -61,11 +64,19 @@ internal BuilderMethod CreateBuilderMethod(MethodSymbolInfo methodSymbolInfo, st
6164
new ParameterAnnotations(i.ParameterKinds)))
6265
.ToList();
6366

64-
List<string> BuildBodyCode(string instancePrefix)
67+
string? returnTypeToRespect = respectReturnType ? methodSymbolInfo.ReturnType : null;
68+
69+
List<string> BuildBodyCode(string instancePrefix, string? returnType)
6570
{
66-
return innerBodyCreationDelegates.GetCallMethodCode(methodSymbolInfo).BuildCode(instancePrefix, parameters);
71+
return innerBodyCreationDelegates.GetCallMethodCode(methodSymbolInfo)
72+
.BuildCode(instancePrefix, parameters, returnType);
6773
}
6874

69-
return new BuilderMethod(methodName, methodSymbolInfo.GenericInfo, parameters, BuildBodyCode);
75+
return new BuilderMethod(
76+
methodName,
77+
methodSymbolInfo.GenericInfo,
78+
parameters,
79+
returnTypeToRespect,
80+
BuildBodyCode);
7081
}
7182
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.InnerBodyGeneration;
44

5-
internal class InnerBodyGenerator : ICodeBoardActor
5+
internal class InnerBodyCreator : ICodeBoardActor
66
{
77
public void Modify(CodeBoard codeBoard)
88
{
9-
LineForMemberGenerator lineForMemberGenerator = new LineForMemberGenerator(codeBoard);
10-
LineForMethodGenerator lineForMethodGenerator = new LineForMethodGenerator(codeBoard);
9+
InnerBodyForMemberGenerator innerBodyForMemberGenerator = new InnerBodyForMemberGenerator(codeBoard);
10+
InnerBodyForMethodGenerator innerBodyForMethodGenerator = new InnerBodyForMethodGenerator(codeBoard);
1111

1212
foreach (FluentApiSymbolInfo symbolInfo in codeBoard.FluentApiInfos.Select(m => m.SymbolInfo))
1313
{
@@ -19,19 +19,19 @@ public void Modify(CodeBoard codeBoard)
1919
switch (symbolInfo)
2020
{
2121
case MemberSymbolInfo memberInfo:
22-
lineForMemberGenerator.GenerateLine(memberInfo);
22+
innerBodyForMemberGenerator.GenerateInnerBody(memberInfo);
2323
break;
2424

2525
case MethodSymbolInfo methodInfo:
26-
lineForMethodGenerator.GenerateLine(methodInfo);
26+
innerBodyForMethodGenerator.GenerateInnerBody(methodInfo);
2727
break;
2828

2929
default:
3030
throw new ArgumentException($"Unknown symbol info type: {symbolInfo.GetType()}");
3131
}
3232
}
3333

34-
if (lineForMemberGenerator.ReflectionRequired || lineForMethodGenerator.ReflectionRequired)
34+
if (innerBodyForMemberGenerator.ReflectionRequired || innerBodyForMethodGenerator.ReflectionRequired)
3535
{
3636
ImportReflectionNamespace(codeBoard);
3737
}

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/LineForMemberGenerator.cs renamed to src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMemberGenerator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.InnerBodyGeneration;
44

5-
internal class LineForMemberGenerator : LineGeneratorBase<MemberSymbolInfo>
5+
internal class InnerBodyForMemberGenerator : InnerBodyGeneratorBase<MemberSymbolInfo>
66
{
7-
internal LineForMemberGenerator(CodeBoard codeBoard)
7+
internal InnerBodyForMemberGenerator(CodeBoard codeBoard)
88
: base(codeBoard)
99
{
1010
}
@@ -14,7 +14,7 @@ protected override string SymbolType(MemberSymbolInfo symbolInfo)
1414
return symbolInfo.IsProperty ? "Property" : "Field";
1515
}
1616

17-
protected override void GenerateLineWithoutReflection(MemberSymbolInfo symbolInfo)
17+
protected override void GenerateInnerBodyWithoutReflection(MemberSymbolInfo symbolInfo)
1818
{
1919
// createStudent.student.Semester = semester;
2020
SetMemberCode setMemberCode =
@@ -28,7 +28,7 @@ string GetPostfix(string value)
2828
}
2929
}
3030

31-
protected override void GenerateLineWithReflection(MemberSymbolInfo symbolInfo, string infoFieldName)
31+
protected override void GenerateInnerBodyWithReflection(MemberSymbolInfo symbolInfo, string infoFieldName)
3232
{
3333
// semesterPropertyInfo.SetValue(createStudent.student, semester);
3434
SetMemberCode setMemberCode =

0 commit comments

Comments
 (0)