using System; using System.Globalization; using System.Linq; using System.Text; namespace Npgsql.NameTranslation; /// /// A name translator which converts standard CLR names (e.g. SomeClass) to snake-case database /// names (some_class) /// public class NpgsqlSnakeCaseNameTranslator : INpgsqlNameTranslator { /// /// Creates a new . /// public NpgsqlSnakeCaseNameTranslator() : this(false) { } /// /// Creates a new . /// /// /// Uses the legacy naming convention if , otherwise it uses the new naming convention. /// public NpgsqlSnakeCaseNameTranslator(bool legacyMode) => LegacyMode = legacyMode; bool LegacyMode { get; } /// /// Given a CLR type name (e.g class, struct, enum), translates its name to a database type name. /// public string TranslateTypeName(string clrName) => TranslateMemberName(clrName); /// /// Given a CLR member name (property or field), translates its name to a database type name. /// public string TranslateMemberName(string clrName) { if (clrName == null) throw new ArgumentNullException(nameof(clrName)); return LegacyMode ? string.Concat(clrName.Select((c, i) => i > 0 && char.IsUpper(c) ? "_" + c.ToString() : c.ToString())).ToLower() : ConvertToSnakeCase(clrName); } /// /// Converts a string to its snake_case equivalent. /// /// The value to convert. public static string ConvertToSnakeCase(string name) { if (string.IsNullOrEmpty(name)) return name; var builder = new StringBuilder(name.Length + Math.Min(2, name.Length / 5)); var previousCategory = default(UnicodeCategory?); for (var currentIndex = 0; currentIndex < name.Length; currentIndex++) { var currentChar = name[currentIndex]; if (currentChar == '_') { builder.Append('_'); previousCategory = null; continue; } var currentCategory = char.GetUnicodeCategory(currentChar); switch (currentCategory) { case UnicodeCategory.UppercaseLetter: case UnicodeCategory.TitlecaseLetter: if (previousCategory == UnicodeCategory.SpaceSeparator || previousCategory == UnicodeCategory.LowercaseLetter || previousCategory != UnicodeCategory.DecimalDigitNumber && previousCategory != null && currentIndex > 0 && currentIndex + 1 < name.Length && char.IsLower(name[currentIndex + 1])) { builder.Append('_'); } currentChar = char.ToLower(currentChar); break; case UnicodeCategory.LowercaseLetter: case UnicodeCategory.DecimalDigitNumber: if (previousCategory == UnicodeCategory.SpaceSeparator) builder.Append('_'); break; default: if (previousCategory != null) previousCategory = UnicodeCategory.SpaceSeparator; continue; } builder.Append(currentChar); previousCategory = currentCategory; } return builder.ToString(); } }