mirror of
https://github.com/fluencelabs/scala-multiaddr
synced 2025-03-15 15:50:51 +00:00
encapsulate and decapsulate methods
This commit is contained in:
parent
bae61c487f
commit
22e7a9f3c3
@ -17,57 +17,37 @@
|
|||||||
|
|
||||||
package fluence.multiaddr
|
package fluence.multiaddr
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
case class Multiaddr private(stringAddress: String, protocolsWithParameters: List[(Protocol, Option[String])]) {
|
||||||
|
override def toString: String = stringAddress
|
||||||
|
|
||||||
case class Multiaddr private(protocols: List[(Protocol, Option[String])])
|
/**
|
||||||
|
* Wraps a given Multiaddr, returning the resulting joined Multiaddr.
|
||||||
|
*
|
||||||
|
* @return new joined Multiaddr
|
||||||
|
*/
|
||||||
|
def encapsulate(addr: Multiaddr): Either[Throwable, Multiaddr] = {
|
||||||
|
Multiaddr(stringAddress + addr.toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
|
||||||
|
*
|
||||||
|
* @return decapsulated Multiaddr
|
||||||
|
*/
|
||||||
|
def decapsulate(addr: Multiaddr): Either[Throwable, Multiaddr] = {
|
||||||
|
val strAddr = addr.toString
|
||||||
|
val lastIndex = stringAddress.lastIndexOf(strAddr)
|
||||||
|
if (lastIndex < 0)
|
||||||
|
Right(this.copy())
|
||||||
|
else
|
||||||
|
Multiaddr(stringAddress.slice(0, lastIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
object Multiaddr {
|
object Multiaddr {
|
||||||
|
|
||||||
def apply(addr: String): Either[Throwable, Multiaddr] = {
|
def apply(addr: String): Either[Throwable, Multiaddr] = MultiaddrParser.parse(addr).map {
|
||||||
if (!addr.startsWith("/")) {
|
case (trimmed, protocols) => new Multiaddr(trimmed, protocols)
|
||||||
Left(new IllegalArgumentException("Address must be started with '/'."))
|
|
||||||
} else {
|
|
||||||
val parts = addr.stripPrefix("/").stripSuffix("/").split("/").toList
|
|
||||||
|
|
||||||
if (parts.isEmpty) {
|
|
||||||
Left(new IllegalArgumentException("Address must be non-empty."))
|
|
||||||
} else {
|
|
||||||
|
|
||||||
parse(parts).map(protocols ⇒ new Multiaddr(protocols))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def parse(list: List[String]): Either[Throwable, List[(Protocol, Option[String])]] = {
|
|
||||||
|
|
||||||
@tailrec
|
|
||||||
def parseRec(
|
|
||||||
list: List[String],
|
|
||||||
res: Either[Throwable, List[(Protocol, Option[String])]]
|
|
||||||
): Either[Throwable, List[(Protocol, Option[String])]] = {
|
|
||||||
list match {
|
|
||||||
case Nil ⇒ res
|
|
||||||
case head :: tail ⇒
|
|
||||||
val protocolOp = Protocol.withNameOption(head)
|
|
||||||
|
|
||||||
protocolOp match {
|
|
||||||
case None ⇒
|
|
||||||
Left(new IllegalArgumentException(s"There is no protocol with name '$head'."))
|
|
||||||
case Some(protocol) ⇒
|
|
||||||
protocol.size match {
|
|
||||||
case 0 ⇒ parseRec(tail, res.map(els ⇒ els :+ (protocol, None)))
|
|
||||||
case _ ⇒
|
|
||||||
tail match {
|
|
||||||
case Nil ⇒
|
|
||||||
Left(new IllegalArgumentException(s"There is no parameter for protocol with name '$head'."))
|
|
||||||
case innerHead :: innerTail ⇒
|
|
||||||
parseRec(innerTail, res.map(els ⇒ els :+ (protocol, Some(innerHead))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parseRec(list, Right(List.empty))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
72
core/src/main/scala/fluence/multiaddr/MultiaddrParser.scala
Normal file
72
core/src/main/scala/fluence/multiaddr/MultiaddrParser.scala
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fluence.multiaddr
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
private[multiaddr] object MultiaddrParser {
|
||||||
|
|
||||||
|
def parse(addr: String): Either[Throwable, (String, List[(Protocol, Option[String])])] = {
|
||||||
|
if (!addr.startsWith("/")) {
|
||||||
|
Left(new IllegalArgumentException("Address must be started with '/'."))
|
||||||
|
} else {
|
||||||
|
val parts = addr.stripPrefix("/").stripSuffix("/").split("/").toList
|
||||||
|
|
||||||
|
if (parts.isEmpty) {
|
||||||
|
Left(new IllegalArgumentException("Address must be non-empty."))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
parsePrepared(parts).map(protocols ⇒ (addr.stripSuffix("/"), protocols))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def parsePrepared(list: List[String]): Either[Throwable, List[(Protocol, Option[String])]] = {
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
def parseRec(
|
||||||
|
list: List[String],
|
||||||
|
res: Either[Throwable, List[(Protocol, Option[String])]]
|
||||||
|
): Either[Throwable, List[(Protocol, Option[String])]] = {
|
||||||
|
list match {
|
||||||
|
case Nil ⇒ res
|
||||||
|
case head :: tail ⇒
|
||||||
|
//todo per-protocol validation
|
||||||
|
val protocolOp = Protocol.withNameOption(head)
|
||||||
|
|
||||||
|
protocolOp match {
|
||||||
|
case None ⇒
|
||||||
|
Left(new IllegalArgumentException(s"There is no protocol with name '$head'."))
|
||||||
|
case Some(protocol) ⇒
|
||||||
|
protocol.size match {
|
||||||
|
case 0 ⇒ parseRec(tail, res.map(els ⇒ els :+ (protocol, None)))
|
||||||
|
case _ ⇒
|
||||||
|
tail match {
|
||||||
|
case Nil ⇒
|
||||||
|
Left(new IllegalArgumentException(s"There is no parameter for protocol with name '$head'."))
|
||||||
|
case innerHead :: innerTail ⇒
|
||||||
|
parseRec(innerTail, res.map(els ⇒ els :+ (protocol, Some(innerHead))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseRec(list, Right(List.empty))
|
||||||
|
}
|
||||||
|
}
|
@ -31,13 +31,19 @@ class MultiaddrParseSpec extends WordSpec with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"parse correct multiaddresses right" in {
|
"parse correct multiaddresses right" in {
|
||||||
val m1 = Multiaddr("/ip4/127.0.0.1/tcp/123")
|
val addr1 = "/ip4/127.0.0.1/tcp/123"
|
||||||
m1.isRight shouldBe true
|
val m1Either = Multiaddr(addr1)
|
||||||
m1.right.get.protocols shouldBe List((IP4, Some("127.0.0.1")), (TCP, Some("123")))
|
m1Either.isRight shouldBe true
|
||||||
|
val m1 = m1Either.right.get
|
||||||
|
m1.protocolsWithParameters shouldBe List((IP4, Some("127.0.0.1")), (TCP, Some("123")))
|
||||||
|
m1.stringAddress shouldBe addr1
|
||||||
|
|
||||||
val m2 = Multiaddr("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/udp/5000/https")
|
val addr2 = "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/udp/5000/https"
|
||||||
m2.isRight shouldBe true
|
val m2Either = Multiaddr(addr2)
|
||||||
m2.right.get.protocols shouldBe List((IP6, Some("2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095")), (UDP, Some("5000")), (HTTPS, None))
|
m2Either.isRight shouldBe true
|
||||||
|
val m2 = m2Either.right.get
|
||||||
|
m2.protocolsWithParameters shouldBe List((IP6, Some("2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095")), (UDP, Some("5000")), (HTTPS, None))
|
||||||
|
m2.stringAddress shouldBe addr2
|
||||||
}
|
}
|
||||||
|
|
||||||
"throw exception if there is no protocol" in {
|
"throw exception if there is no protocol" in {
|
||||||
@ -51,5 +57,37 @@ class MultiaddrParseSpec extends WordSpec with Matchers {
|
|||||||
m.isLeft shouldBe true
|
m.isLeft shouldBe true
|
||||||
m.left.get.getMessage shouldBe "There is no parameter for protocol with name 'tcp'."
|
m.left.get.getMessage shouldBe "There is no parameter for protocol with name 'tcp'."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"encapsulate and decapsulate correct multiaddr" in {
|
||||||
|
val addr1 = "/ip4/127.0.0.1/tcp/123"
|
||||||
|
val m1Either = Multiaddr(addr1)
|
||||||
|
m1Either.isRight shouldBe true
|
||||||
|
val m1 = m1Either.right.get
|
||||||
|
|
||||||
|
val addr2 = "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/udp/5000/https"
|
||||||
|
val m2Either = Multiaddr(addr2)
|
||||||
|
m2Either.isRight shouldBe true
|
||||||
|
val m2 = m2Either.right.get
|
||||||
|
|
||||||
|
val m3Either = m1.encapsulate(m2)
|
||||||
|
m3Either.isRight shouldBe true
|
||||||
|
val m3 = m3Either.right.get
|
||||||
|
|
||||||
|
val result = List((IP4, Some("127.0.0.1")), (TCP, Some("123")), (IP6, Some("2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095")), (UDP, Some("5000")), (HTTPS, None))
|
||||||
|
m3.protocolsWithParameters shouldBe result
|
||||||
|
m3.stringAddress shouldBe (addr1 + addr2)
|
||||||
|
|
||||||
|
m3.decapsulate(m2).right.get.stringAddress shouldBe addr1
|
||||||
|
}
|
||||||
|
|
||||||
|
"decapsulate correct multiaddr" in {
|
||||||
|
val addr1 = "/ip4/127.0.0.1/udp/1234/sctp/5678"
|
||||||
|
val m1Either = Multiaddr(addr1)
|
||||||
|
m1Either.isRight shouldBe true
|
||||||
|
val m1 = m1Either.right.get
|
||||||
|
|
||||||
|
val decapsulated = m1.decapsulate(Multiaddr("/sctp/5678").right.get).right.get
|
||||||
|
decapsulated.stringAddress shouldBe "/ip4/127.0.0.1/udp/1234"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user