Compare commits
6 commits
4faf3a5d2f
...
4b9bffb1a6
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b9bffb1a6 | |||
| 4b1312e293 | |||
| 797c616447 | |||
| 8f02956ecd | |||
| 480e36763f | |||
| 8072ae267a |
14 changed files with 153 additions and 104 deletions
39
.golangci.yaml
Normal file
39
.golangci.yaml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
- copyloopvar
|
||||
- dupl
|
||||
- err113
|
||||
- errname
|
||||
- exptostd
|
||||
- fatcontext
|
||||
- funlen
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- godox
|
||||
- gosec
|
||||
- perfsprint
|
||||
- testifylint
|
||||
|
||||
linters-settings:
|
||||
testifylint:
|
||||
enable-all: true
|
||||
disable:
|
||||
- require-error
|
||||
gocognit:
|
||||
min-complexity: 5
|
||||
gocyclo:
|
||||
min-complexity: 5
|
||||
gocritic:
|
||||
enable-all: true
|
||||
settings:
|
||||
hugeParam:
|
||||
sizeThreshold: 255
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#### Support
|
||||
|
||||
- 📝 Some better examples`
|
||||
- 📝 Some better examples
|
||||
|
||||
### [0.4.0] - 2025-03-15 - ✨ More Generators, and `Generator.Make` method
|
||||
|
||||
|
|
|
|||
62
Taskfile.yml
62
Taskfile.yml
|
|
@ -5,7 +5,7 @@ tasks:
|
|||
cmds:
|
||||
- task: fmt
|
||||
- task: test
|
||||
- task: build
|
||||
- task: lint
|
||||
|
||||
fmt:
|
||||
desc: Format go code
|
||||
|
|
@ -22,66 +22,16 @@ tasks:
|
|||
cmds:
|
||||
- go generate ./...
|
||||
|
||||
vet:
|
||||
desc: Vet go code
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- go vet ./...
|
||||
|
||||
critic:
|
||||
desc: Critique go code
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- gocritic check ./...
|
||||
|
||||
staticcheck:
|
||||
desc: Static check go code
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- staticcheck ./...
|
||||
|
||||
cog-complex:
|
||||
desc: Calculate cognitive complexity
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- gocognit -over 5 .
|
||||
|
||||
cyc-complex:
|
||||
desc: Calculate cyclomatic complexity
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- gocyclo -over 5 .
|
||||
|
||||
complex:
|
||||
desc: Calculate complexities
|
||||
deps:
|
||||
- cog-complex
|
||||
- cyc-complex
|
||||
|
||||
vuln:
|
||||
desc: Check for vulnerabilities
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- govulncheck ./...
|
||||
|
||||
lint:
|
||||
desc: Do static analysis
|
||||
deps:
|
||||
- vet
|
||||
- critic
|
||||
- staticcheck
|
||||
- complex
|
||||
- vuln
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- golangci-lint run
|
||||
|
||||
test:
|
||||
desc: Run unit tests
|
||||
deps: [fmt, vet]
|
||||
deps: [fmt]
|
||||
sources:
|
||||
- '**/*.go'
|
||||
generates:
|
||||
|
|
|
|||
56
examples_test.go
Normal file
56
examples_test.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package nomino_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
// Define a global Config.
|
||||
func NominoConfig() nomino.Config {
|
||||
return nomino.NewConfig(
|
||||
nomino.WithSeparator("-"),
|
||||
nomino.WithPrefix("upload"),
|
||||
nomino.WithGenerator(nomino.UUID(
|
||||
nomino.UUIDv7,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
// / HandleImgUploads generates new filenames for images with a png extension.
|
||||
func HandleImgUploads(orig string) string {
|
||||
newName, _ := nomino.Make(
|
||||
NominoConfig(),
|
||||
nomino.WithExtension("png"),
|
||||
nomino.WithOriginalSlug(orig),
|
||||
)
|
||||
|
||||
return newName
|
||||
}
|
||||
|
||||
// HandleVidUploads generates a new filename for videos.
|
||||
// We ignore the original filename and use a timestamp for the generated part
|
||||
// with a webm extension.
|
||||
func HandleVidUploads() string {
|
||||
newName, _ := nomino.Timestamp(nomino.TimestampUTC()).
|
||||
MakeWithConfig(NominoConfig().AddOptions(
|
||||
nomino.WithExtension("webm"),
|
||||
))
|
||||
return newName
|
||||
}
|
||||
|
||||
// Example shows how to use nomino.
|
||||
func Example() {
|
||||
// Pretend we have an image upload
|
||||
filename := "George"
|
||||
uploadImgName := HandleImgUploads(filename)
|
||||
|
||||
// Upload to storage
|
||||
fmt.Println(uploadImgName)
|
||||
|
||||
// New Video Upload
|
||||
uploadVidName := HandleVidUploads()
|
||||
|
||||
// Upload to storage
|
||||
fmt.Println(uploadVidName)
|
||||
}
|
||||
12
gen_file.go
12
gen_file.go
|
|
@ -2,14 +2,14 @@ package nomino
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
)
|
||||
|
||||
// ErrMissingOriginal is the error returned by Slug if there is no filename
|
||||
// ErrMissingOriginal is the error returned by Slug if there is no filename.
|
||||
var ErrMissingOriginal = errors.New("missing original filename")
|
||||
|
||||
func getOriginal(c *Config) (string, error) {
|
||||
|
|
@ -37,10 +37,10 @@ func Slug(lang ...string) Generator {
|
|||
}
|
||||
}
|
||||
|
||||
// HashingFunc is a function that generates a hash.Hash
|
||||
// HashingFunc is a function that generates a hash.Hash.
|
||||
type HashingFunc func() hash.Hash
|
||||
|
||||
// New allows HashingFunc to be used as a Hasher
|
||||
// New allows HashingFunc to be used as a Hasher.
|
||||
func (hf HashingFunc) New() hash.Hash {
|
||||
return hf()
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ type Hasher interface {
|
|||
New() hash.Hash
|
||||
}
|
||||
|
||||
// ErrInvalidHash is returned by the Hash generator when an invalid HashType is passed
|
||||
// ErrInvalidHash is returned by the Hash generator when an invalid HashType is passed.
|
||||
var ErrInvalidHash = errors.New("invalid hash type")
|
||||
|
||||
// Hash generates a name from a hash of the filename.
|
||||
|
|
@ -70,6 +70,6 @@ func Hash(h Hasher) Generator {
|
|||
}
|
||||
hs := h.New()
|
||||
hs.Write([]byte(name))
|
||||
return fmt.Sprintf("%x", hs.Sum(nil)), nil
|
||||
return hex.EncodeToString(hs.Sum(nil)), nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ type incConf struct {
|
|||
cb func(int) string
|
||||
}
|
||||
|
||||
// IncrementalOption sets an option for the Incremental Generator
|
||||
// IncrementalOption sets an option for the Incremental Generator.
|
||||
type IncrementalOption func(c *incConf)
|
||||
|
||||
// Incremental generates a name that is a series of integers.
|
||||
|
|
@ -30,7 +30,7 @@ func Incremental(opts ...IncrementalOption) Generator {
|
|||
}
|
||||
}
|
||||
|
||||
// IncrementalStart sets the starting integer for Incremental
|
||||
// IncrementalStart sets the starting integer for Incremental.
|
||||
func IncrementalStart(start int) IncrementalOption {
|
||||
return func(c *incConf) {
|
||||
c.start = start
|
||||
|
|
@ -45,7 +45,7 @@ func IncrementalStep(step int) IncrementalOption {
|
|||
}
|
||||
|
||||
// IncrementalFormatsets the format for the number generated by Incremental.
|
||||
// It will be formatted with Printf. This is mostly likely useful with a format like "%02d"
|
||||
// It will be formatted with Printf. This is mostly likely useful with a format like "%02d".
|
||||
func IncrementalFormat(format string) IncrementalOption {
|
||||
return func(c *incConf) {
|
||||
c.cb = func(i int) string {
|
||||
|
|
|
|||
16
gen_rand.go
16
gen_rand.go
|
|
@ -52,10 +52,10 @@ type randConf struct {
|
|||
length int
|
||||
}
|
||||
|
||||
// RandomOption is an option for the Random Generator
|
||||
// RandomOption is an option for the Random Generator.
|
||||
type RandomOption func(*randConf)
|
||||
|
||||
// RandomLength controls the length of the string generated by Random
|
||||
// RandomLength controls the length of the string generated by Random.
|
||||
func RandomLength(length int) RandomOption {
|
||||
return func(c *randConf) {
|
||||
c.length = length
|
||||
|
|
@ -64,11 +64,17 @@ func RandomLength(length int) RandomOption {
|
|||
|
||||
func getRandomBytes(l int) []byte {
|
||||
key := make([]byte, l)
|
||||
rand.Read(key)
|
||||
_, _ = rand.Read(key)
|
||||
e := base62.StdEncoding.Encode(key)
|
||||
return e[:l]
|
||||
}
|
||||
|
||||
func fillBuffer(buff *strings.Builder, length int) {
|
||||
for buff.Len() < length {
|
||||
buff.Write(getRandomBytes(length - buff.Len()))
|
||||
}
|
||||
}
|
||||
|
||||
// Random generates a random string containing the characters [A-Za-z0-9].
|
||||
// By default, it will be eight characters long.
|
||||
func Random(opts ...RandomOption) Generator {
|
||||
|
|
@ -79,9 +85,7 @@ func Random(opts ...RandomOption) Generator {
|
|||
return func(*Config) (string, error) {
|
||||
var buff strings.Builder
|
||||
buff.Grow(c.length)
|
||||
for buff.Len() < c.length {
|
||||
buff.Write(getRandomBytes(c.length - buff.Len()))
|
||||
}
|
||||
fillBuffer(&buff, c.length)
|
||||
return buff.String(), nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
|
@ -18,7 +17,7 @@ func TestUUID(t *testing.T) {
|
|||
type badRead struct{}
|
||||
|
||||
func (badRead) Read([]byte) (int, error) {
|
||||
return 0, errors.New("sorry")
|
||||
return 0, errTest
|
||||
}
|
||||
|
||||
func TestUUIDFail(t *testing.T) {
|
||||
|
|
@ -26,7 +25,7 @@ func TestUUIDFail(t *testing.T) {
|
|||
defer uuid.SetRand(nil)
|
||||
|
||||
_, err := UUID(nil)(nil)
|
||||
assert.Equal(t, errors.New("sorry"), err)
|
||||
assert.ErrorIs(t, err, errTest)
|
||||
}
|
||||
|
||||
func TestRand(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package nomino
|
|||
|
||||
import "time"
|
||||
|
||||
// FileTimestamp is the default format for WithTimestamp and WithTime
|
||||
// FileTimestamp is the default format for WithTimestamp and WithTime.
|
||||
const FileTimestamp string = "2006-01-02T15-04-05-0700"
|
||||
|
||||
// FileTimestampNoTZ is the default format for WithTimestampUTC and WithTimeUTC
|
||||
// FileTimestampNoTZ is the default format for WithTimestampUTC and WithTimeUTC.
|
||||
const FileTimestampNoTZ string = "2006-01-02T15-04-05"
|
||||
|
||||
type timestampConf struct {
|
||||
|
|
@ -14,11 +14,11 @@ type timestampConf struct {
|
|||
utc bool
|
||||
}
|
||||
|
||||
// TimestampOption provides options for the Timestamp Generator
|
||||
// TimestampOption provides options for the Timestamp Generator.
|
||||
type TimestampOption func(c *timestampConf)
|
||||
|
||||
// Timestamp generates a a date and time. By default, it uses the current time, and will.
|
||||
// be formatted accourding to FileTimestamp
|
||||
// be formatted accourding to FileTimestamp.
|
||||
func Timestamp(opts ...TimestampOption) Generator {
|
||||
c := timestampConf{format: FileTimestamp, ts: time.Now()}
|
||||
for _, opt := range opts {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func (g Generator) MakeWithConfig(c Config) (string, error) {
|
|||
return Make(c.AddOptions(WithGenerator(g)))
|
||||
}
|
||||
|
||||
// WithGenerator sets the specified generator
|
||||
// WithGenerator sets the specified generator.
|
||||
func WithGenerator(g Generator) Option {
|
||||
return func(c *Config) {
|
||||
c.generator = g
|
||||
|
|
@ -66,6 +66,7 @@ func MultiGeneratorRandomOrder(gens ...Generator) Generator {
|
|||
}
|
||||
|
||||
return func(c *Config) (string, error) {
|
||||
//nolint:gosec // This is not security sensitive, so a weak number generator is fine.
|
||||
idx := rand.Int() % len(gens)
|
||||
return gens[idx](c)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func ExampleGenerator_Make() {
|
|||
|
||||
func ExampleMultiGeneratorInOrder() {
|
||||
gen1 := func(*nomino.Config) (string, error) {
|
||||
return "hello", nil
|
||||
return "bonjour", nil
|
||||
}
|
||||
gen2 := func(*nomino.Config) (string, error) {
|
||||
return "goodbye", nil
|
||||
|
|
@ -54,17 +54,17 @@ func ExampleMultiGeneratorInOrder() {
|
|||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// hello.txt
|
||||
// bonjour.txt
|
||||
// goodbye.txt
|
||||
// hello.txt
|
||||
// bonjour.txt
|
||||
}
|
||||
|
||||
func ExampleMultiGeneratorRandomOrder() {
|
||||
gen1 := func(*nomino.Config) (string, error) {
|
||||
return "hello", nil
|
||||
return "guten-tag", nil
|
||||
}
|
||||
gen2 := func(*nomino.Config) (string, error) {
|
||||
return "goodbye", nil
|
||||
return "wiedersehen", nil
|
||||
}
|
||||
gen := nomino.MultiGeneratorRandomOrder(gen1, gen2)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithGenerator(t *testing.T) {
|
||||
g := func(*Config) (string, error) { return "abc", nil }
|
||||
var c Config
|
||||
WithGenerator(g)(&c)
|
||||
st, err := c.generator(&c)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "abc", st)
|
||||
}
|
||||
|
||||
const (
|
||||
out1 string = "abc"
|
||||
out2 string = "def"
|
||||
|
|
@ -30,6 +21,15 @@ var (
|
|||
gens = []Generator{gen1, gen2, gen3}
|
||||
)
|
||||
|
||||
func TestWithGenerator(t *testing.T) {
|
||||
g := func(*Config) (string, error) { return out1, nil }
|
||||
var c Config
|
||||
WithGenerator(g)(&c)
|
||||
st, err := c.generator(&c)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, out1, st)
|
||||
}
|
||||
|
||||
func TestMultiGeneratorInOrder(t *testing.T) {
|
||||
g := MultiGeneratorInOrder(gens...)
|
||||
st, err := g(nil)
|
||||
|
|
@ -47,16 +47,15 @@ func TestMultiGeneratorInOrder(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMultiGeneratorInOrderOne(t *testing.T) {
|
||||
st1 := "abc"
|
||||
g1 := func(*Config) (string, error) { return st1, nil }
|
||||
g1 := func(*Config) (string, error) { return out1, nil }
|
||||
g := MultiGeneratorInOrder(g1)
|
||||
|
||||
st, err := g(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, st1, st)
|
||||
assert.Equal(t, out1, st)
|
||||
st, err = g(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, st1, st)
|
||||
assert.Equal(t, out1, st)
|
||||
}
|
||||
|
||||
func TestMultiGeneratorInOrderMissing(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var errTest = errors.New("sorry")
|
||||
|
||||
func TestMake(t *testing.T) {
|
||||
genOpt := WithGenerator(func(*Config) (string, error) { return "abc", nil })
|
||||
testcases := []struct {
|
||||
|
|
@ -48,11 +50,10 @@ func TestMake(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMakeErr(t *testing.T) {
|
||||
retErr := errors.New("oops")
|
||||
conf := NewConfig(WithGenerator(func(*Config) (string, error) { return "foobar", retErr }))
|
||||
conf := NewConfig(WithGenerator(func(*Config) (string, error) { return "foobar", errTest }))
|
||||
st, err := Make(conf)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, retErr)
|
||||
assert.ErrorIs(t, err, errTest)
|
||||
}
|
||||
|
||||
func TestMakeDoesntChangeConf(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ func WithOriginal(o string) Option {
|
|||
}
|
||||
|
||||
// WithOriginal sets the original filename as a slug.
|
||||
// This should not be used with the Slug Generator (as it would be redundant)
|
||||
// This should not be used with the Slug Generator (as it would be redundant).
|
||||
func WithOriginalSlug(o string) Option {
|
||||
return func(c *Config) {
|
||||
c.original = slug.Make(o)
|
||||
|
|
@ -26,7 +26,7 @@ func WithOriginalSlug(o string) Option {
|
|||
}
|
||||
|
||||
// WithOriginal sets the original filename as a slug, taking the language into account.
|
||||
// This should not be used with the Slug Generator (as it would be redundant)
|
||||
// This should not be used with the Slug Generator (as it would be redundant).
|
||||
func WithOriginalSlugLang(o, lang string) Option {
|
||||
return func(c *Config) {
|
||||
c.original = slug.MakeLang(o, lang)
|
||||
|
|
@ -47,7 +47,7 @@ func WithSuffix(s string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithoutExtension sets no extension for the generated filename. By default, it will be txt
|
||||
// WithoutExtension sets no extension for the generated filename. By default, it will be txt.
|
||||
func WithoutExtension() Option {
|
||||
return func(c *Config) {
|
||||
c.extension = ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue