Skip to content

Commit

Permalink
syntax: clearer error for trivial empty string case (#765)
Browse files Browse the repository at this point in the history
Was getting "doesn't match regex" for trivial empty-string situations.
having shorter more descriptive errors helps debugging.
  • Loading branch information
bnewbold authored Sep 26, 2024
2 parents b3609c6 + e022b49 commit f77c6b6
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 33 deletions.
9 changes: 6 additions & 3 deletions atproto/syntax/atidentifier.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"strings"
)

Expand All @@ -10,6 +10,9 @@ type AtIdentifier struct {
}

func ParseAtIdentifier(raw string) (*AtIdentifier, error) {
if raw == "" {
return nil, errors.New("expected AT account identifier, got empty string")
}
if strings.HasPrefix(raw, "did:") {
did, err := ParseDID(raw)
if err != nil {
Expand All @@ -34,7 +37,7 @@ func (n AtIdentifier) AsHandle() (Handle, error) {
if ok {
return handle, nil
}
return "", fmt.Errorf("AT Identifier is not a Handle")
return "", errors.New("AT Identifier is not a Handle")
}

func (n AtIdentifier) IsDID() bool {
Expand All @@ -47,7 +50,7 @@ func (n AtIdentifier) AsDID() (DID, error) {
if ok {
return did, nil
}
return "", fmt.Errorf("AT Identifier is not a DID")
return "", errors.New("AT Identifier is not a DID")
}

func (n AtIdentifier) Normalize() AtIdentifier {
Expand Down
5 changes: 3 additions & 2 deletions atproto/syntax/aturi.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package syntax

import (
"errors"
"fmt"
"regexp"
"strings"
Expand All @@ -17,11 +18,11 @@ type ATURI string

func ParseATURI(raw string) (ATURI, error) {
if len(raw) > 8192 {
return "", fmt.Errorf("ATURI is too long (8192 chars max)")
return "", errors.New("ATURI is too long (8192 chars max)")
}
parts := aturiRegex.FindStringSubmatch(raw)
if parts == nil || len(parts) < 2 || parts[0] == "" {
return "", fmt.Errorf("AT-URI syntax didn't validate via regex")
return "", errors.New("AT-URI syntax didn't validate via regex")
}
// verify authority as either a DID or NSID
_, err := ParseAtIdentifier(parts[1])
Expand Down
13 changes: 8 additions & 5 deletions atproto/syntax/cid.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"regexp"
"strings"
)
Expand All @@ -16,18 +16,21 @@ type CID string
var cidRegex = regexp.MustCompile(`^[a-zA-Z0-9+=]{8,256}$`)

func ParseCID(raw string) (CID, error) {
if raw == "" {
return "", errors.New("expected CID, got empty string")
}
if len(raw) > 256 {
return "", fmt.Errorf("CID is too long (256 chars max)")
return "", errors.New("CID is too long (256 chars max)")
}
if len(raw) < 8 {
return "", fmt.Errorf("CID is too short (8 chars min)")
return "", errors.New("CID is too short (8 chars min)")
}

if !cidRegex.MatchString(raw) {
return "", fmt.Errorf("CID syntax didn't validate via regex")
return "", errors.New("CID syntax didn't validate via regex")
}
if strings.HasPrefix(raw, "Qmb") {
return "", fmt.Errorf("CIDv0 not allowed in this version of atproto")
return "", errors.New("CIDv0 not allowed in this version of atproto")
}
return CID(raw), nil
}
Expand Down
10 changes: 7 additions & 3 deletions atproto/syntax/datetime.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package syntax

import (
"errors"
"fmt"
"regexp"
"strings"
Expand All @@ -24,15 +25,18 @@ type Datetime string
var datetimeRegex = regexp.MustCompile(`^[0-9]{4}-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](.[0-9]{1,20})?(Z|([+-][0-2][0-9]:[0-5][0-9]))$`)

func ParseDatetime(raw string) (Datetime, error) {
if raw == "" {
return "", errors.New("expected datetime, got empty string")
}
if len(raw) > 64 {
return "", fmt.Errorf("Datetime too long (max 64 chars)")
return "", errors.New("Datetime too long (max 64 chars)")
}

if !datetimeRegex.MatchString(raw) {
return "", fmt.Errorf("Datetime syntax didn't validate via regex")
return "", errors.New("Datetime syntax didn't validate via regex")
}
if strings.HasSuffix(raw, "-00:00") {
return "", fmt.Errorf("Datetime can't use '-00:00' for UTC timezone, must use '+00:00', per ISO-8601")
return "", errors.New("Datetime can't use '-00:00' for UTC timezone, must use '+00:00', per ISO-8601")
}
// ensure that the datetime actually parses using golang time lib
_, err := time.Parse(time.RFC3339Nano, raw)
Expand Down
9 changes: 6 additions & 3 deletions atproto/syntax/did.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"regexp"
"strings"
)
Expand All @@ -16,11 +16,14 @@ type DID string
var didRegex = regexp.MustCompile(`^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$`)

func ParseDID(raw string) (DID, error) {
if raw == "" {
return "", errors.New("expected DID, got empty string")
}
if len(raw) > 2*1024 {
return "", fmt.Errorf("DID is too long (2048 chars max)")
return "", errors.New("DID is too long (2048 chars max)")
}
if !didRegex.MatchString(raw) {
return "", fmt.Errorf("DID syntax didn't validate via regex")
return "", errors.New("DID syntax didn't validate via regex")
}
return DID(raw), nil
}
Expand Down
6 changes: 5 additions & 1 deletion atproto/syntax/handle.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package syntax

import (
"errors"
"fmt"
"regexp"
"strings"
Expand All @@ -21,8 +22,11 @@ var (
type Handle string

func ParseHandle(raw string) (Handle, error) {
if raw == "" {
return "", errors.New("expected handle, got empty string")
}
if len(raw) > 253 {
return "", fmt.Errorf("handle is too long (253 chars max)")
return "", errors.New("handle is too long (253 chars max)")
}
if !handleRegex.MatchString(raw) {
return "", fmt.Errorf("handle syntax didn't validate via regex: %s", raw)
Expand Down
9 changes: 6 additions & 3 deletions atproto/syntax/language.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"regexp"
)

Expand All @@ -15,11 +15,14 @@ type Language string
var langRegex = regexp.MustCompile(`^(i|[a-z]{2,3})(-[a-zA-Z0-9]+)*$`)

func ParseLanguage(raw string) (Language, error) {
if raw == "" {
return "", errors.New("expected language code, got empty string")
}
if len(raw) > 128 {
return "", fmt.Errorf("Language is too long (128 chars max)")
return "", errors.New("Language is too long (128 chars max)")
}
if !langRegex.MatchString(raw) {
return "", fmt.Errorf("Language syntax didn't validate via regex")
return "", errors.New("Language syntax didn't validate via regex")
}
return Language(raw), nil
}
Expand Down
9 changes: 6 additions & 3 deletions atproto/syntax/nsid.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"regexp"
"strings"
)
Expand All @@ -16,11 +16,14 @@ var nsidRegex = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.
type NSID string

func ParseNSID(raw string) (NSID, error) {
if raw == "" {
return "", errors.New("expected NSID, got empty string")
}
if len(raw) > 317 {
return "", fmt.Errorf("NSID is too long (317 chars max)")
return "", errors.New("NSID is too long (317 chars max)")
}
if !nsidRegex.MatchString(raw) {
return "", fmt.Errorf("NSID syntax didn't validate via regex")
return "", errors.New("NSID syntax didn't validate via regex")
}
return NSID(raw), nil
}
Expand Down
11 changes: 7 additions & 4 deletions atproto/syntax/recordkey.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"regexp"
)

Expand All @@ -15,14 +15,17 @@ var recordKeyRegex = regexp.MustCompile(`^[a-zA-Z0-9_~.:-]{1,512}$`)
type RecordKey string

func ParseRecordKey(raw string) (RecordKey, error) {
if raw == "" {
return "", errors.New("expected record key, got empty string")
}
if len(raw) > 512 {
return "", fmt.Errorf("recordkey is too long (512 chars max)")
return "", errors.New("recordkey is too long (512 chars max)")
}
if raw == "" || raw == "." || raw == ".." {
return "", fmt.Errorf("recordkey can not be empty, '.', or '..'")
return "", errors.New("recordkey can not be empty, '.', or '..'")
}
if !recordKeyRegex.MatchString(raw) {
return "", fmt.Errorf("recordkey syntax didn't validate via regex")
return "", errors.New("recordkey syntax didn't validate via regex")
}
return RecordKey(raw), nil
}
Expand Down
9 changes: 6 additions & 3 deletions atproto/syntax/tid.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package syntax

import (
"encoding/base32"
"fmt"
"errors"
"regexp"
"strings"
"sync"
Expand All @@ -27,11 +27,14 @@ type TID string
var tidRegex = regexp.MustCompile(`^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$`)

func ParseTID(raw string) (TID, error) {
if raw == "" {
return "", errors.New("expected TID, got empty string")
}
if len(raw) != 13 {
return "", fmt.Errorf("TID is wrong length (expected 13 chars)")
return "", errors.New("TID is wrong length (expected 13 chars)")
}
if !tidRegex.MatchString(raw) {
return "", fmt.Errorf("TID syntax didn't validate via regex")
return "", errors.New("TID syntax didn't validate via regex")
}
return TID(raw), nil
}
Expand Down
9 changes: 6 additions & 3 deletions atproto/syntax/uri.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package syntax

import (
"fmt"
"errors"
"regexp"
)

Expand All @@ -13,12 +13,15 @@ import (
type URI string

func ParseURI(raw string) (URI, error) {
if raw == "" {
return "", errors.New("expected URI, got empty string")
}
if len(raw) > 8192 {
return "", fmt.Errorf("URI is too long (8192 chars max)")
return "", errors.New("URI is too long (8192 chars max)")
}
var uriRegex = regexp.MustCompile(`^[a-z][a-z.-]{0,80}:[[:graph:]]+$`)
if !uriRegex.MatchString(raw) {
return "", fmt.Errorf("URI syntax didn't validate via regex")
return "", errors.New("URI syntax didn't validate via regex")
}
return URI(raw), nil
}
Expand Down

0 comments on commit f77c6b6

Please sign in to comment.