Repository: JanDeDobbeleer/oh-my-posh
Pull request #1161 (2021-11-03 06:33:35)

feat(owm): add template support
### Prerequisites

- [x] I have read and understood the `CONTRIBUTING` guide
- [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)

feat(owm): add template support

[Description of the change]

<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b849af19..a6655675 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,8 +1,15 @@
 {
   "go.lintTool": "golangci-lint",
-  "go.lintOnSave": "file",
+  "go.useLanguageServer": true,
+  "go.languageServerExperimentalFeatures": {
+    "diagnostics": false,
+  },
+  "go.lintFlags": ["--fast"],
   "go.testOnSave": true,
   "[go]": {
-    "editor.formatOnSave": false
+    "editor.formatOnSave": true,
+    "editor.codeActionsOnSave": {
+      "source.organizeImports": true
+    }
   }
 }
diff --git a/docs/docs/segment-owm.md b/docs/docs/segment-owm.md
index 296ff0a3..711f5b49 100644
--- a/docs/docs/segment-owm.md
+++ b/docs/docs/segment-owm.md
@@ -30,7 +30,8 @@ The free tier for *Current weather and forecasts collection* is sufficient.
     "units": "metric",
     "enable_hyperlink" : false,
     "http_timeout": 20,
-    "cache_timeout": 10
+    "cache_timeout": 10,
+    "template": "{{.Weather}} ({{.Temperature}}{{.UnitIcon}})"
   }
 }
 ```
@@ -46,3 +47,12 @@ The free tier for *Current weather and forecasts collection* is sufficient.
 - enable_hyperlink: `bool` - Displays an hyperlink to get openweathermap data
 - http_timeout: `int` - The default timeout for http request is 20ms.
 - cache_timeout: `int` - The default timeout for request caching is 10m. A value of 0 disables the cache.
+- template: `string` - A go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the
+properties below. Defaults to `{{.Weather}} ({{.Temperature}}{{.UnitIcon}})`
+
+## Template Properties
+
+- `.Weather`: `string` - the current weather icon
+- `.Temperature`: `string` - the current temperature
+- `.UnitIcon`: `string` - the current unit icon(based on units property)
+- `.URL`: `string` - the url of the current api call
diff --git a/src/segment_owm.go b/src/segment_owm.go
index 187a79d8..56ebe921 100644
--- a/src/segment_owm.go
+++ b/src/segment_owm.go
@@ -8,10 +8,11 @@ import (
 type owm struct {
 	props       *properties
 	env         environmentInfo
-	temperature float64
-	weather     string
-	url         string
+	Temperature float64
+	Weather     string
+	URL         string
 	units       string
+	UnitIcon    string
 }
 
 const (
@@ -25,7 +26,7 @@ const (
 	CacheTimeout Property = "cache_timeout"
 	// CacheKeyResponse key used when caching the response
 	CacheKeyResponse string = "owm_response"
-	// CacheKeyUrl key used when caching the url responsible for the response
+	// CacheKeyURL key used when caching the url responsible for the response
 	CacheKeyURL string = "owm_url"
 )
 
@@ -38,7 +39,7 @@ type temperature struct {
 	Value float64 `json:"temp"`
 }
 
-type OWMDataResponse struct {
+type owmDataResponse struct {
 	Data        []weather `json:"weather"`
 	temperature `json:"main"`
 }
@@ -49,27 +50,34 @@ func (d *owm) enabled() bool {
 }
 
 func (d *owm) string() string {
-	unitIcon := "\ue33e"
+	d.UnitIcon = "\ue33e"
 	switch d.units {
 	case "imperial":
-		unitIcon = "°F" // \ue341"
+		d.UnitIcon = "°F" // \ue341"
 	case "metric":
-		unitIcon = "°C" // \ue339"
+		d.UnitIcon = "°C" // \ue339"
 	case "":
 		fallthrough
 	case "standard":
-		unitIcon = "°K" // \ufa05"
+		d.UnitIcon = "°K" // \ufa05"
 	}
-	text := fmt.Sprintf("%s (%g%s)", d.weather, d.temperature, unitIcon)
-	if d.props.getBool(EnableHyperlink, false) {
-		text = fmt.Sprintf("[%s](%s)", text, d.url)
+	segmentTemplate := d.props.getString(SegmentTemplate, "{{.Weather}} ({{.Temperature}}{{.UnitIcon}})")
+	template := &textTemplate{
+		Template: segmentTemplate,
+		Context:  d,
+		Env:      d.env,
 	}
+	text, err := template.render()
+	if err != nil {
+		return err.Error()
+	}
+
 	return text
 }
 
-func (d *owm) getResult() (*OWMDataResponse, error) {
+func (d *owm) getResult() (*owmDataResponse, error) {
 	cacheTimeout := int64(d.props.getInt(CacheTimeout, DefaultCacheTimeout))
-	response := new(OWMDataResponse)
+	response := new(owmDataResponse)
 	if cacheTimeout > 0 {
 		// check if data stored in cache
 		val, found := d.env.cache().get(CacheKeyResponse)
@@ -79,7 +87,7 @@ func (d *owm) getResult() (*OWMDataResponse, error) {
 			if err != nil {
 				return nil, err
 			}
-			d.url, _ = d.env.cache().get(CacheKeyURL)
+			d.URL, _ = d.env.cache().get(CacheKeyURL)
 			return response, nil
 		}
 	}
@@ -88,21 +96,21 @@ func (d *owm) getResult() (*OWMDataResponse, error) {
 	location := d.props.getString(Location, "De Bilt,NL")
 	units := d.props.getString(Units, "standard")
 	httpTimeout := d.props.getInt(HTTPTimeout, DefaultHTTPTimeout)
-	d.url = fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&units=%s&appid=%s", location, units, apikey)
+	d.URL = fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&units=%s&appid=%s", location, units, apikey)
 
-	body, err := d.env.doGet(d.url, httpTimeout)
+	body, err := d.env.doGet(d.URL, httpTimeout)
 	if err != nil {
-		return new(OWMDataResponse), err
+		return new(owmDataResponse), err
 	}
 	err = json.Unmarshal(body, &response)
 	if err != nil {
-		return new(OWMDataResponse), err
+		return new(owmDataResponse), err
 	}
 
 	if cacheTimeout > 0 {
 		// persist new forecasts in cache
 		d.env.cache().set(CacheKeyResponse, string(body), cacheTimeout)
-		d.env.cache().set(CacheKeyURL, d.url, cacheTimeout)
+		d.env.cache().set(CacheKeyURL, d.URL, cacheTimeout)
 	}
 	return response, nil
 }
@@ -115,7 +123,7 @@ func (d *owm) setStatus() error {
 		return err
 	}
 
-	d.temperature = q.temperature.Value
+	d.Temperature = q.temperature.Value
 	icon := ""
 	switch q.Data[0].TypeID {
 	case "01n":
@@ -155,7 +163,7 @@ func (d *owm) setStatus() error {
 	case "50d":
 		icon = "\ue313"
 	}
-	d.weather = icon
+	d.Weather = icon
 	d.units = units
 	return nil
 }
diff --git a/themes/schema.json b/themes/schema.json
index 69c668cd..5b6ef6ff 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -1647,6 +1647,9 @@
                     "title": "cache timeout",
                     "description": "The number of minutes the response is cached. A value of 0 disables the cache.",
                     "default": 10
+                  },
+                  "template": {
+                    "$ref": "#/definitions/template"
                   }
                 }
               }


Test patch:
diff --git a/src/segment_owm_test.go b/src/segment_owm_test.go
index dc867bfa..3c6b4fea 100644
--- a/src/segment_owm_test.go
+++ b/src/segment_owm_test.go
@@ -18,6 +18,7 @@ func TestOWMSegmentSingle(t *testing.T) {
 		JSONResponse    string
 		ExpectedString  string
 		ExpectedEnabled bool
+		Template        string
 		Error           error
 	}{
 		{
@@ -26,6 +27,20 @@ func TestOWMSegmentSingle(t *testing.T) {
 			ExpectedString:  "\ufa98 (20°C)",
 			ExpectedEnabled: true,
 		},
+		{
+			Case:            "Sunny Display",
+			JSONResponse:    `{"weather":[{"icon":"01d"}],"main":{"temp":20}}`,
+			ExpectedString:  "\ufa98 (20°C)",
+			ExpectedEnabled: true,
+			Template:        "{{.Weather}} ({{.Temperature}}{{.UnitIcon}})",
+		},
+		{
+			Case:            "Sunny Display",
+			JSONResponse:    `{"weather":[{"icon":"01d"}],"main":{"temp":20}}`,
+			ExpectedString:  "\ufa98 ",
+			ExpectedEnabled: true,
+			Template:        "{{.Weather}} ",
+		},
 		{
 			Case:            "Error in retrieving data",
 			JSONResponse:    "nonsense",
@@ -47,6 +62,10 @@ func TestOWMSegmentSingle(t *testing.T) {
 
 		env.On("doGet", OWMAPIURL).Return([]byte(tc.JSONResponse), tc.Error)
 
+		if tc.Template != "" {
+			props.values[SegmentTemplate] = tc.Template
+		}
+
 		o := &owm{
 			props: props,
 			env:   env,
@@ -204,6 +223,8 @@ func TestOWMSegmentIcons(t *testing.T) {
 
 		env.On("doGet", OWMAPIURL).Return([]byte(response), nil)
 
+		props.values[SegmentTemplate] = "[{{.Weather}} ({{.Temperature}}{{.UnitIcon}})]({{.URL}})"
+
 		o := &owm{
 			props: props,
 			env:   env,
@@ -253,6 +274,9 @@ func TestOWMSegmentFromCacheWithHyperlink(t *testing.T) {
 			EnableHyperlink: true,
 		},
 	}
+
+	props.values[SegmentTemplate] = "[{{.Weather}} ({{.Temperature}}{{.UnitIcon}})]({{.URL}})"
+
 	o := &owm{
 		props: props,
 		env:   env,