salix/if_tag.go

138 lines
2.9 KiB
Go
Raw Normal View History

2023-10-31 01:43:18 +00:00
/*
* Salix - Go templating engine
* Copyright (C) 2023 Elara Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2023-10-29 01:10:17 +00:00
package salix
import (
"errors"
2023-10-30 03:10:56 +00:00
2023-10-31 01:38:53 +00:00
"go.elara.ws/salix/ast"
2023-10-29 01:10:17 +00:00
)
var (
ErrIfExpectsOneArg = errors.New("if tag expects one argument")
ErrIfExpectsBool = errors.New("if tag expects a bool value")
ErrIfTwoElse = errors.New("if tags can only have one else tag inside")
)
// ifTag represents a #if tag within a Salix template
type ifTag struct{}
func (it ifTag) Run(tc *TagContext, block, args []ast.Node) error {
if len(args) != 1 {
return ErrIfExpectsOneArg
}
inner, err := it.findInner(block)
if err != nil {
return err
}
val, err := tc.GetValue(args[0], nil)
if err != nil {
return err
}
cond, ok := val.(bool)
if !ok {
return ErrIfExpectsBool
}
if cond {
return tc.Execute(block[:inner.endRoot], nil)
} else {
if len(inner.elifTags) > 0 {
for i, elifTag := range inner.elifTags {
val, err := tc.GetValue(elifTag.value, nil)
if err != nil {
return err
}
cond, ok := val.(bool)
if !ok {
return ErrIfExpectsBool
}
nextIndex := len(block)
if i < len(inner.elifTags)-1 {
nextIndex = inner.elifTags[i+1].index
} else if inner.elseIndex != 0 {
nextIndex = inner.elseIndex
}
if cond {
return tc.Execute(block[elifTag.index+1:nextIndex], nil)
}
}
}
if inner.elseIndex != 0 {
return tc.Execute(block[inner.elseIndex+1:], nil)
}
}
return nil
}
type innerTags struct {
endRoot int
elifTags []elif
elseIndex int
}
type elif struct {
index int
value ast.Node
}
// findInner finds the inner elif and else tags in a block
// passed to the if tag.
func (it ifTag) findInner(block []ast.Node) (innerTags, error) {
var out innerTags
for i, node := range block {
if tag, ok := node.(ast.Tag); ok {
switch tag.Name.Value {
case "elif":
if out.endRoot == 0 {
out.endRoot = i
}
if len(tag.Params) > 1 {
return innerTags{}, ErrIfExpectsOneArg
}
out.elifTags = append(out.elifTags, elif{
index: i,
value: tag.Params[0],
})
case "else":
if out.elseIndex != 0 {
return innerTags{}, ErrIfTwoElse
}
if out.endRoot == 0 {
out.endRoot = i
}
out.elseIndex = i
break
}
}
}
if out.endRoot == 0 {
out.endRoot = len(block)
}
return out, nil
}