+48
atproto/syntax/cid.go
+48
atproto/syntax/cid.go
···
1
+
package syntax
2
+
3
+
import (
4
+
"fmt"
5
+
"regexp"
6
+
"strings"
7
+
)
8
+
9
+
// Represents a CIDv1 in string format, as would pass Lexicon syntax validation.
10
+
//
11
+
// You usually want to use the github.com/ipfs/go-cid package and type when working with CIDs ("Links") in atproto. This specific type (syntax.CID) is an informal/incomplete helper specifically for doing fast string verification or pass-through without parsing, re-serialization, or normalization.
12
+
//
13
+
// Always use [ParseCID] instead of wrapping strings directly, especially when working with network input.
14
+
type CID string
15
+
16
+
func ParseCID(raw string) (CID, error) {
17
+
if len(raw) > 256 {
18
+
return "", fmt.Errorf("CID is too long (256 chars max)")
19
+
}
20
+
if len(raw) < 8 {
21
+
return "", fmt.Errorf("CID is too short (8 chars min)")
22
+
}
23
+
var cidRegex = regexp.MustCompile(`^[a-zA-Z0-9+=]{8,256}$`)
24
+
if !cidRegex.MatchString(raw) {
25
+
return "", fmt.Errorf("CID syntax didn't validate via regex")
26
+
}
27
+
if strings.HasPrefix(raw, "Qmb") {
28
+
return "", fmt.Errorf("CIDv0 not allowed in this version of atproto")
29
+
}
30
+
return CID(raw), nil
31
+
}
32
+
33
+
func (c CID) String() string {
34
+
return string(c)
35
+
}
36
+
37
+
func (c CID) MarshalText() ([]byte, error) {
38
+
return []byte(c.String()), nil
39
+
}
40
+
41
+
func (c *CID) UnmarshalText(text []byte) error {
42
+
cid, err := ParseCID(string(text))
43
+
if err != nil {
44
+
return err
45
+
}
46
+
*c = cid
47
+
return nil
48
+
}
+50
atproto/syntax/cid_test.go
+50
atproto/syntax/cid_test.go
···
1
+
package syntax
2
+
3
+
import (
4
+
"bufio"
5
+
"fmt"
6
+
"os"
7
+
"testing"
8
+
9
+
"github.com/stretchr/testify/assert"
10
+
)
11
+
12
+
func TestInteropCIDsValid(t *testing.T) {
13
+
assert := assert.New(t)
14
+
file, err := os.Open("testdata/cid_syntax_valid.txt")
15
+
assert.NoError(err)
16
+
defer file.Close()
17
+
scanner := bufio.NewScanner(file)
18
+
for scanner.Scan() {
19
+
line := scanner.Text()
20
+
if len(line) == 0 || line[0] == '#' {
21
+
continue
22
+
}
23
+
_, err := ParseCID(line)
24
+
if err != nil {
25
+
fmt.Println("GOOD: " + line)
26
+
}
27
+
assert.NoError(err)
28
+
}
29
+
assert.NoError(scanner.Err())
30
+
}
31
+
32
+
func TestInteropCIDsInvalid(t *testing.T) {
33
+
assert := assert.New(t)
34
+
file, err := os.Open("testdata/cid_syntax_invalid.txt")
35
+
assert.NoError(err)
36
+
defer file.Close()
37
+
scanner := bufio.NewScanner(file)
38
+
for scanner.Scan() {
39
+
line := scanner.Text()
40
+
if len(line) == 0 || line[0] == '#' {
41
+
continue
42
+
}
43
+
_, err := ParseCID(line)
44
+
if err == nil {
45
+
fmt.Println("BAD: " + line)
46
+
}
47
+
assert.Error(err)
48
+
}
49
+
assert.NoError(scanner.Err())
50
+
}
+16
atproto/syntax/testdata/cid_syntax_invalid.txt
+16
atproto/syntax/testdata/cid_syntax_invalid.txt
···
1
+
example.com
2
+
https://example.com
3
+
cid:bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
4
+
.
5
+
12345
6
+
7
+
# whitespace
8
+
bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
9
+
bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
10
+
bafybe igdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
11
+
12
+
# old CIDv0 not supported
13
+
QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR
14
+
15
+
# https://github.com/ipfs-shipyard/is-ipfs/blob/master/test/test-cid.spec.ts
16
+
noop
+14
atproto/syntax/testdata/cid_syntax_valid.txt
+14
atproto/syntax/testdata/cid_syntax_valid.txt
···
1
+
2
+
# examples from https://docs.ipfs.tech/concepts/content-addressing
3
+
bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
4
+
5
+
# https://github.com/ipfs-shipyard/is-ipfs/blob/master/test/test-cid.spec.ts
6
+
zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7
7
+
bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va
8
+
9
+
# more contrived examples
10
+
mBcDxtdWx0aWhhc2g+
11
+
z7x3CtScH765HvShXT
12
+
zdj7WhuEjrB52m1BisYCtmjH1hSKa7yZ3jEZ9JcXaFRD51wVz
13
+
7134036155352661643226414134664076
14
+
f017012202c5f688262e0ece8569aa6f94d60aad55ca8d9d83734e4a7430d0cff6588ec2b