-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
390 additions
and
65 deletions.
There are no files selected for viewing
75 changes: 75 additions & 0 deletions
75
specs/Qowaiv.CodeGeneration.Specs/Code_name_covention_specs.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
namespace Code_name_covention_specs; | ||
|
||
public class Splits | ||
{ | ||
[TestCase("HelloWorld", "Hello", "World")] | ||
[TestCase("helloWorld", "Hello", "World")] | ||
[TestCase("iAmNuts", "I", "Am", "Nuts")] | ||
[TestCase("XMLIsOld", "XML", "Is", "Old")] | ||
[TestCase("XMLIsOLD", "XML", "Is", "OLD")] | ||
public void With_Pascal_case(string str, params string[] parts) | ||
=> CodeNameConvention.PascalCase.Split(str).Should().BeEquivalentTo(parts); | ||
|
||
[TestCase("HelloWorld", "hello", "World")] | ||
[TestCase("helloWorld", "hello", "World")] | ||
[TestCase("iAmNuts", "i", "Am", "Nuts")] | ||
[TestCase("XMLIsOld", "xml", "Is", "Old")] | ||
[TestCase("XMLIsOLD", "xml", "Is", "OLD")] | ||
public void With_Camel_case(string str, params string[] parts) | ||
=> CodeNameConvention.CamelCase.Split(str).Should().BeEquivalentTo(parts); | ||
|
||
[TestCase("Hello-World", "hello", "world")] | ||
[TestCase("hello--World", "hello", "world")] | ||
public void With_Kebab_case(string str, params string[] parts) | ||
=> CodeNameConvention.KebabCase.Split(str).Should().BeEquivalentTo(parts); | ||
|
||
[TestCase("Hello_World", "hello", "world")] | ||
[TestCase("hello__World", "hello", "world")] | ||
public void With_Snake_case(string str, params string[] parts) | ||
=> CodeNameConvention.SnakeCase.Split(str).Should().BeEquivalentTo(parts); | ||
|
||
[TestCase("Hello_World", "HELLO", "WORLD")] | ||
[TestCase("hello__World", "HELLO", "WORLD")] | ||
public void With_Screaming_Snake_case(string str, params string[] parts) | ||
=> CodeNameConvention.ScreamingSnakeCase.Split(str).Should().BeEquivalentTo(parts); | ||
|
||
[TestCase("Hello World", "Hello", "World")] | ||
[TestCase("Hello\tWorld", "Hello", "World")] | ||
[TestCase("Hello \tWorld", "Hello", "World")] | ||
[TestCase("Hello_World", "Hello", "World")] | ||
[TestCase("Hello World non-breaking", "Hello", "World", "non-breaking")] | ||
public void With_Sentence_case(string str, params string[] parts) | ||
=> CodeNameConvention.SentenceCase.Split(str).Should().BeEquivalentTo(parts); | ||
} | ||
|
||
public class Formats | ||
{ | ||
[TestCase("HelloWorld", "hello", "World")] | ||
[TestCase("HelloWorld", "Hello", "World")] | ||
public void With_Pacal_case(string str, params string[] parts) | ||
=> CodeNameConvention.PascalCase.ToString(parts).Should().Be(str); | ||
|
||
[TestCase("helloWorld", "hello", "World")] | ||
[TestCase("helloWorld", "Hello", "World")] | ||
public void With_Dromedary_case(string str, params string[] parts) | ||
=> CodeNameConvention.CamelCase.ToString(parts).Should().Be(str); | ||
|
||
[TestCase("hello-world", "hello", "world")] | ||
[TestCase("hello-world", "Hello", "World")] | ||
public void With_Kebab_case(string str, params string[] parts) | ||
=> CodeNameConvention.KebabCase.ToString(parts).Should().Be(str); | ||
|
||
[TestCase("hello_world", "hello", "world")] | ||
[TestCase("hello_world", "Hello", "World")] | ||
public void With_Snake_case(string str, params string[] parts) | ||
=> CodeNameConvention.SnakeCase.ToString(parts).Should().Be(str); | ||
|
||
[TestCase("HELLO_WORLD", "hello", "world")] | ||
[TestCase("HELLO_WORLD", "Hello", "World")] | ||
public void With_Screaming_Snake_case(string str, params string[] parts) | ||
=> CodeNameConvention.ScreamingSnakeCase.ToString(parts).Should().Be(str); | ||
|
||
[TestCase("Hello World", "Hello", "World")] | ||
public void With_Sentence_case(string str, params string[] parts) | ||
=> CodeNameConvention.SentenceCase.ToString(parts).Should().Be(str); | ||
} |
42 changes: 42 additions & 0 deletions
42
src/Qowaiv.CodeGeneration.OpenApi/OpenApiEnumNameConvention.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
namespace Qowaiv.CodeGeneration.OpenApi; | ||
|
||
/// <summary>Implements Open API specific name convention for enum values.</summary> | ||
public class OpenApiEnumNameConvention : CodeNameConvention | ||
{ | ||
internal static readonly OpenApiEnumNameConvention Instance = new(); | ||
|
||
/// <inheritdoc /> | ||
public override string Name => "Open API enum name"; | ||
|
||
/// <summary>Splitters used to seperates parts of a name.</summary> | ||
protected virtual IReadOnlyCollection<char> Splitters { get; } = [' ', '-', '_', '\t', '\r', '\n', '.', '!', ',', ';', '#', (char)160]; | ||
|
||
/// <inheritdoc /> | ||
[Pure] | ||
public override IReadOnlyCollection<string> Split(IEnumerable<string?> parts) | ||
=> Guard.NotNull(parts) | ||
.OfType<string>() | ||
.Where(p => p is { Length: > 0 }) | ||
.Select(Format) | ||
.SelectMany(p => p.Split([.. Splitters], StringSplitOptions.RemoveEmptyEntries)) | ||
Check failure on line 21 in src/Qowaiv.CodeGeneration.OpenApi/OpenApiEnumNameConvention.cs GitHub Actions / build
Check failure on line 21 in src/Qowaiv.CodeGeneration.OpenApi/OpenApiEnumNameConvention.cs GitHub Actions / build
|
||
.ToArray(); | ||
|
||
/// <summary>Formats a part.</summary> | ||
[Pure] | ||
protected virtual string Format(string part, int index) => part | ||
.Replace("+", " pls ") | ||
.Replace("(", string.Empty) | ||
.Replace(")", string.Empty); | ||
|
||
/// <inheritdoc /> | ||
[Pure] | ||
public override string ToString(IReadOnlyCollection<string> parts) | ||
{ | ||
var name = string.Join('_', Guard.NotNull(parts).Where(p => p is { Length: > 0 })); | ||
if (char.IsAsciiDigit(name[0])) | ||
{ | ||
name = '_' + name; | ||
} | ||
return CodeName.EscapeKeyword(name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
namespace Qowaiv.CodeGeneration; | ||
|
||
/// <summary>Represents the name of a piece of code (class, property, field, method, etc.).</summary> | ||
[DebuggerDisplay("{ToString()}, Covention = {Convention.Name}")] | ||
public readonly struct CodeName : IFormattable | ||
{ | ||
private CodeName(IReadOnlyCollection<string> nameParts, CodeNameConvention nameConvention) | ||
{ | ||
parts = nameParts; | ||
convention = nameConvention; | ||
} | ||
|
||
private readonly IReadOnlyCollection<string> parts; | ||
|
||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] | ||
private readonly CodeNameConvention? convention; | ||
|
||
/// <summary>The convention applied to create this (code) name.</summary> | ||
public CodeNameConvention Convention => convention ?? CodeNameConvention.None; | ||
|
||
/// <summary>Creates a new (code) name applying the specified convention.</summary> | ||
[Pure] | ||
public static CodeName Create(string? name, CodeNameConvention convention) | ||
=> new(Guard.NotNull(convention).Split(name), convention); | ||
|
||
/// <inheritdoc /> | ||
[Pure] | ||
public override string ToString() => ToString(Convention); | ||
|
||
/// <inheritdoc /> | ||
[Pure] | ||
public string ToString(string? format, IFormatProvider? formatProvider) => format?.ToUpperInvariant() switch | ||
{ | ||
null or "" => ToString(Convention), | ||
"PASCAL" or "PASCALCASE" => ToString(CodeNameConvention.CamelCase), | ||
"CAMEL" or "CAMELCASE" => ToString(CodeNameConvention.CamelCase), | ||
"KEBAB" or "KEBABCASE" => ToString(CodeNameConvention.KebabCase), | ||
"SNAKE" or "SNAKECASE" => ToString(CodeNameConvention.SnakeCase), | ||
"SCREAMINGSNAKE" => ToString(CodeNameConvention.ScreamingSnakeCase), | ||
_ => throw new FormatException($"Format '{format}' is unknown."), | ||
}; | ||
|
||
/// <summary>Represents the code name as string according to the naming convention.</summary> | ||
[Pure] | ||
public string ToString(CodeNameConvention convention) => Guard.NotNull(convention).ToString(parts); | ||
|
||
/// <summary>Represents the code name as property name for the specified enclosing type.</summary> | ||
/// <param name="enclosing"> | ||
/// The enclosing type. | ||
/// </param> | ||
/// <param name="convention"> | ||
/// The optional naming convention, <see cref="CodeNameConvention.PascalCase"/> if not specified. | ||
/// </param> | ||
[Pure] | ||
public string PropertyFor(Type enclosing, CodeNameConvention? convention = null) | ||
{ | ||
Guard.NotNull(enclosing); | ||
convention ??= CodeNameConvention.PascalCase; | ||
|
||
var name = convention.ToString(parts); | ||
return enclosing.Name == name || char.IsAsciiDigit(name[0]) | ||
? '_' + name | ||
: EscapeKeyword(name); | ||
} | ||
|
||
/// <summary>Escapes the name with an '@' if it a C# keyword.</summary> | ||
[Pure] | ||
public static string EscapeKeyword(string name) | ||
=> keywords.Contains(Guard.NotNullOrEmpty(name)) | ||
? "@" + name | ||
: name; | ||
|
||
private static readonly string[] keywords = ["default", "new", "class"]; | ||
} |
Oops, something went wrong.