diff --git a/go.mod b/go.mod index 4bc3be2..2c8d898 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/templates/filters.go b/templates/filters.go new file mode 100644 index 0000000..336e989 --- /dev/null +++ b/templates/filters.go @@ -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 +} diff --git a/templates/templates.go b/templates/templates.go index 1e26ec3..6f9b2ae 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -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) {