diff --git a/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs b/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs index aa8a5b6..f147501 100644 --- a/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs +++ b/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs @@ -12,6 +12,7 @@ public class AutomaticInterfaceGenerator : IIncrementalGenerator public const string IgnoreAutomaticInterfaceAttributeName = "IgnoreAutomaticInterface"; public const string NamespaceParameterName = "namespaceName"; public const string InterfaceParameterName = "interfaceName"; + public const string AsInternalParameterName = "asInternal"; public void Initialize(IncrementalGeneratorInitializationContext context) { diff --git a/AutomaticInterface/AutomaticInterface/Builder.cs b/AutomaticInterface/AutomaticInterface/Builder.cs index 840657b..4f67120 100644 --- a/AutomaticInterface/AutomaticInterface/Builder.cs +++ b/AutomaticInterface/AutomaticInterface/Builder.cs @@ -52,13 +52,12 @@ is not ClassDeclarationSyntax classSyntax { return string.Empty; } - var generationAttribute = GetGenerationAttribute(typeSymbol); - var asInternal = GetAsInternal(generationAttribute); + var symbolDetails = GetSymbolDetails(typeSymbol, classSyntax); var interfaceGenerator = new InterfaceBuilder( symbolDetails.NamespaceName, symbolDetails.InterfaceName, - asInternal + symbolDetails.AccessLevel ); interfaceGenerator.AddClassDocumentation(GetDocumentationForClass(classSyntax)); @@ -80,43 +79,6 @@ is not ClassDeclarationSyntax classSyntax return generatedCode; } - private static AttributeData? GetGenerationAttribute(ISymbol typeSymbol) - { - return typeSymbol - .GetAttributes() - .FirstOrDefault(x => - x.AttributeClass != null - && x.AttributeClass.Name.Contains(AutomaticInterfaceGenerator.DefaultAttributeName) - ); - } - - private static string GetNameSpace(ISymbol typeSymbol, AttributeData? generationAttribute) - { - if (generationAttribute == null) - { - return typeSymbol.ContainingNamespace.ToDisplayString(); - } - - var customNs = generationAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - - return string.IsNullOrWhiteSpace(customNs) - ? typeSymbol.ContainingNamespace.ToDisplayString() - : customNs!; - } - - private static bool GetAsInternal(AttributeData? generationAttribute) - { - if (generationAttribute == null) - { - return false; - } - - var asInternal = (bool?) - generationAttribute.ConstructorArguments.Skip(2).FirstOrDefault().Value; - - return asInternal.GetValueOrDefault(); - } - private static GeneratedSymbolDetails GetSymbolDetails( ITypeSymbol typeSymbol, ClassDeclarationSyntax classSyntax diff --git a/AutomaticInterface/AutomaticInterface/GeneratedSymbolDetails.cs b/AutomaticInterface/AutomaticInterface/GeneratedSymbolDetails.cs index b0c63d9..258134e 100644 --- a/AutomaticInterface/AutomaticInterface/GeneratedSymbolDetails.cs +++ b/AutomaticInterface/AutomaticInterface/GeneratedSymbolDetails.cs @@ -34,11 +34,31 @@ ClassDeclarationSyntax classSyntax $"I{classSyntax.GetClassName()}" ); - private static string PrepareValue( - AttributeData? generationAttribute, - string key, - string defaultValue - ) + /// + /// Determines the access level for the generated interface. + /// This property is derived from the presence of + /// that, if set, defines the interface as `internal`. Otherwise, the interface defaults to `public`. + /// + public string AccessLevel { get; } = + PrepareValue( + generationAttribute, + AutomaticInterfaceGenerator.AsInternalParameterName, + false + ) + ? "internal" + : "public"; + + /// + /// Prepares a value by retrieving it from an attribute's constructor arguments if available; otherwise, returns the provided default value. + /// + /// The type of the value to prepare. + /// The attribute data containing constructor arguments. + /// The key to identify the relevant parameter in the constructor arguments. + /// The default value to return if the attribute does not provide a value. + /// + /// The retrieved value from the attribute constructor's arguments, or the provided default value if the key is not found. + /// + private static T PrepareValue(AttributeData? generationAttribute, string key, T defaultValue) { var parameterSymbol = generationAttribute?.AttributeConstructor?.Parameters.SingleOrDefault( x => x.Name == key @@ -49,10 +69,10 @@ string defaultValue var index = generationAttribute!.AttributeConstructor!.Parameters.IndexOf( parameterSymbol ); - var result = generationAttribute.ConstructorArguments[index].Value!.ToString(); - if (!string.IsNullOrWhiteSpace(result)) + var result = generationAttribute.ConstructorArguments[index].Value; + if (result != null) { - return result; + return (T)result; } } diff --git a/AutomaticInterface/AutomaticInterface/InterfaceBuilder.cs b/AutomaticInterface/AutomaticInterface/InterfaceBuilder.cs index 654a129..fc46f1d 100644 --- a/AutomaticInterface/AutomaticInterface/InterfaceBuilder.cs +++ b/AutomaticInterface/AutomaticInterface/InterfaceBuilder.cs @@ -24,7 +24,7 @@ internal sealed record MethodInfo( public record EventInfo(string Name, string Type, string Documentation); - public class InterfaceBuilder(string nameSpaceName, string interfaceName, bool asInternal) + public class InterfaceBuilder(string nameSpaceName, string interfaceName, string accessLevel) { private const string Autogenerated = """ //-------------------------------------------------------------------------------------------------- @@ -104,7 +104,7 @@ public string Build() cb.AppendLine( "[global::System.CodeDom.Compiler.GeneratedCode(\"AutomaticInterface\", \"\")]" ); - cb.AppendLine($"{(asInternal ? "internal" : "public")} partial interface {interfaceName}{genericType}"); + cb.AppendLine($"{accessLevel} partial interface {interfaceName}{genericType}"); cb.AppendLine("{"); cb.Indent(); diff --git a/AutomaticInterface/AutomaticInterface/RegisterAttributesExtensions.cs b/AutomaticInterface/AutomaticInterface/RegisterAttributesExtensions.cs index c10df77..90e0e37 100644 --- a/AutomaticInterface/AutomaticInterface/RegisterAttributesExtensions.cs +++ b/AutomaticInterface/AutomaticInterface/RegisterAttributesExtensions.cs @@ -29,7 +29,7 @@ namespace AutomaticInterface [AttributeUsage(AttributeTargets.Class)] internal sealed class {{{AutomaticInterfaceGenerator.DefaultAttributeName}}}Attribute : Attribute { - internal {{{AutomaticInterfaceGenerator.DefaultAttributeName}}}Attribute(string {{{AutomaticInterfaceGenerator.NamespaceParameterName}}} = "", string {{{AutomaticInterfaceGenerator.InterfaceParameterName}}} = "", bool asInternal = false) { } + internal {{{AutomaticInterfaceGenerator.DefaultAttributeName}}}Attribute(string? {{{AutomaticInterfaceGenerator.NamespaceParameterName}}} = default(string), string? {{{AutomaticInterfaceGenerator.InterfaceParameterName}}} = default(string), bool {{{AutomaticInterfaceGenerator.AsInternalParameterName}}} = false) { } } } """, diff --git a/AutomaticInterface/Tests/Misc/Misc.cs b/AutomaticInterface/Tests/Misc/Misc.cs index a49a7f3..d1b0677 100644 --- a/AutomaticInterface/Tests/Misc/Misc.cs +++ b/AutomaticInterface/Tests/Misc/Misc.cs @@ -366,8 +366,8 @@ class DemoClass } [Fact] - public async Task AsInternal() - { + public async Task AsInternal() + { const string code = """ using AutomaticInterface; @@ -381,13 +381,13 @@ public string AMethod(DemoClass? x, string y) } } - """; + """; await Verify(Infrastructure.GenerateCode(code)); - } - + } + [Fact] - public async Task AsInternalExplicitFalse() - { + public async Task AsInternalExplicitFalse() + { const string code = """ using AutomaticInterface; @@ -401,7 +401,7 @@ public string AMethod(DemoClass? x, string y) } } - """; + """; await Verify(Infrastructure.GenerateCode(code)); } }