More custom-api additions/tweaks

* Remove need to convert to int for math stuff
* Add `now` and `duration` functions
This commit is contained in:
Svilen Markov 2025-03-28 23:34:49 +00:00
parent bd020c93f5
commit dd74c173a5
2 changed files with 63 additions and 12 deletions

View File

@ -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
<div>{{ sub (.JSON.Int "price" | toFloat) (.JSON.Int "discount" | toFloat) }}</div>
<div>{{ sub (.JSON.Int "price") (.JSON.Int "discount") }}</div>
```
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. `<span {{ toRelativeTime .Time }}></span>`.
- `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.

View File

@ -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,