Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
4kimov committed Jul 16, 2023
1 parent 5a739db commit ec9653a
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 38 deletions.
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,37 @@ import "github.com/sqids/sqids-go"
Simple encode & decode:

```golang
s := sqids.New()
id := s.encode([]uint64{1, 2, 3}) // "8QRLaD"
numbers := s.decode(id) // [1, 2, 3]
s, err := sqids.New()
id := s.Encode([]uint64{1, 2, 3}) // "8QRLaD"
numbers := s.Decode(id) // [1, 2, 3]
```

Randomize IDs by providing a custom alphabet:

```golang
s := sqids.NewCustom("FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE", 0)
id := s.encode([]uint64{1, 2, 3}) // "B5aMa3"
numbers := s.decode(id) // [1, 2, 3]
s, err := sqids.NewCustom("FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE", 0)
id := s.Encode([]uint64{1, 2, 3}) // "B5aMa3"
numbers := s.Decode(id) // [1, 2, 3]
```

Enforce a *minimum* length for IDs:

```golang
s := sqids.NewCustom("", 10)
id := s.encode([]uint64{1, 2, 3}) // "75JT1cd0dL"
numbers := s.decode(id) // [1, 2, 3]
s, err := sqids.NewCustom("", 10)
id := s.Encode([]uint64{1, 2, 3}) // "75JT1cd0dL"
numbers := s.Decode(id) // [1, 2, 3]
```

Prevent specific words from appearing anywhere in the auto-generated IDs:

```golang
s := sqids.New()
s.Blocklist = []string{"word1", "word2"}
id := s.encode([]uint64{1, 2, 3}) // "8QRLaD"
numbers := s.decode(id) // [1, 2, 3]
s, err := sqids.NewSqids(sqids.Options{
Alphabet: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
MinLength: 0,
Blocklist: &[]string{"word1", "word2"}
})
id := s.Encode([]uint64{1, 2, 3}) // "8QRLaD"
numbers := s.Decode(id) // [1, 2, 3]
```

## License
Expand Down
12 changes: 6 additions & 6 deletions alphabet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ func TestSimple(t *testing.T) {
numbers := []uint64{1, 2, 3}
id := "4d9fd2"

sqids, err := NewCustom("0123456789abcdef", 0)
s, err := NewCustom("0123456789abcdef", 0)
if err != nil {
t.Fatal(err)
}

generatedID, err := sqids.Encode(numbers)
generatedID, err := s.Encode(numbers)
if err != nil {
t.Fatal(err)
}
Expand All @@ -23,26 +23,26 @@ func TestSimple(t *testing.T) {
t.Errorf("Encoding `%v` should produce `%v`, but instead produced `%v`", numbers, id, generatedID)
}

decodedNumbers := sqids.Decode(id)
decodedNumbers := s.Decode(id)
if !reflect.DeepEqual(numbers, decodedNumbers) {
t.Errorf("Decoding `%v` should produce `%v`, but instead produced `%v`", id, numbers, decodedNumbers)
}
}

