app/models/BakeId.scala (32 lines of code) (raw):

package models import org.scanamo.DynamoFormat import org.scanamo.{DynamoReadError, TypeCoercionError} import data.PackageList import play.api.libs.json.{JsObject, Json, OWrites, Writes} case class BakeId(recipeId: RecipeId, buildNumber: Int) { override def toString: String = s"${recipeId.value} #$buildNumber" } object BakeId { implicit val writes: Writes[BakeId] = new Writes[BakeId] { def writes(bakeId: BakeId): JsObject = Json.obj( "recipeId" -> bakeId.recipeId.value, "buildNumber" -> bakeId.buildNumber ) } def toFilename(bakeId: BakeId) = s"${bakeId.recipeId.value}--${bakeId.buildNumber}.txt" def toMetadata(bakeId: BakeId) = s"Recipe=${bakeId.recipeId.value},BuildNumber=${bakeId.buildNumber}" // Bake ID is stored as a single string in Dynamo, e.g. "ubuntu-wily-java8 #123" private val DynamoFormatRegex = """(.+) #([0-9]+)""".r def fromString(s: String): Either[DynamoReadError, BakeId] = s match { case DynamoFormatRegex(recipeId, buildNumber) => Right(BakeId(RecipeId(recipeId), buildNumber.toInt)) case _ => Left( TypeCoercionError(new IllegalArgumentException(s"Invalid bake ID: $s")) ) } implicit val dynamoFormat: DynamoFormat[BakeId] = { DynamoFormat.xmap[BakeId, String](fromString, bakeId => bakeId.toString) } }