Home

Awesome

multiformat

Self-describing values for Future-proofing

ci Maven Central Kotlin License

Usage

Kotlin DSL:

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.erwinkok.multiformat:multiformat:$latest")
}

Introduction

This project implements various protocols defined at: https://multiformats.io/

Notably, the following protocols are implemented:

Next to this, it also implements Cid: https://github.com/multiformats/cid

Using the Result Monad

This project is using the result-monad

This means that (almost) all methods of this project return a Result<...>. The caller can check whether an error was generated, or it can use the value. For example:

val connection = createConnection()
    .getOrElse {
        log.error { "Could not create connection: ${errorMessage(it)}" }
        return Err(it)
    }

connection.write(...)


fun createConnection(): Result<Connection> {
  ...
}

The advantage is that it is easier (at least for me) to track the flow of the code and to handle correct/error cases. The disadvantage of exceptions is, is that you do not know which statement in a try-block generated the exception. For example:

var connection: Connection? = null
try {
    ...
    connection = createConnection()
    ...
    methodThrowingException()
    ...
} catch (e: Exception) {
    if (connection != null) {
        connection.close()
    }
}

In the catch-block you do not know where the exception was thrown: before or after creating the connection. This means that you do not know if the connection should be closed, or not. Of course there are many ways to solve this.

With the result-monad you can do something like:

val connection = createConnection()
    .getOrElse {
        log.error { "Could not create connection: ${errorMessage(it)}" }
        return Err(it)
    }
...
methodGeneratingError()
    .onFailure {
        connection.close()
        return
    }
...

Usage

A (very) brief description on how to use multiformats:

...but please also look at the various tests.

multiaddr

val addr1 = Multiaddress.fromString("/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234")
    .getOrElse {
        log.error { "Could not parse Multiaddress: ${errorMessage(it)}" }
        return Err(it)
    }
val ip6Addr = Multiaddress.fromString("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095").getOrThrow()
val tcpAddr = Multiaddress.fromString("/tcp/8000").getOrThrow()
val webAddr = Multiaddress.fromString("/ws").getOrThrow()
val actual1 = Multiaddress.fromString("/").expectNoErrors()
    .encapsulate(ip6Addr).expectNoErrors()
    .encapsulate(tcpAddr).expectNoErrors()
    .encapsulate(webAddr).expectNoErrors()
    .toString()

multibase

val multibase = Multibase.encode("base16", "foobar".toByteArray()).getOrThrow()
val bytes = Multibase.decode("f666f6f626172").getOrThrow()

multicodec

val codec = Multicodec.nameToType("cidv2")

multihash

val multihash = Multihash.fromBase58("QmPfjpVaf593UQJ9a5ECvdh2x17XuJYG5Yanv5UFnH3jPE")

multistream-select

val selected = MultistreamMuxer.selectOneOf(setOf("/a", "/b", "/c"), connection)
    .getOrElse {
        log.error { "Error selecting protocol: ${errorMessage(it)}" }
        return Err(it)
    }

Sub-modules

This project has three submodules:

git submodule add https://github.com/multiformats/multicodec src/main/kotlin/org/erwinkok/multiformat/spec/multicodec
git submodule add https://github.com/multiformats/multibase src/main/kotlin/org/erwinkok/multiformat/spec/multibase
git submodule add https://github.com/multiformats/multihash src/main/kotlin/org/erwinkok/multiformat/spec/multihash

These are the official specifications repositories, which are used here for auto-generation code and or verifying the test results are according to spec.

Contributing

Bug reports and pull requests are welcome on GitHub.

Contact

If you want to contact me, please write an e-mail to: erwin.kok(at)protonmail.com

License

This project is licensed under the BSD-3-Clause license, see LICENSE file for more details.