diff --git a/.golangci.yaml b/.golangci.yaml deleted file mode 100644 index 273e2af..0000000 --- a/.golangci.yaml +++ /dev/null @@ -1,39 +0,0 @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec72e7..c21a656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ #### Support -- 📝 Some better examples +- 📝 Some better examples` ### [0.4.0] - 2025-03-15 - ✨ More Generators, and `Generator.Make` method diff --git a/Taskfile.yml b/Taskfile.yml index ef9f12e..35792f6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -5,7 +5,7 @@ tasks: cmds: - task: fmt - task: test - - task: lint + - task: build fmt: desc: Format go code @@ -22,16 +22,66 @@ tasks: cmds: - go generate ./... - lint: - desc: Do static analysis + vet: + desc: Vet go code sources: - '**/*.go' cmds: - - golangci-lint run + - 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 test: desc: Run unit tests - deps: [fmt] + deps: [fmt, vet] sources: - '**/*.go' generates: diff --git a/examples_test.go b/examples_test.go deleted file mode 100644 index ee5f8e0..0000000 --- a/examples_test.go +++ /dev/null @@ -1,56 +0,0 @@ -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) -} diff --git a/gen_file.go b/gen_file.go index b602557..497fdcc 100644 --- a/gen_file.go +++ b/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 hex.EncodeToString(hs.Sum(nil)), nil + return fmt.Sprintf("%x", hs.Sum(nil)), nil } } diff --git a/gen_int.go b/gen_int.go index 3174d18..aeb6076 100644 --- a/gen_int.go +++ b/gen_int.go @@ -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 { diff --git a/gen_rand.go b/gen_rand.go index bdabef6..de71723 100644 --- a/gen_rand.go +++ b/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,17 +64,11 @@ 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 { @@ -85,7 +79,9 @@ func Random(opts ...RandomOption) Generator { return func(*Config) (string, error) { var buff strings.Builder buff.Grow(c.length) - fillBuffer(&buff, c.length) + for buff.Len() < c.length { + buff.Write(getRandomBytes(c.length - buff.Len())) + } return buff.String(), nil } } diff --git a/gen_rand_test.go b/gen_rand_test.go index 193ac51..51fc73f 100644 --- a/gen_rand_test.go +++ b/gen_rand_test.go @@ -1,6 +1,7 @@ package nomino import ( + "errors" "testing" "github.com/google/uuid" @@ -17,7 +18,7 @@ func TestUUID(t *testing.T) { type badRead struct{} func (badRead) Read([]byte) (int, error) { - return 0, errTest + return 0, errors.New("sorry") } func TestUUIDFail(t *testing.T) { @@ -25,7 +26,7 @@ func TestUUIDFail(t *testing.T) { defer uuid.SetRand(nil) _, err := UUID(nil)(nil) - assert.ErrorIs(t, err, errTest) + assert.Equal(t, errors.New("sorry"), err) } func TestRand(t *testing.T) { diff --git a/gen_ts.go b/gen_ts.go index 05d6e0c..abb4181 100644 --- a/gen_ts.go +++ b/gen_ts.go @@ -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 { diff --git a/generators.go b/generators.go index 9e31913..e01ccc6 100644 --- a/generators.go +++ b/generators.go @@ -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,7 +66,6 @@ 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) } diff --git a/generators_examples_test.go b/generators_examples_test.go index b552089..5efc4d8 100644 --- a/generators_examples_test.go +++ b/generators_examples_test.go @@ -37,7 +37,7 @@ func ExampleGenerator_Make() { func ExampleMultiGeneratorInOrder() { gen1 := func(*nomino.Config) (string, error) { - return "bonjour", nil + return "hello", nil } gen2 := func(*nomino.Config) (string, error) { return "goodbye", nil @@ -54,17 +54,17 @@ func ExampleMultiGeneratorInOrder() { fmt.Println(str) // Output: - // bonjour.txt + // hello.txt // goodbye.txt - // bonjour.txt + // hello.txt } func ExampleMultiGeneratorRandomOrder() { gen1 := func(*nomino.Config) (string, error) { - return "guten-tag", nil + return "hello", nil } gen2 := func(*nomino.Config) (string, error) { - return "wiedersehen", nil + return "goodbye", nil } gen := nomino.MultiGeneratorRandomOrder(gen1, gen2) diff --git a/generators_test.go b/generators_test.go index 4c257ab..f8a81fa 100644 --- a/generators_test.go +++ b/generators_test.go @@ -7,6 +7,15 @@ 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" @@ -21,15 +30,6 @@ 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,15 +47,16 @@ func TestMultiGeneratorInOrder(t *testing.T) { } func TestMultiGeneratorInOrderOne(t *testing.T) { - g1 := func(*Config) (string, error) { return out1, nil } + st1 := "abc" + g1 := func(*Config) (string, error) { return st1, nil } g := MultiGeneratorInOrder(g1) st, err := g(nil) assert.NoError(t, err) - assert.Equal(t, out1, st) + assert.Equal(t, st1, st) st, err = g(nil) assert.NoError(t, err) - assert.Equal(t, out1, st) + assert.Equal(t, st1, st) } func TestMultiGeneratorInOrderMissing(t *testing.T) { diff --git a/make_test.go b/make_test.go index 1ebb48e..0970c48 100644 --- a/make_test.go +++ b/make_test.go @@ -7,8 +7,6 @@ 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 { @@ -50,10 +48,11 @@ func TestMake(t *testing.T) { } func TestMakeErr(t *testing.T) { - conf := NewConfig(WithGenerator(func(*Config) (string, error) { return "foobar", errTest })) + retErr := errors.New("oops") + conf := NewConfig(WithGenerator(func(*Config) (string, error) { return "foobar", retErr })) st, err := Make(conf) assert.Zero(t, st) - assert.ErrorIs(t, err, errTest) + assert.ErrorIs(t, err, retErr) } func TestMakeDoesntChangeConf(t *testing.T) { diff --git a/options.go b/options.go index 6cc87a8..c7e0338 100644 --- a/options.go +++ b/options.go @@ -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 = ""