content: package com.github.pgasync;
  
  import static java.lang.System.getenv;
  
  import com.pgasync.NettyConnectibleBuilder;
  import com.pgasync.Connectible;
  import com.pgasync.ResultSet;
  import org.junit.rules.ExternalResource;
  
  import java.io.IOException;
  import java.util.Collection;
  import java.util.List;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.TimeUnit;
  
  import ru.yandex.qatools.embed.postgresql.PostgresExecutable;
  import ru.yandex.qatools.embed.postgresql.PostgresProcess;
  import ru.yandex.qatools.embed.postgresql.PostgresStarter;
  import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig;
  import ru.yandex.qatools.embed.postgresql.config.PostgresConfig;
  import ru.yandex.qatools.embed.postgresql.distribution.Version;
  
  /**
   * @author Antti Laisi
   */
  class DatabaseRule extends ExternalResource {
  
      private static PostgresProcess process;
  
      final NettyConnectibleBuilder builder;
      Connectible pool;
  
      DatabaseRule() {
          this(createPoolBuilder(1));
      }
  
      DatabaseRule(NettyConnectibleBuilder builder) {
          this.builder = builder;
          if (builder instanceof EmbeddedConnectionPoolBuilder) {
              String port = System.getProperty("asyncpg.test.postgres.port");
              if (port != null && !port.isBlank()) {
                  builder.hostname("localhost");
                  builder.port(Integer.valueOf(port));
              } else {
                  if (process == null) {
                      try {
                          PostgresStarter<PostgresExecutable, PostgresProcess> runtime = PostgresStarter.getDefaultInstance();
                          PostgresConfig config = new PostgresConfig(Version.V11_1, new AbstractPostgresConfig.Net(),
                                  new AbstractPostgresConfig.Storage("async-pg"), new AbstractPostgresConfig.Timeout(),
                                  new AbstractPostgresConfig.Credentials("async-pg", "async-pg"));
                          PostgresExecutable exec = runtime.prepare(config);
                          process = exec.start();
  
                          System.out.printf("Started postgres to %s:%d%n", process.getConfig().net().host(), process.getConfig().net().port());
                      } catch (IOException e) {
                          throw new RuntimeException(e);
                      }
                  }
                  builder.hostname(process.getConfig().net().host());
                  builder.port(process.getConfig().net().port());
              }
          }
      }
  
      @Override
      protected void before() {
          if (pool == null) {
              pool = builder.pool();
          }
      }
  
      @Override
      protected void after() {
          if (pool != null) {
              try {
                  pool.close();
              } catch (Exception e) {
                  throw new RuntimeException(e);
              }
          }
      }
  
      ResultSet query(String sql) {
          return block(pool().completeQuery(sql));
      }
  
      ResultSet query(String sql, List<?> params) {
          return block(pool().completeQuery(sql, params.toArray()));
      }
  
      Collection<ResultSet> script(String sql) {
          return block(pool().completeScript(sql));
      }
  
      private <T> T block(CompletableFuture<T> future) {
          try {
              return future.get(5_0000000, TimeUnit.SECONDS);
          } catch (Throwable th) {
              throw new RuntimeException(th);
          }
      }
  
      Connectible pool() {
          before();
          return pool;
      }
  
      Connectible plain() {
          before();
          return pool;
      }
  
      static class EmbeddedConnectionPoolBuilder extends NettyConnectibleBuilder {
          EmbeddedConnectionPoolBuilder() {
              database("async-pg");
              username("async-pg");
              password("async-pg");
              encoding(System.getProperty("file.encoding", "utf-8"));
          }
      }
  
      static Connectible createPool(int size) {
          return createPoolBuilder(size).pool();
      }
  
      static NettyConnectibleBuilder createPoolBuilder(int size) {
          String db = getenv("PG_DATABASE");
          String user = getenv("PG_USERNAME");
          String pass = getenv("PG_PASSWORD");
  
          if (db == null && user == null && pass == null) {
              return new EmbeddedConnectionPoolBuilder().maxConnections(size);
          } else {
              return new EmbeddedConnectionPoolBuilder()
                      .database(envOrDefault("PG_DATABASE", "postgres"))
                      .username(envOrDefault("PG_USERNAME", "postgres"))
                      .password(envOrDefault("PG_PASSWORD", "postgres"))
                      .ssl(true)
                      .maxConnections(size);
          }
      }
  
  
      static String envOrDefault(String var, String def) {
          String value = getenv(var);
          return value != null ? value : def;
      }
  }
  