Expression System
Overview
As you can see from the dynamic SQL examples, Juice supports expressions inside if and when tags. The syntax is broadly similar to Go expressions.
Usage Restrictions
Juice supports only single-line expressions.
When writing a string inside an expression, wrap the
testattribute value in single quotes:<if test='name == "eatmoreapple"'> </if>
Keep expressions as simple as possible.
Basic Operators
Comparison Operators
Juice supports these comparison operators:
==: equal to!=: not equal to>: greater than>=: greater than or equal to
Attention
< and <= conflict with XML syntax. Use < and <= instead.
Logical Operators
Juice supports these logical operators:
and: logical ANDor: logical OR!: logical NOT
Arithmetic Operators
Juice supports these arithmetic operators:
+: addition-: subtraction*: multiplication/: division%: remainder
Reserved Keywords
The following keywords are reserved and cannot be used as variable names:
truefalsenilandor
Built-In Functions
Juice includes several built-in functions. Built-ins are registered by the github.com/go-juicedev/juice/eval package and can be used directly in dynamic SQL expressions.
len: returns the length of a string, array, slice, map, or channelfunc len(any) (int, error)
substr: returns a substringfunc substr(string, int, int) (string, error)
join: joins a string array or slice with a separatorfunc join(any, string) (string, error)
slice: performs slicing on arrays and slicesfunc slice(any, int, int) ([]any, error)
lower: converts to lowercasefunc lower(string) (string, error)
upper: converts to uppercasefunc upper(string) (string, error)
trim,trimLeft,trimRight: trim characters from stringsfunc trim(string, string) (string, error)
replaceandreplaceAll: replace substringsfunc replace(string, string, string, int) (string, error) func replaceAll(string, string, string) (string, error)
split,splitN,splitAfter: split stringsfunc split(string, string) ([]string, error) func splitN(string, string, int) ([]string, error) func splitAfter(string, string) ([]string, error)
Custom Functions
Registration requirements:
It must be a function.
It must return two values. The first can be any type and the second must be
error.
Example:
func add(x, y int) (int, error) {
return x + y, nil
}
func main() {
if err := eval.RegisterEvalFunc("add", add); err != nil {
panic(err)
}
}
When registering custom functions, import the eval package explicitly:
import "github.com/go-juicedev/juice/eval"
Usage:
<if test='add(1, 2) == 3'>
</if>
Function Calls
Basic function call:
<if test='MyFunc() == "eatmoreapple"'>
</if>
func MyFunc() (string, error) {
return "eatmoreapple", nil
}
param := juice.H{
"MyFunc": MyFunc,
}
Function with parameters:
<if test='MyFunc("eatmoreapple") == "eatmoreapple"'>
</if>
func MyFunc(str string) (string, error) {
return str, nil
}
param := juice.H{
"MyFunc": MyFunc,
}
Referencing parameters:
<if test='MyFunc(eatmoreapple) == "eatmoreapple"'>
</if>
param := juice.H{
"MyFunc": MyFunc,
"eatmoreapple": "eatmoreapple",
}
Multiple parameters:
<if test='MyFunc(eatmoreapple, 1, 2) == "eatmoreapple"'>
</if>
func MyFunc(str string, x, y int) (string, error) {
return str, nil
}
Methods on Custom Types
Method calls:
<if test='a.MyFunc() == "eatmoreapple"'>
</if>
type A struct {
Name string
}
func (a *A) MyFunc() (string, error) {
return a.Name, nil
}
param := juice.H{
"a": &A{Name: "eatmoreapple"},
}
Attribute Access
Struct fields:
<if test='a.Name == "eatmoreapple"'>
</if>
type A struct {
Name string
}
param := juice.H{
"a": &A{Name: "eatmoreapple"},
}
Map Operations
Indexed access:
param := juice.H{
"a": juice.H{
"Name": "eatmoreapple",
},
}
<if test='a["Name"] == "eatmoreapple"'>
</if>
<if test='a.Name == "eatmoreapple"'>
</if>
Attention
Difference between the two forms:
Indexed access such as
a["Name"]returns a default value when the key is missing.Dot access such as
a.Namethrows an error when the property is missing.Dot access also supports method calls.
Array Operations
<if test='a[0] == "eatmoreapple"'>
</if>
param := juice.H{
"a": []string{"eatmoreapple"},
}
Slice expressions are also supported:
<if test='len(a[1:]) > 0'>
</if>
<if test='len(a[start:end]) > 0'>
</if>
Index and slice bounds must evaluate to integer values. Negative single indexes count from the end of the array or slice, for example a[-1] returns the last element.
Tip
Best practices:
Keep expressions simple and readable.
Move complex logic into Go code when possible.
Use built-in functions appropriately.
Pay attention to XML escaping rules.
Test every condition branch.