Compare commits
No commits in common. "stable" and "v0.2.1" have entirely different histories.
31 changed files with 612 additions and 1189 deletions
|
|
@ -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
|
||||
95
CHANGELOG.md
95
CHANGELOG.md
|
|
@ -1,111 +1,54 @@
|
|||
# Changelog
|
||||
|
||||
### [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
|
||||
### [0.2.1] - 2025-03-14
|
||||
|
||||
#### Features
|
||||
|
||||
- ✨ 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
|
||||
|
||||
### [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 - ✨ New `Generator`s
|
||||
### [0.2.0] - 2025-03-14
|
||||
|
||||
#### 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 - ✨ `WithSeparator`
|
||||
## [0.0.3] - 2025-03-11
|
||||
|
||||
### 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 - 🐛 Bugfix
|
||||
## [0.0.2] - 2025-03-11
|
||||
|
||||
Bugfix release
|
||||
|
||||
### Fixes
|
||||
|
||||
- 🐛 Extension being ignored. Original included twice.
|
||||
- Extension being ignored. Original included twice.
|
||||
|
||||
## [0.0.1] - 2025-03-10 - 🚀 Initial Release
|
||||
## [0.0.1] - 2025-03-10
|
||||
|
||||
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,80 +4,8 @@ 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).
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
For now, check [official documentation](https://pkg.go.dev/codeberg.org/danjones000/nomino).
|
||||
|
|
|
|||
61
Taskfile.yml
61
Taskfile.yml
|
|
@ -5,7 +5,7 @@ tasks:
|
|||
cmds:
|
||||
- task: fmt
|
||||
- task: test
|
||||
- task: lint
|
||||
- task: build
|
||||
|
||||
fmt:
|
||||
desc: Format go code
|
||||
|
|
@ -22,16 +22,67 @@ tasks:
|
|||
cmds:
|
||||
- go generate ./...
|
||||
|
||||
lint:
|
||||
desc: Do static analysis
|
||||
vet:
|
||||
desc: Vet go code
|
||||
sources:
|
||||
- '**/*.go'
|
||||
deps: [gen]
|
||||
cmds:
|
||||
- go vet ./...
|
||||
|
||||
critic:
|
||||
desc: Critique go code
|
||||
sources:
|
||||
- '**/*.go'
|
||||
cmds:
|
||||
- golangci-lint run
|
||||
- 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, gen]
|
||||
sources:
|
||||
- '**/*.go'
|
||||
generates:
|
||||
|
|
|
|||
14
config.go
14
config.go
|
|
@ -1,6 +1,5 @@
|
|||
package nomino
|
||||
|
||||
// Config controls how the generatred filename is created.
|
||||
type Config struct {
|
||||
original string
|
||||
prefix string
|
||||
|
|
@ -10,25 +9,14 @@ 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: UUID(nil),
|
||||
generator: uuidGen,
|
||||
}
|
||||
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,12 +20,3 @@ 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,60 +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 {
|
||||
// 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)
|
||||
}
|
||||
75
gen_file.go
75
gen_file.go
|
|
@ -1,75 +0,0 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"hash"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
if c.original == "" {
|
||||
return "", ErrMissingOriginal
|
||||
}
|
||||
name := c.original
|
||||
c.original = ""
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// Slug generates a name from the original filename.
|
||||
// When this is used, the original filename will be removed from the final filename.
|
||||
// If a language is specified, that may affect the resulting slug.
|
||||
func Slug(lang ...string) Generator {
|
||||
ret := slug.Make
|
||||
if len(lang) > 0 {
|
||||
ret = func(in string) string {
|
||||
return slug.MakeLang(in, lang[0])
|
||||
}
|
||||
}
|
||||
return func(c *Config) (string, error) {
|
||||
name, err := getOriginal(c)
|
||||
return ret(name), err
|
||||
}
|
||||
}
|
||||
|
||||
// HashingFunc is a function that generates a [hash.Hash].
|
||||
type HashingFunc func() hash.Hash
|
||||
|
||||
// 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(h Hasher) Generator {
|
||||
if h == nil {
|
||||
h = crypto.MD5
|
||||
}
|
||||
return func(c *Config) (string, error) {
|
||||
if h == crypto.MD5SHA1 {
|
||||
return "", ErrInvalidHash
|
||||
}
|
||||
name, err := getOriginal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hs := h.New()
|
||||
hs.Write([]byte(name))
|
||||
return hex.EncodeToString(hs.Sum(nil)), nil
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package nomino_test
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleSlug() {
|
||||
str, _ := nomino.Slug().Make(nomino.WithOriginal("My name is Jimmy"))
|
||||
fmt.Println(str)
|
||||
|
||||
// Output: my-name-is-jimmy.txt
|
||||
}
|
||||
|
||||
func ExampleSlug_withLang() {
|
||||
str, _ := nomino.Slug("de").
|
||||
Make(nomino.WithOriginal("Diese & Dass"))
|
||||
fmt.Println(str)
|
||||
|
||||
// Output: diese-und-dass.txt
|
||||
}
|
||||
|
||||
func ExampleHash_mD5() {
|
||||
str, _ := nomino.Hash(crypto.MD5).
|
||||
Make(nomino.WithOriginal("foobar"))
|
||||
fmt.Println(str)
|
||||
// Output: 3858f62230ac3c915f300c664312c63f.txt
|
||||
}
|
||||
|
||||
func ExampleHash_sHA1() {
|
||||
str, _ := nomino.Hash(crypto.SHA1).
|
||||
Make(nomino.WithOriginal("foobar"))
|
||||
fmt.Println(str)
|
||||
// Output: 8843d7f92416211de9ebb963ff4ce28125932878.txt
|
||||
}
|
||||
|
||||
func ExampleHash_sHA256() {
|
||||
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,37 +0,0 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSlugMissingFilename(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Slug()))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, ErrMissingOriginal)
|
||||
}
|
||||
|
||||
func TestSlugRemovesOriginal(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Slug()), WithOriginal("Hello, World"))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Zero(t, conf.original)
|
||||
assert.Equal(t, "hello-world", st)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestHashBadHash(t *testing.T) {
|
||||
conf := NewConfig(WithOriginal("foobar"), WithGenerator(Hash(crypto.MD5SHA1)))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Equal(t, "", st)
|
||||
assert.ErrorIs(t, err, ErrInvalidHash)
|
||||
}
|
||||
|
||||
func TestHashMissingOriginal(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Hash(nil)))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Equal(t, "", st)
|
||||
assert.ErrorIs(t, err, ErrMissingOriginal)
|
||||
}
|
||||
55
gen_int.go
55
gen_int.go
|
|
@ -1,55 +0,0 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type incConf struct {
|
||||
start int
|
||||
step int
|
||||
cb func(int) string
|
||||
}
|
||||
|
||||
// IncrementalOption sets an option for the [Incremental] [Generator].
|
||||
type IncrementalOption func(c *incConf)
|
||||
|
||||
// Incremental generates a name that is a series of integers.
|
||||
// By default it begins at 0 and increments by 1 each time.
|
||||
func Incremental(opts ...IncrementalOption) Generator {
|
||||
c := incConf{step: 1, cb: strconv.Itoa}
|
||||
for _, opt := range opts {
|
||||
opt(&c)
|
||||
}
|
||||
|
||||
next := c.start
|
||||
return func(*Config) (string, error) {
|
||||
out := c.cb(next)
|
||||
next += c.step
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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".
|
||||
func IncrementalFormat(format string) IncrementalOption {
|
||||
return func(c *incConf) {
|
||||
c.cb = func(i int) string {
|
||||
return fmt.Sprintf(format, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
package nomino_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleIncremental() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("foo"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
)
|
||||
|
||||
str, _ := nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_0.txt
|
||||
// foo_1.txt
|
||||
// foo_2.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalStart() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("foo"),
|
||||
nomino.WithGenerator(nomino.Incremental(
|
||||
nomino.IncrementalStart(42),
|
||||
)),
|
||||
)
|
||||
|
||||
str, _ := nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_42.txt
|
||||
// foo_43.txt
|
||||
// foo_44.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalStep() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("foo"),
|
||||
nomino.WithGenerator(nomino.Incremental(
|
||||
nomino.IncrementalStep(2),
|
||||
)),
|
||||
)
|
||||
|
||||
str, _ := nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_0.txt
|
||||
// foo_2.txt
|
||||
// foo_4.txt
|
||||
}
|
||||
|
||||
func ExampleIncremental_withStartAndStep() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("foo"),
|
||||
nomino.WithGenerator(nomino.Incremental(
|
||||
nomino.IncrementalStart(42),
|
||||
nomino.IncrementalStep(2),
|
||||
)),
|
||||
)
|
||||
|
||||
str, _ := nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_42.txt
|
||||
// foo_44.txt
|
||||
// foo_46.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalFormat() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("foo"),
|
||||
nomino.WithGenerator(nomino.Incremental(
|
||||
nomino.IncrementalFormat("%03d"),
|
||||
)),
|
||||
)
|
||||
|
||||
str, _ := nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = nomino.Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_000.txt
|
||||
// foo_001.txt
|
||||
// foo_002.txt
|
||||
}
|
||||
91
gen_rand.go
91
gen_rand.go
|
|
@ -1,91 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
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)
|
||||
}
|
||||
59
gen_ts.go
59
gen_ts.go
|
|
@ -1,59 +0,0 @@
|
|||
package nomino
|
||||
|
||||
import "time"
|
||||
|
||||
// FileTimestamp is the default format for [Timestamp].
|
||||
const FileTimestamp string = "2006-01-02T15-04-05-0700"
|
||||
|
||||
// FileTimestampNoTZ is the default format when using the [TimestampUTC] [TimestampOption].
|
||||
const FileTimestampNoTZ string = "2006-01-02T15-04-05"
|
||||
|
||||
type timestampConf struct {
|
||||
format string
|
||||
ts time.Time
|
||||
utc bool
|
||||
}
|
||||
|
||||
// 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.
|
||||
func Timestamp(opts ...TimestampOption) Generator {
|
||||
c := timestampConf{format: FileTimestamp, ts: time.Now()}
|
||||
for _, opt := range opts {
|
||||
opt(&c)
|
||||
}
|
||||
|
||||
if c.utc {
|
||||
c.ts = c.ts.UTC()
|
||||
}
|
||||
|
||||
return func(*Config) (string, error) {
|
||||
return c.ts.Format(c.format), nil
|
||||
}
|
||||
}
|
||||
|
||||
// TimestampFormat sets the format for the generated name.
|
||||
// Consult [time.Time.Format] for details on the format.
|
||||
func TimestampFormat(format string) TimestampOption {
|
||||
return func(c *timestampConf) {
|
||||
c.format = format
|
||||
}
|
||||
}
|
||||
|
||||
// TimestampTime sets the time for the generated name.
|
||||
// By default, [Timestamp] uses the current time.
|
||||
func TimestampTime(t time.Time) TimestampOption {
|
||||
return func(c *timestampConf) {
|
||||
c.ts = t
|
||||
}
|
||||
}
|
||||
|
||||
// TimestampUTC uses the time in UTC, while also stripping the timezone from the format.
|
||||
func TimestampUTC() TimestampOption {
|
||||
return func(c *timestampConf) {
|
||||
c.utc = true
|
||||
c.format = FileTimestampNoTZ
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package nomino_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleTimestamp() {
|
||||
gen := nomino.Timestamp()
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
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))
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
// Output: 2009-01-20T12-05-00-0500.txt
|
||||
}
|
||||
|
||||
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"))
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
// Output: 2009#01#20<>12|05|00-0500.txt
|
||||
}
|
||||
|
||||
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())
|
||||
s, _ := gen.Make()
|
||||
fmt.Println(s)
|
||||
// Output: 2009-01-20T17-05-00.txt
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTimestamp(t *testing.T) {
|
||||
n := time.Now()
|
||||
st, err := Timestamp()(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, n.Format(FileTimestamp), st)
|
||||
}
|
||||
213
generators.go
213
generators.go
|
|
@ -1,8 +1,17 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gosimple/slug"
|
||||
)
|
||||
|
||||
// Generator is a function that returns the "random" portion of the returned filename.
|
||||
|
|
@ -10,25 +19,14 @@ import (
|
|||
// for example.
|
||||
type Generator func(conf *Config) (string, error)
|
||||
|
||||
// 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].
|
||||
// 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 a [Generator] isn't supplied.
|
||||
// ErrMissingGenerators is returned by a multi-generator if no generators are supplied.
|
||||
var ErrMissingGenerators = errors.New("no generators supplied")
|
||||
|
||||
func missingGen(*Config) (string, error) {
|
||||
|
|
@ -36,7 +34,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
|
||||
|
|
@ -54,20 +52,181 @@ func MultiGeneratorInOrder(gens ...Generator) Generator {
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
func uuidGen(*Config) (string, error) {
|
||||
u, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
if len(gens) == 1 {
|
||||
return gens[0]
|
||||
}
|
||||
// UUID generates a UUIDv4.
|
||||
func UUID() Generator {
|
||||
return uuidGen
|
||||
}
|
||||
|
||||
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)
|
||||
// FileTimestamp is the default format for WithTimestamp and WithTime
|
||||
const FileTimestamp string = "2006-01-02_03-05-06-0700"
|
||||
|
||||
// Timestamp generates a a date and time for the current time.
|
||||
// It is formatted accourding to FileTimestamp
|
||||
func Timestamp() Generator {
|
||||
return TimestampWithFormat(FileTimestamp)
|
||||
}
|
||||
|
||||
// TimestampWithFormat generates a date and time for the current time with the supplied format.
|
||||
func TimestampWithFormat(f string) Generator {
|
||||
return FormattedTime(time.Now(), FileTimestamp)
|
||||
}
|
||||
|
||||
// Time generates a date and time for the supplied time.
|
||||
// It is formatted accourding to FileTimestamp
|
||||
func Time(t time.Time) Generator {
|
||||
return FormattedTime(t, FileTimestamp)
|
||||
}
|
||||
|
||||
// FormattedTime generates a date and time for the supplied time with the supplied format.
|
||||
func FormattedTime(t time.Time, f string) Generator {
|
||||
return func(*Config) (string, error) {
|
||||
return t.Format(f), nil
|
||||
}
|
||||
}
|
||||
|
||||
// FileTimestampNoTZ is the default format for WithTimestampUTC and WithTimeUTC
|
||||
const FileTimestampNoTZ string = "2006-01-02_03-05-06"
|
||||
|
||||
// TimestampUTC generates a date and time for the current time in UTC without a timezone in the format.
|
||||
func TimestampUTC() Generator {
|
||||
return TimeUTC(time.Now())
|
||||
}
|
||||
|
||||
// TimeUTC generates a date and time for the supplied time in UTC without a timezone in the format.
|
||||
func TimeUTC(t time.Time) Generator {
|
||||
return FormattedTime(t.UTC(), FileTimestampNoTZ)
|
||||
}
|
||||
|
||||
// Incremental generates a name that is a series of integers starting at 0
|
||||
func Incremental() Generator {
|
||||
return IncrementalWithStartAndStep(0, 1)
|
||||
}
|
||||
|
||||
// IncrementalFormat generates a name that is a series of integers starting at 0, formatted with Printf
|
||||
// This is mostly likely useful with a format like "%02d"
|
||||
func IncrementalFormat(format string) Generator {
|
||||
return IncrementalFormatWithStartAndStep(0, 1, format)
|
||||
}
|
||||
|
||||
// IncrementalWithStart generates a name that is a series of integers starting at the specified number
|
||||
func IncrementalWithStart(start int) Generator {
|
||||
return IncrementalWithStartAndStep(start, 1)
|
||||
}
|
||||
|
||||
// IncrementalFormatWithStart generates a name that is a series of integers starting at the specified number, formatted with Printf
|
||||
func IncrementalFormatWithStart(start int, format string) Generator {
|
||||
return IncrementalFormatWithStartAndStep(start, 1, format)
|
||||
}
|
||||
|
||||
// IncrementalWithStep generates a name that is a series of integers, starting at 0, and increasing the specified number each time
|
||||
func IncrementalWithStep(step int) Generator {
|
||||
return IncrementalWithStartAndStep(0, step)
|
||||
}
|
||||
|
||||
// IncrementalFormatWithStep generates a name that is a series of integers, starting at 0, and increasing the specified number each time,
|
||||
// formatted with Printf
|
||||
func IncrementalFormatWithStep(step int, format string) Generator {
|
||||
return IncrementalFormatWithStartAndStep(0, step, format)
|
||||
}
|
||||
|
||||
func incrementalHelper(start, step int, cb func(int) string) Generator {
|
||||
next := start
|
||||
return func(*Config) (string, error) {
|
||||
out := cb(next)
|
||||
next += step
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
// InrementalWithStartAndStep generates a name that is a series of integers, starting at the specified number,
|
||||
// and increasing the specified step each time
|
||||
func IncrementalWithStartAndStep(start, step int) Generator {
|
||||
return incrementalHelper(start, step, strconv.Itoa)
|
||||
}
|
||||
|
||||
// IncrementalFormatWithStartAndStep generates a name that is a series of integers, starting at the specified number,
|
||||
// and increasing the specified step each time, formatted with Printf
|
||||
func IncrementalFormatWithStartAndStep(start, step int, format string) Generator {
|
||||
return incrementalHelper(start, step, func(i int) string {
|
||||
return fmt.Sprintf(format, i)
|
||||
})
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if c.original == "" {
|
||||
return "", ErrMissingOriginal
|
||||
}
|
||||
name := c.original
|
||||
c.original = ""
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// Slug generates a name from the original filename.
|
||||
// When this is used, the original filename will be removed from the final filename.
|
||||
func Slug() Generator {
|
||||
return func(c *Config) (string, error) {
|
||||
name, err := getOriginal(c)
|
||||
return slug.Make(name), err
|
||||
}
|
||||
}
|
||||
|
||||
// SlugWithLang generates a name from the original filename, accounting for the given language.
|
||||
// When this is used, the original filename will be removed from the final filename.
|
||||
func SlugWithLang(lang string) Generator {
|
||||
return func(c *Config) (string, error) {
|
||||
name, err := getOriginal(c)
|
||||
return slug.MakeLang(name, lang), err
|
||||
}
|
||||
}
|
||||
|
||||
// HashingFunc is a function that generates a hash.Hash
|
||||
type HashingFunc func() hash.Hash
|
||||
|
||||
//go:generate stringer -type=HashType
|
||||
|
||||
// HashType represents a particular hashing algorithm
|
||||
type HashType uint8
|
||||
|
||||
const (
|
||||
MD5 HashType = iota + 1
|
||||
SHA1
|
||||
SHA256
|
||||
)
|
||||
|
||||
// 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{
|
||||
MD5: md5.New,
|
||||
SHA1: sha1.New,
|
||||
SHA256: sha256.New,
|
||||
}
|
||||
|
||||
// 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]
|
||||
return func(c *Config) (string, error) {
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", ErrInvalidHashType, t)
|
||||
}
|
||||
name, err := getOriginal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hs := f()
|
||||
hs.Write([]byte(name))
|
||||
return fmt.Sprintf("%x", hs.Sum(nil)), nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
package nomino_test
|
||||
package nomino
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
import "fmt"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleWithGenerator_customGenerator() {
|
||||
var gen nomino.Generator = func(*nomino.Config) (string, error) {
|
||||
func ExampleWithGenerator_custom_generator() {
|
||||
gen := func(*Config) (string, error) {
|
||||
return "hello", nil
|
||||
}
|
||||
option := WithGenerator(gen)
|
||||
|
||||
str, _ := gen.Make()
|
||||
str, _ := Make(NewConfig(option))
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make(nomino.WithoutExtension())
|
||||
str, _ = Make(NewConfig(option, WithoutExtension()))
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
|
|
@ -22,58 +19,175 @@ func ExampleWithGenerator_customGenerator() {
|
|||
// hello
|
||||
}
|
||||
|
||||
func ExampleGenerator_Make() {
|
||||
g := nomino.Incremental()
|
||||
st, _ := g.Make()
|
||||
fmt.Println(st)
|
||||
func ExampleIncremental() {
|
||||
conf := NewConfig(WithPrefix("foo"), WithGenerator(Incremental()))
|
||||
|
||||
st, _ = g.Make(nomino.WithPrefix("foo"))
|
||||
fmt.Println(st)
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// 0.txt
|
||||
// foo_0.txt
|
||||
// foo_1.txt
|
||||
// foo_2.txt
|
||||
}
|
||||
|
||||
func ExampleMultiGeneratorInOrder() {
|
||||
gen1 := func(*nomino.Config) (string, error) {
|
||||
return "bonjour", nil
|
||||
}
|
||||
gen2 := func(*nomino.Config) (string, error) {
|
||||
return "goodbye", nil
|
||||
}
|
||||
gen := nomino.MultiGeneratorInOrder(gen1, gen2)
|
||||
func ExampleIncrementalWithStart() {
|
||||
conf := NewConfig(WithPrefix("foo"), WithGenerator(IncrementalWithStart(42)))
|
||||
|
||||
str, _ := gen.Make()
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// bonjour.txt
|
||||
// goodbye.txt
|
||||
// bonjour.txt
|
||||
// foo_42.txt
|
||||
// foo_43.txt
|
||||
// foo_44.txt
|
||||
}
|
||||
|
||||
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)
|
||||
func ExampleIncrementalWithStep() {
|
||||
conf := NewConfig(WithPrefix("foo"), WithGenerator(IncrementalWithStep(2)))
|
||||
|
||||
str, _ := gen.Make()
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = gen.Make()
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_0.txt
|
||||
// foo_2.txt
|
||||
// foo_4.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalWithStartAndStep() {
|
||||
conf := NewConfig(WithPrefix("foo"), WithGenerator(IncrementalWithStartAndStep(42, 2)))
|
||||
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_42.txt
|
||||
// foo_44.txt
|
||||
// foo_46.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalFormat() {
|
||||
conf := NewConfig(
|
||||
WithPrefix("foo"),
|
||||
WithGenerator(IncrementalFormat("%03d")),
|
||||
)
|
||||
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_000.txt
|
||||
// foo_001.txt
|
||||
// foo_002.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalFormatWithStart() {
|
||||
conf := NewConfig(
|
||||
WithPrefix("foo"),
|
||||
WithGenerator(IncrementalFormatWithStart(9, "%02d")),
|
||||
)
|
||||
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_09.txt
|
||||
// foo_10.txt
|
||||
}
|
||||
|
||||
func ExampleIncrementalFormatWithStep() {
|
||||
conf := NewConfig(
|
||||
WithPrefix("foo"),
|
||||
WithGenerator(IncrementalFormatWithStep(10, "%02d")),
|
||||
)
|
||||
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
str, _ = Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// foo_00.txt
|
||||
// foo_10.txt
|
||||
}
|
||||
|
||||
func ExampleSlug() {
|
||||
conf := NewConfig(WithGenerator(Slug()), WithOriginal("My name is Jimmy"))
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output: my-name-is-jimmy.txt
|
||||
}
|
||||
|
||||
func ExampleSlugWithLang() {
|
||||
conf := NewConfig(WithGenerator(SlugWithLang("de")), WithOriginal("Diese & Dass"))
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
|
||||
// Output: diese-und-dass.txt
|
||||
}
|
||||
|
||||
func ExampleHash_mD5() {
|
||||
conf := NewConfig(
|
||||
WithOriginal("foobar"),
|
||||
WithGenerator(Hash(MD5)),
|
||||
)
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
// Output: 3858f62230ac3c915f300c664312c63f.txt
|
||||
}
|
||||
|
||||
func ExampleHash_sHA1() {
|
||||
conf := NewConfig(
|
||||
WithOriginal("foobar"),
|
||||
WithGenerator(Hash(SHA1)),
|
||||
)
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
// Output: 8843d7f92416211de9ebb963ff4ce28125932878.txt
|
||||
}
|
||||
|
||||
func ExampleHash_sHA256() {
|
||||
conf := NewConfig(
|
||||
WithOriginal("foobar"),
|
||||
WithGenerator(Hash(SHA256)),
|
||||
)
|
||||
str, _ := Make(conf)
|
||||
fmt.Println(str)
|
||||
// Output: c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2.txt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,60 +2,56 @@ package nomino
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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 out1, nil }
|
||||
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, out1, st)
|
||||
assert.Equal(t, "abc", st)
|
||||
}
|
||||
|
||||
func TestMultiGeneratorInOrder(t *testing.T) {
|
||||
g := MultiGeneratorInOrder(gens...)
|
||||
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)
|
||||
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, out2, st)
|
||||
assert.Equal(t, st2, st)
|
||||
st, err = g(nil)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, err1)
|
||||
assert.ErrorIs(t, err, er1)
|
||||
st, err = g(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, out1, st)
|
||||
assert.Equal(t, st1, st)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -68,38 +64,80 @@ func TestMultiGeneratorInOrderMissing(t *testing.T) {
|
|||
assert.ErrorIs(t, err, ErrMissingGenerators)
|
||||
}
|
||||
|
||||
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)
|
||||
func TestUUID(t *testing.T) {
|
||||
st, err := UUID()(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, st1, st)
|
||||
st, err = g(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, st1, st)
|
||||
_, parseErr := uuid.Parse(st)
|
||||
assert.NoError(t, parseErr)
|
||||
}
|
||||
|
||||
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)
|
||||
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 TestTimestamp(t *testing.T) {
|
||||
n := time.Now()
|
||||
st, err := Timestamp()(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, n.Format(FileTimestamp), st)
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
d := time.Date(1986, time.March, 28, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
st, err := Time(d)(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, d.Format(FileTimestamp), st)
|
||||
}
|
||||
|
||||
func TestTimestampUTC(t *testing.T) {
|
||||
n := time.Now()
|
||||
st, err := TimestampUTC()(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, n.UTC().Format(FileTimestampNoTZ), st)
|
||||
}
|
||||
|
||||
func TestSlugMissingFilename(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Slug()))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Zero(t, st)
|
||||
assert.ErrorIs(t, err, ErrMissingOriginal)
|
||||
}
|
||||
|
||||
func TestSlugRemovesOriginal(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Slug()), WithOriginal("Hello, World"))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Zero(t, conf.original)
|
||||
assert.Equal(t, "hello-world", st)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestHashBadHash(t *testing.T) {
|
||||
conf := NewConfig(WithOriginal("foobar"), WithGenerator(Hash(0)))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Equal(t, "", st)
|
||||
assert.ErrorIs(t, err, ErrInvalidHashType)
|
||||
assert.ErrorContains(t, err, "invalid hash type: HashType(0)")
|
||||
}
|
||||
|
||||
func TestHashMissingOriginal(t *testing.T) {
|
||||
conf := NewConfig(WithGenerator(Hash(MD5)))
|
||||
st, err := conf.generator(&conf)
|
||||
assert.Equal(t, "", st)
|
||||
assert.ErrorIs(t, err, ErrMissingOriginal)
|
||||
}
|
||||
|
||||
func TestHashTypeStringer(t *testing.T) {
|
||||
s := fmt.Sprintf("%s", MD5)
|
||||
assert.Equal(t, "MD5", s)
|
||||
}
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -3,7 +3,6 @@ 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,7 +1,5 @@
|
|||
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=
|
||||
|
|
|
|||
26
hashtype_string.go
Normal file
26
hashtype_string.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Code generated by "stringer -type=HashType"; 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[MD5-1]
|
||||
_ = x[SHA1-2]
|
||||
_ = x[SHA256-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]]
|
||||
}
|
||||
18
make.go
18
make.go
|
|
@ -2,24 +2,15 @@ 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 error will be returned instead.
|
||||
func Make(conf Config, opts ...Option) (string, error) {
|
||||
for _, opt := range opts {
|
||||
opt(&conf)
|
||||
}
|
||||
|
||||
// If the name generator returns an error (generally, it shouldn't), that will be returned instead.
|
||||
func Make(conf Config) (string, error) {
|
||||
name, err := conf.generator(&conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
seperateConf(&conf)
|
||||
|
||||
return fmt.Sprintf("%s%s%s%s%s", conf.prefix, name, conf.original, conf.suffix, conf.extension), nil
|
||||
}
|
||||
|
||||
func seperateConf(conf *Config) {
|
||||
if conf.prefix != "" {
|
||||
conf.prefix += conf.separator
|
||||
}
|
||||
|
|
@ -28,5 +19,8 @@ func seperateConf(conf *Config) {
|
|||
}
|
||||
if conf.suffix != "" {
|
||||
conf.suffix = conf.separator + conf.suffix
|
||||
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s%s%s%s", conf.prefix, name, conf.original, conf.suffix, conf.extension), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,9 @@
|
|||
package nomino_test
|
||||
package nomino
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
func ExampleMake_basic() {
|
||||
// Use default config
|
||||
out, _ := nomino.Make(nomino.NewConfig())
|
||||
out, _ := Make(NewConfig())
|
||||
fmt.Println(out)
|
||||
}
|
||||
|
||||
func ExampleMake_withExtraOptions() {
|
||||
gen := nomino.Incremental()
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithGenerator(gen),
|
||||
nomino.WithPrefix("pre"),
|
||||
)
|
||||
|
||||
st, _ := nomino.Make(conf, nomino.WithOriginal("foobar"))
|
||||
fmt.Println(st)
|
||||
st, _ = nomino.Make(conf, nomino.WithOriginal("baz"))
|
||||
fmt.Println(st)
|
||||
|
||||
// Output:
|
||||
// pre_0_foobar.txt
|
||||
// pre_1_baz.txt
|
||||
}
|
||||
|
|
|
|||
22
make_test.go
22
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) {
|
||||
|
|
@ -67,18 +66,3 @@ func TestMakeDoesntChangeConf(t *testing.T) {
|
|||
assert.Equal(t, "foo.txt", st)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestMakeOptsDoesntChangeConf(t *testing.T) {
|
||||
gen := Incremental()
|
||||
conf := NewConfig(WithGenerator(gen), WithPrefix("pre"))
|
||||
|
||||
st, err := Make(conf, WithOriginal("foobar"))
|
||||
assert.Equal(t, "", conf.original)
|
||||
assert.Equal(t, "pre_0_foobar.txt", st)
|
||||
assert.NoError(t, err)
|
||||
|
||||
st, err = Make(conf, WithOriginal("baz"))
|
||||
assert.Equal(t, "", conf.original)
|
||||
assert.Equal(t, "pre_1_baz.txt", st)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
// 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 = ""
|
||||
|
|
|
|||
|
|
@ -1,91 +1,11 @@
|
|||
package nomino_test
|
||||
package nomino
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/danjones000/nomino"
|
||||
)
|
||||
|
||||
func ExampleWithExtension() {
|
||||
st, _ := nomino.Make(nomino.NewConfig(
|
||||
nomino.WithExtension("xml"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
))
|
||||
|
||||
fmt.Println(st)
|
||||
// Output: 0.xml
|
||||
}
|
||||
|
||||
func ExampleWithoutExtension() {
|
||||
st, _ := nomino.Make(nomino.NewConfig(
|
||||
nomino.WithoutExtension(),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
))
|
||||
|
||||
fmt.Println(st)
|
||||
// Output: 0
|
||||
}
|
||||
|
||||
func ExampleWithPrefix() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("pref"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
)
|
||||
st, _ := nomino.Make(conf)
|
||||
fmt.Println(st)
|
||||
|
||||
st, _ = nomino.Make(conf)
|
||||
fmt.Println(st)
|
||||
// Output:
|
||||
// pref_0.txt
|
||||
// pref_1.txt
|
||||
}
|
||||
|
||||
func ExampleWithSeparator() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithPrefix("pref"),
|
||||
nomino.WithSeparator("---"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
)
|
||||
st, _ := nomino.Make(conf)
|
||||
fmt.Println(st)
|
||||
|
||||
st, _ = nomino.Make(conf)
|
||||
fmt.Println(st)
|
||||
// Output:
|
||||
// pref---0.txt
|
||||
// pref---1.txt
|
||||
}
|
||||
|
||||
func ExampleWithSuffix() {
|
||||
conf := nomino.NewConfig(
|
||||
nomino.WithSuffix("suff"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
)
|
||||
st, _ := nomino.Make(conf)
|
||||
fmt.Println(st)
|
||||
|
||||
st, _ = nomino.Make(conf)
|
||||
fmt.Println(st)
|
||||
// Output:
|
||||
// 0_suff.txt
|
||||
// 1_suff.txt
|
||||
}
|
||||
|
||||
func ExampleWithOriginal() {
|
||||
st, _ := nomino.Make(nomino.NewConfig(
|
||||
nomino.WithOriginal("Hello, World"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
))
|
||||
|
||||
fmt.Println(st)
|
||||
// Output: 0_Hello, World.txt
|
||||
}
|
||||
import "fmt"
|
||||
|
||||
func ExampleWithOriginalSlug() {
|
||||
st, _ := nomino.Make(nomino.NewConfig(
|
||||
nomino.WithOriginalSlug("Hello, World"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
st, _ := Make(NewConfig(
|
||||
WithOriginalSlug("Hello, World"),
|
||||
WithGenerator(Incremental()),
|
||||
))
|
||||
|
||||
fmt.Println(st)
|
||||
|
|
@ -93,9 +13,9 @@ func ExampleWithOriginalSlug() {
|
|||
}
|
||||
|
||||
func ExampleWithOriginalSlugLang() {
|
||||
st, _ := nomino.Make(nomino.NewConfig(
|
||||
nomino.WithOriginalSlugLang("Diese & Dass", "de"),
|
||||
nomino.WithGenerator(nomino.Incremental()),
|
||||
st, _ := Make(NewConfig(
|
||||
WithOriginalSlugLang("Diese & Dass", "de"),
|
||||
WithGenerator(Incremental()),
|
||||
))
|
||||
|
||||
fmt.Println(st)
|
||||
|
|
|
|||
48
options_test.go
Normal file
48
options_test.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package nomino
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithOriginal(t *testing.T) {
|
||||
var c Config
|
||||
name := "foobar"
|
||||
WithOriginal(name)(&c)
|
||||
assert.Equal(t, name, c.original)
|
||||
}
|
||||
|
||||
func TestWithPrefix(t *testing.T) {
|
||||
var c Config
|
||||
pref := "draft"
|
||||
WithPrefix(pref)(&c)
|
||||
assert.Equal(t, pref, c.prefix)
|
||||
}
|
||||
|
||||
func TestWithSuffix(t *testing.T) {
|
||||
var c Config
|
||||
suff := "out"
|
||||
WithSuffix(suff)(&c)
|
||||
assert.Equal(t, suff, c.suffix)
|
||||
}
|
||||
|
||||
func TestWithoutExtension(t *testing.T) {
|
||||
c := Config{extension: ".foobar"}
|
||||
WithoutExtension()(&c)
|
||||
assert.Equal(t, "", c.extension)
|
||||
}
|
||||
|
||||
func TestWithExtension(t *testing.T) {
|
||||
var c Config
|
||||
ext := "yaml"
|
||||
WithExtension(ext)(&c)
|
||||
assert.Equal(t, "."+ext, c.extension)
|
||||
}
|
||||
|
||||
func TestWithSeparator(t *testing.T) {
|
||||
var c Config
|
||||
sep := "---"
|
||||
WithSeparator(sep)(&c)
|
||||
assert.Equal(t, sep, c.separator)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue