diff --git a/src/.editorconfig b/src/.editorconfig index cd130dc..bff6dc9 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -149,6 +149,35 @@ resharper_redundant_base_qualifier_highlighting=warning resharper_suggest_var_or_type_built_in_types_highlighting=hint resharper_suggest_var_or_type_elsewhere_highlighting=hint resharper_suggest_var_or_type_simple_types_highlighting=hint +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = false:suggestion +csharp_prefer_braces = true:suggestion +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = when_on_single_line:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = no_change +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_space_around_binary_operators = before_and_after # Visual Studio Solution Files @@ -207,9 +236,9 @@ spaces_around_operators=true # Dotnet code style [*.{cs,csx,vb,vbx,cake}] -end_of_line=crlf +end_of_line= crlf indent_brace_style=Allman -indent_size=4 +indent_size= 4 insert_final_newline=true spaces_around_brackets=inside spaces_around_operators=true @@ -242,21 +271,21 @@ dotnet_style_parentheses_in_other_operators=always_for_clarity:silent # Modifier preferences dotnet_style_require_accessibility_modifiers=always:warning -dotnet_style_readonly_field=true:suggestion +dotnet_style_readonly_field= true:suggestion # Expression-level preferences -dotnet_style_object_initializer=true:suggestion -dotnet_style_collection_initializer=true:suggestion -dotnet_style_explicit_tuple_names=true:suggestion -dotnet_style_null_propagation=true:suggestion -dotnet_style_coalesce_expression=true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method=true:silent +dotnet_style_object_initializer= true:suggestion +dotnet_style_collection_initializer= true:suggestion +dotnet_style_explicit_tuple_names= true:suggestion +dotnet_style_null_propagation= true:suggestion +dotnet_style_coalesce_expression= true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method= true:silent dotnet_prefer_inferred_tuple_names=true:suggestion dotnet_prefer_inferred_anonymous_type_member_names=true:suggestion -dotnet_style_prefer_auto_properties=true:silent -dotnet_style_prefer_conditional_expression_over_assignment=true:silent -dotnet_style_prefer_conditional_expression_over_return=true:silent +dotnet_style_prefer_auto_properties= true:silent +dotnet_style_prefer_conditional_expression_over_assignment= true:silent +dotnet_style_prefer_conditional_expression_over_return= true:silent # Naming Styles: @@ -338,52 +367,52 @@ dotnet_naming_symbols.type_parameters.applicable_kinds=type_parameter # Note (From the docs): Naming conventions should be ordered from most-specific to least-specific. # The first rule encountered that can be applied is the only rule that is applied, so put more-specific rules first. dotnet_naming_rule.private_constant_fields.symbols=constant_fields -dotnet_naming_rule.private_constant_fields.style=camel_case_style -dotnet_naming_rule.private_constant_fields.severity=warning +dotnet_naming_rule.private_constant_fields.style = camel_case_style +dotnet_naming_rule.private_constant_fields.severity = warning dotnet_naming_rule.non_private_constant_fields.symbols=constant_fields -dotnet_naming_rule.non_private_constant_fields.style=pascal_case_style -dotnet_naming_rule.non_private_constant_fields.severity=warning +dotnet_naming_rule.non_private_constant_fields.style = pascal_case_style +dotnet_naming_rule.non_private_constant_fields.severity = warning dotnet_naming_rule.private_static_readonly_fields.symbols=private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields.style=pascal_case_style -dotnet_naming_rule.private_static_readonly_fields.severity=warning +dotnet_naming_rule.private_static_readonly_fields.style = pascal_case_style +dotnet_naming_rule.private_static_readonly_fields.severity = warning dotnet_naming_rule.private_readonly_fields.symbols=private_readonly_fields -dotnet_naming_rule.private_readonly_fields.style=underscore_style -dotnet_naming_rule.private_readonly_fields.severity=warning +dotnet_naming_rule.private_readonly_fields.style = underscore_style +dotnet_naming_rule.private_readonly_fields.severity = warning dotnet_naming_rule.private_fields.symbols=private_fields -dotnet_naming_rule.private_fields.style=underscore_style -dotnet_naming_rule.private_fields.severity=warning +dotnet_naming_rule.private_fields.style = underscore_style +dotnet_naming_rule.private_fields.severity = warning dotnet_naming_rule.namespace.symbols=namespaces -dotnet_naming_rule.namespace.style=pascal_case_style -dotnet_naming_rule.namespace.severity=error +dotnet_naming_rule.namespace.style = pascal_case_style +dotnet_naming_rule.namespace.severity = error dotnet_naming_rule.interfaces.symbols=interfaces -dotnet_naming_rule.interfaces.style=interface_style -dotnet_naming_rule.interfaces.severity=warning +dotnet_naming_rule.interfaces.style = interface_style +dotnet_naming_rule.interfaces.severity = warning dotnet_naming_rule.non_private_members.symbols=non_private_symbols -dotnet_naming_rule.non_private_members.style=pascal_case_style -dotnet_naming_rule.non_private_members.severity=warning +dotnet_naming_rule.non_private_members.style = pascal_case_style +dotnet_naming_rule.non_private_members.severity = warning dotnet_naming_rule.async_methods.symbols=async_methods -dotnet_naming_rule.async_methods.style=async_style -dotnet_naming_rule.async_methods.severity=warning +dotnet_naming_rule.async_methods.style = async_style +dotnet_naming_rule.async_methods.severity = warning dotnet_naming_rule.methods_and_properties.symbols=methods_and_properties -dotnet_naming_rule.methods_and_properties.style=pascal_case_style -dotnet_naming_rule.methods_and_properties.severity=warning +dotnet_naming_rule.methods_and_properties.style = pascal_case_style +dotnet_naming_rule.methods_and_properties.severity = warning dotnet_naming_rule.parameters.symbols=parameters -dotnet_naming_rule.parameters.style=camel_case_style -dotnet_naming_rule.parameters.severity=warning +dotnet_naming_rule.parameters.style = camel_case_style +dotnet_naming_rule.parameters.severity = warning dotnet_naming_rule.type_parameters.symbols=type_parameters -dotnet_naming_rule.type_parameters.style=type_parameter_style -dotnet_naming_rule.type_parameters.severity=suggestion +dotnet_naming_rule.type_parameters.style = type_parameter_style +dotnet_naming_rule.type_parameters.severity = suggestion # Prefer "var" over explicit types @@ -563,3 +592,11 @@ csharp_place_simple_initializer_on_single_line=true csharp_trailing_comma_in_multiline_lists=true csharp_wrap_array_initializer_style=chop_if_long csharp_wrap_object_and_collection_initializer_style=chop_if_long +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 diff --git a/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/BuilderTests.cs b/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/BuilderTests.cs index ab263b4..2000a6a 100644 --- a/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/BuilderTests.cs +++ b/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/BuilderTests.cs @@ -19,7 +19,7 @@ public class BuilderTests public void Simple_does_not_populate_Orders() { var actual = UserBuilder.Simple().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldBeNull(); } @@ -30,7 +30,7 @@ public void Simple_does_not_populate_Orders() public void Typical_populates_Orders() { var actual = UserBuilder.Typical().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldNotBeNull(); } diff --git a/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/OrderBuilderTests.cs b/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/OrderBuilderTests.cs index e896a8c..29a5bda 100644 --- a/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/OrderBuilderTests.cs +++ b/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/OrderBuilderTests.cs @@ -29,7 +29,7 @@ public void OrderBuilder_can_set_properties() public void Simple_does_not_populate_Items() { var actual = OrderBuilder.Simple().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Items.ShouldBeNull(); } @@ -40,7 +40,7 @@ public void Simple_does_not_populate_Items() public void Typical_populates_Items() { var actual = OrderBuilder.Typical().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Items.ShouldNotBeNull(); } diff --git a/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/UserBuilderTests.cs b/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/UserBuilderTests.cs index 1acfd65..5c6d875 100644 --- a/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/UserBuilderTests.cs +++ b/src/BuilderGenerator.IntegrationTests.Net60.FromPackage/Tests/UserBuilderTests.cs @@ -19,7 +19,7 @@ public class UserBuilderTests public void Simple_does_not_populate_Orders() { var actual = UserBuilder.Simple().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldBeNull(); } @@ -30,7 +30,7 @@ public void Simple_does_not_populate_Orders() public void Typical_populates_Orders() { var actual = UserBuilder.Typical().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldNotBeNull(); } diff --git a/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/BuilderTests.cs b/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/BuilderTests.cs index cf9a472..a81d839 100644 --- a/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/BuilderTests.cs +++ b/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/BuilderTests.cs @@ -19,7 +19,7 @@ public class BuilderTests public void Simple_does_not_populate_Orders() { var actual = UserBuilder.Simple().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldBeNull(); } @@ -30,7 +30,7 @@ public void Simple_does_not_populate_Orders() public void Typical_populates_Orders() { var actual = UserBuilder.Typical().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldNotBeNull(); } diff --git a/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/OrderBuilderTests.cs b/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/OrderBuilderTests.cs index 3e7454b..4b384e2 100644 --- a/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/OrderBuilderTests.cs +++ b/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/OrderBuilderTests.cs @@ -29,7 +29,7 @@ public void OrderBuilder_can_set_properties() public void Simple_does_not_populate_Items() { var actual = OrderBuilder.Simple().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Items.ShouldBeNull(); } @@ -40,7 +40,7 @@ public void Simple_does_not_populate_Items() public void Typical_populates_Items() { var actual = OrderBuilder.Typical().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Items.ShouldNotBeNull(); } diff --git a/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/UserBuilderTests.cs b/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/UserBuilderTests.cs index b656541..8f7ee56 100644 --- a/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/UserBuilderTests.cs +++ b/src/BuilderGenerator.IntegrationTests.Net60.FromProject/Tests/UserBuilderTests.cs @@ -19,7 +19,7 @@ public class UserBuilderTests public void Simple_does_not_populate_Orders() { var actual = UserBuilder.Simple().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldBeNull(); } @@ -30,7 +30,7 @@ public void Simple_does_not_populate_Orders() public void Typical_populates_Orders() { var actual = UserBuilder.Typical().Build(); - ShouldBeTestExtensions.ShouldBeOfType(actual); + actual.ShouldBeOfType(); actual.Orders.ShouldNotBeNull(); } diff --git a/src/BuilderGenerator.sln.DotSettings b/src/BuilderGenerator.sln.DotSettings index d8cf6b2..2e5bc9c 100644 --- a/src/BuilderGenerator.sln.DotSettings +++ b/src/BuilderGenerator.sln.DotSettings @@ -385,21 +385,6 @@ <Name /> </Entry.SortBy> </Entry> - <Entry DisplayName="Instance Fields"> - <Entry.Match> - <And> - <Not> - <Static /> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Access /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> <Entry DisplayName="Events"> <Entry.Match> <Kind Is="Event" /> diff --git a/src/BuilderGenerator/BuilderGenerator.cs b/src/BuilderGenerator/BuilderGenerator.cs index 5c25a80..722f305 100644 --- a/src/BuilderGenerator/BuilderGenerator.cs +++ b/src/BuilderGenerator/BuilderGenerator.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading; using BuilderGenerator.Diagnostics; @@ -14,19 +16,59 @@ namespace BuilderGenerator; [Generator] internal class BuilderGenerator : IIncrementalGenerator { - public void Initialize(IncrementalGeneratorInitializationContext context) + private static readonly string BuilderBaseClass; + private static readonly string BuilderClass; + private static readonly string BuilderForAttribute; + private static readonly string BuildMethod; + private static readonly string BuildMethodSetter; + private static readonly string Property; + private static readonly string WithMethod; + + static BuilderGenerator() { - var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider(Predicate, Transform).Where(static node => node is not null); - var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); - context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2!, spc)); + var assembly = typeof(BuilderGenerator).Assembly; + + BuilderBaseClass = GetResourceAsString(assembly, nameof(BuilderBaseClass)); + BuilderClass = GetResourceAsString(assembly, nameof(BuilderClass)); + BuilderForAttribute = GetResourceAsString(assembly, nameof(BuilderForAttribute)); + BuildMethodSetter = GetResourceAsString(assembly, nameof(BuildMethodSetter)); + BuildMethod = GetResourceAsString(assembly, nameof(BuildMethod)); + Property = GetResourceAsString(assembly, nameof(Property)); + WithMethod = GetResourceAsString(assembly, nameof(WithMethod)); + } + + public static string GetResourceAsString(Assembly assembly, string resourceName) + { + resourceName = assembly.GetManifestResourceNames().Single(x => x.Equals($"BuilderGenerator.Templates.{resourceName}.txt", StringComparison.OrdinalIgnoreCase)); + + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream == null) + { + throw new InvalidOperationException($"Resource '{resourceName}' not found."); + } + using (var reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Register injection of classes that never change. context.RegisterPostInitializationOutput( x => { - // Inject base classes that never change - x.AddSource(nameof(Templates.BuilderBaseClass), SourceText.From(Templates.BuilderBaseClass, Encoding.UTF8)); - x.AddSource(nameof(Templates.BuilderForAttribute), SourceText.From(Templates.BuilderForAttribute, Encoding.UTF8)); + x.AddSource(nameof(BuilderBaseClass), SourceText.From(BuilderBaseClass, Encoding.UTF8)); + x.AddSource(nameof(BuilderForAttribute), SourceText.From(BuilderForAttribute, Encoding.UTF8)); }); + + // Register generation for classes based on the project contents + var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider(Predicate, Transform).Where(static node => node is not null); + var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); + context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Left, source.Right!, spc)); } private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context) @@ -50,7 +92,7 @@ private static void Execute(Compilation compilation, ImmutableArray x.AttributeClass!.Name == "BuilderForAttribute"); + var attributeSymbol = namedTypeSymbol.GetAttributes().SingleOrDefault(x => x.AttributeClass!.Name == nameof(BuilderForAttribute)); if (attributeSymbol is null) { continue; } @@ -86,7 +128,7 @@ private static void Execute(Compilation compilation, ImmutableArray properties) { var setters = string.Join( -#pragma warning disable RS1035 // Do not use APIs banned for analyzers - Environment.NewLine, -#pragma warning restore RS1035 // Do not use APIs banned for analyzers + "\n", properties.Select( x => { templateParser.SetTag("PropertyName", x.Name); - return templateParser.ParseString(Templates.BuildMethodSetter); + return templateParser.ParseString(BuildMethodSetter); })); templateParser.SetTag("Setters", setters); - var result = templateParser.ParseString(Templates.BuildMethod); + var result = templateParser.ParseString(BuildMethod); return result; } @@ -120,16 +160,14 @@ private static string GenerateBuildMethod(TemplateParser templateParser, IEnumer private static string GenerateProperties(TemplateParser templateParser, IEnumerable<(string Name, string TypeName)> properties) { var result = string.Join( -#pragma warning disable RS1035 // Do not use APIs banned for analyzers - Environment.NewLine, -#pragma warning restore RS1035 // Do not use APIs banned for analyzers + "\n", properties.Select( x => { templateParser.SetTag("PropertyName", x.Name); templateParser.SetTag("PropertyType", x.TypeName); - return templateParser.ParseString(Templates.Property); + return templateParser.ParseString(Property); })); return result; @@ -138,16 +176,14 @@ private static string GenerateProperties(TemplateParser templateParser, IEnumera private static string GenerateWithMethods(TemplateParser templateParser, IEnumerable<(string Name, string TypeName)> properties) { var result = string.Join( -#pragma warning disable RS1035 // Do not use APIs banned for analyzers - Environment.NewLine, -#pragma warning restore RS1035 // Do not use APIs banned for analyzers + "\n", properties.Select( x => { templateParser.SetTag("PropertyName", x.Name); templateParser.SetTag("PropertyType", x.TypeName); - return templateParser.ParseString(Templates.WithMethod); + return templateParser.ParseString(WithMethod); })); return result; diff --git a/src/BuilderGenerator/BuilderGenerator.csproj b/src/BuilderGenerator/BuilderGenerator.csproj index ff758d0..c254781 100644 --- a/src/BuilderGenerator/BuilderGenerator.csproj +++ b/src/BuilderGenerator/BuilderGenerator.csproj @@ -86,6 +86,10 @@ https://github.com/MelGrubb/BuilderGenerator + + + + all diff --git a/src/BuilderGenerator/BuilderGenerator.csproj.DotSettings b/src/BuilderGenerator/BuilderGenerator.csproj.DotSettings new file mode 100644 index 0000000..e037f29 --- /dev/null +++ b/src/BuilderGenerator/BuilderGenerator.csproj.DotSettings @@ -0,0 +1,2 @@ + + False \ No newline at end of file diff --git a/src/BuilderGenerator/Diagnostics/DiagnosticDescriptors.cs b/src/BuilderGenerator/Diagnostics/DiagnosticDescriptors.cs index b364b23..cfb4c14 100644 --- a/src/BuilderGenerator/Diagnostics/DiagnosticDescriptors.cs +++ b/src/BuilderGenerator/Diagnostics/DiagnosticDescriptors.cs @@ -1,12 +1,11 @@ using System; using Microsoft.CodeAnalysis; -namespace BuilderGenerator.Diagnostics +namespace BuilderGenerator.Diagnostics; + +internal class DiagnosticDescriptors { - internal class DiagnosticDescriptors - { - internal static Diagnostic TargetClassIsAbstract(Location location, string identifier) => Diagnostic.Create(new DiagnosticDescriptor("BGN002", "Target class cannot be abstract", $"Cannot generate a builder for '{identifier}' because it is abstract", "BuilderGenerator", DiagnosticSeverity.Error, true), location); + internal static Diagnostic TargetClassIsAbstract(Location location, string identifier) => Diagnostic.Create(new DiagnosticDescriptor("BGN002", "Target class cannot be abstract", $"Cannot generate a builder for '{identifier}' because it is abstract", "BuilderGenerator", DiagnosticSeverity.Error, true), location); - internal static Diagnostic UnexpectedErrorDiagnostic(Exception exception, Location location, string identifier) => Diagnostic.Create(new DiagnosticDescriptor("BGN001", "Unexpected error", $"An error occurred while generating a builder for '{identifier}'\n{exception.Message}", "BuilderGenerator", DiagnosticSeverity.Error, true), location); - } + internal static Diagnostic UnexpectedErrorDiagnostic(Exception exception, Location location, string identifier) => Diagnostic.Create(new DiagnosticDescriptor("BGN001", "Unexpected error", $"An error occurred while generating a builder for '{identifier}'\n{exception.Message}", "BuilderGenerator", DiagnosticSeverity.Error, true), location); } diff --git a/src/BuilderGenerator/Properties/launchSettings.json b/src/BuilderGenerator/Properties/launchSettings.json new file mode 100644 index 0000000..61e4cb1 --- /dev/null +++ b/src/BuilderGenerator/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "FromProject": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\BuilderGenerator.IntegrationTests.Net60.FromProject\\BuilderGenerator.IntegrationTests.Net60.FromProject.csproj" + } + } +} \ No newline at end of file diff --git a/src/BuilderGenerator/Templates.cs b/src/BuilderGenerator/Templates.cs deleted file mode 100644 index 78464f6..0000000 --- a/src/BuilderGenerator/Templates.cs +++ /dev/null @@ -1,116 +0,0 @@ -// ReSharper disable ArrangeNamespaceBody -// ReSharper disable UnusedMember.Global - -namespace BuilderGenerator; - -internal static class Templates -{ - internal const string BuilderBaseClass = @"#nullable disable - -namespace BuilderGenerator -{ - /// Base class for object builder classes. - /// The type of the objects built by this builder. - public abstract class Builder where T : class - { - /// Gets or sets the object returned by this builder. - /// The constructed object. - #pragma warning disable CA1720 // Identifier contains type name - protected System.Lazy Object { get; set; } - #pragma warning restore CA1720 // Identifier contains type name - - /// Builds the object instance. - /// The constructed object. - public abstract T Build(); - - protected virtual void PostProcess(T value) - { - } - - /// Sets the object to be returned by this instance. - /// The object to be returned. - /// A reference to this builder instance. - public Builder WithObject(T value) - { - Object = new System.Lazy(() => value); - - return this; - } - } -}"; - - internal const string BuilderClass = @"// -{{BuilderClassUsingBlock}} -using System.CodeDom.Compiler; - -#nullable disable - -namespace {{BuilderClassNamespace}} -{ - {{BuilderClassAccessibility}} partial class {{BuilderClassName}} : BuilderGenerator.Builder<{{TargetClassFullName}}> - { -{{Properties}} -{{BuildMethod}} -{{WithMethods}} - } -}"; - - internal const string BuilderForAttribute = @"namespace BuilderGenerator -{ - [System.AttributeUsage(System.AttributeTargets.Class)] - public class BuilderForAttribute : System.Attribute - { - public bool IncludeInternals { get; } - public System.Type Type { get; } - - public BuilderForAttribute(System.Type type, bool includeInternals = false) - { - this.IncludeInternals = includeInternals; - this.Type = type; - } - } -}"; - - internal const string BuildMethod = @" - public override {{TargetClassFullName}} Build() - { - if (Object?.IsValueCreated != true) - { - Object = new System.Lazy<{{TargetClassFullName}}>(() => - { - var result = new {{TargetClassFullName}} - { -{{Setters}} - }; - - return result; - }); - - PostProcess(Object.Value); - } - - return Object.Value; - }"; - - internal const string BuildMethodSetter = " {{PropertyName}} = {{PropertyName}}.Value,"; - - internal const string Property = @" public System.Lazy<{{PropertyType}}> {{PropertyName}} = new System.Lazy<{{PropertyType}}>(() => default({{PropertyType}}));"; - - internal const string WithMethod = @" - public {{BuilderClassName}} With{{PropertyName}}({{PropertyType}} value) - { - return With{{PropertyName}}(() => value); - } - - public {{BuilderClassName}} With{{PropertyName}}(System.Func<{{PropertyType}}> func) - { - {{PropertyName}} = new System.Lazy<{{PropertyType}}>(func); - return this; - } - - public {{BuilderClassName}} Without{{PropertyName}}() - { - {{PropertyName}} = new System.Lazy<{{PropertyType}}>(() => default({{PropertyType}})); - return this; - }"; -} diff --git a/src/BuilderGenerator/Templates/BuildMethod.txt b/src/BuilderGenerator/Templates/BuildMethod.txt new file mode 100644 index 0000000..e612daf --- /dev/null +++ b/src/BuilderGenerator/Templates/BuildMethod.txt @@ -0,0 +1,19 @@ + public override {{TargetClassFullName}} Build() + { + if (Object?.IsValueCreated != true) + { + Object = new System.Lazy<{{TargetClassFullName}}>(() => + { + var result = new {{TargetClassFullName}} + { + {{Setters}} + }; + + return result; + }); + + PostProcess(Object.Value); + } + + return Object.Value; + } \ No newline at end of file diff --git a/src/BuilderGenerator/Templates/BuildMethodSetter.txt b/src/BuilderGenerator/Templates/BuildMethodSetter.txt new file mode 100644 index 0000000..c58f18f --- /dev/null +++ b/src/BuilderGenerator/Templates/BuildMethodSetter.txt @@ -0,0 +1 @@ + {{PropertyName}} = {{PropertyName}}.Value, \ No newline at end of file diff --git a/src/BuilderGenerator/Templates/BuilderBaseClass.txt b/src/BuilderGenerator/Templates/BuilderBaseClass.txt new file mode 100644 index 0000000..f261ae9 --- /dev/null +++ b/src/BuilderGenerator/Templates/BuilderBaseClass.txt @@ -0,0 +1,33 @@ +#nullable disable + +namespace BuilderGenerator +{ + /// Base class for object builder classes. + /// The type of the objects built by this builder. + public abstract class Builder where T : class + { + /// Gets or sets the object returned by this builder. + /// The constructed object. + #pragma warning disable CA1720 // Identifier contains type name + protected System.Lazy Object { get; set; } + #pragma warning restore CA1720 // Identifier contains type name + + /// Builds the object instance. + /// The constructed object. + public abstract T Build(); + + protected virtual void PostProcess(T value) + { + } + + /// Sets the object to be returned by this instance. + /// The object to be returned. + /// A reference to this builder instance. + public Builder WithObject(T value) + { + Object = new System.Lazy(() => value); + + return this; + } + } +} diff --git a/src/BuilderGenerator/Templates/BuilderClass.txt b/src/BuilderGenerator/Templates/BuilderClass.txt new file mode 100644 index 0000000..8c400f8 --- /dev/null +++ b/src/BuilderGenerator/Templates/BuilderClass.txt @@ -0,0 +1,15 @@ +// +{{BuilderClassUsingBlock}} +using System.CodeDom.Compiler; + +#nullable disable + +namespace {{BuilderClassNamespace}} +{ + {{BuilderClassAccessibility}} partial class {{BuilderClassName}} : BuilderGenerator.Builder<{{TargetClassFullName}}> + { +{{Properties}} +{{BuildMethod}} +{{WithMethods}} + } +} \ No newline at end of file diff --git a/src/BuilderGenerator/Templates/BuilderForAttribute.txt b/src/BuilderGenerator/Templates/BuilderForAttribute.txt new file mode 100644 index 0000000..0d73a06 --- /dev/null +++ b/src/BuilderGenerator/Templates/BuilderForAttribute.txt @@ -0,0 +1,15 @@ +namespace BuilderGenerator +{ + [System.AttributeUsage(System.AttributeTargets.Class)] + public class BuilderForAttribute : System.Attribute + { + public bool IncludeInternals { get; } + public System.Type Type { get; } + + public BuilderForAttribute(System.Type type, bool includeInternals = false) + { + IncludeInternals = includeInternals; + Type = type; + } + } +} diff --git a/src/BuilderGenerator/Templates/Property.txt b/src/BuilderGenerator/Templates/Property.txt new file mode 100644 index 0000000..fd6f719 --- /dev/null +++ b/src/BuilderGenerator/Templates/Property.txt @@ -0,0 +1 @@ + public System.Lazy<{{PropertyType}}> {{PropertyName}} = new System.Lazy<{{PropertyType}}>(() => default({{PropertyType}})); diff --git a/src/BuilderGenerator/Templates/WithMethod.txt b/src/BuilderGenerator/Templates/WithMethod.txt new file mode 100644 index 0000000..d11ef60 --- /dev/null +++ b/src/BuilderGenerator/Templates/WithMethod.txt @@ -0,0 +1,17 @@ + + public {{BuilderClassName}} With{{PropertyName}}({{PropertyType}} value) + { + return With{{PropertyName}}(() => value); + } + + public {{BuilderClassName}} With{{PropertyName}}(System.Func<{{PropertyType}}> func) + { + {{PropertyName}} = new System.Lazy<{{PropertyType}}>(func); + return this; + } + + public {{BuilderClassName}} Without{{PropertyName}}() + { + {{PropertyName}} = new System.Lazy<{{PropertyType}}>(() => default({{PropertyType}})); + return this; + } \ No newline at end of file