├── .gitignore ├── .gut ├── LICENSE ├── README.md ├── go.mod ├── ternary.go └── ternary_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Go template downloaded with gut 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.test 8 | *.out 9 | go.work 10 | 11 | .gut 12 | .vscode 13 | -------------------------------------------------------------------------------- /.gut: -------------------------------------------------------------------------------- 1 | profile_id = "vC3BN3KRX2iZJzWeVX41q" 2 | updated_at = "2023-01-19 18:13:13" 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 - Julien CAGNIART 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go ternary 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/julien040/go-ternary.svg)](https://pkg.go.dev/github.com/julien040/go-ternary) 4 | 5 | The missing ternary operator for Go. 6 | 7 | It's a blazing fast, 7 lines of code package that will save you a lot of lines of code over readability. 8 | 9 | Honestly, don't take this package too seriously. It's just a joke to keep me busy one evening. 10 | 11 | ## Installation 12 | 13 | ```bash 14 | go get github.com/julien040/go-ternary 15 | ``` 16 | 17 | ## Usage/Examples 18 | 19 | ```go 20 | import ( 21 | "github.com/julien040/go-ternary" 22 | ) 23 | 24 | ternary.If(true, "foo", "bar") // "foo" 25 | ternary.If(false, "foo", "bar") // "bar" 26 | 27 | // Use it for pluralization 28 | ternary.If(len(slice) > 2, "objects", "object") // "objects" 29 | 30 | ternary.If(true, 5.4, 3.2) // 5.4 31 | 32 | ``` 33 | 34 | ## FAQ 35 | 36 | ### Which type can I use? 37 | 38 | Thanks to generics, you can use any type you want. 39 | 40 | ### What is the minimum Go version? 41 | 42 | Because of generics, you need at least Go 1.18. 43 | 44 | ## Contributing 45 | 46 | Contributions are always welcome! 47 | 48 | I accept any contribution, from code to documentation, bug reports, and feature requests. 49 | 50 | However, I wonder if there is much to contribute to this project since it's pretty simple. 51 | 52 | ## License 53 | 54 | [MIT](https://choosealicense.com/licenses/mit/) 55 | 56 | ## Authors 57 | 58 | - [@julien040](https://www.github.com/julien040) 59 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/julien040/go-ternary 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /ternary.go: -------------------------------------------------------------------------------- 1 | package ternary 2 | 3 | // If is the replacement ternary operator in Go 4 | // 5 | // Usage: 6 | // 7 | // ternary.If(condition bool, elementReturnedIfTrue, elementReturnedIfFalse) 8 | // 9 | // Example: 10 | // 11 | // ternary.If(true, "foo", "bar") // returns "foo" 12 | // ternary.If(false, "foo", "bar") // returns "bar" 13 | func If[T any](condition bool, a, b T) T { 14 | if condition { 15 | return a 16 | } 17 | return b 18 | } 19 | 20 | // IfFunc implements a given function using the chosen element as parameter 21 | // 22 | // Usage: 23 | // 24 | // ternary.If(condition bool, elementPassedIfTrue, elementPassedIfFalse, function func(element)) 25 | // 26 | // Example: 27 | // 28 | // ternary.If(true, "foo", "bar", func(e any) { 29 | // return fmt.Sprintf("%v", e) 30 | // }) // returns "foo" 31 | func IfFunc[T any, R any](condition bool, a, b T, f func(T) R) R { 32 | if condition { 33 | return f(a) 34 | } 35 | return f(b) 36 | } 37 | -------------------------------------------------------------------------------- /ternary_test.go: -------------------------------------------------------------------------------- 1 | package ternary 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | type T any 9 | type F func(T) T 10 | 11 | func TestIf(t *testing.T) { 12 | type args struct { 13 | condition bool 14 | a T 15 | b T 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | want T 21 | }{ 22 | { 23 | name: "String True", 24 | args: args{ 25 | condition: true, 26 | a: "foo", 27 | b: "bar", 28 | }, 29 | want: "foo", 30 | }, 31 | 32 | { 33 | name: "String False", 34 | args: args{ 35 | condition: false, 36 | a: "foo", 37 | b: "bar", 38 | }, 39 | want: "bar", 40 | }, 41 | 42 | { 43 | name: "Int True", 44 | args: args{ 45 | condition: true, 46 | a: 1, 47 | b: 2, 48 | }, 49 | want: 1, 50 | }, 51 | 52 | { 53 | name: "Int False", 54 | args: args{ 55 | condition: false, 56 | a: 1, 57 | b: 2, 58 | }, 59 | want: 2, 60 | }, 61 | 62 | { 63 | name: "Float True", 64 | args: args{ 65 | condition: true, 66 | a: 1.1, 67 | b: 2.2, 68 | }, 69 | want: 1.1, 70 | }, 71 | 72 | { 73 | name: "Float False", 74 | args: args{ 75 | condition: false, 76 | a: 1.1, 77 | b: 2.2, 78 | }, 79 | want: 2.2, 80 | }, 81 | 82 | { 83 | name: "Bool True", 84 | args: args{ 85 | condition: true, 86 | a: true, 87 | b: false, 88 | }, 89 | want: true, 90 | }, 91 | 92 | { 93 | name: "Bool False", 94 | args: args{ 95 | condition: false, 96 | a: true, 97 | b: false, 98 | }, 99 | want: false, 100 | }, 101 | 102 | { 103 | name: "Struct True", 104 | args: args{ 105 | condition: true, 106 | a: struct{ foo string }{foo: "foo"}, 107 | b: struct{ foo string }{foo: "bar"}, 108 | }, 109 | want: struct{ foo string }{foo: "foo"}, 110 | }, 111 | 112 | { 113 | name: "Struct False", 114 | args: args{ 115 | condition: false, 116 | a: struct{ foo string }{foo: "foo"}, 117 | b: struct{ foo string }{foo: "bar"}, 118 | }, 119 | want: struct{ foo string }{foo: "bar"}, 120 | }, 121 | 122 | { 123 | name: "Slice True", 124 | args: args{ 125 | condition: true, 126 | a: []string{"foo"}, 127 | b: []string{"bar"}, 128 | }, 129 | want: []string{"foo"}, 130 | }, 131 | 132 | { 133 | name: "Slice False", 134 | args: args{ 135 | condition: false, 136 | a: []string{"foo"}, 137 | b: []string{"bar"}, 138 | }, 139 | want: []string{"bar"}, 140 | }, 141 | 142 | { 143 | name: "Map True", 144 | args: args{ 145 | condition: true, 146 | a: map[string]string{"foo": "foo"}, 147 | b: map[string]string{"foo": "bar"}, 148 | }, 149 | want: map[string]string{"foo": "foo"}, 150 | }, 151 | 152 | { 153 | name: "Map False", 154 | args: args{ 155 | condition: false, 156 | a: map[string]string{"foo": "foo"}, 157 | b: map[string]string{"foo": "bar"}, 158 | }, 159 | want: map[string]string{"foo": "bar"}, 160 | }, 161 | 162 | { 163 | name: "Interface True", 164 | args: args{ 165 | condition: true, 166 | a: interface{}("foo"), 167 | b: interface{}("bar"), 168 | }, 169 | want: interface{}("foo"), 170 | }, 171 | 172 | { 173 | name: "Interface False", 174 | args: args{ 175 | condition: false, 176 | a: interface{}("foo"), 177 | b: interface{}("bar"), 178 | }, 179 | want: interface{}("bar"), 180 | }, 181 | } 182 | for _, tt := range tests { 183 | t.Run(tt.name, func(t *testing.T) { 184 | if got := If(tt.args.condition, tt.args.a, tt.args.b); !reflect.DeepEqual(got, tt.want) { 185 | t.Errorf("Ternary() = %v, want %v", got, tt.want) 186 | } 187 | }) 188 | } 189 | } 190 | 191 | func TestIfFunc(t *testing.T) { 192 | type args struct { 193 | condition bool 194 | a T 195 | b T 196 | f F 197 | } 198 | testFunc := func(t T) T { 199 | return t 200 | } 201 | tests := []struct { 202 | name string 203 | args args 204 | want T 205 | }{ 206 | { 207 | name: "String True", 208 | args: args{ 209 | condition: true, 210 | a: "foo", 211 | b: "bar", 212 | f: testFunc, 213 | }, 214 | want: "foo", 215 | }, 216 | 217 | { 218 | name: "String False", 219 | args: args{ 220 | condition: false, 221 | a: "foo", 222 | b: "bar", 223 | f: testFunc, 224 | }, 225 | want: "bar", 226 | }, 227 | 228 | { 229 | name: "Int True", 230 | args: args{ 231 | condition: true, 232 | a: 1, 233 | b: 2, 234 | f: testFunc, 235 | }, 236 | want: 1, 237 | }, 238 | 239 | { 240 | name: "Int False", 241 | args: args{ 242 | condition: false, 243 | a: 1, 244 | b: 2, 245 | f: testFunc, 246 | }, 247 | want: 2, 248 | }, 249 | 250 | { 251 | name: "Float True", 252 | args: args{ 253 | condition: true, 254 | a: 1.1, 255 | b: 2.2, 256 | f: testFunc, 257 | }, 258 | want: 1.1, 259 | }, 260 | 261 | { 262 | name: "Float False", 263 | args: args{ 264 | condition: false, 265 | a: 1.1, 266 | b: 2.2, 267 | f: testFunc, 268 | }, 269 | want: 2.2, 270 | }, 271 | 272 | { 273 | name: "Bool True", 274 | args: args{ 275 | condition: true, 276 | a: true, 277 | b: false, 278 | f: testFunc, 279 | }, 280 | want: true, 281 | }, 282 | 283 | { 284 | name: "Bool False", 285 | args: args{ 286 | condition: false, 287 | a: true, 288 | b: false, 289 | f: testFunc, 290 | }, 291 | want: false, 292 | }, 293 | 294 | { 295 | name: "Struct True", 296 | args: args{ 297 | condition: true, 298 | a: struct{ foo string }{foo: "foo"}, 299 | b: struct{ foo string }{foo: "bar"}, 300 | f: testFunc, 301 | }, 302 | want: struct{ foo string }{foo: "foo"}, 303 | }, 304 | 305 | { 306 | name: "Struct False", 307 | args: args{ 308 | condition: false, 309 | a: struct{ foo string }{foo: "foo"}, 310 | b: struct{ foo string }{foo: "bar"}, 311 | f: testFunc, 312 | }, 313 | want: struct{ foo string }{foo: "bar"}, 314 | }, 315 | 316 | { 317 | name: "Slice True", 318 | args: args{ 319 | condition: true, 320 | a: []string{"foo"}, 321 | b: []string{"bar"}, 322 | f: testFunc, 323 | }, 324 | want: []string{"foo"}, 325 | }, 326 | 327 | { 328 | name: "Slice False", 329 | args: args{ 330 | condition: false, 331 | a: []string{"foo"}, 332 | b: []string{"bar"}, 333 | f: testFunc, 334 | }, 335 | want: []string{"bar"}, 336 | }, 337 | 338 | { 339 | name: "Map True", 340 | args: args{ 341 | condition: true, 342 | a: map[string]string{"foo": "foo"}, 343 | b: map[string]string{"foo": "bar"}, 344 | f: testFunc, 345 | }, 346 | want: map[string]string{"foo": "foo"}, 347 | }, 348 | 349 | { 350 | name: "Map False", 351 | args: args{ 352 | condition: false, 353 | a: map[string]string{"foo": "foo"}, 354 | b: map[string]string{"foo": "bar"}, 355 | f: testFunc, 356 | }, 357 | want: map[string]string{"foo": "bar"}, 358 | }, 359 | 360 | { 361 | name: "Interface True", 362 | args: args{ 363 | condition: true, 364 | a: interface{}("foo"), 365 | b: interface{}("bar"), 366 | f: testFunc, 367 | }, 368 | want: interface{}("foo"), 369 | }, 370 | 371 | { 372 | name: "Interface False", 373 | args: args{ 374 | condition: false, 375 | a: interface{}("foo"), 376 | b: interface{}("bar"), 377 | f: testFunc, 378 | }, 379 | want: interface{}("bar"), 380 | }, 381 | } 382 | for _, tt := range tests { 383 | t.Run(tt.name, func(t *testing.T) { 384 | if got := IfFunc(tt.args.condition, tt.args.a, tt.args.b, tt.args.f); !reflect.DeepEqual(got, tt.want) { 385 | t.Errorf("Ternary() = %v, want %v", got, tt.want) 386 | } 387 | }) 388 | } 389 | } 390 | --------------------------------------------------------------------------------