Skip to content

Instantly share code, notes, and snippets.

@cretz
Last active November 29, 2016 20:09
Show Gist options
  • Save cretz/15f53b39ba70ee797c48eb457ff234cb to your computer and use it in GitHub Desktop.
Save cretz/15f53b39ba70ee797c48eb457ff234cb to your computer and use it in GitHub Desktop.
Parse Hstore in Scala
import scala.annotation.tailrec
object PgUtil {
def parseHstore(str: String): Map[String, String] = {
// Find next key or value piece; Expects str to be trimmed
def nextPiece(str: String): (Option[String], String) = if (str.isEmpty) None -> str else {
if (str.charAt(0) != '"') {
// Run until the next non key char or the end
str.indexWhere(c => c.isWhitespace || c == '=' || c == ',') match {
case -1 => Some(str) -> ""
case end => Some(str.take(end)) -> str.drop(end)
}
} else {
// Run until the next unescaped quote
@tailrec
def nextUnescapedQuote(str: String, from: Int): Int = {
str.indexOf('"', from) match {
case -1 => -1
case index =>
// Count the number of backslashes to see if it's really unescaped
val firstNonBackslashIndex = Math.max(from, str.lastIndexWhere(_ != '\\', index - 1))
val numberOfBackslashes = index - (firstNonBackslashIndex + 1)
if (numberOfBackslashes % 2 == 0) index else nextUnescapedQuote(str, index + 1)
}
}
nextUnescapedQuote(str, 1) match {
case -1 => sys.error("Unable to find end quote")
case end => Some(str.substring(1, end).replace("\\\"", "\"").replace("\\\\", "\\")) -> str.drop(end + 1)
}
}
}
// Add entries recursively; Expects str to be trimmed
@tailrec
def addEntries(str: String, acc: Map[String, String]): Map[String, String] = {
nextPiece(str) match {
case (None, _) => acc
case (Some(key), leftover) =>
val trimmed = leftover.trim
require(trimmed.take(2) == "=>", "Expected arrow")
nextPiece(trimmed.drop(2).trim) match {
case (None, _) => sys.error("No value for key")
case (Some(value), leftover) =>
val trimmed = leftover.trim
addEntries(
str = if (trimmed.headOption.contains(',')) trimmed.drop(1).trim else trimmed,
acc = acc + (key -> value)
)
}
}
}
addEntries(str.trim, Map.empty)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment