-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #544 from cranst0n/ltree
Add codec for LTREE type.
- Loading branch information
Showing
9 changed files
with
156 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,5 +12,6 @@ trait AllCodecs | |
with EnumCodec | ||
with UuidCodec | ||
with BinaryCodecs | ||
with LTreeCodec | ||
|
||
object all extends AllCodecs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright (c) 2018-2021 by Rob Norris | ||
// This software is licensed under the MIT License (MIT). | ||
// For more information see LICENSE or https://opensource.org/licenses/MIT | ||
|
||
package skunk | ||
package codec | ||
|
||
import skunk.data.Type | ||
import skunk.data.LTree | ||
|
||
trait LTreeCodec { | ||
|
||
val ltree: Codec[LTree] = | ||
Codec.simple[LTree]( | ||
ltree => ltree.toString(), | ||
s => LTree.fromString(s), | ||
Type("ltree") | ||
) | ||
|
||
} | ||
|
||
object ltree extends LTreeCodec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright (c) 2018-2021 by Rob Norris | ||
// This software is licensed under the MIT License (MIT). | ||
// For more information see LICENSE or https://opensource.org/licenses/MIT | ||
|
||
package skunk.data | ||
|
||
import cats.Eq | ||
|
||
sealed abstract case class LTree (labels: List[String]) { | ||
|
||
def isAncestorOf(other: LTree): Boolean = | ||
other.labels.startsWith(labels) | ||
|
||
def isDescendantOf(other: LTree): Boolean = other.isAncestorOf(this) | ||
|
||
override def toString: String = labels.mkString(LTree.Separator.toString()) | ||
} | ||
|
||
object LTree { | ||
val Empty = new LTree(Nil) {} | ||
|
||
def fromLabels(s: String*): Either[String, LTree] = | ||
fromString(s.toList.mkString(Separator.toString())) | ||
|
||
def fromString(s: String): Either[String, LTree] = { | ||
|
||
if (s.isEmpty()) { | ||
Right(new LTree(Nil){}) | ||
} else { | ||
// We have a failure sentinal and a helper to set it. | ||
var failure: String = null | ||
def fail(msg: String): Unit = | ||
failure = s"ltree parse error: $msg" | ||
|
||
val labels = s.split(Separator).toList | ||
|
||
if(labels.length > MaxTreeLength) | ||
fail(s"ltree size (${labels.size}) must be <= $MaxTreeLength") | ||
|
||
labels.foreach(l => l match { | ||
case ValidLabelRegex() => () | ||
case _ => fail(s"invalid ltree label '$l'. Only alphanumeric characters and '_' are allowed.") | ||
}) | ||
|
||
if(failure != null) | ||
Left(failure) | ||
else | ||
Right(new LTree(labels){}) | ||
} | ||
} | ||
|
||
final val MaxLabelLength = 255 | ||
final val MaxTreeLength = 65535 | ||
|
||
private final val Separator = '.' | ||
private final val ValidLabelRegex = s"""^[\\p{L}0-9_]{1,$MaxLabelLength}$$""".r | ||
|
||
implicit val ltreeEq: Eq[LTree] = Eq.fromUniversalEquals[LTree] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
modules/tests/shared/src/test/scala/codec/LTreeCodecTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) 2018-2021 by Rob Norris | ||
// This software is licensed under the MIT License (MIT). | ||
// For more information see LICENSE or https://opensource.org/licenses/MIT | ||
|
||
package tests | ||
package codec | ||
import skunk.codec.all._ | ||
import skunk.data.LTree | ||
import skunk.util.Typer | ||
|
||
class LTreeCodecTest extends CodecTest(strategy = Typer.Strategy.SearchPath) { | ||
|
||
roundtripTest(ltree)(LTree.Empty) | ||
roundtripTest(ltree)(LTree.fromLabels("abc", "def").toOption.get) | ||
roundtripTest(ltree)(LTree.fromLabels("abcdefghijklmnopqrstuvwxyz0123456789".toList.map(_.toString()) :_*).toOption.get) | ||
roundtripTest(ltree)(LTree.fromString("foo.βar.baz").toOption.get) | ||
|
||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright (c) 2018-2021 by Rob Norris | ||
// This software is licensed under the MIT License (MIT). | ||
// For more information see LICENSE or https://opensource.org/licenses/MIT | ||
|
||
package tests | ||
package data | ||
|
||
import skunk.data.LTree | ||
|
||
class LTreeTest extends ffstest.FTest { | ||
|
||
lazy val foo = LTree.fromLabels("foo").toOption.get | ||
lazy val foobar = LTree.fromLabels("foo", "bar").toOption.get | ||
|
||
test("LTree parsing") { | ||
assertEquals(LTree.fromString("").getOrElse(fail("Failed to parse empty LTree")), LTree.Empty) | ||
|
||
assert(LTree.fromString("abc.d!f").isLeft, "regex failed") | ||
assert(LTree.fromString("abc.d_f").isRight, "regex failed") | ||
assert(LTree.fromString("abc1.d_f2").isRight, "regex failed") | ||
assert(LTree.fromString("foo.βar.baΣΩ").isRight, "regex failed") | ||
assert(LTree.fromString("foo.βar.❤").isLeft, "regex failed") | ||
|
||
assert(LTree.fromString(List.fill(LTree.MaxTreeLength)("a").mkString(".")).isRight, "max tree len failed") | ||
assert(LTree.fromString(List.fill(LTree.MaxTreeLength + 1)("a").mkString(".")).isLeft, "max tree len failed") | ||
|
||
assert(LTree.fromString(List.fill(3)("a" * LTree.MaxLabelLength).mkString(".")).isRight, "max label len failed") | ||
assert(LTree.fromString(List.fill(3)("a" * LTree.MaxLabelLength + 1).mkString(".")).isLeft, "max label len failed") | ||
} | ||
|
||
test("LTree.isAncestorOf") { | ||
assert(LTree.Empty.isAncestorOf(foo)) | ||
assert(foo.isAncestorOf(foo)) | ||
assert(foo.isAncestorOf(foobar)) | ||
|
||
assert(!foo.isAncestorOf(LTree.Empty)) | ||
assert(!foobar.isAncestorOf(foo)) | ||
} | ||
|
||
test("LTree.isDescendantOf") { | ||
assert(foo.isDescendantOf(LTree.Empty)) | ||
assert(foobar.isDescendantOf(foo)) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CREATE EXTENSION ltree ; |