Skip to content

Commit

Permalink
By default : generate only the With*** builder methods (#53)
Browse files Browse the repository at this point in the history
* Add FluentBuilderMethods attribute argument

* ,...

* ,,,

* xxx
  • Loading branch information
StefH authored Apr 3, 2023
1 parent 33127ad commit bfb3cc3
Show file tree
Hide file tree
Showing 34 changed files with 255 additions and 511 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,17 @@ class Program
Console.WriteLine($"{user.FirstName} {user.LastName}");
}
}
```

## :books: Notes

Since version 0.8.0, this FluentBuilder will only generate the `With***` methods. If you want the builder to also generate the `Without***` methods, add the enum `FluentBuilderMethods.WithAndWithout` to the attribute.

``` c#
using FluentBuilder;

[AutoGenerateBuilder(typeof(UserDto), FluentBuilderMethods.WithAndWithout)]
public partial class MyUserDtoBuilder
{
}
```
11 changes: 1 addition & 10 deletions src-examples/BuilderConsumer/BuilderConsumer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,4 @@
<ProjectReference Include="..\ConsumerClassLibrary\ConsumerClassLibrary.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="ThingWithOnlyParameterizedConstructors.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
<Compile Update="ThingWithParameterizedConstructor.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
</ItemGroup>

</Project>
</Project>
62 changes: 51 additions & 11 deletions src/FluentBuilderGenerator/FileGenerators/ExtraFilesGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// This source code is partly based on https://justsimplycode.com/2020/12/06/auto-generate-builders-using-source-generator-in-net-5

using System.Text;
using FluentBuilderGenerator.Extensions;
using FluentBuilderGenerator.Models;
using FluentBuilderGenerator.Types;

Expand All @@ -11,7 +13,7 @@ internal class ExtraFilesGenerator : IFileGenerator

private readonly string _assemblyName;
private readonly bool _supportsNullable;

public ExtraFilesGenerator(string assemblyName, bool supportsNullable)
{
_assemblyName = assemblyName;
Expand All @@ -20,53 +22,84 @@ public ExtraFilesGenerator(string assemblyName, bool supportsNullable)

public FileData GenerateFile()
{
//var arguments = new[] { "bool handleBaseClasses", "FluentBuilderAccessibility accessibility", "FluentBuilderMethods methods" };
//var defaultValues = new int[] { "true", "FluentBuilderAccessibility.Public", "FluentBuilderMethods.WithOnly" };

//var constructors = new StringBuilder();
//foreach (var argument in arguments)
//{
// constructors.AppendLine(8, "public AutoGenerateBuilderAttribute(");
//}

return new FileData
(
FileDataType.Attribute,
Name,
$@"{Header.Text}
{(_supportsNullable ? "#nullable enable" : string.Empty)}
{_supportsNullable.IIf("#nullable enable")}
using System;
namespace FluentBuilder
{{
[AttributeUsage(AttributeTargets.Class)]
internal sealed class AutoGenerateBuilderAttribute : Attribute
{{
public Type{(_supportsNullable ? "?" : string.Empty)} Type {{ get; }}
public Type{_supportsNullable.IIf("?")} Type {{ get; }}
public bool HandleBaseClasses {{ get; }}
public FluentBuilderAccessibility Accessibility {{ get; }}
public FluentBuilderMethods Methods {{ get; }}
public AutoGenerateBuilderAttribute() : this(null, true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(bool handleBaseClasses) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(FluentBuilderAccessibility accessibility) : this(null, true, accessibility, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(FluentBuilderMethods methods) : this(null, true, FluentBuilderAccessibility.Public, methods)
{{
}}
public AutoGenerateBuilderAttribute() : this(null, true, FluentBuilderAccessibility.Public)
public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(null, handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(bool handleBaseClasses) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public)
public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderMethods methods) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public, methods)
{{
}}
public AutoGenerateBuilderAttribute(FluentBuilderAccessibility accessibility) : this(null, true, accessibility)
public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type) : this(type, true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(null, handleBaseClasses, accessibility)
public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses) : this(type, handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(Type{(_supportsNullable ? "?" : string.Empty)} type) : this(type, true, FluentBuilderAccessibility.Public)
public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, FluentBuilderAccessibility accessibility) : this(type, true, accessibility, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(Type{(_supportsNullable ? "?" : string.Empty)} type, FluentBuilderAccessibility accessibility) : this(type, true, accessibility)
public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(type, handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly)
{{
}}
public AutoGenerateBuilderAttribute(Type{(_supportsNullable ? "?" : string.Empty)} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility = FluentBuilderAccessibility.Public)
public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses, FluentBuilderMethods methods) : this(type, handleBaseClasses, FluentBuilderAccessibility.Public, methods)
{{
}}
public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility, FluentBuilderMethods methods)
{{
Type = type;
HandleBaseClasses = handleBaseClasses;
Accessibility = accessibility;
Methods = methods;
}}
}}
Expand All @@ -81,8 +114,15 @@ internal enum FluentBuilderAccessibility
Public = 0,
PublicAndPrivate = 1
}}
[Flags]
internal enum FluentBuilderMethods
{{
WithOnly = 0,
WithAndWithout = 1
}}
}}
{(_supportsNullable ? "#nullable disable" : string.Empty)}"
{_supportsNullable.IIf("#nullable disable")}"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,16 @@ IReadOnlyList<IMethodSymbol> publicConstructors

sb.Append(GeneratePropertyActionMethodIfApplicable(classSymbol, property, allClassSymbols));

sb.AppendLine($" public {builderClassName} Without{property.Name}()");
sb.AppendLine(" {");
sb.AppendLine($" With{property.Name}(() => {defaultValue});");
sb.AppendLine($" _{CamelCase(property.Name)}IsSet = false;");
sb.AppendLine(" return this;");
sb.AppendLine(" }");
sb.AppendLine();
if (fluentData.Methods == FluentBuilderMethods.WithAndWithout)
{
sb.AppendLine($" public {builderClassName} Without{property.Name}()");
sb.AppendLine(" {");
sb.AppendLine($" With{property.Name}(() => {defaultValue});");
sb.AppendLine($" _{CamelCase(property.Name)}IsSet = false;");
sb.AppendLine(" return this;");
sb.AppendLine(" }");
sb.AppendLine();
}
}

return (sb, extraUsings.Distinct().ToList());
Expand Down
2 changes: 2 additions & 0 deletions src/FluentBuilderGenerator/Models/FluentData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ internal struct FluentData
public FluentBuilderAccessibility Accessibility { get; init; }

public BuilderType BuilderType { get; init; }

public FluentBuilderMethods Methods { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,69 @@ public static FluentBuilderAttributeArguments ParseAttributeArguments(AttributeA
return result;
}

if (argumentList.Arguments.Count is < 0 or > 3)
if (argumentList.Arguments.Count is < 0 or > 4)
{
throw new ArgumentException("The AutoGenerateBuilderAttribute requires 0, 1, 2 or 3 arguments.");
throw new ArgumentException("The AutoGenerateBuilderAttribute requires 0, 1, 2, 3 or 4 arguments.");
}

if (argumentList.Arguments.Count == 1)
{
if (TryParseAsBoolean(argumentList.Arguments[0].Expression, out var handleBaseClasses))
{
return result with { HandleBaseClasses = handleBaseClasses };
}
//if (argumentList.Arguments.Count == 1)
//{
// if (TryParseAsBoolean(argumentList.Arguments[0].Expression, out var handleBaseClasses))
// {
// return result with { HandleBaseClasses = handleBaseClasses };
// }

if (TryParseAsType(argumentList.Arguments[0].Expression, out var rawTypeValue))
{
return result with { RawTypeName = rawTypeValue };
}
// if (TryParseAsType(argumentList.Arguments[0].Expression, out var rawTypeValue))
// {
// return result with { RawTypeName = rawTypeValue };
// }

if (TryParseAsEnum<FluentBuilderAccessibility>(argumentList.Arguments[0].Expression, out var accessibility))
{
return result with { Accessibility = accessibility };
}
// if (TryParseAsEnum<FluentBuilderAccessibility>(argumentList.Arguments[0].Expression, out var accessibility))
// {
// return result with { Accessibility = accessibility };
// }

throw new ArgumentException("When the AutoGenerateBuilderAttribute is used with 1 argument, the only argument should be a Type, boolean or FluentBuilderAccessibility.");
}
// if (TryParseAsEnum<FluentBuilderMethods>(argumentList.Arguments[0].Expression, out var methods))
// {
// return result with { Methods = methods };
// }

// throw new ArgumentException($"When the AutoGenerateBuilderAttribute is used with 1 argument, the only argument should be a Type, bool, {nameof(FluentBuilderAccessibility)} or {nameof(FluentBuilderMethods)}.");
//}

int argumentsParsed = 0;
foreach (var argument in argumentList.Arguments)
{
if (TryParseAsType(argument.Expression, out var rawTypeValue))
{
result = result with { RawTypeName = rawTypeValue };
argumentsParsed++;
}

if (TryParseAsBoolean(argument.Expression, out var handleBaseClasses))
{
result = result with { HandleBaseClasses = handleBaseClasses };
argumentsParsed++;
}

if (TryParseAsEnum<FluentBuilderAccessibility>(argument.Expression, out var accessibility))
{
result = result with { Accessibility = accessibility };
argumentsParsed++;
}

if (TryParseAsEnum<FluentBuilderMethods>(argument.Expression, out var methods))
{
result = result with { Methods = methods };
argumentsParsed++;
}
}

if (argumentList.Arguments.Count == 1 & argumentsParsed == 0)
{
throw new ArgumentException($"When the AutoGenerateBuilderAttribute is used with 1 argument, the only argument should be a Type, bool, {nameof(FluentBuilderAccessibility)} or {nameof(FluentBuilderMethods)}.");
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ private static bool TryGet(ClassDeclarationSyntax classDeclarationSyntax, out Fl
Usings = usings,
HandleBaseClasses = fluentBuilderAttributeArguments.HandleBaseClasses,
Accessibility = fluentBuilderAttributeArguments.Accessibility,
BuilderType = BuilderType.Custom
BuilderType = BuilderType.Custom,
Methods = fluentBuilderAttributeArguments.Methods
};

return true;
Expand All @@ -97,7 +98,8 @@ private static bool TryGet(ClassDeclarationSyntax classDeclarationSyntax, out Fl
Usings = usings,
HandleBaseClasses = fluentBuilderAttributeArguments.HandleBaseClasses,
Accessibility = fluentBuilderAttributeArguments.Accessibility,
BuilderType = BuilderType.Generated
BuilderType = BuilderType.Generated,
Methods = fluentBuilderAttributeArguments.Methods
};

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ internal record FluentBuilderAttributeArguments
public bool HandleBaseClasses { get; set; } = true;

public FluentBuilderAccessibility Accessibility { get; set; } = FluentBuilderAccessibility.Public;

public FluentBuilderMethods Methods { get; set; } = FluentBuilderMethods.WithOnly;
}
8 changes: 8 additions & 0 deletions src/FluentBuilderGenerator/Types/FluentBuilderMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace FluentBuilderGenerator.Types;

[Flags]
internal enum FluentBuilderMethods
{
WithOnly = 0,
WithAndWithout = 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ public ClassOnOtherNamespaceBuilder WithId(Func<int> func)
_idIsSet = true;
return this;
}
public ClassOnOtherNamespaceBuilder WithoutId()
{
WithId(() => default(int));
_idIsSet = false;
return this;
}


private bool _Constructor1204632294_IsSet;
private Lazy<AbcTest.OtherNamespace.ClassOnOtherNamespace> _Constructor1204632294 = new Lazy<AbcTest.OtherNamespace.ClassOnOtherNamespace>(() => new AbcTest.OtherNamespace.ClassOnOtherNamespace());
Expand Down
Loading

0 comments on commit bfb3cc3

Please sign in to comment.