diff --git a/courscala/src/main/scala/org/coursera/common/stringkey/StringKeyFormat.scala b/courscala/src/main/scala/org/coursera/common/stringkey/StringKeyFormat.scala index 7f59359..eb67805 100644 --- a/courscala/src/main/scala/org/coursera/common/stringkey/StringKeyFormat.scala +++ b/courscala/src/main/scala/org/coursera/common/stringkey/StringKeyFormat.scala @@ -28,6 +28,7 @@ import org.joda.time.Instant import scala.annotation.implicitNotFound import scala.reflect.ClassTag import scala.util.Try +import scala.collection.immutable /** * Conversion from [[T]] to [[StringKey]] for use in datastores, URLs, etc. The format must @@ -235,6 +236,24 @@ sealed trait CommonStringKeyFormats extends DefaultTupleFormats { implicit val instantFormat: StringKeyFormat[Instant] = StringKeyFormat.delegateFormat[Instant, Long](millis => Some(new Instant(millis)), _.getMillis) + /** + * Defines a StringKeyFormat for an immutable Sequence. WARNING: SLIGHTLY INCORRECT. + * + * In particular, this seqFormat does not correctly handle the case when an element can be + * serialized to the empty string. + */ + implicit def seqFormat[T](implicit format: StringKeyFormat[T]): + StringKeyFormat[immutable.Seq[T]] = SeqFormat[T](",") + + /** + * Defines a StringKeyFormat for an immutable Set. WARNING: SLIGHTLY INCORRECT. + * + * In particular, this setFormat does not correctly handle the case when an element can be + * serialized to the empty string. + */ + implicit def setFormat[T](implicit format: StringKeyFormat[T]): + StringKeyFormat[Set[T]] = SetFormat[T](",") + } private case class PrimitiveFormat[T](from: String => T, to: T => String = (t: T) => t.toString) @@ -273,3 +292,85 @@ private case object UuidFormat extends StringKeyFormat[UUID] { } } + + +/** + * Modified from [[TupleFormats.Tuple2Format]]. + */ +private case class SeqFormat[T](separator: String)(implicit format: StringKeyFormat[T]) + extends StringKeyFormat[immutable.Seq[T]] { + + private[this] val splitRegex = s"(? None + case item => format.reads(StringKey(unescapeSeparator(item))) + }.toList + + if (items.forall(_.nonEmpty)) { + Some(items.flatten) + } else { + None + } + } + } + + override def writes(seq: immutable.Seq[T]): StringKey = { + val string = seq.map { item => + escapeSeparator(format.writes(item).key) + }.mkString(",") + + StringKey(string) + } +} + +/** + * Represents sets in a canonical manner by ascii-sorting the string representation of the elements. + * + * Similar to [[SeqFormat]] + */ +private case class SetFormat[T](separator: String)(implicit format: StringKeyFormat[T]) + extends StringKeyFormat[Set[T]] { + private[this] val splitRegex = s"(? None + case item: String => format.reads(StringKey(unescapeSeparator(item))) + }.toList + + if (items.forall(_.nonEmpty)) { + Some(items.flatten.toSet) + } else { + None + } + } + } + + override def writes(set: Set[T]): StringKey = { + val string = set.map { item => + escapeSeparator(format.writes(item).key) + }.toList.sorted.mkString(",") + + StringKey(string) + } +} diff --git a/version.sbt b/version.sbt index 57fa3d9..ad16e05 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.0.11" +version in ThisBuild := "0.0.12"