diff --git a/README.md b/README.md index 90f4d56..f663127 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ PM> Install-Package M31.FluentApi 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: ```xml - + ``` If you would like to examine the generated code, you may emit it by adding the following lines to your `csproj` file: @@ -222,11 +222,11 @@ FluentCollection( int builderStep, string singularName, string withItems = "With{Name}", - string withItem = "With{SingularName}", - string withZeroItems = "WithZero{Name}") + string? withItem = "With{SingularName}", + string? withZeroItems = "WithZero{Name}") ``` -Can be used instead of the `FluentMember` attribute if the decorated member is a collection. This attribute generates methods for setting multiple items, one item and zero items. The supported collection types can be seen in the source file [CollectionInference.cs](src/M31.FluentApi.Generator/SourceGenerators/Collections/CollectionInference.cs). +Can be used instead of the `FluentMember` attribute if the decorated member is a collection. This attribute generates methods for setting multiple items, one item and zero items. The supported collection types can be seen in the source file [CollectionInference.cs](src/M31.FluentApi.Generator/SourceGenerators/Collections/CollectionInference.cs). If `withItem` or `withZeroItems` is set to `null`, the corresponding method will not be generated. ```cs [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")] diff --git a/src/ExampleProject/Program.cs b/src/ExampleProject/Program.cs index 0666fd6..50ddc6e 100644 --- a/src/ExampleProject/Program.cs +++ b/src/ExampleProject/Program.cs @@ -1,6 +1,8 @@ using System.Text.Json; using ExampleProject; +#pragma warning disable CS8321 // Local function is declared but never used + // Student // diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs index d5f4d4e..b317922 100644 --- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs +++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs @@ -89,8 +89,13 @@ internal BuilderMethod CreateWithItemsParamsMethod(MethodCreator methodCreator) computeValueCode); } - internal BuilderMethod CreateWithItemMethod(MethodCreator methodCreator) + internal BuilderMethod? CreateWithItemMethod(MethodCreator methodCreator) { + if (collectionAttributeInfo.WithItem == null) + { + return null; + } + Parameter parameter = new Parameter(genericTypeArgument, collectionAttributeInfo.SingularNameInCamelCase); return methodCreator.CreateMethodWithComputedValue( symbolInfo, @@ -101,7 +106,7 @@ internal BuilderMethod CreateWithItemMethod(MethodCreator methodCreator) internal BuilderMethod? CreateWithItemLambdaMethod(MethodCreator methodCreator) { - if (collectionAttributeInfo.LambdaBuilderInfo == null) + if (collectionAttributeInfo.WithItem == null || collectionAttributeInfo.LambdaBuilderInfo == null) { return null; } @@ -123,8 +128,13 @@ internal BuilderMethod CreateWithItemMethod(MethodCreator methodCreator) computeValueCode); } - internal BuilderMethod CreateWithZeroItemsMethod(MethodCreator methodCreator) + internal BuilderMethod? CreateWithZeroItemsMethod(MethodCreator methodCreator) { + if (collectionAttributeInfo.WithZeroItems == null) + { + return null; + } + string collectionWithZeroItemsCode = CreateCollectionWithZeroItems(genericTypeArgument); return methodCreator.CreateMethodWithFixedValue( symbolInfo, diff --git a/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj b/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj index 5ccac8f..228681a 100644 --- a/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj +++ b/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj @@ -11,7 +11,7 @@ true true true - 1.9.1 + 1.10.0 Kevin Schaal The generator package for M31.FluentAPI. Don't install this package explicitly, install M31.FluentAPI instead. fluentapi fluentbuilder fluentinterface fluentdesign fluent codegeneration diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs index a700399..85816ad 100644 --- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs +++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs @@ -10,8 +10,8 @@ private FluentCollectionAttributeInfo( int builderStep, string singularName, string withItems, - string withItem, - string withZeroItems, + string? withItem, + string? withZeroItems, LambdaBuilderInfo? lambdaBuilderInfo) : base(builderStep) { @@ -26,8 +26,8 @@ private FluentCollectionAttributeInfo( internal string SingularName { get; } internal string SingularNameInCamelCase { get; } internal string WithItems { get; } - internal string WithItem { get; } - internal string WithZeroItems { get; } + internal string? WithItem { get; } + internal string? WithZeroItems { get; } internal LambdaBuilderInfo? LambdaBuilderInfo { get; } internal override string FluentMethodName => WithItems; @@ -36,12 +36,20 @@ internal static FluentCollectionAttributeInfo Create( string memberName, LambdaBuilderInfo? lambdaBuilderInfo) { - (int builderStep, string singularName, string withItems, string withItem, string withZeroItems) = - attributeData.GetConstructorArguments(); + (int builderStep, string singularName, string withItems, string? withItem, string? withZeroItems) = + attributeData.GetConstructorArguments(); withItems = NameCreator.CreateName(withItems, memberName, singularName); - withItem = NameCreator.CreateName(withItem, memberName, singularName); - withZeroItems = NameCreator.CreateName(withZeroItems, memberName, singularName); + + if (withItem != null) + { + withItem = NameCreator.CreateName(withItem, memberName, singularName); + } + + if (withZeroItems != null) + { + withZeroItems = NameCreator.CreateName(withZeroItems, memberName, singularName); + } return new FluentCollectionAttributeInfo( builderStep, singularName, withItems, withItem, withZeroItems, lambdaBuilderInfo); diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/CreateStudent.expected.txt new file mode 100644 index 0000000..abbf511 --- /dev/null +++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/CreateStudent.expected.txt @@ -0,0 +1,96 @@ +// +// This code was generated by the library M31.FluentAPI. +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member +#nullable enable + +using System.Collections.Generic; + +namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.CollectionMemberClassWithSuppression; + +public class CreateStudent : + CreateStudent.ICreateStudent, + CreateStudent.IWhoseFriendsAre, + CreateStudent.IWithPets, + CreateStudent.IWithBackpackContent +{ + private readonly Student student; + + private CreateStudent() + { + student = new Student(); + } + + public static ICreateStudent InitialStep() + { + return new CreateStudent(); + } + + public static IWithPets WhoseFriendsAre(System.Collections.Generic.List friends) + { + CreateStudent createStudent = new CreateStudent(); + createStudent.student.Friends = friends; + return createStudent; + } + + public static IWithPets WhoseFriendsAre(params string[] friends) + { + CreateStudent createStudent = new CreateStudent(); + createStudent.student.Friends = new List(friends); + return createStudent; + } + + IWithPets IWhoseFriendsAre.WhoseFriendsAre(System.Collections.Generic.List friends) + { + student.Friends = friends; + return this; + } + + IWithPets IWhoseFriendsAre.WhoseFriendsAre(params string[] friends) + { + student.Friends = new List(friends); + return this; + } + + IWithBackpackContent IWithPets.WithPets(params string[] pets) + { + student.Pets = pets; + return this; + } + + Student IWithBackpackContent.WithBackpackContent(System.Collections.Generic.HashSet backpackContent) + { + student.BackpackContent = backpackContent; + return student; + } + + Student IWithBackpackContent.WithBackpackContent(params string[] backpackContent) + { + student.BackpackContent = new HashSet(backpackContent); + return student; + } + + public interface ICreateStudent : IWhoseFriendsAre + { + } + + public interface IWhoseFriendsAre + { + IWithPets WhoseFriendsAre(System.Collections.Generic.List friends); + + IWithPets WhoseFriendsAre(params string[] friends); + } + + public interface IWithPets + { + IWithBackpackContent WithPets(params string[] pets); + } + + public interface IWithBackpackContent + { + Student WithBackpackContent(System.Collections.Generic.HashSet backpackContent); + + Student WithBackpackContent(params string[] backpackContent); + } +} \ No newline at end of file diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/CreateStudent.g.cs new file mode 100644 index 0000000..abbf511 --- /dev/null +++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/CreateStudent.g.cs @@ -0,0 +1,96 @@ +// +// This code was generated by the library M31.FluentAPI. +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member +#nullable enable + +using System.Collections.Generic; + +namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.CollectionMemberClassWithSuppression; + +public class CreateStudent : + CreateStudent.ICreateStudent, + CreateStudent.IWhoseFriendsAre, + CreateStudent.IWithPets, + CreateStudent.IWithBackpackContent +{ + private readonly Student student; + + private CreateStudent() + { + student = new Student(); + } + + public static ICreateStudent InitialStep() + { + return new CreateStudent(); + } + + public static IWithPets WhoseFriendsAre(System.Collections.Generic.List friends) + { + CreateStudent createStudent = new CreateStudent(); + createStudent.student.Friends = friends; + return createStudent; + } + + public static IWithPets WhoseFriendsAre(params string[] friends) + { + CreateStudent createStudent = new CreateStudent(); + createStudent.student.Friends = new List(friends); + return createStudent; + } + + IWithPets IWhoseFriendsAre.WhoseFriendsAre(System.Collections.Generic.List friends) + { + student.Friends = friends; + return this; + } + + IWithPets IWhoseFriendsAre.WhoseFriendsAre(params string[] friends) + { + student.Friends = new List(friends); + return this; + } + + IWithBackpackContent IWithPets.WithPets(params string[] pets) + { + student.Pets = pets; + return this; + } + + Student IWithBackpackContent.WithBackpackContent(System.Collections.Generic.HashSet backpackContent) + { + student.BackpackContent = backpackContent; + return student; + } + + Student IWithBackpackContent.WithBackpackContent(params string[] backpackContent) + { + student.BackpackContent = new HashSet(backpackContent); + return student; + } + + public interface ICreateStudent : IWhoseFriendsAre + { + } + + public interface IWhoseFriendsAre + { + IWithPets WhoseFriendsAre(System.Collections.Generic.List friends); + + IWithPets WhoseFriendsAre(params string[] friends); + } + + public interface IWithPets + { + IWithBackpackContent WithPets(params string[] pets); + } + + public interface IWithBackpackContent + { + Student WithBackpackContent(System.Collections.Generic.HashSet backpackContent); + + Student WithBackpackContent(params string[] backpackContent); + } +} \ No newline at end of file diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/Student.cs new file mode 100644 index 0000000..ab7b474 --- /dev/null +++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/CollectionMemberClassWithSuppression/Student.cs @@ -0,0 +1,21 @@ +// Non-nullable member is uninitialized +#pragma warning disable CS8618 +// ReSharper disable All + +using System.Collections.Generic; +using M31.FluentApi.Attributes; + +namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.CollectionMemberClassWithSuppression; + +[FluentApi] +public class Student +{ + [FluentCollection(0, "Friend", "WhoseFriendsAre", null, null)] + public List Friends { get; set; } + + [FluentCollection(1, "Pet", withItem: null, withZeroItems: null)] + public string[] Pets { get; set; } + + [FluentCollection(2, "BackpackContent", "WithBackpackContent", null, null)] + public HashSet BackpackContent { get; set; } +} \ No newline at end of file diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs index 6a0b15f..9ad6a26 100644 --- a/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs +++ b/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs @@ -13,6 +13,7 @@ internal class TestDataProvider : IEnumerable new object[] { "Abstract", "AliasNamespaceClass", "Student" }, new object[] { "Abstract", "CollectionInterfaceMemberClass", "Student" }, new object[] { "Abstract", "CollectionMemberClass", "Student" }, + new object[] { "Abstract", "CollectionMemberClassWithSuppression", "Student" }, new object[] { "Abstract", "CollectionNullableArrayClass", "Student" }, new object[] { "Abstract", "ContinueWithAfterCompoundClass", "Student" }, new object[] { "Abstract", "ContinueWithInForkClass", "Student" }, diff --git a/src/M31.FluentApi/Attributes/FluentCollectionAttribute.cs b/src/M31.FluentApi/Attributes/FluentCollectionAttribute.cs index caff7f8..04e6211 100644 --- a/src/M31.FluentApi/Attributes/FluentCollectionAttribute.cs +++ b/src/M31.FluentApi/Attributes/FluentCollectionAttribute.cs @@ -14,16 +14,16 @@ public class FluentCollectionAttribute : Attribute /// The builder step in which the collection can be set. /// The singular of the collection name. /// The name of the builder method that sets multiple items. - /// The name of the builder method that sets a single item. + /// The name of the builder method that sets a single item. + /// If set to null, the builder method will not be generated. /// The name of the builder method that sets zero items. - /// + /// If set to null, the builder method will not be generated. public FluentCollectionAttribute( int builderStep, string singularName, string withItems = "With{Name}", - string withItem = "With{SingularName}", - string withZeroItems = "WithZero{Name}") + string? withItem = "With{SingularName}", + string? withZeroItems = "WithZero{Name}") { - } } \ No newline at end of file diff --git a/src/M31.FluentApi/M31.FluentApi.csproj b/src/M31.FluentApi/M31.FluentApi.csproj index 1dbe75b..e522469 100644 --- a/src/M31.FluentApi/M31.FluentApi.csproj +++ b/src/M31.FluentApi/M31.FluentApi.csproj @@ -7,7 +7,7 @@ enable true true - 1.9.1 + 1.10.0 Kevin Schaal Generate fluent builders in C#. fluentapi fluentbuilder fluentinterface fluentdesign fluent codegeneration