port liquid filters from gojekyll

This commit is contained in:
facundoolano 2024-02-15 13:53:45 -03:00
parent bacc9d9052
commit b788826423
3 changed files with 143 additions and 3 deletions

2
go.mod
View file

@ -6,12 +6,12 @@ require (
github.com/fsnotify/fsnotify v1.7.0
github.com/niklasfasching/go-org v1.7.0
github.com/osteele/liquid v1.3.2
github.com/yuin/goldmark v1.7.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/osteele/tuesday v1.0.3 // indirect
github.com/yuin/goldmark v1.7.0 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.4.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

123
templates/filters.go Normal file
View file

@ -0,0 +1,123 @@
package templates
import (
"fmt"
"reflect"
"github.com/osteele/liquid/evaluator"
"github.com/osteele/liquid/expressions"
)
// copied from https://github.com/osteele/gojekyll/blob/main/filters/filters.go
func filter(values []map[string]interface{}, key string) []interface{} {
var result []interface{}
for _, value := range values {
if _, ok := value[key]; ok {
result = append(result, value)
}
}
return result
}
func groupByExpFilter(array []map[string]interface{}, name string, expr expressions.Closure) ([]map[string]interface{}, error) {
rt := reflect.ValueOf(array)
if !(rt.Kind() != reflect.Array || rt.Kind() == reflect.Slice) {
return nil, nil
}
groups := map[interface{}][]interface{}{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i).Interface()
key, err := expr.Bind(name, item).Evaluate()
if err != nil {
return nil, err
}
if group, found := groups[key]; found {
groups[key] = append(group, item)
} else {
groups[key] = []interface{}{item}
}
}
var result []map[string]interface{}
for k, v := range groups {
result = append(result, map[string]interface{}{"name": k, "items": v})
}
return result, nil
}
func groupByFilter(array []map[string]interface{}, property string) []map[string]interface{} {
rt := reflect.ValueOf(array)
if !(rt.Kind() != reflect.Array || rt.Kind() == reflect.Slice) {
return nil
}
groups := map[interface{}][]interface{}{}
for i := 0; i < rt.Len(); i++ {
irt := rt.Index(i)
if irt.Kind() == reflect.Map && irt.Type().Key().Kind() == reflect.String {
krt := irt.MapIndex(reflect.ValueOf(property))
if krt.IsValid() && krt.CanInterface() {
key := krt.Interface()
if group, found := groups[key]; found {
groups[key] = append(group, irt.Interface())
} else {
groups[key] = []interface{}{irt.Interface()}
}
}
}
}
var result []map[string]interface{}
for k, v := range groups {
result = append(result, map[string]interface{}{"name": k, "items": v})
}
return result
}
func sortFilter(array []interface{}, key interface{}, nilFirst func(bool) bool) []interface{} {
nf := nilFirst(true)
result := make([]interface{}, len(array))
copy(result, array)
if key == nil {
evaluator.Sort(result)
} else {
// TODO error if key is not a string
evaluator.SortByProperty(result, key.(string), nf)
}
return result
}
func whereExpFilter(array []interface{}, name string, expr expressions.Closure) ([]interface{}, error) {
rt := reflect.ValueOf(array)
if rt.Kind() != reflect.Array && rt.Kind() != reflect.Slice {
return nil, nil
}
var result []interface{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i).Interface()
value, err := expr.Bind(name, item).Evaluate()
if err != nil {
return nil, err
}
if value != nil && value != false {
result = append(result, item)
}
}
return result, nil
}
func whereFilter(array []map[string]interface{}, key string, value interface{}) []interface{} {
rt := reflect.ValueOf(array)
if rt.Kind() != reflect.Array && rt.Kind() != reflect.Slice {
return nil
}
var result []interface{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i)
if item.Kind() == reflect.Map && item.Type().Key().Kind() == reflect.String {
attr := item.MapIndex(reflect.ValueOf(key))
if attr.IsValid() && fmt.Sprint(attr) == value {
result = append(result, item.Interface())
}
}
}
return result
}

View file

@ -1,4 +1,3 @@
// TODO consider making this another package
package templates
import (
@ -27,7 +26,25 @@ type Template struct {
}
func NewEngine() *Engine {
return liquid.NewEngine()
// a lot of the filters and tags available at jekyll aren't default liquid
// manually adding them here as in https://github.com/osteele/gojekyll/blob/main/filters/filters.go
e := liquid.NewEngine()
e.RegisterFilter("filter", filter)
e.RegisterFilter("group_by", groupByFilter)
e.RegisterFilter("group_by_exp", groupByExpFilter)
e.RegisterFilter("sort", sortFilter)
e.RegisterFilter("where", whereFilter)
e.RegisterFilter("where_exp", whereExpFilter)
e.RegisterFilter("absolute_url", func(s string) string {
// FIXME implement after adding a config struct, using the url
// return utils.URLJoin(c.AbsoluteURL, c.BaseURL, s)
return s
})
return e
}
func Parse(engine *Engine, path string) (*Template, error) {