From bb536a88a35b6e64ece835302eb0c18f0658558f Mon Sep 17 00:00:00 2001 From: Alexandru Macocian Date: Tue, 22 Mar 2022 10:14:01 +0100 Subject: [PATCH] Stable implementation 0.1 --- Sybil.IntegrationTests/FlowTests.cs | 67 +++++++++++++ .../Sybil.IntegrationTests.csproj | 22 +++++ Sybil.UnitTests/Sybil.UnitTests.csproj | 16 ++- Sybil.sln | 10 +- Sybil/AccessorBuilder.cs | 79 +++++++++++++++ Sybil/BaseConstructorBuilder.cs | 31 ++++++ Sybil/ClassBuilder.cs | 85 ++++++++++++++++ Sybil/ConstructorBuilder.cs | 86 ++++++++++++++++ Sybil/FieldBuilder.cs | 48 +++++++++ Sybil/IBuilder.cs | 9 ++ Sybil/MethodBuilder.cs | 97 +++++++++++++++++++ Sybil/NamespaceBuilder.cs | 51 ++++++++++ Sybil/PropertyBuilder.cs | 62 ++++++++++++ Sybil/Sybil.csproj | 8 +- Sybil/SyntaxBuilder.cs | 44 +++++++++ 15 files changed, 705 insertions(+), 10 deletions(-) create mode 100644 Sybil.IntegrationTests/FlowTests.cs create mode 100644 Sybil.IntegrationTests/Sybil.IntegrationTests.csproj create mode 100644 Sybil/AccessorBuilder.cs create mode 100644 Sybil/BaseConstructorBuilder.cs create mode 100644 Sybil/ClassBuilder.cs create mode 100644 Sybil/ConstructorBuilder.cs create mode 100644 Sybil/FieldBuilder.cs create mode 100644 Sybil/IBuilder.cs create mode 100644 Sybil/MethodBuilder.cs create mode 100644 Sybil/NamespaceBuilder.cs create mode 100644 Sybil/PropertyBuilder.cs create mode 100644 Sybil/SyntaxBuilder.cs diff --git a/Sybil.IntegrationTests/FlowTests.cs b/Sybil.IntegrationTests/FlowTests.cs new file mode 100644 index 0000000..8cae339 --- /dev/null +++ b/Sybil.IntegrationTests/FlowTests.cs @@ -0,0 +1,67 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Sybil.IntegrationTests +{ + [TestClass] + public sealed class FlowTests + { + private const string ExpectedNamespace = +@"namespace TestNamespace +{ + using System; + + public sealed class TestClass + { + TestClass(string fieldString) : base(fieldString) + { + this.fieldString = fieldString ?? throw new ArgumentNullException(); + } + + private string fieldString; + public string PropertyString { private set; get; } + + public string GetFieldString() + { + return this.fieldString; + } + } +}"; + + [TestMethod] + public void NewNamespace_GeneratesExpected() + { + var namespaceSyntax = SyntaxBuilder.CreateNamespace("TestNamespace") + .WithUsing("System") + .WithClass( + SyntaxBuilder.CreateClass("TestClass") + .WithModifiers("public sealed") + .WithConstructor( + SyntaxBuilder.CreateConstructor("TestClass") + .WithBase( + SyntaxBuilder.CreateBaseConstructor() + .WithArgument("fieldString")) + .WithParameter("string", "fieldString") + .WithBody("this.fieldString = fieldString ?? throw new ArgumentNullException();")) + .WithField( + SyntaxBuilder.CreateField("string", "fieldString") + .WithModifier("private")) + .WithProperty( + SyntaxBuilder.CreateProperty("string", "PropertyString") + .WithModifier("public") + .WithAccessor( + SyntaxBuilder.CreateSetter() + .WithModifier("private")) + .WithAccessor( + SyntaxBuilder.CreateGetter())) + .WithMethod( + SyntaxBuilder.CreateMethod("string", "GetFieldString") + .WithModifier("public") + .WithBody("return this.fieldString;"))) + .Build(); + + var namespaceString = namespaceSyntax.ToFullString(); + namespaceString.Should().Be(ExpectedNamespace); + } + } +} diff --git a/Sybil.IntegrationTests/Sybil.IntegrationTests.csproj b/Sybil.IntegrationTests/Sybil.IntegrationTests.csproj new file mode 100644 index 0000000..f2037c1 --- /dev/null +++ b/Sybil.IntegrationTests/Sybil.IntegrationTests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + diff --git a/Sybil.UnitTests/Sybil.UnitTests.csproj b/Sybil.UnitTests/Sybil.UnitTests.csproj index f72bee8..1982f8c 100644 --- a/Sybil.UnitTests/Sybil.UnitTests.csproj +++ b/Sybil.UnitTests/Sybil.UnitTests.csproj @@ -8,10 +8,18 @@ - - - - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/Sybil.sln b/Sybil.sln index 22dbd5e..0b2ce68 100644 --- a/Sybil.sln +++ b/Sybil.sln @@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sybil", "Sybil\Sybil.csproj", "{5B024A18-C109-4CED-8952-B1793EC634CD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sybil", "Sybil\Sybil.csproj", "{5B024A18-C109-4CED-8952-B1793EC634CD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sybil.UnitTests", "Sybil.UnitTests\Sybil.UnitTests.csproj", "{458625E9-7427-47C6-BEE9-6269BDA00282}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sybil.UnitTests", "Sybil.UnitTests\Sybil.UnitTests.csproj", "{458625E9-7427-47C6-BEE9-6269BDA00282}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sybil.IntegrationTests", "Sybil.IntegrationTests\Sybil.IntegrationTests.csproj", "{4BE3F7BF-602C-447E-B29B-011AE7274E4A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {458625E9-7427-47C6-BEE9-6269BDA00282}.Debug|Any CPU.Build.0 = Debug|Any CPU {458625E9-7427-47C6-BEE9-6269BDA00282}.Release|Any CPU.ActiveCfg = Release|Any CPU {458625E9-7427-47C6-BEE9-6269BDA00282}.Release|Any CPU.Build.0 = Release|Any CPU + {4BE3F7BF-602C-447E-B29B-011AE7274E4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BE3F7BF-602C-447E-B29B-011AE7274E4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BE3F7BF-602C-447E-B29B-011AE7274E4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BE3F7BF-602C-447E-B29B-011AE7274E4A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Sybil/AccessorBuilder.cs b/Sybil/AccessorBuilder.cs new file mode 100644 index 0000000..9ff078a --- /dev/null +++ b/Sybil/AccessorBuilder.cs @@ -0,0 +1,79 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; + +namespace Sybil +{ + public sealed class AccessorBuilder : IBuilder + { + private readonly SyntaxKind accessor; + + private SyntaxTokenList Modifiers { get; set; } + private BlockSyntax BlockSyntax { get; set; } + private ArrowExpressionClauseSyntax ArrowExpressionClauseSyntax { get; set; } + + internal AccessorBuilder( + SyntaxKind accessor) + { + this.accessor = accessor; + } + + public AccessorBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.Modifiers = SyntaxFactory.TokenList(SyntaxFactory.ParseToken(modifier)); + + return this; + } + public AccessorBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.Modifiers = SyntaxFactory.TokenList(SyntaxFactory.ParseTokens(modifiers)); + + return this; + } + public AccessorBuilder WithBody(string body) + { + _ = string.IsNullOrWhiteSpace(body) ? throw new ArgumentNullException(nameof(body)) : body; + + this.ArrowExpressionClauseSyntax = null; + this.BlockSyntax = SyntaxFactory.Block(SyntaxFactory.ParseStatement(body)); + + return this; + } + public AccessorBuilder WithArrowExpression(string expression) + { + _ = string.IsNullOrWhiteSpace(expression) ? throw new ArgumentNullException(nameof(expression)) : expression; + + this.BlockSyntax = null; + this.ArrowExpressionClauseSyntax = SyntaxFactory.ArrowExpressionClause(SyntaxFactory.ParseExpression(expression)); + + return this; + } + + public AccessorDeclarationSyntax Build() + { + return this.BuildAccessorDeclaration().NormalizeWhitespace(); + } + + private AccessorDeclarationSyntax BuildAccessorDeclaration() + { + var accessorDeclaration = SyntaxFactory.AccessorDeclaration( + kind: this.accessor, + attributeLists: SyntaxFactory.List(), + modifiers: this.Modifiers, + body: this.BlockSyntax, + expressionBody: this.ArrowExpressionClauseSyntax); + + if (this.BlockSyntax is null && this.ArrowExpressionClauseSyntax is null) + { + accessorDeclaration = accessorDeclaration.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + } + + return accessorDeclaration; + } + } +} diff --git a/Sybil/BaseConstructorBuilder.cs b/Sybil/BaseConstructorBuilder.cs new file mode 100644 index 0000000..5841d11 --- /dev/null +++ b/Sybil/BaseConstructorBuilder.cs @@ -0,0 +1,31 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; + +namespace Sybil +{ + public sealed class BaseConstructorBuilder : IBuilder + { + private ConstructorInitializerSyntax ConstructorInitializerSyntax { get; set; } + + internal BaseConstructorBuilder() + { + this.ConstructorInitializerSyntax = SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer); + } + + public BaseConstructorBuilder WithArgument(string argumentName) + { + _ = string.IsNullOrWhiteSpace(argumentName) ? throw new ArgumentNullException(nameof(argumentName)) : argumentName; + + this.ConstructorInitializerSyntax = this.ConstructorInitializerSyntax.AddArgumentListArguments(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName))); + + return this; + } + + public ConstructorInitializerSyntax Build() + { + return ConstructorInitializerSyntax.NormalizeWhitespace(); + } + } +} diff --git a/Sybil/ClassBuilder.cs b/Sybil/ClassBuilder.cs new file mode 100644 index 0000000..0a64114 --- /dev/null +++ b/Sybil/ClassBuilder.cs @@ -0,0 +1,85 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Sybil +{ + public sealed class ClassBuilder : IBuilder + { + private readonly string className; + private ClassDeclarationSyntax ClassDeclaration { get; set; } + + private readonly List Constructors = new List(); + private readonly List Fields = new List(); + private readonly List Properties = new List(); + private readonly List Methods = new List(); + + internal ClassBuilder( + string className) + { + _ = string.IsNullOrWhiteSpace(className) ? throw new ArgumentNullException(nameof(className)) : className; + + this.className = className; + this.ClassDeclaration = SyntaxFactory.ClassDeclaration(className); + } + + public ClassBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.ClassDeclaration = this.ClassDeclaration.AddModifiers(SyntaxFactory.ParseToken(modifier)); + + return this; + } + + public ClassBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.ClassDeclaration = this.ClassDeclaration.AddModifiers(SyntaxFactory.ParseTokens(modifiers).ToArray()); + + return this; + } + + public ClassBuilder WithField(FieldBuilder fieldBuilder) + { + this.Fields.Add(fieldBuilder ?? throw new ArgumentNullException(nameof(fieldBuilder))); + + return this; + } + + public ClassBuilder WithProperty(PropertyBuilder propertyBuilder) + { + this.Properties.Add(propertyBuilder ?? throw new ArgumentNullException(nameof(propertyBuilder))); + + return this; + } + + public ClassBuilder WithMethod(MethodBuilder methodBuilder) + { + this.Methods.Add(methodBuilder ?? throw new ArgumentNullException(nameof(methodBuilder))); + + return this; + } + + public ClassBuilder WithConstructor(ConstructorBuilder constructorBuilder) + { + this.Constructors.Add(constructorBuilder ?? throw new ArgumentNullException(nameof(constructorBuilder))); + + return this; + } + + public ClassDeclarationSyntax Build() + { + return this.ClassDeclaration + .AddMembers(this.Constructors.Select(p => p.Build()).ToArray()) + .AddMembers(this.Fields.Select(p => p.Build()).ToArray()) + .AddMembers(this.Properties.Select(p => p.Build()).ToArray()) + .AddMembers(this.Methods.Select(p => p.Build()).ToArray()) + .NormalizeWhitespace(); + } + } +} diff --git a/Sybil/ConstructorBuilder.cs b/Sybil/ConstructorBuilder.cs new file mode 100644 index 0000000..8e7ce09 --- /dev/null +++ b/Sybil/ConstructorBuilder.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Linq; + +namespace Sybil +{ + public sealed class ConstructorBuilder : IBuilder + { + private BaseConstructorBuilder baseConstructorBuilder; + + private ConstructorDeclarationSyntax ConstructorDeclarationSyntax { get; set; } + + internal ConstructorBuilder( + string className) + { + _ = string.IsNullOrWhiteSpace(className) ? throw new ArgumentNullException(nameof(className)) : className; + + this.ConstructorDeclarationSyntax = SyntaxFactory.ConstructorDeclaration(className); + this.ConstructorDeclarationSyntax = this.ConstructorDeclarationSyntax.WithBody(SyntaxFactory.Block()); + } + + public ConstructorBuilder WithBase(BaseConstructorBuilder baseConstructorBuilder) + { + this.baseConstructorBuilder = baseConstructorBuilder; + return this; + } + public ConstructorBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.ConstructorDeclarationSyntax = this.ConstructorDeclarationSyntax.AddModifiers(SyntaxFactory.ParseTokens(modifiers).ToArray()); + + return this; + } + public ConstructorBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.ConstructorDeclarationSyntax = this.ConstructorDeclarationSyntax.AddModifiers(SyntaxFactory.ParseToken(modifier)); + + return this; + } + public ConstructorBuilder WithParameter(string parameterType, string parameterName, string defaultValue = null) + { + _ = string.IsNullOrWhiteSpace(parameterName) ? throw new ArgumentNullException(nameof(parameterName)) : parameterName; + _ = string.IsNullOrWhiteSpace(parameterType) ? throw new ArgumentNullException(nameof(parameterType)) : parameterType; + + EqualsValueClauseSyntax equalsValueClauseSyntax = null; + if (string.IsNullOrWhiteSpace(defaultValue) is false) + { + equalsValueClauseSyntax = SyntaxFactory.EqualsValueClause( + SyntaxFactory.ParseExpression(defaultValue)); + } + + var parameter = SyntaxFactory.Parameter( + new SyntaxList(), + new SyntaxTokenList(), + SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(parameterType)), + SyntaxFactory.Identifier(parameterName), + equalsValueClauseSyntax); + this.ConstructorDeclarationSyntax = this.ConstructorDeclarationSyntax.AddParameterListParameters(parameter); + + return this; + } + public ConstructorBuilder WithBody(string body) + { + _ = string.IsNullOrWhiteSpace(body) ? throw new ArgumentNullException(nameof(body)) : body; + + this.ConstructorDeclarationSyntax = this.ConstructorDeclarationSyntax.WithBody(SyntaxFactory.Block(SyntaxFactory.ParseStatement(body))); + + return this; + } + + public ConstructorDeclarationSyntax Build() + { + if (this.baseConstructorBuilder is null is false) + { + this.ConstructorDeclarationSyntax = this.ConstructorDeclarationSyntax.WithInitializer(this.baseConstructorBuilder.Build()); + } + + return this.ConstructorDeclarationSyntax.NormalizeWhitespace(); + } + } +} diff --git a/Sybil/FieldBuilder.cs b/Sybil/FieldBuilder.cs new file mode 100644 index 0000000..959a622 --- /dev/null +++ b/Sybil/FieldBuilder.cs @@ -0,0 +1,48 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Linq; + +namespace Sybil +{ + public sealed class FieldBuilder : IBuilder + { + private FieldDeclarationSyntax FieldDeclarationSyntax { get; set; } + + internal FieldBuilder( + string fieldType, + string fieldName) + { + _ = string.IsNullOrWhiteSpace(fieldType) ? throw new ArgumentNullException(nameof(fieldType)) : fieldType; + _ = string.IsNullOrWhiteSpace(fieldName) ? throw new ArgumentNullException(nameof(fieldName)) : fieldName; + + var variableDeclaration = SyntaxFactory.VariableDeclaration( + SyntaxFactory.ParseTypeName(fieldType)) + .AddVariables(SyntaxFactory.VariableDeclarator(fieldName)); + this.FieldDeclarationSyntax = SyntaxFactory.FieldDeclaration(variableDeclaration); + } + + public FieldBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.FieldDeclarationSyntax = this.FieldDeclarationSyntax.AddModifiers(SyntaxFactory.ParseToken(modifier)); + + return this; + } + public FieldBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.FieldDeclarationSyntax = this.FieldDeclarationSyntax.AddModifiers(SyntaxFactory.ParseTokens(modifiers).ToArray()); + + return this; + } + + public FieldDeclarationSyntax Build() + { + return this.FieldDeclarationSyntax.NormalizeWhitespace(); + } + } +} diff --git a/Sybil/IBuilder.cs b/Sybil/IBuilder.cs new file mode 100644 index 0000000..eb8620b --- /dev/null +++ b/Sybil/IBuilder.cs @@ -0,0 +1,9 @@ +using Microsoft.CodeAnalysis.CSharp; + +namespace Sybil +{ + public interface IBuilder where T : CSharpSyntaxNode + { + T Build(); + } +} diff --git a/Sybil/MethodBuilder.cs b/Sybil/MethodBuilder.cs new file mode 100644 index 0000000..be10508 --- /dev/null +++ b/Sybil/MethodBuilder.cs @@ -0,0 +1,97 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Linq; + +namespace Sybil +{ + public sealed class MethodBuilder : IBuilder + { + private BlockSyntax BlockBody { get; set; } + private ArrowExpressionClauseSyntax ArrowExpression { get; set; } + private MethodDeclarationSyntax MethodDeclarationSyntax { get; set; } + + internal MethodBuilder( + string returnType, string name) + { + _ = string.IsNullOrWhiteSpace(returnType) ? throw new ArgumentNullException(nameof(returnType)) : returnType; + _ = string.IsNullOrWhiteSpace(name) ? throw new ArgumentNullException(nameof(name)) : name; + + this.MethodDeclarationSyntax = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(returnType), SyntaxFactory.ParseToken(name)); + this.ArrowExpression = SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.ParseExpression("throw new NotImplementedException();")); + } + + public MethodBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.MethodDeclarationSyntax = this.MethodDeclarationSyntax.AddModifiers(SyntaxFactory.ParseToken(modifier)); + + return this; + } + public MethodBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.MethodDeclarationSyntax = this.MethodDeclarationSyntax.AddModifiers(SyntaxFactory.ParseTokens(modifiers).ToArray()); + + return this; + } + public MethodBuilder WithParameter(string parameterType, string parameterName, string defaultValue = null) + { + _ = string.IsNullOrWhiteSpace(parameterName) ? throw new ArgumentNullException(nameof(parameterName)) : parameterName; + _ = string.IsNullOrWhiteSpace(parameterType) ? throw new ArgumentNullException(nameof(parameterType)) : parameterType; + + EqualsValueClauseSyntax equalsValueClauseSyntax = null; + if (string.IsNullOrWhiteSpace(defaultValue) is false) + { + equalsValueClauseSyntax = SyntaxFactory.EqualsValueClause( + SyntaxFactory.ParseExpression(defaultValue)); + } + + var parameter = SyntaxFactory.Parameter( + SyntaxFactory.List(), + new SyntaxTokenList(), + type: SyntaxFactory.ParseTypeName(parameterType), + identifier: SyntaxFactory.ParseToken(parameterName), + @default: equalsValueClauseSyntax); + + this.MethodDeclarationSyntax = this.MethodDeclarationSyntax.AddParameterListParameters(parameter); + + return this; + } + public MethodBuilder WithBody(string body) + { + this.BlockBody = SyntaxFactory.Block( + SyntaxFactory.ParseStatement(body)); + this.ArrowExpression = null; + + return this; + } + public MethodBuilder WithExpression(string expression) + { + this.ArrowExpression = SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.ParseExpression(expression)); + this.BlockBody = null; + + return this; + } + + public MethodDeclarationSyntax Build() + { + if (this.BlockBody is null is false) + { + return this.MethodDeclarationSyntax.WithBody(this.BlockBody); + } + + if (this.ArrowExpression is null is false) + { + return this.MethodDeclarationSyntax.WithExpressionBody(this.ArrowExpression); + } + + return this.MethodDeclarationSyntax; + } + } +} diff --git a/Sybil/NamespaceBuilder.cs b/Sybil/NamespaceBuilder.cs new file mode 100644 index 0000000..13c4a31 --- /dev/null +++ b/Sybil/NamespaceBuilder.cs @@ -0,0 +1,51 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Sybil +{ + public sealed class NamespaceBuilder : IBuilder + { + private readonly List classBuilders = new List(); + private NamespaceDeclarationSyntax NamespaceDeclaration { get; set; } + + internal NamespaceBuilder( + string @namespace) + { + _ = string.IsNullOrWhiteSpace(@namespace) ? throw new ArgumentNullException(nameof(@namespace)) : @namespace; + + this.NamespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace)); + } + + public NamespaceBuilder WithUsing(string usingName) + { + _ = string.IsNullOrWhiteSpace(usingName) ? throw new ArgumentNullException(nameof(usingName)) : usingName; + + this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(usingName))); + + return this; + } + + public NamespaceBuilder WithClass(ClassBuilder classBuilder) + { + this.classBuilders.Add(classBuilder); + + return this; + } + + public NamespaceDeclarationSyntax Build() + { + return this.NamespaceDeclaration + .AddMembers(this.classBuilders.Select(c => c.Build()).ToArray()) + .NormalizeWhitespace(); + } + + public static NamespaceBuilder Create(string @namespace) + { + return new NamespaceBuilder(@namespace); + } + } +} diff --git a/Sybil/PropertyBuilder.cs b/Sybil/PropertyBuilder.cs new file mode 100644 index 0000000..29e4f1d --- /dev/null +++ b/Sybil/PropertyBuilder.cs @@ -0,0 +1,62 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Sybil +{ + public sealed class PropertyBuilder : IBuilder + { + private readonly List accessors = new List(); + + private PropertyDeclarationSyntax PropertyDeclarationSyntax { get; set; } + + internal PropertyBuilder( + string typeName, + string propertyName) + { + _ = string.IsNullOrWhiteSpace(typeName) ? throw new ArgumentNullException(nameof(typeName)) : typeName; + _ = string.IsNullOrWhiteSpace(propertyName) ? throw new ArgumentNullException(nameof(propertyName)) : propertyName; + + this.PropertyDeclarationSyntax = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(typeName), propertyName); + } + + public PropertyBuilder WithModifier(string modifier) + { + _ = string.IsNullOrWhiteSpace(modifier) ? throw new ArgumentNullException(nameof(modifier)) : modifier; + + this.PropertyDeclarationSyntax = this.PropertyDeclarationSyntax.AddModifiers(SyntaxFactory.ParseToken(modifier)); + + return this; + } + public PropertyBuilder WithModifiers(string modifiers) + { + _ = string.IsNullOrWhiteSpace(modifiers) ? throw new ArgumentNullException(nameof(modifiers)) : modifiers; + + this.PropertyDeclarationSyntax = this.PropertyDeclarationSyntax.AddModifiers(SyntaxFactory.ParseTokens(modifiers).ToArray()); + + return this; + } + public PropertyBuilder WithAccessor(AccessorBuilder accessorBuilder) + { + this.accessors.Add(accessorBuilder); + + return this; + } + + public PropertyDeclarationSyntax Build() + { + return this.PropertyDeclarationSyntax.WithAccessorList(this.BuildAccessorList()).NormalizeWhitespace(); + } + + private AccessorListSyntax BuildAccessorList() + { + return SyntaxFactory.AccessorList( + SyntaxFactory.Token(SyntaxKind.OpenBraceToken), + accessors: SyntaxFactory.List(this.accessors.Select(a => a.Build())), + SyntaxFactory.Token(SyntaxKind.CloseBraceToken)); + } + } +} diff --git a/Sybil/Sybil.csproj b/Sybil/Sybil.csproj index 3a54d8b..a20e7ff 100644 --- a/Sybil/Sybil.csproj +++ b/Sybil/Sybil.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -7,12 +7,12 @@ https://github.com/AlexMacocian/Sybil git 0.1 - C:\Users\Owner\source\repos\Sybil\LICENSE - True - + + Always + True \ diff --git a/Sybil/SyntaxBuilder.cs b/Sybil/SyntaxBuilder.cs new file mode 100644 index 0000000..5522ab7 --- /dev/null +++ b/Sybil/SyntaxBuilder.cs @@ -0,0 +1,44 @@ +using Microsoft.CodeAnalysis.CSharp; + +namespace Sybil +{ + public static class SyntaxBuilder + { + public static NamespaceBuilder CreateNamespace(string @namespace) + { + return new NamespaceBuilder(@namespace); + } + public static ClassBuilder CreateClass(string className) + { + return new ClassBuilder(className); + } + public static ConstructorBuilder CreateConstructor(string className) + { + return new ConstructorBuilder(className); + } + public static BaseConstructorBuilder CreateBaseConstructor() + { + return new BaseConstructorBuilder(); + } + public static PropertyBuilder CreateProperty(string typeName, string propertyName) + { + return new PropertyBuilder(typeName, propertyName); + } + public static AccessorBuilder CreateSetter() + { + return new AccessorBuilder(SyntaxKind.SetAccessorDeclaration); + } + public static AccessorBuilder CreateGetter() + { + return new AccessorBuilder(SyntaxKind.GetAccessorDeclaration); + } + public static FieldBuilder CreateField(string fieldType, string fieldName) + { + return new FieldBuilder(fieldType, fieldName); + } + public static MethodBuilder CreateMethod(string returnType, string methodName) + { + return new MethodBuilder(returnType, methodName); + } + } +}