diff --git a/interface.go b/interface.go index 512cb62..18de2a2 100644 --- a/interface.go +++ b/interface.go @@ -39,4 +39,7 @@ type Multiaddr interface { // /ip4/1.2.3.4/tcp/80 decapsulate /ip4/1.2.3.4 = /tcp/80 // Decapsulate(Multiaddr) Multiaddr + + // ValueForProtocol returns the value (if any) following the specified protocol + ValueForProtocol(code int) (string, error) } diff --git a/multiaddr.go b/multiaddr.go index 1cb7ad4..2343538 100644 --- a/multiaddr.go +++ b/multiaddr.go @@ -113,3 +113,32 @@ func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr { } return ma } + +var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr") + +func (m *multiaddr) ValueForProtocol(code int) (string, error) { + protos := m.Protocols() + found := -1 + index := 2 + + for i, p := range protos { + if code == p.Code { + if p.Size == 0 { + return "", nil + } + found = i + break + } else { + index += 2 + if p.Size == 0 { + index-- + } + } + } + + if found == -1 { + return "", ErrProtocolNotFound + } + + return strings.Split(m.String(), "/")[index], nil +} diff --git a/multiaddr_test.go b/multiaddr_test.go index 3a60751..e1994ff 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -300,3 +300,34 @@ func TestEncapsulate(t *testing.T) { t.Error("decapsulate /ip4 failed.", "/", s) } } + +func assertValueForProto(t *testing.T, a Multiaddr, p int, exp string) { + t.Logf("checking for %s in %s", ProtocolWithCode(p).Name, a) + fv, err := a.ValueForProtocol(p) + if err != nil { + t.Fatal(err) + } + + if fv != exp { + t.Fatalf("expected %q for %d in %d, but got %q instead", exp, p, a, fv) + } +} + +func TestGetValue(t *testing.T) { + a := newMultiaddr(t, "/ip4/127.0.0.1/utp/tcp/5555/udp/1234/utp/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP") + assertValueForProto(t, a, P_IP4, "127.0.0.1") + assertValueForProto(t, a, P_UTP, "") + assertValueForProto(t, a, P_TCP, "5555") + assertValueForProto(t, a, P_UDP, "1234") + assertValueForProto(t, a, P_IPFS, "QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP") + + _, err := a.ValueForProtocol(P_IP6) + switch err { + case ErrProtocolNotFound: + break + case nil: + t.Fatal("expected value lookup to fail") + default: + t.Fatalf("expected ErrProtocolNotFound but got: %s", err) + } +}