Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CodeGenerator/Generators/DataClassesGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ private string GetFieldName(Column column, Dictionary<string, int> seenEmbed)
if (column.EmbedTable is null)
return column.Name.ToPascalCase();

var fieldName = column.Name.ToModelName(column.EmbedTable.Schema, dbDriver.DefaultSchema);
var fieldName = column.Name.ToModelName(column.EmbedTable.Schema, dbDriver.DefaultSchema,
dbDriver.Options.UseProperSingularization);
fieldName = seenEmbed.TryGetValue(fieldName, out var value)
? $"{fieldName}{value}" : fieldName;
seenEmbed.TryAdd(fieldName, 1);
Expand Down
3 changes: 2 additions & 1 deletion CodeGenerator/Generators/ModelsGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ private MemberDeclarationSyntax[] GenerateDataClasses(Dictionary<string, Diction
return (
from schemaTables in tables
from table in schemaTables.Value
let className = table.Value.Rel.Name.ToModelName(table.Value.Rel.Schema, dbDriver.DefaultSchema)
let className = table.Value.Rel.Name.ToModelName(table.Value.Rel.Schema, dbDriver.DefaultSchema,
dbDriver.Options.UseProperSingularization)
select DataClassesGen.Generate(className, null, table.Value.Columns, dbDriver.Options, null)
).ToArray();
}
Expand Down
3 changes: 2 additions & 1 deletion Drivers/DbDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,8 @@ public bool IsTypeNullable(string csharpType)
protected virtual string GetCsharpTypeWithoutNullableSuffix(Column column, Query? query)
{
if (column.EmbedTable != null)
return column.EmbedTable.Name.ToModelName(column.EmbedTable.Schema, DefaultSchema);
return column.EmbedTable.Name.ToModelName(column.EmbedTable.Schema, DefaultSchema,
Options.UseProperSingularization);

if (string.IsNullOrEmpty(column.Type.Name))
return "object";
Expand Down
2 changes: 1 addition & 1 deletion Drivers/EnumDbDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public EnumDbDriver(Options options, Catalog catalog, IList<Query> queries) : ba

foreach (var schemaEnums in Enums)
foreach (var e in schemaEnums.Value)
NullableTypes.Add(e.Key.ToModelName(schemaEnums.Key, DefaultSchema));
NullableTypes.Add(e.Key.ToModelName(schemaEnums.Key, DefaultSchema, options.UseProperSingularization));
}

public virtual MemberDeclarationSyntax[] GetEnumExtensionsMembers(string name, IList<string> possibleValues)
Expand Down
3 changes: 2 additions & 1 deletion Drivers/Generators/CommonGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ public string InstantiateDataclass(Column[] columns, string returnInterface, Que
continue;
}

var tableFieldType = column.EmbedTable.Name.ToModelName(column.EmbedTable.Schema, dbDriver.DefaultSchema);
var tableFieldType = column.EmbedTable.Name.ToModelName(column.EmbedTable.Schema, dbDriver.DefaultSchema,
dbDriver.Options.UseProperSingularization);
var tableFieldName = seenEmbed.TryGetValue(tableFieldType, out var value)
? $"{tableFieldType}{value}" : tableFieldType;
seenEmbed.TryAdd(tableFieldType, 1);
Expand Down
22 changes: 20 additions & 2 deletions Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,28 @@ public static string ToCamelCase(this string value)
return string.Concat(newValue[0].ToString().ToLower(), newValue.AsSpan(1));
}

public static string ToModelName(this string value, string schema, string defaultSchema)
public static string ToModelName(this string value, string schema, string defaultSchema,
bool properSingularization = false)
{
var schemaName = schema == defaultSchema ? string.Empty : schema;
return $"{schemaName}_{value.TrimEnd('s')}".ToPascalCase(); // TODO implement better way to turn words to singular
var singular = properSingularization ? value.Singularize() : value.TrimEnd('s');
return $"{schemaName}_{singular}".ToPascalCase();
}

private static readonly string[] DropEsSuffixes = ["sses", "uses", "xes", "ches", "shes"];

// Rule-based English singularization for identifiers, e.g. policies -> policy,
// statuses -> status, devices -> device, responses -> response. Words not ending
// in a plural-looking "s" (address, people) pass through unchanged.
private static string Singularize(this string value)
{
if (!value.EndsWith('s') || value.EndsWith("ss", StringComparison.OrdinalIgnoreCase))
return value;
if (value.EndsWith("ies", StringComparison.OrdinalIgnoreCase) && value.Length > 3)
return $"{value[..^3]}y";
if (DropEsSuffixes.Any(suffix => value.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)))
return value[..^2];
return value[..^1];
}

public static string ToMethodName(this string value, bool withAsyncSuffix)
Expand Down
9 changes: 9 additions & 0 deletions PluginOptions/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public Options(GenerateRequest generateRequest)
WithAsyncSuffix = rawOptions.WithAsyncSuffix;
WithCancellationToken = rawOptions.WithCancellationToken;
UseCentralPackageManagement = rawOptions.UseCentralPackageManagement;
UseProperSingularization = rawOptions.UseProperSingularization;

if (rawOptions.DebugRequest && generateRequest.Settings.Codegen.Wasm is not null)
throw new ArgumentException("Debug request mode cannot be used with WASM plugin");
Expand Down Expand Up @@ -63,6 +64,14 @@ public Options(GenerateRequest generateRequest)
/// </summary>
public bool WithCancellationToken { get; }

/// <summary>
/// When true, model names are singularized with English grammar rules (policies -> Policy,
/// statuses -> Status). Defaults to the legacy behavior of trimming trailing 's' characters,
/// which mangles some names (policies -> Policie, access -> Acces). Opt-in because enabling
/// it renames existing generated classes.
/// </summary>
public bool UseProperSingularization { get; }

private static readonly Dictionary<string, DriverName> EngineToDriverMapping = new()
{
{ "mysql", DriverName.MySqlConnector },
Expand Down
3 changes: 3 additions & 0 deletions PluginOptions/RawOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public record RawOptions

[JsonPropertyName("withCancellationToken")]
public bool WithCancellationToken { get; init; }

[JsonPropertyName("useProperSingularization")]
public bool UseProperSingularization { get; init; }
}

public class OverrideOption
Expand Down
Loading
Loading