Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

randx:新增生成随机code方法 #207

Merged
merged 13 commits into from
Sep 8, 2023
2 changes: 2 additions & 0 deletions .CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
- [slice: 添加Add函数,在指定位置插入元素](https://github.com/ecodeclub/ekit/pull/201)
- [slice: 优化delete方法,无需从头开始遍历](https://github.com/ecodeclub/ekit/pull/203)
- [slice: 重构 slice 中使用 equalFunc 的方法](https://github.com/ecodeclub/ekit/pull/205)
- [randx: 新增生成随机code方法](https://github.com/ecodeclub/ekit/pull/207)
- [slice: intersect方法优化, symmetricDiffSet重构](https://github.com/ecodeclub/ekit/pull/208)


# v0.0.7
- [slice: FilterDelete](https://github.com/ecodeclub/ekit/pull/152)
- [reflectx: IsNil 方法](https://github.com/ecodeclub/ekit/pull/150)
Expand Down
88 changes: 88 additions & 0 deletions randx/rand_code.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2021 ecodeclub
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package randx

import (
"errors"
"math/rand"
)

var ERRTYPENOTSUPPORTTED = errors.New("ekit:不支持的类型")

type TYPE int

const (
TYPE_DEFAULT TYPE = 0 //默认类型
TYPE_DIGIT TYPE = 1 //数字//
TYPE_LETTER TYPE = 2 //小写字母
TYPE_CAPITAL TYPE = 3 //大写字母
TYPE_MIXED TYPE = 4 //数字+字母混合
)

// RandCode 根据传入的长度和类型生成随机字符串,这个方法目前可以生成数字、字母、数字+字母的随机字符串
func RandCode(length int, typ TYPE) (string, error) {
switch typ {
case TYPE_DEFAULT:
fallthrough
case TYPE_DIGIT:
return generate("0123456789", length, 4), nil
case TYPE_LETTER:
return generate("abcdefghijklmnopqrstuvwxyz", length, 5), nil
case TYPE_CAPITAL:
return generate("ABCDEFGHIJKLMNOPQRSTUVWXYZ", length, 5), nil
case TYPE_MIXED:
return generate("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", length, 7), nil
default:
return "", ERRTYPENOTSUPPORTTED
}
}

// generate 根据传入的随机源和长度生成随机字符串,一次随机,多次使用
func generate(source string, length, idxBits int) string {

//掩码
//例如: 使用低6位:0000 0000 --> 0011 1111
idxMask := 1<<idxBits - 1

// 63位最多可以使用多少次
remain := 63 / idxBits

//cache 随机位缓存
cache := rand.Int63()

result := make([]byte, length)

for i := 0; i < length; {
//如果使用次数剩余0,重新获取随机
if remain == 0 {
cache, remain = rand.Int63(), 63/idxBits
}

//利用掩码获取有效的随机数位
if randIndex := int(cache & int64(idxMask)); randIndex < len(source) {
flycash marked this conversation as resolved.
Show resolved Hide resolved
result[i] = source[randIndex]
i++
}

//使用下一组随机位
cache >>= idxBits

//扣减remain
remain--

}
return string(result)

}
92 changes: 92 additions & 0 deletions randx/rand_code_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2021 ecodeclub
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package randx

import (
"errors"
"regexp"
"testing"
)

func TestRandCode(t *testing.T) {
testCases := []struct {
name string
length int
typ TYPE
wantMatch string
wantErr error
}{
{
name: "默认类型",
length: 8,
typ: TYPE_DEFAULT,
wantMatch: "^[0-9]+$",
wantErr: nil,
},
{
name: "数字验证码",
length: 8,
typ: TYPE_DIGIT,
wantMatch: "^[0-9]+$",
wantErr: nil,
}, {
name: "小写字母验证码",
length: 8,
typ: TYPE_LETTER,
wantMatch: "^[a-z]+$",
wantErr: nil,
}, {
name: "大写字母验证码",
length: 8,
typ: TYPE_CAPITAL,
wantMatch: "^[A-Z]+$",
wantErr: nil,
}, {
name: "混合验证码",
length: 8,
typ: TYPE_MIXED,
wantMatch: "^[0-9a-zA-Z]+$",
wantErr: nil,
}, {
name: "未定义类型",
length: 8,
typ: 9,
wantMatch: "",
wantErr: ERRTYPENOTSUPPORTTED,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
code, err := RandCode(tc.length, tc.typ)
if err != nil {
if !errors.Is(err, tc.wantErr) {
t.Errorf("unexpected error: %v", err)
}
} else {
//长度检验
if len(code) != tc.length {
t.Errorf("expected length: %d but got length:%d ", tc.length, len(code))
}
//模式检验
matched, _ := regexp.MatchString(tc.wantMatch, code)
if !matched {
t.Errorf("expected %s but got %s", tc.wantMatch, code)
}
}
})
}

}
Loading