├── go.mod ├── README.md ├── .github └── workflows │ └── main.yml ├── dancing_door_test.go ├── LICENSE ├── go.sum └── dancing_door.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pyr/dancing-door 2 | 3 | go 1.21 4 | 5 | require github.com/stretchr/testify v1.8.4 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dancing door: when you feel uninspired 2 | ====================================== 3 | 4 | Somewhat randomly generates names that you can remember easily. 5 | 6 | Generate a name composed of two or three parts. This can be used to 7 | generate release names for the uninspired. 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: All builds 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | go_version: 10 | - "1.19" 11 | - "1.20" 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Setup Go 15 | uses: actions/setup-go@v3.2.0 16 | with: 17 | go-version: ${{ matrix.go_version }} 18 | - run: expr $(gofmt -l .|wc -l) = 0 >/dev/null || (go fmt -d . && exit 1) 19 | - run: go vet ./... 20 | - run: go test -v -race ./... 21 | -------------------------------------------------------------------------------- /dancing_door_test.go: -------------------------------------------------------------------------------- 1 | package dancingdoor 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestOptions(t *testing.T) { 10 | smallCorpus := []string{"a"} 11 | smallSuffixes := []string{"b"} 12 | 13 | t.Run("Default options work", func(t *testing.T) { 14 | require.Equal(t, " ", MakeOptions().Separator) 15 | require.Equal(t, "-", MakeOptions().WithSeparator("-").Separator) 16 | }) 17 | 18 | t.Run("Generation works", func(t *testing.T) { 19 | opts := MakeOptions().WithCorpus(smallCorpus).WithSuffixes(smallSuffixes) 20 | require.Contains(t, []string{"a a b", "b a", "a b"}, Codename(opts)) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Pierre-Yves Ritschard 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 6 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /dancing_door.go: -------------------------------------------------------------------------------- 1 | // Package dancingdoor provides a function to quickly generate easily remembered names 2 | package dancingdoor 3 | 4 | import ( 5 | "math/rand" 6 | "strings" 7 | ) 8 | 9 | var colors = []string{ 10 | "white", "black", "yellow", "red", "blue", "brown", "green", 11 | "purple", "orange", "silver", "scarlet", "rainbow", "indigo", 12 | "ivory", "navy", "olive", "teal", "pink", "magenta", "maroon", 13 | "sienna", "gold", "golden", 14 | } 15 | 16 | var adjectives = []string{ 17 | "abandoned", "aberrant", "accidentally", "aggressive", "aimless", 18 | "alien", "angry", "appropriate", "barbaric", "beacon", "big", "bitter", 19 | "bleeding", "brave", "brutal", "cheerful", "dancing", "dangerous", 20 | "dead", "deserted", "digital", "dirty", "disappointed", "discarded", 21 | "dreaded", "eastern", "eastern", "elastic", "empty", "endless", 22 | "essential", "eternal", "everyday", "fierce", "flaming", "flying", 23 | "forgotten", "forsaken", "freaky", "frozen", "full", "furious", "ghastly", 24 | "global", "gloomy", "grim", "gruesome", "gutsy", "helpless", "hidden", 25 | "hideous", "homeless", "hungry", "insane", "intense", "intensive", 26 | "itchy", "liquid", "lone", "lost", "meaningful", "modern", 27 | "monday's", "morbid", "moving", "needless", "nervous", "new", "next", 28 | "ninth", "nocturnal", "northernmost", "official", "old", "permanent", 29 | "persistent", "pointless", "pure", "quality", "random", "rare", "raw", 30 | "reborn", "remote", "restless", "rich", "risky", "rocky", "rough", 31 | "running", "rusty", "sad", "saturday's", "screaming", "serious", 32 | "severe", "silly", "skilled", "sleepy", "sliding", "small", "solid", 33 | "steamy", "stony", "stormy", "straw", "strawberry", "streaming", 34 | "strong", "subtle", "supersonic", "surreal", "tainted", "temporary", "third", "tidy", 35 | "timely", "unique", "vital", "western", "wild", "wooden", "worthy", "bitter", 36 | "boiling", "brave", "cloudy", "cold", "confidential", "dreadful", "dusty", "eager", 37 | "early", "grotesque", "harsh", "heavy", "hollow", "hot", "husky", "icy", 38 | "late", "lonesome", "long", "lucky", "massive", "maximum", "minimum", 39 | "mysterious", "outstanding", "rapid", "rebel", "scattered", "shiny", 40 | "solid", "square", "steady", "steep", "sticky", "stormy", "strong", 41 | "sunday's", "swift", "tasty", 42 | } 43 | 44 | var defaultSuffixes = []string{ 45 | "alarm", "albatross", "anaconda", "antique", "artificial", "autopsy", 46 | "autumn", "avenue", "backpack", "balcony", "barbershop", "boomerang", 47 | "bulldozer", "butter", "canal", "cloud", "clown", "coffin", "comic", 48 | "compass", "cosmic", "crayon", "creek", "crossbow", "dagger", "dinosaur", 49 | "dog", "donut", "door", "doorstop", "electrical", "electron", "eyelid", 50 | "firecracker", "fish", "flag", "flannel", "flea", "frostbite", "gravel", 51 | "haystack", "helium", "kangaroo", "lantern", "leather", "limousine", 52 | "lobster", "locomotive", "logbook", "longitude", "metaphor", "microphone", 53 | "monkey", "moose", "morning", "mountain", "mustard", "neutron", "nitrogen", 54 | "notorious", "obscure", "ostrich", "oyster", "parachute", "peasant", 55 | "pineapple", "plastic", "postal", "pottery", "proton", "puppet", "railroad", 56 | "rhinestone", "roadrunner", "rubber", "scarecrow", "scoreboard", "scorpion", 57 | "shower", "skunk", "sound", "street", "subdivision", "summer", "sunshine", 58 | "tea", "temple", "test", "tire", "tombstone", "toothbrush", "torpedo", 59 | "toupee", "trendy", "trombone", "tuba", "tuna", "tungsten", "vegetable", 60 | "venom", "vulture", "waffle", "warehouse", "waterbird", "weather", "weeknight", 61 | "windshield", "winter", "wrench", "xylophone", "alpha", "arm", "beam", "beta", 62 | "bird", "breeze", "burst", "cat", "cobra", "crystal", "drill", "eagle", 63 | "emerald", "epsilon", "finger", "fist", "foot", "fox", "galaxy", "gamma", 64 | "hammer", "heart", "hook", "hurricane", "iron", "jazz", "jupiter", "knife", 65 | "lama", "laser", "lion", "mars", "mercury", "moon", "moose", "neptune", 66 | "omega", "panther", "planet", "pluto", "plutonium", "poseidon", "python", 67 | "ray", "sapphire", "scissors", "screwdriver", "serpent", "sledgehammer", 68 | "smoke", "snake", "space", "spider", "star", "steel", "storm", "sun", 69 | "swallow", "tiger", "uranium", "venus", "viper", "wrench", "yard", "zeus", 70 | } 71 | 72 | var defaultCorpus = append(colors, adjectives...) // Concatenate the two slices 73 | 74 | const defaultSeparator = " " 75 | 76 | // Options holds the parameters for generating a code name. 77 | type Options struct { 78 | Corpus []string 79 | Suffixes []string 80 | Separator string 81 | } 82 | 83 | var defaultOptions = Options{ 84 | Corpus: defaultCorpus, 85 | Suffixes: defaultSuffixes, 86 | Separator: defaultSeparator, 87 | } 88 | 89 | // MakeOptions yields a ready to use Options struct with defaults provided. 90 | func MakeOptions() *Options { 91 | options := defaultOptions 92 | return &options 93 | } 94 | 95 | // WithCorpus returns a copy of the provided options structure with corpus set to the provided string array. 96 | func (opts *Options) WithCorpus(corpus []string) *Options { 97 | opts.Corpus = corpus 98 | return opts 99 | } 100 | 101 | // WithSuffixes returns a copy of the provided options structure with suffixes set to the provided string array. 102 | func (opts *Options) WithSuffixes(suffixes []string) *Options { 103 | opts.Suffixes = suffixes 104 | return opts 105 | } 106 | 107 | // WithSeparator returns a copy of the provided options structure with separator set to the provided string. 108 | func (opts *Options) WithSeparator(separator string) *Options { 109 | opts.Separator = separator 110 | return opts 111 | } 112 | 113 | func ensureDefaults(opts *Options) *Options { 114 | if len(opts.Corpus) == 0 { 115 | opts.Corpus = defaultCorpus 116 | } 117 | if len(opts.Suffixes) == 0 { 118 | opts.Suffixes = defaultSuffixes 119 | } 120 | if opts.Separator == "" { 121 | opts.Separator = defaultSeparator 122 | } 123 | return opts 124 | } 125 | 126 | // CodenameElements generates a name composed of two or three parts. 127 | // The parts are returned as an array of strings. 128 | func CodenameElements(options *Options) []string { 129 | 130 | name1 := options.Corpus[rand.Intn(len(options.Corpus))] 131 | name2 := options.Corpus[rand.Intn(len(options.Corpus))] 132 | suffix := options.Suffixes[rand.Intn(len(options.Suffixes))] 133 | i := rand.Intn(100) 134 | 135 | var names []string 136 | 137 | switch { 138 | case i <= 15: 139 | names = []string{name1, name2, suffix} 140 | case i > 15 && i < 31: 141 | names = []string{suffix, name1} 142 | default: 143 | names = []string{name1, suffix} 144 | } 145 | return names 146 | } 147 | 148 | // Codename generates a name composed of two or three parts. 149 | // The parts are joined with the separator configured in Options or a space 150 | // by default and returned as a single string 151 | func Codename(opts ...*Options) string { 152 | var options *Options 153 | if len(opts) == 0 { 154 | options = MakeOptions() 155 | } else { 156 | options = ensureDefaults(opts[0]) 157 | } 158 | return strings.Join(CodenameElements(options), options.Separator) 159 | } 160 | --------------------------------------------------------------------------------