diff --git a/atproto/syntax/path.go b/atproto/syntax/path.go new file mode 100644 index 00000000..310bee17 --- /dev/null +++ b/atproto/syntax/path.go @@ -0,0 +1,26 @@ +package syntax + +import ( + "errors" + "fmt" + "strings" +) + +// Parses an atproto repo path string in to "collection" (NSID) and record key parts. +// +// Does not return partial success: either both collection and record key are complete (and error is nil), or both are empty string (and error is not nil) +func ParseRepoPath(raw string) (NSID, RecordKey, error) { + parts := strings.SplitN(raw, "/", 3) + if len(parts) != 2 { + return "", "", errors.New("expected path to have two parts, separated by single slash") + } + nsid, err := ParseNSID(parts[0]) + if err != nil { + return "", "", fmt.Errorf("collection part of path not a valid NSID: %w", err) + } + rkey, err := ParseRecordKey(parts[1]) + if err != nil { + return "", "", fmt.Errorf("record key part of path not valid: %w", err) + } + return nsid, rkey, nil +} diff --git a/atproto/syntax/path_test.go b/atproto/syntax/path_test.go new file mode 100644 index 00000000..e55f94fa --- /dev/null +++ b/atproto/syntax/path_test.go @@ -0,0 +1,41 @@ +package syntax + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRepoPath(t *testing.T) { + assert := assert.New(t) + + testValid := [][]string{ + {"app.bsky.feed.post/asdf", "app.bsky.feed.post", "asdf"}, + } + + testErr := []string{ + "", + "/", + "/app.bsky.feed.post/asdf", + "/asdf", + "./app.bsky.feed.post", + "blob/asdf", + "app.bsky.feed.post/", + "app.bsky.feed.post/.", + "app.bsky.feed.post/!", + } + + for _, parts := range testValid { + nsid, rkey, err := ParseRepoPath(parts[0]) + assert.NoError(err) + assert.Equal(parts[1], nsid.String()) + assert.Equal(parts[2], rkey.String()) + } + + for _, raw := range testErr { + nsid, rkey, err := ParseRepoPath(raw) + assert.Error(err) + assert.Equal("", nsid.String()) + assert.Equal("", rkey.String()) + } +}