Compare commits
33 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e3132a5cf | |||
| 2561fe3cff | |||
| 17961ddd41 | |||
| cdb504c1a3 | |||
| 70c74c2b03 | |||
| 81e04ea319 | |||
| b6994e73a8 | |||
| c5a9f09166 | |||
| cdf12a767c | |||
| 5cbd63a227 | |||
| ac3d1a5565 | |||
| c32a15f4a1 | |||
| 4b9bffb1a6 | |||
| 4b1312e293 | |||
| 797c616447 | |||
| 8f02956ecd | |||
| 480e36763f | |||
| 8072ae267a | |||
| 4faf3a5d2f | |||
| c17f23595c | |||
| f20e737f2b | |||
| 1abfaa44d1 | |||
| d7b14f804c | |||
| fee2e3cc2f | |||
| f121b7dbce | |||
| 55038ea295 | |||
| 4d6cd82b74 | |||
| 7c016df30f | |||
| 2440f55563 | |||
| 1008a064d0 | |||
| 1677a692d1 | |||
| 5c1132e414 | |||
| 10eb3f2491 |
26 changed files with 642 additions and 278 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
|
||||
91
CHANGELOG.md
91
CHANGELOG.md
|
|
@ -1,68 +1,111 @@
|
|||
# Changelog
|
||||
|
||||
### [0.3.0] - 2025-03-14
|
||||
### [1.0.0] - 2025-03-31 - 🚀 Stable release!
|
||||
|
||||
#### Support
|
||||
|
||||
- 📝 Vastly improved [go docs](https://pkg.go.dev/codeberg.org/danjones000/nomino).
|
||||
- 📝 Fill out README with examples and links.
|
||||
|
||||
#### Tools
|
||||
|
||||
- 🛠 Replace all linting with golangci-lint
|
||||
|
||||
#### Code quality
|
||||
|
||||
- 🚨 A bunch of small improvements from new linter
|
||||
|
||||
### [0.5.0] - 2025-03-19 - ✨ Different types of UUIDs
|
||||
|
||||
#### Features
|
||||
|
||||
- Simplified multiple Generator functions to single function with options
|
||||
- Added a lot of examples for docs
|
||||
- Can add extra Options to Make
|
||||
- ✨ Allow for different types of UUIDs in the UUID `Generator`
|
||||
- ✨ `Config.AddOptions` method
|
||||
- ✨ `Generator.MakeWithConfig` method
|
||||
|
||||
#### Support
|
||||
|
||||
- 📝 Some better examples
|
||||
|
||||
### [0.4.0] - 2025-03-15 - ✨ More Generators, and `Generator.Make` method
|
||||
|
||||
#### Features
|
||||
|
||||
- ✨ Add Random Generator
|
||||
- ✨ Add Make method to Generator
|
||||
- ✨ Add MultiGeneratorRandomOrder
|
||||
|
||||
#### Changes
|
||||
|
||||
- 💔 Breaking changes: Replace HashType with Hasher: This supports all crypto.Hash
|
||||
|
||||
#### Support
|
||||
|
||||
- 📝 Add some missing doc comments
|
||||
|
||||
### [0.3.0] - 2025-03-14 - ♻️ Refactor multiple Generators into one
|
||||
|
||||
#### Features
|
||||
|
||||
- ♻️ Simplified multiple `Generator` functions to single function with options
|
||||
- 📝 Added a lot of examples for docs
|
||||
- ✨ Can add extra `Option`s to `Make`
|
||||
|
||||
Multiple breaking changes around Generators.
|
||||
|
||||
#### Bugs
|
||||
|
||||
- Fixed date formats
|
||||
- 🐛 Fixed date formats
|
||||
|
||||
### [0.2.1] - 2025-03-14
|
||||
### [0.2.1] - 2025-03-14 - ✨ New Hash Generator
|
||||
|
||||
#### Features
|
||||
|
||||
- Add Hash Generator
|
||||
- ✨ Add Hash Generator
|
||||
|
||||
#### Dev Tooling
|
||||
|
||||
- Added a task to serve docs
|
||||
- Added tasts to check code complexity
|
||||
- 🛠 Added a task to serve docs
|
||||
- 🛠 Added tasts to check code complexity
|
||||
|
||||
#### Miscellaneous
|
||||
|
||||
- Fixed some `go vet` complaints
|
||||
- 💚 Fixed some `go vet` complaints
|
||||
|
||||
### [0.2.0] - 2025-03-14
|
||||
### [0.2.0] - 2025-03-14 - ✨ New `Generator`s
|
||||
|
||||
#### Features
|
||||
|
||||
- Add `IncrementalFormat`* Generators
|
||||
- Add `Slug`* Generators
|
||||
- Add `WithOriginalSlug`* Options
|
||||
- Change signature of `Generator` function
|
||||
- ✨ Add `IncrementalFormat`* Generators
|
||||
- ✨ Add `Slug`* Generators
|
||||
- ✨ Add `WithOriginalSlug`* Options
|
||||
- 💔 Change signature of `Generator` function
|
||||
|
||||
Note that this last change **is** a breaking change from 0.0.3, but only for custom Generators.
|
||||
|
||||
## [0.0.3] - 2025-03-11
|
||||
## [0.0.3] - 2025-03-11 - ✨ `WithSeparator`
|
||||
|
||||
### Features
|
||||
|
||||
- Added `WithSeparator` to allow for different separators between the parts of the generated filename.
|
||||
- ✨ Added `WithSeparator` to allow for different separators between the parts of the generated filename.
|
||||
|
||||
## [0.0.2] - 2025-03-11
|
||||
## [0.0.2] - 2025-03-11 - 🐛 Bugfix
|
||||
|
||||
Bugfix release
|
||||
|
||||
### Fixes
|
||||
|
||||
- Extension being ignored. Original included twice.
|
||||
- 🐛 Extension being ignored. Original included twice.
|
||||
|
||||
## [0.0.1] - 2025-03-10
|
||||
## [0.0.1] - 2025-03-10 - 🚀 Initial Release
|
||||
|
||||
Initial Release! Hope you like it!
|
||||
|
||||
### Added
|
||||
|
||||
- nomino.Make
|
||||
- nomino.Config
|
||||
- nomino.Generator
|
||||
- ✨ `nomino.Make``
|
||||
- ✨ `nomino.Config`
|
||||
- ✨ `nomino.Generator`
|
||||
+ We needs more of these until I'm ready
|
||||
- Lots of tests!
|
||||
- ✅ Lots of tests!
|
||||
|
||||
|
|
|
|||
78
README.md
78
README.md
|
|
@ -4,10 +4,80 @@ The purpose of nomino is to generate (probably random) filenames, for example, i
|
|||
|
||||
It takes a lot of inspiration (although no actual code) from [Onym](https://github.com/Blaspsoft/onym).
|
||||
|
||||
Note that this is still not at a stable release. There will be breaking changes between minor releases until it reaches 1.0.0. Patch releases shouldn't contain breaking changes however. Once it reaches 1.0.0, breaking changes will only happen between major releases.
|
||||
Make sure to check out the [official documentation][docs].
|
||||
|
||||
## TODO
|
||||
## Installation
|
||||
|
||||
I'll fill this out more in depth later.
|
||||
Add `codeberg.org/danjones000/nomino` to your project. You can do `go get codeberg.org/danjones000/nomino`, or simply add it to an import and run `go mod tidy`.
|
||||
|
||||
For now, check [official documentation](https://pkg.go.dev/codeberg.org/danjones000/nomino).
|
||||
But, you probably already know this.
|
||||
|
||||
## Usage
|
||||
|
||||
There are **a lot** of examples in the [official documentation][examples]. Take a look through these to get an idea.
|
||||
|
||||
The main concept in `nomino` is the [Generator][]. The simplest way to generate a random filename, is to use the [Generator][] directly, in this way:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func main() {
|
||||
name, _ := nomino.Random().Make()
|
||||
fmt.Println(name) // Du3Sfh8p.txt
|
||||
}
|
||||
```
|
||||
|
||||
The second argument is an error. Most of the generators are always successful, and that error will be `nil`, but you should check the errors if you're not sure.
|
||||
|
||||
The second way to generate a new filename is using the [Make][] function.
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := nomino.NewConfig(nomino.WithGenerator(nomino.Random()))
|
||||
name, _ := nomino.Make(config)
|
||||
fmt.Println(name) // Du3Sfh8p.txt
|
||||
}
|
||||
```
|
||||
|
||||
Although in these examples, `nomino.Make` is more verbose, it can be beneficial to using that function if you have some customizations to how you generate the filenames.
|
||||
|
||||
### Configuration
|
||||
|
||||
The [Config][] allows you to customize how the generated filename works with various [Options][Option]. The [options][Option] allows you to customize things like [adding a prefix](https://pkg.go.dev/codeberg.org/danjones000/nomino#WithPrefix), or changing the [extension](https://pkg.go.dev/codeberg.org/danjones000/nomino#WithExtension) of the generated filename (by default, it uses `.txt`).
|
||||
|
||||
Have a look at [all the Options][Option].
|
||||
|
||||
### Generator
|
||||
|
||||
The [Generator][] is the piece that returns the "random" portion of the generated filename, although, it doesn't actually have to be random.
|
||||
|
||||
Here are the built-in [Generators][Generator]:
|
||||
|
||||
- [UUID](https://pkg.go.dev/codeberg.org/danjones000/nomino#UUID) generates a UUID. This is the default if none is specified.
|
||||
- [Random](https://pkg.go.dev/codeberg.org/danjones000/nomino#Random) generates a random string. By default, it's 8 characters long, but can be whatever length you provide.
|
||||
- [Incremental](https://pkg.go.dev/codeberg.org/danjones000/nomino#Incremental) will generate just a series of integers, starting at 0.
|
||||
- [Timestamp](https://pkg.go.dev/codeberg.org/danjones000/nomino#Timestamp) generates a string from the current time. It will look like "2009-11-10T23-00-00+0000.txt", although this can be customized.
|
||||
- Both [Slug](https://pkg.go.dev/codeberg.org/danjones000/nomino#Slug) and [Hash](https://pkg.go.dev/codeberg.org/danjones000/nomino#Hash) work on the original name provided by [WithOriginal](https://pkg.go.dev/codeberg.org/danjones000/nomino#WithOriginal). Slug generats a slug from the name, while Hash hashes it. By default, it uses MD5.
|
||||
|
||||
You can also use multiple generators, either [in order](https://pkg.go.dev/codeberg.org/danjones000/nomino#MultiGeneratorInOrder), or [in a random order](https://pkg.go.dev/codeberg.org/danjones000/nomino#MultiGeneratorRandomOrder).
|
||||
|
||||
Finally, you can create a [custom generator](https://pkg.go.dev/codeberg.org/danjones000/nomino#example-WithGenerator-CustomGenerator) as well.
|
||||
|
||||
## RTFM (Read the fabulous manual)
|
||||
|
||||
[Official docs][docs], especially the [examples][]. Especially check out the [full example](https://pkg.go.dev/codeberg.org/danjones000/nomino#example-package), which includes how to use a global configuration.
|
||||
|
||||
[docs]: https://pkg.go.dev/codeberg.org/danjones000/nomino
|
||||
[examples]: https://pkg.go.dev/codeberg.org/danjones000/nomino#pkg-examples
|
||||
[Generator]: https://pkg.go.dev/codeberg.org/danjones000/nomino#Generator
|
||||
[Config]: https://pkg.go.dev/codeberg.org/danjones000/nomino#Config
|
||||
[Option]: https://pkg.go.dev/codeberg.org/danjones000/nomino#Option
|
||||
[Make]: https://pkg.go.dev/codeberg.org/danjones000/nomino#Make
|
||||
|
|
|
|||
63
Taskfile.yml
63
Taskfile.yml
|
|
@ -5,7 +5,7 @@ tasks:
|
|||
cmds:
|
||||
- task: fmt
|
||||
- task: test
|
||||
- task: build
|
||||
- task: lint
|
||||
|
||||
fmt:
|
||||
desc: Format go code
|
||||
|
|
@ -22,67 +22,16 @@ tasks:
|
|||
cmds:
|
||||
- go generate ./...
|
||||
|
||||
vet:
|
||||
desc: Vet go code
|
||||
sources:
|
||||
- '**/*.go'
|
||||
deps: [gen]
|
||||
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, gen]
|
||||
deps: [fmt]
|
||||
sources:
|
||||
- '**/*.go'
|
||||
generates:
|
||||
|
|
|
|||
14
config.go
14
config.go
|
|
@ -1,5 +1,6 @@
|
|||
package nomino
|
||||
|
||||
// Config controls how the generatred filename is created.
|
||||
type Config struct {
|
||||
original string
|
||||
prefix string
|
||||
|
|
@ -9,14 +10,25 @@ type Config struct {
|
|||
generator Generator
|
||||
}
|
||||
|
||||
// NewConfig returns a new [Config] with each [Option] specified.
|
||||
// With no Options, the Config uses an extension of .txt, a separator
|
||||
// of _, and the [UUID] [Generator].
|
||||
func NewConfig(options ...Option) Config {
|
||||
conf := Config{
|
||||
extension: ".txt",
|
||||
separator: "_",
|
||||
generator: uuidGen,
|
||||
generator: UUID(nil),
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(&conf)
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
// AddOptions creates a new [Config] with each [Option] added.
|
||||
func (c Config) AddOptions(options ...Option) Config {
|
||||
for _, opt := range options {
|
||||
opt(&c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,3 +20,12 @@ func TestNewConfWithOpts(t *testing.T) {
|
|||
assert.Equal(t, "", c.extension)
|
||||
assert.Equal(t, "foobar", c.prefix)
|
||||
}
|
||||
|
||||
func TestConfAddOpts(t *testing.T) {
|
||||
c := Config{original: "hi"}
|
||||
c2 := c.AddOptions(WithOriginalSlug("Hello, my dear"), WithPrefix("yo"))
|
||||
assert.Equal(t, "", c.prefix)
|
||||
assert.Equal(t, "hi", c.original)
|
||||
assert.Equal(t, "hello-my-dear", c2.original)
|
||||
assert.Equal(t, "yo", c2.prefix)
|
||||
}
|
||||
|
|
|
|||
60
examples_test.go
Normal file
60
examples_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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 {
|
||||
// Here, we use nomino.Make function to generate the filename.
|
||||
// We use our global config, and add in a few extra Options specific to this.
|
||||
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 {
|
||||
// Because we're using a different Generator, we chose to use the Make method on the Generator.
|
||||
// We add in additional Options with the `AddOptions` method on the `Config`
|
||||
newName, _ := nomino.Timestamp(nomino.TimestampUTC()).
|
||||
MakeWithConfig(NominoConfig().AddOptions(
|
||||
nomino.WithExtension("webm"),
|
||||
))
|
||||
return newName
|
||||
}
|
||||
|
||||
// This 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)
|
||||
}
|
||||
54
gen_file.go
54
gen_file.go
|
|
@ -1,17 +1,15 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"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) {
|
||||
|
|
@ -39,43 +37,39 @@ 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
|
||||
|
||||
//go:generate stringer -type=HashType -trimprefix=Hash
|
||||
|
||||
// HashType represents a particular hashing algorithm
|
||||
type HashType uint8
|
||||
|
||||
const (
|
||||
HashMD5 HashType = iota + 1
|
||||
HashSHA1
|
||||
HashSHA256
|
||||
)
|
||||
|
||||
// ErrInvalidHashType is returned by the Hash generator when an invalid HashType is passed
|
||||
var ErrInvalidHashType = errors.New("invalid hash type")
|
||||
|
||||
var hashMap = map[HashType]HashingFunc{
|
||||
HashMD5: md5.New,
|
||||
HashSHA1: sha1.New,
|
||||
HashSHA256: sha256.New,
|
||||
// New allows [HashingFunc] to be used as a [Hasher].
|
||||
func (hf HashingFunc) New() hash.Hash {
|
||||
return hf()
|
||||
}
|
||||
|
||||
// Hasher is a type returns a [hash.Hash].
|
||||
// All [crypto.Hash] may be used.
|
||||
type Hasher interface {
|
||||
New() hash.Hash
|
||||
}
|
||||
|
||||
// ErrInvalidHash is returned by the [Hash] [Generator] when an invalid [Hasher] is passed.
|
||||
var ErrInvalidHash = errors.New("invalid hash type")
|
||||
|
||||
// Hash generates a name from a hash of the filename.
|
||||
// When this is used, the original filename will be removed from the final filename.
|
||||
func Hash(t HashType) Generator {
|
||||
f, ok := hashMap[t]
|
||||
func Hash(h Hasher) Generator {
|
||||
if h == nil {
|
||||
h = crypto.MD5
|
||||
}
|
||||
return func(c *Config) (string, error) {
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", ErrInvalidHashType, t)
|
||||
if h == crypto.MD5SHA1 {
|
||||
return "", ErrInvalidHash
|
||||
}
|
||||
name, err := getOriginal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hs := f()
|
||||
hs := h.New()
|
||||
hs.Write([]byte(name))
|
||||
return fmt.Sprintf("%x", hs.Sum(nil)), nil
|
||||
return hex.EncodeToString(hs.Sum(nil)), nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,66 +1,56 @@
|
|||
package nomino_test
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleSlug() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithOriginal("My name is Jimmy"),
|
||||
nomino.WithGenerator(nomino.Slug()),
|
||||
)
|
||||
str, _ := nomino.Make(conf)
|
||||
str, _ := nomino.Slug().Make(nomino.WithOriginal("My name is Jimmy"))
|
||||
fmt.Println(str)
|
||||
|
||||
// Output: my-name-is-jimmy.txt
|
||||
}
|
||||
|
||||
func ExampleSlug_withLang() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithOriginal("Diese & Dass"),
|
||||
nomino.WithGenerator(nomino.Slug("de")),
|
||||
)
|
||||
|
||||
str, _ := nomino.Make(conf)
|
||||
str, _ := nomino.Slug("de").
|
||||
Make(nomino.WithOriginal("Diese & Dass"))
|
||||
fmt.Println(str)
|
||||
|
||||
// Output: diese-und-dass.txt
|
||||
}
|
||||
|
||||
func ExampleHash_mD5() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithOriginal("foobar"),
|
||||
nomino.WithGenerator(
|
||||
nomino.Hash(nomino.HashMD5),
|
||||
),
|
||||
)
|
||||
str, _ := nomino.Make(conf)
|
||||
str, _ := nomino.Hash(crypto.MD5).
|
||||
Make(nomino.WithOriginal("foobar"))
|
||||
fmt.Println(str)
|
||||
// Output: 3858f62230ac3c915f300c664312c63f.txt
|
||||
}
|
||||
|
||||
func ExampleHash_sHA1() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithOriginal("foobar"),
|
||||
nomino.WithGenerator(
|
||||
nomino.Hash(nomino.HashSHA1),
|
||||
),
|
||||
)
|
||||
str, _ := nomino.Make(conf)
|
||||
str, _ := nomino.Hash(crypto.SHA1).
|
||||
Make(nomino.WithOriginal("foobar"))
|
||||
fmt.Println(str)
|
||||
// Output: 8843d7f92416211de9ebb963ff4ce28125932878.txt
|
||||
}
|
||||
|
||||
func ExampleHash_sHA256() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithOriginal("foobar"),
|
||||
nomino.WithGenerator(
|
||||
nomino.Hash(nomino.HashSHA256),
|
||||
),
|
||||
)
|
||||
str, _ := nomino.Make(conf)
|
||||
str, _ := nomino.Hash(crypto.SHA256).
|
||||
Make(nomino.WithOriginal("foobar"))
|
||||
fmt.Println(str)
|
||||
// Output: c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2.txt
|
||||
}
|
||||
|
||||
func ExampleHashingFunc_hMAC() {
|
||||
var hasher nomino.HashingFunc = func() hash.Hash {
|
||||
return hmac.New(crypto.SHA1.New, []byte("hello"))
|
||||
}
|
||||
g := nomino.Hash(hasher)
|
||||
str, _ := g.Make(nomino.WithOriginal("foobar"))
|
||||
fmt.Println(str)
|
||||
// Output: 85f767c284c80a3a59a9635194321d20dd90f31b.txt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -22,21 +23,15 @@ func TestSlugRemovesOriginal(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHashBadHash(t *testing.T) {
|
||||
conf := NewConfig(WithOriginal("foobar"), WithGenerator(Hash(0)))
|
||||
conf := NewConfig(WithOriginal("foobar"), WithGenerator(Hash(crypto.MD5SHA1)))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Equal(t, "", st)
|
||||
assert.ErrorIs(t, err, ErrInvalidHashType)
|
||||
assert.ErrorContains(t, err, "invalid hash type: HashType(0)")
|
||||
assert.ErrorIs(t, err, ErrInvalidHash)
|
||||
}
|
||||
|
||||
func TestHashMissingOriginal(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Hash(HashMD5)))
|
||||
conf := NewConfig(WithGenerator(Hash(nil)))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Equal(t, "", st)
|
||||
assert.ErrorIs(t, err, ErrMissingOriginal)
|
||||
}
|
||||
|
||||
func TestHashTypeStringer(t *testing.T) {
|
||||
s := HashMD5.String()
|
||||
assert.Equal(t, "MD5", s)
|
||||
}
|
||||
|
|
|
|||
10
gen_int.go
10
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,22 +30,22 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
// IncrementalStepsets the step by which Incremental increases with each invocation.
|
||||
// IncrementalStepsets the step by which [Incremental] increases with each invocation.
|
||||
func IncrementalStep(step int) IncrementalOption {
|
||||
return func(c *incConf) {
|
||||
c.step = step
|
||||
}
|
||||
}
|
||||
|
||||
// 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"
|
||||
// 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".
|
||||
func IncrementalFormat(format string) IncrementalOption {
|
||||
return func(c *incConf) {
|
||||
c.cb = func(i int) string {
|
||||
|
|
|
|||
91
gen_rand.go
Normal file
91
gen_rand.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/deatil/go-encoding/base62"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// UUIDer is an interface for generating UUIDs, by the [UUID] [Generator].
|
||||
// It is recommended that you use either the [UUIDv4] or [UUIDv7] variables.
|
||||
type UUIDer interface {
|
||||
UUID() (uuid.UUID, error)
|
||||
}
|
||||
|
||||
// UUIDFunc is a function that generates a UUID.
|
||||
type UUIDFunc func() (uuid.UUID, error)
|
||||
|
||||
// UUID allows [UUIDFunc] to be used as a [UUIDer].
|
||||
func (u UUIDFunc) UUID() (uuid.UUID, error) {
|
||||
return u()
|
||||
}
|
||||
|
||||
var (
|
||||
// UUIDv1. You probably don't want to use this. It is included for completeness sake.
|
||||
UUIDv1 = UUIDFunc(uuid.NewUUID)
|
||||
// UUIDv4 is the default.
|
||||
UUIDv4 = UUIDFunc(uuid.NewRandom)
|
||||
// UUIDv6 is primarily a replacement for UUIDv1. You probably should use 4 or 7.
|
||||
UUIDv6 = UUIDFunc(uuid.NewV6)
|
||||
// UUIDv7 should be used if you want it sortable by time.
|
||||
UUIDv7 = UUIDFunc(uuid.NewV7)
|
||||
)
|
||||
|
||||
// UUID generates a UUID. If nil is passed as an argument,
|
||||
// a UUIDv4 is generated.
|
||||
func UUID(u UUIDer) Generator {
|
||||
if u == nil {
|
||||
u = UUIDv4
|
||||
}
|
||||
return func(*Config) (string, error) {
|
||||
uu, err := u.UUID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return uu.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
type randConf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
// RandomOption is an option for the [Random] [Generator].
|
||||
type RandomOption func(*randConf)
|
||||
|
||||
// RandomLength controls the length of the string generated by [Random].
|
||||
func RandomLength(length int) RandomOption {
|
||||
return func(c *randConf) {
|
||||
c.length = length
|
||||
}
|
||||
}
|
||||
|
||||
func getRandomBytes(l int) []byte {
|
||||
key := make([]byte, l)
|
||||
_, _ = 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 {
|
||||
c := randConf{8}
|
||||
for _, opt := range opts {
|
||||
opt(&c)
|
||||
}
|
||||
return func(*Config) (string, error) {
|
||||
var buff strings.Builder
|
||||
buff.Grow(c.length)
|
||||
fillBuffer(&buff, c.length)
|
||||
return buff.String(), nil
|
||||
}
|
||||
}
|
||||
40
gen_rand_examples_test.go
Normal file
40
gen_rand_examples_test.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package nomino_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleUUID() {
|
||||
gen := nomino.UUID(nil)
|
||||
|
||||
str, _ := gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
func ExampleUUID_v7() {
|
||||
gen := nomino.UUID(nomino.UUIDv7)
|
||||
|
||||
str, _ := gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
func ExampleRandom() {
|
||||
str, _ := nomino.Random().Make()
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
func ExampleRandomLength() {
|
||||
str, _ := nomino.Random(nomino.RandomLength(32)).Make()
|
||||
fmt.Println(str)
|
||||
}
|
||||
41
gen_rand_test.go
Normal file
41
gen_rand_test.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
st, err := UUID(nil)(nil)
|
||||
assert.NoError(t, err)
|
||||
_, parseErr := uuid.Parse(st)
|
||||
assert.NoError(t, parseErr)
|
||||
}
|
||||
|
||||
type badRead struct{}
|
||||
|
||||
func (badRead) Read([]byte) (int, error) {
|
||||
return 0, errTest
|
||||
}
|
||||
|
||||
func TestUUIDFail(t *testing.T) {
|
||||
uuid.SetRand(badRead{})
|
||||
defer uuid.SetRand(nil)
|
||||
|
||||
_, err := UUID(nil)(nil)
|
||||
assert.ErrorIs(t, err, errTest)
|
||||
}
|
||||
|
||||
func TestRand(t *testing.T) {
|
||||
st, err := Random()(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, st, 8)
|
||||
}
|
||||
|
||||
func TestRandLen(t *testing.T) {
|
||||
st, err := Random(RandomLength(32))(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, st, 32)
|
||||
}
|
||||
12
gen_ts.go
12
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 [Timestamp].
|
||||
const FileTimestamp string = "2006-01-02T15-04-05-0700"
|
||||
|
||||
// FileTimestampNoTZ is the default format for WithTimestampUTC and WithTimeUTC
|
||||
// FileTimestampNoTZ is the default format when using the [TimestampUTC] [TimestampOption].
|
||||
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 {
|
||||
|
|
@ -35,7 +35,7 @@ func Timestamp(opts ...TimestampOption) Generator {
|
|||
}
|
||||
|
||||
// TimestampFormat sets the format for the generated name.
|
||||
// Consult time.Time.Format for details on the format.
|
||||
// Consult [time.Time.Format] for details on the format.
|
||||
func TimestampFormat(format string) TimestampOption {
|
||||
return func(c *timestampConf) {
|
||||
c.format = format
|
||||
|
|
@ -43,7 +43,7 @@ func TimestampFormat(format string) TimestampOption {
|
|||
}
|
||||
|
||||
// TimestampTime sets the time for the generated name.
|
||||
// By default, it uses the current time.
|
||||
// By default, [Timestamp] uses the current time.
|
||||
func TimestampTime(t time.Time) TimestampOption {
|
||||
return func(c *timestampConf) {
|
||||
c.ts = t
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import (
|
|||
|
||||
func ExampleTimestamp() {
|
||||
gen := nomino.Timestamp()
|
||||
conf := nomino.NewConfig(nomino.WithGenerator(gen))
|
||||
s, _ := nomino.Make(conf)
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
|
|
@ -18,8 +17,7 @@ func ExampleTimestampTime() {
|
|||
tz, _ := time.LoadLocation("America/New_York")
|
||||
ts := time.Date(2009, time.January, 20, 12, 5, 0, 0, tz)
|
||||
gen := nomino.Timestamp(nomino.TimestampTime(ts))
|
||||
conf := nomino.NewConfig(nomino.WithGenerator(gen))
|
||||
s, _ := nomino.Make(conf)
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
// Output: 2009-01-20T12-05-00-0500.txt
|
||||
}
|
||||
|
|
@ -28,8 +26,7 @@ func ExampleTimestampFormat() {
|
|||
tz, _ := time.LoadLocation("America/New_York")
|
||||
ts := time.Date(2009, time.January, 20, 12, 5, 0, 0, tz)
|
||||
gen := nomino.Timestamp(nomino.TimestampTime(ts), nomino.TimestampFormat("2006#01#02<>15|04|05-0700"))
|
||||
conf := nomino.NewConfig(nomino.WithGenerator(gen))
|
||||
s, _ := nomino.Make(conf)
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
// Output: 2009#01#20<>12|05|00-0500.txt
|
||||
}
|
||||
|
|
@ -38,8 +35,7 @@ func ExampleTimestampUTC() {
|
|||
tz, _ := time.LoadLocation("America/New_York")
|
||||
ts := time.Date(2009, time.January, 20, 12, 5, 0, 0, tz)
|
||||
gen := nomino.Timestamp(nomino.TimestampTime(ts), nomino.TimestampUTC())
|
||||
conf := nomino.NewConfig(nomino.WithGenerator(gen))
|
||||
s, _ := nomino.Make(conf)
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
// Output: 2009-01-20T17-05-00.txt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ package nomino
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Generator is a function that returns the "random" portion of the returned filename.
|
||||
|
|
@ -11,14 +10,25 @@ import (
|
|||
// for example.
|
||||
type Generator func(conf *Config) (string, error)
|
||||
|
||||
// WithGenerator sets the specified generator
|
||||
// Make allows you to generate a new string directly from a [Generator].
|
||||
func (g Generator) Make(opts ...Option) (string, error) {
|
||||
return g.MakeWithConfig(NewConfig(opts...))
|
||||
}
|
||||
|
||||
// MakeWithConfig allows you to generate a new string directly from a [Generator]
|
||||
// with a pre-existing Config.
|
||||
func (g Generator) MakeWithConfig(c Config) (string, error) {
|
||||
return Make(c.AddOptions(WithGenerator(g)))
|
||||
}
|
||||
|
||||
// WithGenerator sets the specified [Generator].
|
||||
func WithGenerator(g Generator) Option {
|
||||
return func(c *Config) {
|
||||
c.generator = g
|
||||
}
|
||||
}
|
||||
|
||||
// ErrMissingGenerators is returned by a multi-generator if no generators are supplied.
|
||||
// ErrMissingGenerators is returned by a multi-generator if a [Generator] isn't supplied.
|
||||
var ErrMissingGenerators = errors.New("no generators supplied")
|
||||
|
||||
func missingGen(*Config) (string, error) {
|
||||
|
|
@ -26,7 +36,7 @@ func missingGen(*Config) (string, error) {
|
|||
}
|
||||
|
||||
// MultiGeneratorInOrder allows the use of multiple generators. Each new invokation will use the next generator in turn.
|
||||
// If none are passed, the generator will always return ErrMissingGenerators.
|
||||
// If none are passed, the generator will always return [ErrMissingGenerators].
|
||||
func MultiGeneratorInOrder(gens ...Generator) Generator {
|
||||
if len(gens) == 0 {
|
||||
return missingGen
|
||||
|
|
@ -44,15 +54,20 @@ func MultiGeneratorInOrder(gens ...Generator) Generator {
|
|||
}
|
||||
}
|
||||
|
||||
func uuidGen(*Config) (string, error) {
|
||||
u, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
// MultiGeneratorRandomOrder allows the use of multiple generators. Each new invokation will use one of the generators randomly.
|
||||
// If none are passed, the generator will always return [ErrMissingGenerators].
|
||||
func MultiGeneratorRandomOrder(gens ...Generator) Generator {
|
||||
if len(gens) == 0 {
|
||||
return missingGen
|
||||
}
|
||||
|
||||
// UUID generates a UUIDv4.
|
||||
func UUID() Generator {
|
||||
return uuidGen
|
||||
if len(gens) == 1 {
|
||||
return gens[0]
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,14 @@ import (
|
|||
)
|
||||
|
||||
func ExampleWithGenerator_customGenerator() {
|
||||
gen := func(*nomino.Config) (string, error) {
|
||||
var gen nomino.Generator = func(*nomino.Config) (string, error) {
|
||||
return "hello", nil
|
||||
}
|
||||
option := nomino.WithGenerator(gen)
|
||||
|
||||
str, _ := nomino.Make(nomino.NewConfig(option))
|
||||
str, _ := gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(nomino.NewConfig(
|
||||
option,
|
||||
nomino.WithoutExtension(),
|
||||
))
|
||||
str, _ = gen.Make(nomino.WithoutExtension())
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
|
|
@ -26,40 +22,58 @@ func ExampleWithGenerator_customGenerator() {
|
|||
// hello
|
||||
}
|
||||
|
||||
func ExampleGenerator_Make() {
|
||||
g := nomino.Incremental()
|
||||
st, _ := g.Make()
|
||||
fmt.Println(st)
|
||||
|
||||
st, _ = g.Make(nomino.WithPrefix("foo"))
|
||||
fmt.Println(st)
|
||||
|
||||
// Output:
|
||||
// 0.txt
|
||||
// foo_1.txt
|
||||
}
|
||||
|
||||
func ExampleMultiGeneratorInOrder() {
|
||||
gen1 := func(*nomino.Config) (string, error) {
|
||||
return "hello", nil
|
||||
return "bonjour", nil
|
||||
}
|
||||
gen2 := func(*nomino.Config) (string, error) {
|
||||
return "goodbye", nil
|
||||
}
|
||||
gen := nomino.MultiGeneratorInOrder(gen1, gen2)
|
||||
option := nomino.WithGenerator(gen)
|
||||
|
||||
str, _ := nomino.Make(nomino.NewConfig(option))
|
||||
str, _ := gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(nomino.NewConfig(option))
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(nomino.NewConfig(option))
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// hello.txt
|
||||
// bonjour.txt
|
||||
// goodbye.txt
|
||||
// hello.txt
|
||||
// bonjour.txt
|
||||
}
|
||||
|
||||
func ExampleUUID() {
|
||||
option := nomino.WithGenerator(nomino.UUID())
|
||||
func ExampleMultiGeneratorRandomOrder() {
|
||||
gen1 := func(*nomino.Config) (string, error) {
|
||||
return "guten-tag", nil
|
||||
}
|
||||
gen2 := func(*nomino.Config) (string, error) {
|
||||
return "wiedersehen", nil
|
||||
}
|
||||
gen := nomino.MultiGeneratorRandomOrder(gen1, gen2)
|
||||
|
||||
str, _ := nomino.Make(nomino.NewConfig(option))
|
||||
str, _ := gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(nomino.NewConfig(option))
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(nomino.NewConfig(option))
|
||||
str, _ = gen.Make()
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,52 +4,58 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
out1 string = "abc"
|
||||
out2 string = "def"
|
||||
)
|
||||
|
||||
var (
|
||||
outs = []string{out1, out2}
|
||||
err1 = errors.New("oops")
|
||||
gen1 Generator = func(*Config) (string, error) { return out1, nil }
|
||||
gen2 Generator = func(*Config) (string, error) { return out2, nil }
|
||||
gen3 Generator = func(*Config) (string, error) { return "", err1 }
|
||||
gens = []Generator{gen1, gen2, gen3}
|
||||
)
|
||||
|
||||
func TestWithGenerator(t *testing.T) {
|
||||
g := func(*Config) (string, error) { return "abc", nil }
|
||||
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, "abc", st)
|
||||
assert.Equal(t, out1, st)
|
||||
}
|
||||
|
||||
func TestMultiGeneratorInOrder(t *testing.T) {
|
||||
st1 := "abc"
|
||||
st2 := "def"
|
||||
er1 := errors.New("oops")
|
||||
g1 := func(*Config) (string, error) { return st1, nil }
|
||||
g2 := func(*Config) (string, error) { return st2, nil }
|
||||
g3 := func(*Config) (string, error) { return "", er1 }
|
||||
g := MultiGeneratorInOrder(g1, g2, g3)
|
||||
g := MultiGeneratorInOrder(gens...)
|
||||
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, st2, st)
|
||||
assert.Equal(t, out2, st)
|
||||
st, err = g(nil)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, er1)
|
||||
assert.ErrorIs(t, err, err1)
|
||||
st, err = g(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, st1, st)
|
||||
assert.Equal(t, out1, st)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -62,23 +68,38 @@ func TestMultiGeneratorInOrderMissing(t *testing.T) {
|
|||
assert.ErrorIs(t, err, ErrMissingGenerators)
|
||||
}
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
st, err := UUID()(nil)
|
||||
func TestMultiGeneratorRandomOrder(t *testing.T) {
|
||||
g := MultiGeneratorRandomOrder(gens...)
|
||||
for i := 0; i < 4; i++ {
|
||||
st, err := g(nil)
|
||||
if err != nil {
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, err1)
|
||||
} else {
|
||||
assert.Contains(t, outs, st)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiGeneratorRandomOrderOne(t *testing.T) {
|
||||
st1 := "abc"
|
||||
g1 := func(*Config) (string, error) { return st1, nil }
|
||||
g := MultiGeneratorRandomOrder(g1)
|
||||
|
||||
st, err := g(nil)
|
||||
assert.NoError(t, err)
|
||||
_, parseErr := uuid.Parse(st)
|
||||
assert.NoError(t, parseErr)
|
||||
assert.Equal(t, st1, st)
|
||||
st, err = g(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, st1, st)
|
||||
}
|
||||
|
||||
type badRead struct{}
|
||||
|
||||
func (badRead) Read([]byte) (int, error) {
|
||||
return 0, errors.New("sorry")
|
||||
}
|
||||
|
||||
func TestUUIDFail(t *testing.T) {
|
||||
uuid.SetRand(badRead{})
|
||||
defer uuid.SetRand(nil)
|
||||
|
||||
_, err := UUID()(nil)
|
||||
assert.Equal(t, errors.New("sorry"), err)
|
||||
func TestMultiGeneratorRandomOrderMissing(t *testing.T) {
|
||||
g := MultiGeneratorRandomOrder()
|
||||
st, err := g(nil)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, ErrMissingGenerators)
|
||||
st, err = g(nil)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, ErrMissingGenerators)
|
||||
}
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -3,6 +3,7 @@ module codeberg.org/danjones000/nomino
|
|||
go 1.23.6
|
||||
|
||||
require (
|
||||
github.com/deatil/go-encoding v1.0.3003
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gosimple/slug v1.15.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -1,5 +1,7 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deatil/go-encoding v1.0.3003 h1:2b05UO+5JfVcXcOa8n/X3pm8aC6L6ET0mBZCb1kj3ck=
|
||||
github.com/deatil/go-encoding v1.0.3003/go.mod h1:lTMMKsG0RRPGZzdW2EPVJCA7HQy4o1ZQKPf5CmVDy2k=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo=
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
// Code generated by "stringer -type=HashType -trimprefix=Hash"; DO NOT EDIT.
|
||||
|
||||
package nomino
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[HashMD5-1]
|
||||
_ = x[HashSHA1-2]
|
||||
_ = x[HashSHA256-3]
|
||||
}
|
||||
|
||||
const _HashType_name = "MD5SHA1SHA256"
|
||||
|
||||
var _HashType_index = [...]uint8{0, 3, 7, 13}
|
||||
|
||||
func (i HashType) String() string {
|
||||
i -= 1
|
||||
if i >= HashType(len(_HashType_index)-1) {
|
||||
return "HashType(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
}
|
||||
return _HashType_name[_HashType_index[i]:_HashType_index[i+1]]
|
||||
}
|
||||
4
make.go
4
make.go
|
|
@ -2,9 +2,9 @@ package nomino
|
|||
|
||||
import "fmt"
|
||||
|
||||
// Make generates a random filename. The behavior can be controlled by specifying Options
|
||||
// Make generates a random filename. The behavior can be controlled by specifying Options.
|
||||
// In general, the final filename will be [prefix]_[generated_string]_[original_filename]_[suffix].[extension].
|
||||
// If the name generator returns an error (generally, it shouldn't), that will be returned instead.
|
||||
// If the name generator returns an error (generally, it shouldn't), that error will be returned instead.
|
||||
func Make(conf Config, opts ...Option) (string, error) {
|
||||
for _, opt := range opts {
|
||||
opt(&conf)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
7
nomino.go
Normal file
7
nomino.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// Package nomino is a utility that allows us to generate random filenames.
|
||||
//
|
||||
// There are two main methods of using nomino.
|
||||
//
|
||||
// 1. Using the [Make] function.
|
||||
// 2. Creating a generator, and using its [Generator.Make] method.
|
||||
package nomino
|
||||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/gosimple/slug"
|
||||
)
|
||||
|
||||
// Option sets configuration parameters for Config.
|
||||
// Option sets configuration parameters for [Config].
|
||||
type Option func(c *Config)
|
||||
|
||||
// WithOriginal sets the original filename.
|
||||
|
|
@ -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