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..0dbcede 100644 --- a/multiaddr.go +++ b/multiaddr.go @@ -113,3 +113,19 @@ 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) { + for _, sub := range Split(m) { + p := sub.Protocols()[0] + if p.Code == code { + if p.Size == 0 { + return "", nil + } + return strings.Split(sub.String(), "/")[2], nil + } + } + + return "", ErrProtocolNotFound +} diff --git a/multiaddr_test.go b/multiaddr_test.go index 3a60751..7c75274 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -300,3 +300,45 @@ 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) + } + + a = newMultiaddr(t, "/ip4/0.0.0.0") // only one addr + assertValueForProto(t, a, P_IP4, "0.0.0.0") + + a = newMultiaddr(t, "/ip4/0.0.0.0/ip4/0.0.0.0/ip4/0.0.0.0") // same sub-addr + assertValueForProto(t, a, P_IP4, "0.0.0.0") + + a = newMultiaddr(t, "/ip4/0.0.0.0/udp/12345/utp") // ending in a no-value one. + assertValueForProto(t, a, P_IP4, "0.0.0.0") + assertValueForProto(t, a, P_UDP, "12345") + assertValueForProto(t, a, P_UTP, "") +} diff --git a/protocols.go b/protocols.go index c233cef..8364d4c 100644 --- a/protocols.go +++ b/protocols.go @@ -55,6 +55,20 @@ var Protocols = []Protocol{ Protocol{P_IPFS, LengthPrefixedVarSize, "ipfs", CodeToVarint(P_IPFS)}, } +func AddProtocol(p Protocol) error { + for _, pt := range Protocols { + if pt.Code == p.Code { + return fmt.Errorf("protocol code %d already taken by %q", p.Code, pt.Name) + } + if pt.Name == p.Name { + return fmt.Errorf("protocol by the name %q already exists", p.Name) + } + } + + Protocols = append(Protocols, p) + return nil +} + // ProtocolWithName returns the Protocol description with given string name. func ProtocolWithName(s string) Protocol { for _, p := range Protocols {