| 
									
										
										
										
											2025-03-07 17:00:38 -06:00
										 |  |  | package nomino | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2025-03-14 14:45:55 -05:00
										 |  |  | 	"crypto/md5" | 
					
						
							|  |  |  | 	"crypto/sha1" | 
					
						
							|  |  |  | 	"crypto/sha256" | 
					
						
							| 
									
										
										
										
											2025-03-10 14:25:00 -05:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2025-03-14 11:19:20 -05:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2025-03-14 14:45:55 -05:00
										 |  |  | 	"hash" | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 17:00:38 -06:00
										 |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2025-03-13 16:58:55 -05:00
										 |  |  | 	"github.com/gosimple/slug" | 
					
						
							| 
									
										
										
										
											2025-03-07 17:00:38 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // Generator is a function that returns the "random" portion of the returned filename. | 
					
						
							|  |  |  | // Technically, it doesn't necessarily need to be random, and could be based on time, or a counter, | 
					
						
							|  |  |  | // for example. | 
					
						
							| 
									
										
										
										
											2025-03-13 15:36:30 -05:00
										 |  |  | type Generator func(conf *Config) (string, error) | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // WithGenerator sets the specified generator | 
					
						
							|  |  |  | func WithGenerator(g Generator) Option { | 
					
						
							| 
									
										
										
										
											2025-03-10 14:52:50 -05:00
										 |  |  | 	return func(c *Config) { | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | 		c.generator = g | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-03-07 17:00:38 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 14:25:00 -05:00
										 |  |  | // ErrMissingGenerators is returned by a multi-generator if no generators are supplied. | 
					
						
							|  |  |  | var ErrMissingGenerators = errors.New("no generators supplied") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 15:36:30 -05:00
										 |  |  | func missingGen(*Config) (string, error) { | 
					
						
							| 
									
										
										
										
											2025-03-10 14:25:00 -05:00
										 |  |  | 	return "", ErrMissingGenerators | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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. | 
					
						
							|  |  |  | func MultiGeneratorInOrder(gens ...Generator) Generator { | 
					
						
							|  |  |  | 	if len(gens) == 0 { | 
					
						
							|  |  |  | 		return missingGen | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(gens) == 1 { | 
					
						
							|  |  |  | 		return gens[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var idx int | 
					
						
							| 
									
										
										
										
											2025-03-13 15:36:30 -05:00
										 |  |  | 	return func(c *Config) (string, error) { | 
					
						
							|  |  |  | 		st, err := gens[idx](c) | 
					
						
							| 
									
										
										
										
											2025-03-10 14:25:00 -05:00
										 |  |  | 		idx = (idx + 1) % len(gens) | 
					
						
							|  |  |  | 		return st, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 15:36:30 -05:00
										 |  |  | func uuidGen(*Config) (string, error) { | 
					
						
							| 
									
										
										
										
											2025-03-07 17:00:38 -06:00
										 |  |  | 	u, err := uuid.NewRandom() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return u.String(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // UUID generates a UUIDv4. | 
					
						
							|  |  |  | func UUID() Generator { | 
					
						
							|  |  |  | 	return uuidGen | 
					
						
							| 
									
										
										
										
											2025-03-07 17:00:38 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:46:13 -05:00
										 |  |  | // FileTimestamp is the default format for WithTimestamp and WithTime | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | const FileTimestamp string = "2006-01-02_03-05-06-0700" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // Timestamp generates a a date and time for the current time. | 
					
						
							|  |  |  | // It is formatted accourding to FileTimestamp | 
					
						
							|  |  |  | func Timestamp() Generator { | 
					
						
							|  |  |  | 	return TimestampWithFormat(FileTimestamp) | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // TimestampWithFormat generates a date and time for the current time with the supplied format. | 
					
						
							|  |  |  | func TimestampWithFormat(f string) Generator { | 
					
						
							|  |  |  | 	return FormattedTime(time.Now(), FileTimestamp) | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // 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) | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // FormattedTime generates a date and time for the supplied time with the supplied format. | 
					
						
							|  |  |  | func FormattedTime(t time.Time, f string) Generator { | 
					
						
							| 
									
										
										
										
											2025-03-13 15:36:30 -05:00
										 |  |  | 	return func(*Config) (string, error) { | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | 		return t.Format(f), nil | 
					
						
							| 
									
										
										
										
											2025-03-10 11:47:01 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-10 11:52:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | // FileTimestampNoTZ is the default format for WithTimestampUTC and WithTimeUTC | 
					
						
							| 
									
										
										
										
											2025-03-10 11:52:55 -05:00
										 |  |  | const FileTimestampNoTZ string = "2006-01-02_03-05-06" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // 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()) | 
					
						
							| 
									
										
										
										
											2025-03-10 11:52:55 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 13:48:11 -05:00
										 |  |  | // 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) | 
					
						
							| 
									
										
										
										
											2025-03-10 11:52:55 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Incremental generates a name that is a series of integers starting at 0 | 
					
						
							|  |  |  | func Incremental() Generator { | 
					
						
							|  |  |  | 	return IncrementalWithStartAndStep(0, 1) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 11:19:20 -05:00
										 |  |  | // 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | // IncrementalWithStart generates a name that is a series of integers starting at the specified number | 
					
						
							|  |  |  | func IncrementalWithStart(start int) Generator { | 
					
						
							|  |  |  | 	return IncrementalWithStartAndStep(start, 1) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 11:19:20 -05:00
										 |  |  | // 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | // 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 11:19:20 -05:00
										 |  |  | // 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 { | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | 	next := start | 
					
						
							| 
									
										
										
										
											2025-03-13 15:36:30 -05:00
										 |  |  | 	return func(*Config) (string, error) { | 
					
						
							| 
									
										
										
										
											2025-03-14 11:19:20 -05:00
										 |  |  | 		out := cb(next) | 
					
						
							| 
									
										
										
										
											2025-03-13 15:22:54 -05:00
										 |  |  | 		next += step | 
					
						
							|  |  |  | 		return out, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-13 16:58:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 11:19:20 -05:00
										 |  |  | // 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) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 16:58:55 -05:00
										 |  |  | // 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-14 14:45:55 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |