Merge pull request #208 from jroper/file-info-hash-byte-array

Use byte arrays instead of lists of bytes in FileInfo
This commit is contained in:
eugene yokota 2019-08-13 08:03:28 -04:00 committed by GitHub
commit ca776cf3f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 15 deletions

View File

@ -152,6 +152,19 @@ lazy val utilCache = (project in file("util-cache"))
Seq(sjsonnewScalaJson.value, sjsonnewMurmurhash.value, scalaReflect.value),
libraryDependencies ++= Seq(scalaTest),
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// These are private case classes that have changed
exclude[IncompatibleMethTypeProblem]("sbt.util.FileHashModified.apply"),
exclude[IncompatibleResultTypeProblem]("sbt.util.FileHashModified.copy$default$2"),
exclude[IncompatibleMethTypeProblem]("sbt.util.FileHashModified.copy"),
exclude[IncompatibleMethTypeProblem]("sbt.util.FileHashModified.this"),
exclude[IncompatibleMethTypeProblem]("sbt.util.FileHash.apply"),
exclude[IncompatibleResultTypeProblem]("sbt.util.FileHash.copy$default$2"),
exclude[IncompatibleMethTypeProblem]("sbt.util.FileHash.copy"),
exclude[IncompatibleMethTypeProblem]("sbt.util.FileHash.this"),
// Added a method to a sealed trait, technically not a problem for Scala
exclude[ReversedMissingMethodProblem]("sbt.util.HashFileInfo.hashArray"),
)
)
.configure(addSbtIO)

View File

@ -4,13 +4,18 @@
package sbt.util
import java.io.File
import scala.util.control.NonFatal
import sbt.io.{ Hash, IO }
import sjsonnew.{ Builder, JsonFormat, Unbuilder, deserializationError }
import CacheImplicits._
import sjsonnew.{ Builder, DeserializationException, JsonFormat, Unbuilder, deserializationError }
import CacheImplicits.{ arrayFormat => _, _ }
sealed trait FileInfo { def file: File }
sealed trait HashFileInfo extends FileInfo { def hash: List[Byte] }
sealed trait HashFileInfo extends FileInfo {
@deprecated("Use hashArray instead", "1.3.0")
def hash: List[Byte] = hashArray.toList
private[util] def hashArray: Array[Byte]
}
sealed trait ModifiedFileInfo extends FileInfo { def lastModified: Long }
sealed trait PlainFileInfo extends FileInfo { def exists: Boolean }
@ -31,8 +36,8 @@ object HashModifiedFileInfo {
private final case class PlainFile(file: File, exists: Boolean) extends PlainFileInfo
private final case class FileModified(file: File, lastModified: Long) extends ModifiedFileInfo
private final case class FileHash(file: File, hash: List[Byte]) extends HashFileInfo
private final case class FileHashModified(file: File, hash: List[Byte], lastModified: Long)
private final case class FileHash(file: File, hashArray: Array[Byte]) extends HashFileInfo
private final case class FileHashModified(file: File, hashArray: Array[Byte], lastModified: Long)
extends HashModifiedFileInfo
final case class FilesInfo[F <: FileInfo] private (files: Set[F])
@ -50,6 +55,29 @@ object FilesInfo {
object FileInfo {
/**
* Stores byte arrays as hex encoded strings, but falls back to reading an array of integers,
* which is how it used to be stored, if that fails.
*/
implicit val byteArrayFormat: JsonFormat[Array[Byte]] = new JsonFormat[Array[Byte]] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Array[Byte] = {
jsOpt match {
case Some(js) =>
try {
Hash.fromHex(unbuilder.readString(js))
} catch {
case _: DeserializationException =>
CacheImplicits.arrayFormat[Byte].read(jsOpt, unbuilder)
}
case None => Array.empty
}
}
override def write[J](obj: Array[Byte], builder: Builder[J]): Unit = {
builder.writeString(Hash.toHex(obj))
}
}
sealed trait Style {
type F <: FileInfo
@ -71,7 +99,7 @@ object FileInfo {
def write[J](obj: HashModifiedFileInfo, builder: Builder[J]) = {
builder.beginObject()
builder.addField("file", obj.file)
builder.addField("hash", obj.hash)
builder.addField("hash", obj.hashArray)
builder.addField("lastModified", obj.lastModified)
builder.endObject()
}
@ -80,7 +108,7 @@ object FileInfo {
case Some(js) =>
unbuilder.beginObject(js)
val file = unbuilder.readField[File]("file")
val hash = unbuilder.readField[List[Byte]]("hash")
val hash = unbuilder.readField[Array[Byte]]("hash")
val lastModified = unbuilder.readField[Long]("lastModified")
unbuilder.endObject()
FileHashModified(file, hash, lastModified)
@ -89,8 +117,8 @@ object FileInfo {
}
implicit def apply(file: File): HashModifiedFileInfo =
FileHashModified(file.getAbsoluteFile, Hash(file).toList, IO.getModifiedTimeOrZero(file))
def apply(file: File, hash: List[Byte], lastModified: Long): HashModifiedFileInfo =
FileHashModified(file.getAbsoluteFile, Hash(file), IO.getModifiedTimeOrZero(file))
def apply(file: File, hash: Array[Byte], lastModified: Long): HashModifiedFileInfo =
FileHashModified(file.getAbsoluteFile, hash, lastModified)
}
@ -101,7 +129,7 @@ object FileInfo {
def write[J](obj: HashFileInfo, builder: Builder[J]) = {
builder.beginObject()
builder.addField("file", obj.file)
builder.addField("hash", obj.hash)
builder.addField("hash", obj.hashArray)
builder.endObject()
}
@ -109,7 +137,7 @@ object FileInfo {
case Some(js) =>
unbuilder.beginObject(js)
val file = unbuilder.readField[File]("file")
val hash = unbuilder.readField[List[Byte]]("hash")
val hash = unbuilder.readField[Array[Byte]]("hash")
unbuilder.endObject()
FileHash(file, hash)
case None => deserializationError("Expected JsObject but found None")
@ -117,12 +145,12 @@ object FileInfo {
}
implicit def apply(file: File): HashFileInfo = FileHash(file.getAbsoluteFile, computeHash(file))
def apply(file: File, bytes: List[Byte]): HashFileInfo =
def apply(file: File, bytes: Array[Byte]): HashFileInfo =
FileHash(file.getAbsoluteFile, bytes)
private def computeHash(file: File): List[Byte] =
try Hash(file).toList
catch { case NonFatal(_) => Nil }
private def computeHash(file: File): Array[Byte] =
try Hash(file)
catch { case NonFatal(_) => Array.empty }
}
object lastModified extends Style {