Extending an Existing Data Source

Say you wanted to add a resolver for a certain type it is possible to extend an existing implementation of a data source to parse the new type. Below is an example adding a new decoder for URL from a datasource of Map[String, String]:

import cats.Applicative
import cats.effect.IO
import cats.syntax.either._
import cats.syntax.validated._
import java.net.URL
import extruder.core._

trait WithURL {
  implicit def urlMapDecoder[F[_], S <: Settings](implicit F: Applicative[F], error: ExtruderErrors[F]): Decoder[F, S, URL, Map[String, String]] =
    Decoder.make[F, S, URL, Map[String, String]]((path, settings, default, input) =>
      (input.get(settings.pathElementListToString(path)), default) match {
        case (Some(v), _) => error.fromEitherThrowable(Either.catchNonFatal(new URL(v)))
        case (None, Some(url)) => F.pure(url)
        case _ => error.missing(s"Could not find value for ${settings.pathElementListToString(path)}")
      }
    )

  implicit def urlEncoder[F[_], S <: Settings](implicit F: Applicative[F]): Encoder[F, S, URL, Map[String, String]]  =
    Encoder.make[F, S, URL, Map[String, String]]((path, settings, value) =>
      F.pure(Map(settings.pathElementListToString(path) -> value.toString))
    )
}

object WithURL extends WithURL

This is a fairly verbose implementation which repeats some of the abstracted functionality found in ParserDecoderInstances and ShowEncoderInstances, it must also be implemented for each data type.

import cats.syntax.either._
import java.net.URL
import extruder.core._

object WithURL {
  implicit val urlParser: Parser[URL] = Parser.fromEitherException(url =>
    Either.catchNonFatal(new URL(url))
  )

  implicit val urlShow: Show[URL] = Show.make(_.toString)
}

The object WithURL may then be imported wherever extruder is used and being to provide support for the new type.