package s3.website.model import scala.util.{Failure, Try} import scala.collection.JavaConversions._ import s3.website.Ruby.rubyRuntime case class Config( s3_id: String, s3_secret: String, s3_bucket: String, s3_endpoint: S3Endpoint, max_age: Option[Either[Int, Map[String, Int]]], gzip: Option[Either[Boolean, Seq[String]]], gzip_zopfli: Option[Boolean], ignore_on_server: Option[Either[String, Seq[String]]], exclude_from_upload: Option[Either[String, Seq[String]]], s3_reduced_redundancy: Option[Boolean], cloudfront_distribution_id: Option[String], cloudfront_invalidate_root: Option[Boolean], redirects: Option[Map[String, String]], concurrency_level: Int ) object Config { def loadOptionalBooleanOrStringSeq(key: String)(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[Either[Boolean, Seq[String]]]] = { val yamlValue = for { optionalValue <- loadOptionalValue(key) } yield { Right(optionalValue.map { case value if value.isInstanceOf[Boolean] => Left(value.asInstanceOf[Boolean]) case value if value.isInstanceOf[java.util.List[_]] => Right(value.asInstanceOf[java.util.List[String]].toIndexedSeq) }) } yamlValue getOrElse Left(UserError(s"The key $key has to have a boolean or [string] value")) } def loadOptionalStringOrStringSeq(key: String)(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[Either[String, Seq[String]]]] = { val yamlValue = for { valueOption <- loadOptionalValue(key) } yield { Right(valueOption.map { case value if value.isInstanceOf[String] => Left(value.asInstanceOf[String]) case value if value.isInstanceOf[java.util.List[_]] => Right(value.asInstanceOf[java.util.List[String]].toIndexedSeq) }) } yamlValue getOrElse Left(UserError(s"The key $key has to have a string or [string] value")) } def loadMaxAge(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[Either[Int, Map[String, Int]]]] = { val key = "max_age" val yamlValue = for { maxAgeOption <- loadOptionalValue(key) } yield { Right(maxAgeOption.map { case maxAge if maxAge.isInstanceOf[Int] => Left(maxAge.asInstanceOf[Int]) case maxAge if maxAge.isInstanceOf[java.util.Map[_,_]] => Right(maxAge.asInstanceOf[java.util.Map[String,Int]].toMap) }) } yamlValue getOrElse Left(UserError(s"The key $key has to have an int or (string -> int) value")) } def loadEndpoint(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[S3Endpoint]] = loadOptionalString("s3_endpoint").right flatMap { endpointString => endpointString.map(S3Endpoint.forString) match { case Some(Right(endpoint)) => Right(Some(endpoint)) case Some(Left(endpointError)) => Left(endpointError) case None => Right(None) } } def loadRedirects(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[Map[String, String]]] = { val key = "redirects" val yamlValue = for { redirectsOption <- loadOptionalValue(key) redirects <- Try(redirectsOption.map(_.asInstanceOf[java.util.Map[String,String]].toMap)) } yield Right(redirects) yamlValue getOrElse Left(UserError(s"The key $key has to have an int or (string -> int) value")) } def loadRequiredString(key: String)(implicit unsafeYaml: UnsafeYaml): Either[Error, String] = { val yamlValue = for { valueOption <- loadOptionalValue(key) stringValue <- Try(valueOption.asInstanceOf[Option[String]].get) } yield { Right(stringValue) } yamlValue getOrElse { Left(UserError(s"The key $key has to have a string value")) } } def loadOptionalString(key: String)(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[String]] = { val yamlValueOption = for { valueOption <- loadOptionalValue(key) optionalString <- Try(valueOption.asInstanceOf[Option[String]]) } yield { Right(optionalString) } yamlValueOption getOrElse { Left(UserError(s"The key $key has to have a string value")) } } def loadOptionalBoolean(key: String)(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[Boolean]] = { val yamlValueOption = for { valueOption <- loadOptionalValue(key) optionalBoolean <- Try(valueOption.asInstanceOf[Option[Boolean]]) } yield { Right(optionalBoolean) } yamlValueOption getOrElse { Left(UserError(s"The key $key has to have a boolean value")) } } def loadOptionalInt(key: String)(implicit unsafeYaml: UnsafeYaml): Either[Error, Option[Int]] = { val yamlValueOption = for { valueOption <- loadOptionalValue(key) optionalInt <- Try(valueOption.asInstanceOf[Option[Int]]) } yield { Right(optionalInt) } yamlValueOption getOrElse { Left(UserError(s"The key $key has to have an integer value")) } } def loadOptionalValue(key: String)(implicit unsafeYaml: UnsafeYaml): Try[Option[_]] = Try { unsafeYaml.yamlObject.asInstanceOf[java.util.Map[String, _]].toMap get key } def erbEval(erbString: String): Try[String] = Try { val erbStringWithoutComments = erbString.replaceAll("#.*", "") rubyRuntime.evalScriptlet( s""" require 'erb' ERB.new("$erbStringWithoutComments").result """ ).asJavaString() } case class UnsafeYaml(yamlObject: AnyRef) }