Repository: JanDeDobbeleer/oh-my-posh
Pull request #5644 (2024-09-19 19:31:08)

Random template
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [x] The commit message follows the [conventional commits][cc] guidelines.
- [x] Tests for the changes have been added (for bug fixes / features).
- [x] Docs have been added/updated (for bug fixes / features).

### Description

Add a template function to select a random item from a list.

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary


oliviaBahr: Woops! Didn't realize renaming the branch would close the pr.

Patch:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index f1cec202..26aab8b6 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -27,10 +27,10 @@ jobs:
     - name: Checkout code
       uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3
       with:
         languages: go
     - name: Autobuild
-      uses: github/codeql-action/autobuild@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 5f61b90c..7b1e0ef6 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -27,7 +27,7 @@ jobs:
         with:
           go-version: 1.21
           cache-dependency-path: src/go.sum
-      - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b
+      - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6
         with:
           node-version: 20.9.0
         # Create Kind cluster to have a Kubernetes context for cloud-native-azure theme
diff --git a/src/template/func_map.go b/src/template/func_map.go
index e1eed05d..57cba57b 100644
--- a/src/template/func_map.go
+++ b/src/template/func_map.go
@@ -17,6 +17,7 @@ func funcMap() template.FuncMap {
 		"replaceP":     replaceP,
 		"gt":           gt,
 		"lt":           lt,
+		"random":       random,
 		"reason":       GetReasonFromStatus,
 		"hresult":      hresult,
 		"trunc":        trunc,
diff --git a/src/template/random.go b/src/template/random.go
new file mode 100644
index 00000000..9fc3cced
--- /dev/null
+++ b/src/template/random.go
@@ -0,0 +1,22 @@
+package template
+
+import (
+	"errors"
+	"fmt"
+	"math/rand"
+	"reflect"
+)
+
+func random(list interface{}) (string, error) {
+	v := reflect.ValueOf(list)
+
+	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+		return "", errors.New("input must be a slice or array")
+	}
+
+	if v.Len() == 0 {
+		return "", errors.New("input slice or array is empty")
+	}
+
+	return fmt.Sprintf("%v", v.Index(rand.Intn(v.Len()))), nil
+}
diff --git a/website/docs/configuration/templates.mdx b/website/docs/configuration/templates.mdx
index 38261a4d..4e435dfb 100644
--- a/website/docs/configuration/templates.mdx
+++ b/website/docs/configuration/templates.mdx
@@ -182,6 +182,7 @@ use them.
 | `{{ replaceP "c.t" "cut code cat" "dog" }}`                        | Exposes [regexp.ReplaceAllString][regexpra] as a string template function.                                                 |
 | <code>\{\{ .Code &vert; hresult \}\}</code>                        | Transform a status code to its HRESULT value for easy troubleshooting. For example `-1978335212` becomes `0x8A150014`.     |
 | `{{ readFile ".version.json" }}`                                   | Read a file in the current directory. Returns a string.                                                                    |
+| `{{ random (list \"a\" 2 .MyThirdItem) }}`                         | Selects a random element from a list. The list can be an array or slice containing any types (use sprig's `list`).         |
 
 <!-- markdownlint-enable MD013 -->
 


Test patch:
diff --git a/src/template/random_test.go b/src/template/random_test.go
new file mode 100644
index 00000000..da70e37e
--- /dev/null
+++ b/src/template/random_test.go
@@ -0,0 +1,65 @@
+package template
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRandom(t *testing.T) {
+	cases := []struct {
+		Case        string
+		Input       interface{}
+		ShouldError bool
+	}{
+		{
+			Case:  "valid slice",
+			Input: []int{1, 2, 3, 4, 5},
+		},
+		{
+			Case:  "valid array",
+			Input: [5]int{1, 2, 3, 4, 5},
+		},
+		{
+			Case:        "empty slice",
+			Input:       []int{},
+			ShouldError: true,
+		},
+		{
+			Case:        "not a slice or array",
+			Input:       "not a slice",
+			ShouldError: true,
+		},
+		{
+			Case:  "valid string slice",
+			Input: []string{"a", "b", "c"},
+		},
+		{
+			Case:  "valid float slice",
+			Input: []float64{1.1, 2.2, 3.3},
+		},
+		{
+			Case: "interface with multiple types",
+			Input: []interface{}{
+				"a",
+				1,
+				true,
+			},
+		},
+		{
+			Case:  "valid struct slice",
+			Input: []struct{ Name string }{{Name: "Alice"}, {Name: "Bob"}},
+		},
+	}
+
+	for _, tc := range cases {
+		result, err := random(tc.Input)
+		if tc.ShouldError {
+			assert.Error(t, err, tc.Case)
+		} else {
+			assert.NoError(t, err, tc.Case)
+			assert.Contains(t, fmt.Sprintf("%v", tc.Input), result, tc.Case)
+		}
+	}
+}