func TestShortAlphabet(t *testing.T) {
sqids, err := NewCustom("abcde", 0)
s, err := NewCustom("abcde", 0)
if err != nil {
t.Fatal(err)
}

numbers := []uint64{1, 2, 3}

id, err := sqids.Encode(numbers)
id, err := s.Encode(numbers)
if err != nil {
t.Fatal(err)
}

decodedNumbers := sqids.Decode(id)
decodedNumbers := s.Decode(id)
if !reflect.DeepEqual(numbers, decodedNumbers) {
t.Errorf("Decoding `%v` should produce `%v`, but instead produced `%v`", id, numbers, decodedNumbers)
}
Expand Down
39 changes: 39 additions & 0 deletions blocklist_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package sqids

import (
"reflect"
"testing"
)

func TestBlocklist(t *testing.T) {
numbers := []uint64{1, 2, 3}
id := "TM0x1Mxz"

s, err := NewSqids(Options{
Alphabet: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
MinLength: 0,
Blocklist: &[]string{
"8QRLaD", // normal result of 1st encoding, let's block that word on purpose
"7T1cd0dL", // result of 2nd encoding
"UeIe", // result of 3rd encoding is `RA8UeIe7`, let's block a substring
"imhw", // result of 4th encoding is `WM3Limhw`, let's block the postfix
"LfUQ", // result of 4th encoding is `LfUQh4HN`, let's block the prefix
},
})
if err != nil {
t.Fatal(err)
}

generatedID, err := s.Encode(numbers)
if err != nil {
t.Fatal(err)
}
if id != generatedID {
t.Errorf("Encoding `%v` should produce `%v`, but instead produced `%v`", numbers, id, generatedID)
}

decodedNumbers := s.Decode(id)
if !reflect.DeepEqual(numbers, decodedNumbers) {
t.Errorf("Decoding `%v` should produce `%v`, but instead produced `%v`", id, numbers, decodedNumbers)
}
}
9 changes: 4 additions & 5 deletions encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ import (
func TestEncodeDecode(t *testing.T) {
numbers := []uint64{1, 2, 3}

sqids, err := New()
s, err := New()
if err != nil {
t.Fatal(err)
}

id, err := sqids.Encode(numbers)
id, err := s.Encode(numbers)
if err != nil {
t.Fatal(err)
}

decoded := sqids.Decode(id)

if !reflect.DeepEqual(numbers, decoded) {
decodedNumbers := s.Decode(id)
if !reflect.DeepEqual(numbers, decodedNumbers) {
t.Errorf("Could not encode/decode `%v`", numbers)
}
}
28 changes: 14 additions & 14 deletions sqids.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"math"
"regexp"
"strings"
)

Expand Down Expand Up @@ -42,12 +43,6 @@ func NewCustom(alphabet string, minLength int) (*Sqids, error) {

// NewSqids -
func NewSqids(options Options) (*Sqids, error) {
blank := Sqids{
alphabet: "",
minLength: 0,
blocklist: []string{},
}

alphabet := options.Alphabet
minLength := options.MinLength
blocklist := options.Blocklist
Expand All @@ -67,8 +62,8 @@ func NewSqids(options Options) (*Sqids, error) {
}

// test min length (type [might be lang-specific] + min length + max length)
if minLength < int(blank.MinValue()) || minLength > len(alphabet) {
return nil, fmt.Errorf("minimum length has to be between %d and %d", blank.MinValue(), len(alphabet))
if minLength < int(MinValue()) || minLength > len(alphabet) {
return nil, fmt.Errorf("minimum length has to be between %d and %d", MinValue(), len(alphabet))
}

// clean up blocklist:
Expand Down Expand Up @@ -103,13 +98,13 @@ func (s *Sqids) Encode(numbers []uint64) (string, error) {

inRangeNumbers := []uint64{}
for _, n := range numbers {
if n >= s.MinValue() && n <= s.MaxValue() {
if n >= MinValue() && n <= MaxValue() {
inRangeNumbers = append(inRangeNumbers, n)
}
}

if len(inRangeNumbers) != len(numbers) {
return "", fmt.Errorf("encoding supports numbers between %d and %d", s.MinValue(), s.MaxValue())
return "", fmt.Errorf("encoding supports numbers between %d and %d", MinValue(), MaxValue())
}

return s.encodeNumbers(inRangeNumbers, false)
Expand Down Expand Up @@ -166,7 +161,7 @@ func (s *Sqids) encodeNumbers(numbers []uint64, partitioned bool) (string, error

if s.isBlockedID(id) {
if partitioned {
if numbers[0]+1 > s.MaxValue() {
if numbers[0]+1 > MaxValue() {
return "", errors.New("ran out of range checking against the blocklist")
}

Expand Down Expand Up @@ -232,12 +227,12 @@ func (s *Sqids) Decode(id string) []uint64 {
}

// MinValue -
func (s *Sqids) MinValue() uint64 {
func MinValue() uint64 {
return 0
}

// MaxValue -
func (s *Sqids) MaxValue() uint64 {
func MaxValue() uint64 {
return math.MaxUint64
}

Expand Down Expand Up @@ -316,14 +311,19 @@ func contains(slice []string, str string) bool {

func (s *Sqids) isBlockedID(id string) bool {
id = strings.ToLower(id)
r := regexp.MustCompile(`\d`)

for _, word := range s.blocklist {
if len(word) <= len(id) {
if len(id) <= 3 || len(word) <= 3 {
if id == word {
return true
}
} else if strings.Contains(id, word) || strings.HasPrefix(id, word) || strings.HasSuffix(id, word) {
} else if r.MatchString(word) {
if strings.HasPrefix(id, word) || strings.HasSuffix(id, word) {
return true
}
} else if strings.Contains(id, word) {
return true
}
}
Expand Down

0 comments on commit ec9653a

Please sign in to comment.