using System.Linq.Expressions; using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore.Storage; using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; #pragma warning disable EF1001 namespace Content.Server.Database { // Taken from https://github.com/npgsql/efcore.pg/issues/1158 // To support inet -> (IPAddress, int) mapping. public class CustomNpgsqlTypeMappingSource : NpgsqlTypeMappingSource { public CustomNpgsqlTypeMappingSource( TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies, ISqlGenerationHelper sqlGenerationHelper, INpgsqlOptions? npgsqlOptions = null) : base(dependencies, relationalDependencies, sqlGenerationHelper, npgsqlOptions) { StoreTypeMappings["inet"] = new RelationalTypeMapping[] { new NpgsqlInetWithMaskTypeMapping(), new NpgsqlInetTypeMapping() }; } } // Basically copied from NpgsqlCidrTypeMapping public class NpgsqlInetWithMaskTypeMapping : NpgsqlTypeMapping { public NpgsqlInetWithMaskTypeMapping() : base("inet", typeof((IPAddress, int)), NpgsqlTypes.NpgsqlDbType.Inet) { } protected NpgsqlInetWithMaskTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters, NpgsqlTypes.NpgsqlDbType.Inet) { } protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) { return new NpgsqlInetWithMaskTypeMapping(parameters); } protected override string GenerateNonNullSqlLiteral(object value) { var (address, subnet) = ((IPAddress, int)) value; return $"INET '{address}/{subnet}'"; } public override Expression GenerateCodeLiteral(object value) { var (address, subnet) = ((IPAddress, int)) value; return Expression.New( Constructor, Expression.Call(ParseMethod, Expression.Constant(address.ToString())), Expression.Constant(subnet)); } private static readonly MethodInfo ParseMethod = typeof(IPAddress).GetMethod("Parse", new[] {typeof(string)})!; private static readonly ConstructorInfo Constructor = typeof((IPAddress, int)).GetConstructor(new[] {typeof(IPAddress), typeof(int)})!; } }