diff --git a/docs/custom-api.md b/docs/custom-api.md
index 7eb6ca6..8e432c9 100644
--- a/docs/custom-api.md
+++ b/docs/custom-api.md
@@ -226,10 +226,10 @@ JSON response:
}
```
-Calculations can be performed, however all numbers must be converted to floats first if they are not already:
+Calculations can be performed on either ints or floats. If both numbers are ints, an int will be returned, otherwise a float will be returned. If you try to divide by zero, 0 will be returned. If you provide non-numeric values, `NaN` will be returned.
```html
-
{{ sub (.JSON.Int "price" | toFloat) (.JSON.Int "discount" | toFloat) }}
+{{ sub (.JSON.Int "price") (.JSON.Int "discount") }}
```
Output:
@@ -325,6 +325,8 @@ The following helper functions provided by Glance are available:
- `toFloat(i int) float`: Converts an integer to a float.
- `toInt(f float) int`: Converts a float to an integer.
- `toRelativeTime(t time.Time) template.HTMLAttr`: Converts Time to a relative time such as 2h, 1d, etc which dynamically updates. **NOTE:** the value of this function should be used as an attribute in an HTML tag, e.g. ``.
+- `now() time.Time`: Returns the current time.
+- `duration(str string) time.Duration`: Parses a string such as `1h`, `1d`, `5h30m`, etc into a `time.Duration`.
- `parseTime(layout string, s string) time.Time`: Parses a string into time.Time. The layout must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants). You can alternatively use these values instead of the literal format: "unix", "RFC3339", "RFC3339Nano", "DateTime", "DateOnly".
- `parseRelativeTime(layout string, s string) time.Time`: A shorthand for `{{ .String "date" | parseTime "rfc3339" | toRelativeTime }}`.
- `add(a, b float) float`: Adds two numbers.
diff --git a/internal/glance/widget-custom-api.go b/internal/glance/widget-custom-api.go
index ec88016..be83032 100644
--- a/internal/glance/widget-custom-api.go
+++ b/internal/glance/widget-custom-api.go
@@ -342,6 +342,23 @@ func (r *decoratedGJSONResult) Bool(key string) bool {
return r.Get(key).Bool()
}
+func customAPIDoMathOp[T int | float64](a, b T, op string) T {
+ switch op {
+ case "add":
+ return a + b
+ case "sub":
+ return a - b
+ case "mul":
+ return a * b
+ case "div":
+ if b == 0 {
+ return 0
+ }
+ return a / b
+ }
+ return 0
+}
+
var customAPITemplateFuncs = func() template.FuncMap {
var regexpCacheMu sync.Mutex
var regexpCache = make(map[string]*regexp.Regexp)
@@ -359,6 +376,31 @@ var customAPITemplateFuncs = func() template.FuncMap {
return regex
}
+ doMathOpWithAny := func(a, b any, op string) any {
+ switch at := a.(type) {
+ case int:
+ switch bt := b.(type) {
+ case int:
+ return customAPIDoMathOp(at, bt, op)
+ case float64:
+ return customAPIDoMathOp(float64(at), bt, op)
+ default:
+ return math.NaN()
+ }
+ case float64:
+ switch bt := b.(type) {
+ case int:
+ return customAPIDoMathOp(at, float64(bt), op)
+ case float64:
+ return customAPIDoMathOp(at, bt, op)
+ default:
+ return math.NaN()
+ }
+ default:
+ return math.NaN()
+ }
+ }
+
funcs := template.FuncMap{
"toFloat": func(a int) float64 {
return float64(a)
@@ -366,21 +408,28 @@ var customAPITemplateFuncs = func() template.FuncMap {
"toInt": func(a float64) int {
return int(a)
},
- "add": func(a, b float64) float64 {
- return a + b
+ "add": func(a, b any) any {
+ return doMathOpWithAny(a, b, "add")
},
- "sub": func(a, b float64) float64 {
- return a - b
+ "sub": func(a, b any) any {
+ return doMathOpWithAny(a, b, "sub")
},
- "mul": func(a, b float64) float64 {
- return a * b
+ "mul": func(a, b any) any {
+ return doMathOpWithAny(a, b, "mul")
},
- "div": func(a, b float64) float64 {
- if b == 0 {
- return math.NaN()
+ "div": func(a, b any) any {
+ return doMathOpWithAny(a, b, "div")
+ },
+ "now": func() time.Time {
+ return time.Now()
+ },
+ "duration": func(str string) time.Duration {
+ d, err := time.ParseDuration(str)
+ if err != nil {
+ return 0
}
- return a / b
+ return d
},
"parseTime": customAPIFuncParseTime,
"toRelativeTime": dynamicRelativeTimeAttrs,