Skip to content

Megular Expressions #263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
7 changes: 7 additions & 0 deletions component.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ func (c *Component) Protocol() Protocol {
return *c.protocol
}

func (c *Component) Code() int {
if c == nil {
return 0
}
return c.Protocol().Code
}

func (c *Component) RawValue() []byte {
if c == nil {
return nil
Expand Down
54 changes: 54 additions & 0 deletions meg_capturers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package multiaddr

import (
"encoding/binary"
"fmt"
"net/netip"

"github.com/multiformats/go-multiaddr/x/meg"
)

func CaptureAddrPort(network *string, ipPort *netip.AddrPort) (capturePattern meg.Pattern) {
var ipOnly netip.Addr
capturePort := func(s meg.Matchable) error {
switch s.Code() {
case P_UDP:
*network = "udp"
case P_TCP:
*network = "tcp"
default:
return fmt.Errorf("invalid network: %s", s.Value())
}

port := binary.BigEndian.Uint16(s.RawValue())
*ipPort = netip.AddrPortFrom(ipOnly, port)
return nil
}

pattern := meg.Cat(
meg.Or(
meg.CaptureWithF(P_IP4, func(s meg.Matchable) error {
var ok bool
ipOnly, ok = netip.AddrFromSlice(s.RawValue())
if !ok {
return fmt.Errorf("invalid ip4 address: %s", s.Value())
}
return nil
}),
meg.CaptureWithF(P_IP6, func(s meg.Matchable) error {
var ok bool
ipOnly, ok = netip.AddrFromSlice(s.RawValue())
if !ok {
return fmt.Errorf("invalid ip6 address: %s", s.Value())
}
return nil
}),
),
meg.Or(
meg.CaptureWithF(P_UDP, capturePort),
meg.CaptureWithF(P_TCP, capturePort),
),
)

return pattern
}
72 changes: 72 additions & 0 deletions meg_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package multiaddr

import (
"net/netip"
"testing"

"github.com/multiformats/go-multiaddr/x/meg"
)

func TestMatchAndCaptureMultiaddr(t *testing.T) {
m := StringCast("/ip4/1.2.3.4/udp/8231/quic-v1/webtransport/certhash/b2uaraocy6yrdblb4sfptaddgimjmmpy/certhash/zQmbWTwYGcmdyK9CYfNBcfs9nhZs17a6FQ4Y8oea278xx41")

var udpPort string
var certhashes []string
found, _ := m.Match(
meg.Or(
meg.Val(P_IP4),
meg.Val(P_IP6),
),
meg.CaptureStringVal(P_UDP, &udpPort),
meg.Val(P_QUIC_V1),
meg.Val(P_WEBTRANSPORT),
meg.CaptureZeroOrMoreStringVals(P_CERTHASH, &certhashes),
)
if !found {
t.Fatal("failed to match")
}
if udpPort != "8231" {
t.Fatal("unexpected value")
}

if len(certhashes) != 2 {
t.Fatal("Didn't capture all certhashes")
}

{
m, c := SplitLast(m)
if c.Value() != certhashes[1] {
t.Fatal("unexpected value. Expected", c.RawValue(), "but got", []byte(certhashes[1]))
}
_, c = SplitLast(m)
if c.Value() != certhashes[0] {
t.Fatal("unexpected value. Expected", c.RawValue(), "but got", []byte(certhashes[0]))
}
}
}

func TestCaptureAddrPort(t *testing.T) {
m := StringCast("/ip4/1.2.3.4/udp/8231/quic-v1/webtransport")
var addrPort netip.AddrPort
var network string

found, err := m.Match(
CaptureAddrPort(&network, &addrPort),
meg.ZeroOrMore(meg.Any),
)
if err != nil {
t.Fatal("error", err)
}
if !found {
t.Fatal("failed to match")
}
if !addrPort.IsValid() {
t.Fatal("failed to capture addrPort")
}
if network != "udp" {
t.Fatal("unexpected network", network)
}
if addrPort.String() != "1.2.3.4:8231" {
t.Fatal("unexpected ipPort", addrPort)
}
}
16 changes: 16 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package multiaddr

import (
"fmt"

"github.com/multiformats/go-multiaddr/x/meg"
)

// Split returns the sub-address portions of a multiaddr.
Expand Down Expand Up @@ -120,3 +122,17 @@ func ForEach(m Multiaddr, cb func(c Component) bool) {
}
}
}

type componentList []Component

func (m componentList) Get(i int) meg.Matchable {
return &m[i]
}

func (m componentList) Len() int {
return len(m)
}
func (m Multiaddr) Match(p ...meg.Pattern) (bool, error) {
matcher := meg.PatternToMatcher(p...)
return meg.Match(matcher, componentList(m))
}
Loading