From 4ef5ee5fc78757fb4b58c44e9628889165ecf66a Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Wed, 13 Sep 2023 22:04:29 -0700 Subject: [PATCH] syntax: more ergonomic sub-field accessors --- atproto/syntax/aturi.go | 52 +++++++++++++++++++++++------------- atproto/syntax/aturi_test.go | 13 +++++---- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/atproto/syntax/aturi.go b/atproto/syntax/aturi.go index 4677e7395..cdf340fc4 100644 --- a/atproto/syntax/aturi.go +++ b/atproto/syntax/aturi.go @@ -43,16 +43,23 @@ func ParseATURI(raw string) (ATURI, error) { return ATURI(raw), nil } -func (n ATURI) Authority() (*AtIdentifier, error) { +// Every valid ATURI has a valid AtIdentifier in the authority position. +// +// If this ATURI is malformed, returns empty +func (n ATURI) Authority() AtIdentifier { parts := strings.SplitN(string(n), "/", 4) if len(parts) < 3 { // something has gone wrong (would not validate) - return nil, fmt.Errorf("AT-URI has no authority segment (invalid)") + return AtIdentifier{} + } + atid, err := ParseAtIdentifier(parts[2]) + if err != nil { + return AtIdentifier{} } - return ParseAtIdentifier(parts[2]) + return *atid } -// Returns path segment, without leading slash, as would be used in an atproto repository key +// Returns path segment, without leading slash, as would be used in an atproto repository key. Or empty string if there is no path. func (n ATURI) Path() string { parts := strings.SplitN(string(n), "/", 5) if len(parts) < 3 { @@ -65,38 +72,45 @@ func (n ATURI) Path() string { return parts[2] + "/" + parts[3] } -func (n ATURI) Collection() (NSID, error) { +// Returns a valid NSID if there is one in the appropriate part of the path, otherwise empty. +func (n ATURI) Collection() NSID { parts := strings.SplitN(string(n), "/", 5) if len(parts) < 4 { // something has gone wrong (would not validate) - return NSID(""), fmt.Errorf("AT-URI has no collection segment") + return NSID("") + } + nsid, err := ParseNSID(parts[3]) + if err != nil { + return NSID("") } - // re-parsing is safest - return ParseNSID(parts[3]) + return nsid } -func (n ATURI) RecordKey() (RecordKey, error) { +func (n ATURI) RecordKey() RecordKey { parts := strings.SplitN(string(n), "/", 6) if len(parts) < 5 { // something has gone wrong (would not validate) - return RecordKey(""), fmt.Errorf("AT-URI has no record key segment") + return RecordKey("") } - // re-parsing is safest - return ParseRecordKey(parts[4]) + rkey, err := ParseRecordKey(parts[4]) + if err != nil { + return RecordKey("") + } + return rkey } func (n ATURI) Normalize() ATURI { - auth, err := n.Authority() - if err != nil { - // invalid AT-URI + auth := n.Authority() + if auth.Inner == nil { + // invalid AT-URI; return the current value (!) return n } - coll, err := n.Collection() - if err != nil { + coll := n.Collection() + if coll == NSID("") { return ATURI("at://" + auth.Normalize().String()) } - rkey, err := n.RecordKey() - if err != nil { + rkey := n.RecordKey() + if rkey == RecordKey("") { return ATURI("at://" + auth.Normalize().String() + "/" + coll.String()) } return ATURI("at://" + auth.Normalize().String() + "/" + coll.Normalize().String() + "/" + rkey.String()) diff --git a/atproto/syntax/aturi_test.go b/atproto/syntax/aturi_test.go index d63fdbacc..2851e35ae 100644 --- a/atproto/syntax/aturi_test.go +++ b/atproto/syntax/aturi_test.go @@ -60,12 +60,11 @@ func TestATURIParts(t *testing.T) { for _, parts := range testVec { uri, err := ParseATURI(parts[0]) assert.NoError(err) - auth, _ := uri.Authority() + auth := uri.Authority() assert.Equal(parts[1], auth.String()) - col, _ := uri.Collection() + col := uri.Collection() assert.Equal(parts[2], col.String()) - rkey, _ := uri.RecordKey() - assert.NoError(err) + rkey := uri.RecordKey() assert.Equal(parts[3], rkey.String()) } @@ -89,9 +88,9 @@ func TestATURINormalize(t *testing.T) { func TestATURINoPanic(t *testing.T) { for _, s := range []string{"", ".", "at://", "at:///", "at://e.com", "at://e.com/", "at://e.com//"} { bad := ATURI(s) - _, _ = bad.Authority() - _, _ = bad.Collection() - _, _ = bad.RecordKey() + _ = bad.Authority() + _ = bad.Collection() + _ = bad.RecordKey() _ = bad.Normalize() _ = bad.String() }