diff --git a/Sybil.IntegrationTests/FlowTests.cs b/Sybil.IntegrationTests/FlowTests.cs index 95b89c1..7bdd48c 100644 --- a/Sybil.IntegrationTests/FlowTests.cs +++ b/Sybil.IntegrationTests/FlowTests.cs @@ -13,7 +13,7 @@ public sealed class FlowTests [InternalsVisibleTo(""FlowTests"")] namespace TestNamespace; [SomeAttribute(SomeIntProperty = 1, SomeStringProperty = ""Hello"", SomeEnumProperty = MyEnum.Value, SomeTypeProperty = typeof(String), SomeNullProperty = null)] -public sealed class TestClass : BaseTestClass +public sealed class TestClass : BaseTestClass, IInterface { [SomeAttribute5(null)] TestClass(string fieldString) : base(fieldString) @@ -48,6 +48,7 @@ public sealed class TestClass : BaseTestClass .WithClass( SyntaxBuilder.CreateClass("TestClass") .WithBaseClass("BaseTestClass") + .WithInterface("IInterface") .WithAttribute(SyntaxBuilder.CreateAttribute("SomeAttribute") .WithArgument("SomeIntProperty", 1) .WithArgument("SomeStringProperty", "Hello") diff --git a/Sybil.UnitTests/ClassBuilderTests.cs b/Sybil.UnitTests/ClassBuilderTests.cs index 5ac4056..ffd32b1 100644 --- a/Sybil.UnitTests/ClassBuilderTests.cs +++ b/Sybil.UnitTests/ClassBuilderTests.cs @@ -10,6 +10,7 @@ public class ClassBuilderTests { private const string Name = "Test"; private const string Base = "Base"; + private const string IBase = "IBase"; private const string PublicClass = @"public class Test { @@ -21,6 +22,10 @@ public class ClassBuilderTests private const string ClassWithBase = @"class Test : Base { +}"; + private const string ClassWithInterface = +@"class Test : IBase +{ }"; private readonly ClassBuilder builder; @@ -142,6 +147,25 @@ public class ClassBuilderTests returnedBuilder.Should().NotBeNull().And.Subject.Should().BeOfType(); } + [TestMethod] + public void WithInterface_NullInterface_ThrowsArgumentNullException() + { + var action = () => + { + this.builder.WithInterface(null); + }; + + action.Should().Throw(); + } + + [TestMethod] + public void WithInterface_InterfaceValid_ReturnsBuilder() + { + var returnedBuilder = this.builder.WithInterface(Base); + + returnedBuilder.Should().NotBeNull().And.Subject.Should().BeOfType(); + } + [TestMethod] public void Build_ReturnsClassDeclarationSyntax() { @@ -176,4 +200,15 @@ public class ClassBuilderTests result.Should().Be(ClassWithBase); } + + [TestMethod] + public void WithInterface_ReturnsExpectedString() + { + var result = this.builder + .WithInterface(IBase) + .Build() + .ToFullString(); + + result.Should().Be(ClassWithInterface); + } } \ No newline at end of file diff --git a/Sybil.UnitTests/InterfaceBuilderTests.cs b/Sybil.UnitTests/InterfaceBuilderTests.cs new file mode 100644 index 0000000..480e98a --- /dev/null +++ b/Sybil.UnitTests/InterfaceBuilderTests.cs @@ -0,0 +1,124 @@ +using FluentAssertions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace Sybil.Tests; + +[TestClass] +public class InterfaceBuilderTests +{ + private const string Name = "ITest"; + private const string PublicInterface = +@"public interface ITest +{ +}"; + private const string Interface = +@"interface ITest +{ +}"; + + private readonly InterfaceBuilder builder; + + public InterfaceBuilderTests() + { + this.builder = new InterfaceBuilder(Name); + } + + [TestMethod] + public void Constructor_NullName_ThrowArgumentNullException() + { + var action = () => + { + _ = new InterfaceBuilder(null); + }; + + action.Should().Throw(); + } + + [TestMethod] + public void WithModifier_ModifierNull_ThrowsArgumentNullException() + { + var action = () => + { + this.builder.WithModifier(null); + }; + + action.Should().Throw(); + } + + [TestMethod] + public void WithModifier_ModifierValid_ReturnsBuilder() + { + var returnedBuilder = this.builder.WithModifier("public"); + + returnedBuilder.Should().NotBeNull().And.Subject.Should().Be(this.builder); + } + + [TestMethod] + public void WithModifiers_ModifiersNull_ThrowsArgumentNullException() + { + var action = () => + { + this.builder.WithModifiers(null); + }; + + action.Should().Throw(); + } + + [TestMethod] + public void WithModifiers_ModifiersValid_ReturnsBuilder() + { + var returnedBuilder = this.builder.WithModifiers("public static"); + + returnedBuilder.Should().NotBeNull().And.Subject.Should().Be(this.builder); + } + + [TestMethod] + public void WithProperty_NullPropertyBuilder_ThrowsArgumentNullException() + { + var action = () => + { + this.builder.WithProperty(null); + }; + + action.Should().Throw(); + } + + [TestMethod] + public void WithMethod_NullMethodBuilder_ThrowsArgumentNullException() + { + var action = () => + { + this.builder.WithMethod(null); + }; + + action.Should().Throw(); + } + + [TestMethod] + public void Build_ReturnsInterfaceDeclarationSyntax() + { + var syntax = this.builder.Build(); + + syntax.Should().NotBeNull().And.Subject.Should().BeOfType(); + } + + [TestMethod] + public void WithModifier_ReturnsExpectedString() + { + var result = this.builder.WithModifier("public").Build().ToFullString(); + + result.Should().Be(PublicInterface); + } + + [TestMethod] + public void Build_ReturnsExpectedString() + { + var result = this.builder + .Build() + .ToFullString(); + + result.Should().Be(Interface); + } +} \ No newline at end of file diff --git a/Sybil/ClassBuilder.cs b/Sybil/ClassBuilder.cs index 2afd1af..4eeff36 100644 --- a/Sybil/ClassBuilder.cs +++ b/Sybil/ClassBuilder.cs @@ -35,6 +35,16 @@ namespace Sybil return this; } + public ClassBuilder WithInterface(string interfaceName) + { + _ = string.IsNullOrWhiteSpace(interfaceName) ? throw new ArgumentNullException(nameof(interfaceName)) : interfaceName; + + this.ClassDeclaration = this.ClassDeclaration.AddBaseListTypes( + SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(interfaceName))); + + return this; + } + public ClassBuilder WithModifier(string modifier) { _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; diff --git a/Sybil/InterfaceBuilder.cs b/Sybil/InterfaceBuilder.cs new file mode 100644 index 0000000..8a82449 --- /dev/null +++ b/Sybil/InterfaceBuilder.cs @@ -0,0 +1,65 @@ +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace Sybil +{ + public sealed class InterfaceBuilder : IBuilder + { + private InterfaceDeclarationSyntax InterfaceDeclaration { get; set; } + + private readonly List Properties = new List(); + private readonly List Methods = new List(); + + internal InterfaceBuilder( + string interfaceName) + { + _ = string.IsNullOrWhiteSpace(interfaceName) ? throw new ArgumentNullException(nameof(interfaceName)) : interfaceName; + + this.InterfaceDeclaration = SyntaxFactory.InterfaceDeclaration(interfaceName); + } + + public InterfaceBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.InterfaceDeclaration = this.InterfaceDeclaration.AddModifiers(SyntaxFactory.ParseToken(modifier)); + + return this; + } + + public InterfaceBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.InterfaceDeclaration = this.InterfaceDeclaration.AddModifiers(SyntaxFactory.ParseTokens(modifiers).ToArray()); + + return this; + } + + public InterfaceBuilder WithProperty(PropertyBuilder propertyBuilder) + { + this.Properties.Add(propertyBuilder ?? throw new ArgumentNullException(nameof(propertyBuilder))); + + return this; + } + + public InterfaceBuilder WithMethod(MethodBuilder methodBuilder) + { + this.Methods.Add(methodBuilder ?? throw new ArgumentNullException(nameof(methodBuilder))); + + return this; + } + + public InterfaceDeclarationSyntax Build() + { + return this.InterfaceDeclaration + .AddMembers(this.Properties.Select(p => p.Build()).ToArray()) + .AddMembers(this.Methods.Select(p => p.Build()).ToArray()) + .NormalizeWhitespace(); + } + } +} diff --git a/Sybil/Sybil.csproj b/Sybil/Sybil.csproj index df118b1..82aa816 100644 --- a/Sybil/Sybil.csproj +++ b/Sybil/Sybil.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.6.0 + 0.7.0 diff --git a/Sybil/SyntaxBuilder.cs b/Sybil/SyntaxBuilder.cs index a9b6bea..75ff2aa 100644 --- a/Sybil/SyntaxBuilder.cs +++ b/Sybil/SyntaxBuilder.cs @@ -7,6 +7,7 @@ namespace Sybil public static NamespaceBuilder CreateNamespace(string @namespace) => new NamespaceBuilder(@namespace, false); public static NamespaceBuilder CreateFileScopedNamespace(string @namespace) => new NamespaceBuilder(@namespace, true); public static ClassBuilder CreateClass(string className) => new ClassBuilder(className); + public static InterfaceBuilder CreateInterface(string interfaceName) => new InterfaceBuilder(interfaceName); public static ConstructorBuilder CreateConstructor(string className) => new ConstructorBuilder(className); public static BaseConstructorBuilder CreateBaseConstructor() => new BaseConstructorBuilder(); public static PropertyBuilder CreateProperty(string typeName, string propertyName) => new PropertyBuilder(typeName, propertyName);