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,