Total Lines | 60 |
Duplicated Lines | 0 % |
Changes | 0 |
1 | package validate |
||
2 | |||
3 | import "errors" |
||
4 | |||
5 | var ( |
||
6 | ErrTooShort = errors.New("too short") |
||
7 | ErrTooLong = errors.New("too long") |
||
8 | ErrTooLarge = errors.New("too large") |
||
9 | ErrInvalidCharacters = errors.New("invalid characters") |
||
10 | ) |
||
11 | |||
12 | var ulidChars = newCharSet("0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz") |
||
13 | |||
14 | // ULID validates whether the value is a valid ULID (Universally Unique Lexicographically Sortable Identifier). |
||
15 | // See https://github.com/ulid/spec for ULID specifications. |
||
16 | // |
||
17 | // Possible errors: |
||
18 | // - [ErrTooShort] on values with length less than 26; |
||
19 | // - [ErrTooLong] on values with length greater than 26; |
||
20 | // - [ErrInvalidCharacters] on values with unexpected characters; |
||
21 | // - [ErrTooLarge] on too big value (larger than '7ZZZZZZZZZZZZZZZZZZZZZZZZZ'). |
||
22 | func ULID(value string) error { |
||
23 | if len(value) < 26 { |
||
24 | return ErrTooShort |
||
25 | } |
||
26 | if len(value) > 26 { |
||
27 | return ErrTooLong |
||
28 | } |
||
29 | |||
30 | for _, c := range value { |
||
31 | if !ulidChars.Contains(c) { |
||
32 | return ErrInvalidCharacters |
||
33 | } |
||
34 | } |
||
35 | |||
36 | // Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ' |
||
37 | // See https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings |
||
38 | if value[0] > '7' { |
||
39 | return ErrTooLarge |
||
40 | } |
||
41 | |||
42 | return nil |
||
43 | } |
||
44 | |||
45 | type charSet map[rune]struct{} |
||
46 | |||
47 | func (s charSet) Contains(c rune) bool { |
||
48 | _, exist := s[c] |
||
49 | |||
50 | return exist |
||
51 | } |
||
52 | |||
53 | func newCharSet(s string) charSet { |
||
54 | chars := make(charSet, len(s)) |
||
55 | |||
56 | for _, c := range s { |
||
57 | chars[c] = struct{}{} |
||
58 | } |
||
59 | |||
60 | return chars |
||
61 | } |
||
62 |