2

I'm building a librairy to give access to several catalog of data. A catalog can refer to a database and the associated tables defined in configuration files (HOCON). Here is a config file example (book.conf):

author {
  database = "db_books"
  table = "author"
}

book {
  database = "db_books"
  table = "book"
}

I define a case class representing this config file (BookCatalogConfig.scala):

trait CatalogConfig

case class TableConfig(database: String, table: String)

case class BookCatalogConfig(author: TableConfig, book: TableConfig) extends CatalogConfig

Then I define my BookCatalog like that:

case class BookCatalog(environment: String) {
  /* ... */
  val config: BookCatalogConfig = CatalogConfigLoader[BookCatalogConfig](environment).load()
  /* ... */
}

And my CatalogConfigLoader utility:

case class CatalogConfigLoader[A <: CatalogConfig](environment: String) {
  def load(): A = {
    import pureconfig.generic.auto._
    // Note: ConfigHelper.getConfig(environment) return a Typesafe Config object with the content of book.conf)
    ConfigSource.fromConfig(ConfigHelper.getConfig(environment)).load[A] match {
      case Right(catalogConfig: A) => catalogConfig
      case Left(error: ConfigReaderFailures) => throw new Exception(error.toString())
    }
  }
}

But this doesn't compile with an error: Cannot find an implicit instance of pureconfig.ConfigReader[A]. If you are trying to read or write a case class or sealed trait consider using PureConfig's auto derivation by adding import pureconfig.generic.auto._

I've already read a lot on the subject, but no solution has worked

Note: I'm using Pureconfig 0.14 for scala 2.11 (I cannot upgade because 0.14 is the last one for scala 2.11... and I cannot upgrade the scala version too...).

Adagyo
  • 422
  • 1
  • 4
  • 16
  • 2
    Do `CatalogConfigLoader[A <: CatalogConfig : ConfigReader]` - BTW, that should not be case class. – Luis Miguel Mejía Suárez Nov 15 '21 at 16:25
  • Thank you, it works... But could you explain why ? – Adagyo Nov 15 '21 at 16:42
  • Because as the error said, you needed that – Luis Miguel Mejía Suárez Nov 15 '21 at 17:11
  • 1
    `class CatalogConfigLoader[A: ConfigReader]` is syntactic sugar for `class CatalogConfigLoader[A](implicit ev: ConfigReader[A])`. iow, it simply declares that if you want to instantiate a `CatalogConfigLoader` with some type parameter `A`, then you also need to provide a `ConfigReader` instance for `A`. The `load` method can then use that. You can't use auto derivation inside `CatalogConfigLoader`, because `A` is just a type parameter that nothing is known about at this point. So the auto derivation macros don't know what fields that type has etc. – Matthias Berndt Nov 15 '21 at 17:38

1 Answers1

1

The solution is (thanks to Luis Miguel and Matthias), with some rework:

object CatalogConfigLoader {
  def load[A <: CatalogConfig : ConfigReader : ClassTag](environment: Environment): A = {    
  ConfigSource.fromConfig(ConfigHelper.getConfig(environment.filepath)).load[A] match {
    case Right(catalogConfig: A) => catalogConfig
    case Left(error: ConfigReaderFailures) => throw new Exception(error.toString())
  }
}
Adagyo
  • 422
  • 1
  • 4
  • 16