Rewrite to new yaml format
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aziis98/cabret"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerType("categorize", &Categorize{})
|
||||
}
|
||||
|
||||
type Categorize struct {
|
||||
Key string
|
||||
|
||||
// Operation to be executed for each category
|
||||
Operation cabret.Operation
|
||||
}
|
||||
|
||||
func (op *Categorize) Load(config map[string]any) error {
|
||||
{
|
||||
v, ok := config["key"]
|
||||
if !ok {
|
||||
return fmt.Errorf(`missing "key" field`)
|
||||
}
|
||||
key, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected string but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
op.Key = key
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (op *Categorize) Process(content cabret.Content) (*cabret.Content, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package operation
|
||||
|
||||
import "github.com/aziis98/cabret"
|
||||
|
||||
type GroupBy struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
func (op GroupBy) Process(content cabret.Content) (*cabret.Content, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
+58
-29
@@ -1,38 +1,73 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"fmt"
|
||||
goHtmlTemplate "html/template"
|
||||
"log"
|
||||
"mime"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
gopath "path"
|
||||
|
||||
"github.com/alecthomas/repr"
|
||||
"github.com/aziis98/cabret"
|
||||
"github.com/aziis98/cabret/operation/layout"
|
||||
"github.com/aziis98/cabret/operation/template"
|
||||
"github.com/aziis98/cabret/util"
|
||||
)
|
||||
|
||||
var HtmlMimeType = mime.TypeByExtension(".html")
|
||||
|
||||
var _ cabret.FlatMapOperation = Layout{}
|
||||
|
||||
type Layout struct {
|
||||
// TemplateFilesPattern is a comma separated list of unix glob patterns
|
||||
TemplateFilesPattern string
|
||||
|
||||
Options map[string]any
|
||||
func init() {
|
||||
registerType("layout", &Layout{})
|
||||
}
|
||||
|
||||
func (op Layout) FlatMap(content cabret.Content) (*cabret.Content, error) {
|
||||
var tmpl layout.Template
|
||||
type Layout struct {
|
||||
// TemplatePatterns is a list of glob patterns of templates that will be loaded
|
||||
TemplatePatterns []string
|
||||
}
|
||||
|
||||
patterns := strings.Split(op.TemplateFilesPattern, ",")
|
||||
func (op *Layout) Load(config map[string]any) error {
|
||||
if v, ok := config[ShortFormValueKey]; ok {
|
||||
globPatternsStr, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected a comma separated list of glob patterns but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
globPatterns := strings.Split(globPatternsStr, ",")
|
||||
for _, pat := range globPatterns {
|
||||
op.TemplatePatterns = append(op.TemplatePatterns, strings.TrimSpace(pat))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if v, ok := config["paths"]; ok {
|
||||
globPatterns, ok := v.([]string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected a list of glob patterns but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
for _, pat := range globPatterns {
|
||||
op.TemplatePatterns = append(op.TemplatePatterns, strings.TrimSpace(pat))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if v, ok := config["path"]; ok {
|
||||
globPatternStr, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected a glob pattern but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
op.TemplatePatterns = []string{strings.TrimSpace(globPatternStr)}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(`invalid config`)
|
||||
}
|
||||
|
||||
func (op Layout) ProcessItem(content cabret.Content) (*cabret.Content, error) {
|
||||
// expand glob patterns
|
||||
tmplFiles := []string{}
|
||||
for _, pat := range patterns {
|
||||
for _, pat := range op.TemplatePatterns {
|
||||
files, err := filepath.Glob(strings.TrimSpace(pat))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -41,28 +76,22 @@ func (op Layout) FlatMap(content cabret.Content) (*cabret.Content, error) {
|
||||
tmplFiles = append(tmplFiles, files...)
|
||||
}
|
||||
|
||||
log.Printf(`[Layout] template pattern "%s" expanded to %s`, op.TemplateFilesPattern, repr.String(tmplFiles))
|
||||
|
||||
if gopath.Ext(tmplFiles[0]) == ".html" {
|
||||
var err error
|
||||
if tmpl, err = layout.NewHtmlTemplate(tmplFiles...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if tmpl, err = layout.NewTextTemplate(tmplFiles...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// create template
|
||||
tmpl, err := template.ParseFiles(tmplFiles...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := util.CloneMap(content.Metadata)
|
||||
|
||||
if content.Type == HtmlMimeType {
|
||||
ctx["Content"] = template.HTML(content.Data)
|
||||
ctx["Content"] = goHtmlTemplate.HTML(content.Data)
|
||||
} else {
|
||||
ctx["Content"] = content.Data
|
||||
}
|
||||
|
||||
log.Printf(`[operation.Layout] rendering into layout "%s"`, strings.Join(op.TemplatePatterns, ", "))
|
||||
|
||||
data, err := tmpl.Render(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package layout
|
||||
|
||||
type Template interface {
|
||||
Render(ctx map[string]any) ([]byte, error)
|
||||
}
|
||||
+11
-2
@@ -2,6 +2,7 @@ package operation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
|
||||
"github.com/aziis98/cabret"
|
||||
"github.com/iancoleman/strcase"
|
||||
@@ -11,13 +12,19 @@ import (
|
||||
"github.com/yuin/goldmark/parser"
|
||||
)
|
||||
|
||||
var _ cabret.FlatMapOperation = Markdown{}
|
||||
func init() {
|
||||
registerType("markdown", &Markdown{})
|
||||
}
|
||||
|
||||
type Markdown struct {
|
||||
Options map[string]any
|
||||
}
|
||||
|
||||
func (op Markdown) FlatMap(content cabret.Content) (*cabret.Content, error) {
|
||||
func (op *Markdown) Load(config map[string]any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (op Markdown) ProcessItem(content cabret.Content) (*cabret.Content, error) {
|
||||
md := goldmark.New(
|
||||
goldmark.WithExtensions(
|
||||
extension.GFM,
|
||||
@@ -30,6 +37,8 @@ func (op Markdown) FlatMap(content cabret.Content) (*cabret.Content, error) {
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
log.Printf(`[operation.Markdown] rendering markdown`)
|
||||
|
||||
context := parser.NewContext()
|
||||
if err := md.Convert(content.Data, &buf, parser.WithContext(context)); err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"os"
|
||||
gopath "path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aziis98/cabret"
|
||||
)
|
||||
|
||||
var _ cabret.ListOperation = Read{}
|
||||
|
||||
type Read struct {
|
||||
Patterns []string
|
||||
}
|
||||
|
||||
func (op Read) MapAll(contents []cabret.Content) ([]cabret.Content, error) {
|
||||
for _, pattern := range op.Patterns {
|
||||
files, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contents = append(contents, cabret.Content{
|
||||
Type: mime.TypeByExtension(gopath.Ext(file)),
|
||||
Data: data,
|
||||
Metadata: cabret.Map{},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return contents, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"github.com/aziis98/cabret"
|
||||
)
|
||||
|
||||
// ShortFormValueKey is used by some operations that support an inline form
|
||||
const ShortFormValueKey = "value"
|
||||
|
||||
var registry = map[string]reflect.Type{}
|
||||
|
||||
func registerType(name string, op cabret.Operation) {
|
||||
typ := reflect.TypeOf(op).Elem()
|
||||
log.Printf(`[operation] registered type "%v"`, typ)
|
||||
registry[name] = typ
|
||||
}
|
||||
|
||||
func Build(name string, options map[string]any) (cabret.Operation, error) {
|
||||
typ, ok := registry[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`no registered operation named %q`, name)
|
||||
}
|
||||
|
||||
op := reflect.New(typ).Interface().(cabret.Operation)
|
||||
if err := op.Load(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return op, nil
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
op, err := Build("categorize", map[string]any{
|
||||
"key": "tags",
|
||||
})
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, op, &Categorize{
|
||||
Key: "tags",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"mime"
|
||||
"os"
|
||||
gopath "path"
|
||||
|
||||
"github.com/aziis98/cabret"
|
||||
"github.com/aziis98/cabret/path"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerType("source", &Source{})
|
||||
}
|
||||
|
||||
// Source is a ListOperation that appends the matched files to the processing items
|
||||
type Source struct {
|
||||
Patterns []string
|
||||
}
|
||||
|
||||
func (op *Source) Load(config map[string]any) error {
|
||||
if v, ok := config[ShortFormValueKey]; ok {
|
||||
pattern, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected pattern but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
op.Patterns = []string{pattern}
|
||||
return nil
|
||||
}
|
||||
if v, ok := config["paths"]; ok {
|
||||
patterns, ok := v.([]string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected list of patterns but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
op.Patterns = patterns
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(`invalid config for "source": %#v`, config)
|
||||
}
|
||||
|
||||
func (op Source) ProcessList(contents []cabret.Content) ([]cabret.Content, error) {
|
||||
files, err := cabret.FindFiles([]string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches := []cabret.MatchResult{}
|
||||
|
||||
for _, patternStr := range op.Patterns {
|
||||
pat, err := path.ParsePattern(patternStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if ok, captures, _ := pat.Match(f); ok {
|
||||
matches = append(matches, cabret.MatchResult{
|
||||
File: f,
|
||||
Captures: captures,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range matches {
|
||||
log.Printf(`[operation.Source] reading "%s"`, m.File)
|
||||
|
||||
data, err := os.ReadFile(m.File)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contents = append(contents, cabret.Content{
|
||||
Type: mime.TypeByExtension(gopath.Ext(m.File)),
|
||||
Data: data,
|
||||
Metadata: cabret.Map{
|
||||
cabret.MatchResultKey: m.Captures,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return contents, nil
|
||||
}
|
||||
+23
-4
@@ -2,6 +2,7 @@ package operation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
gopath "path"
|
||||
@@ -10,20 +11,38 @@ import (
|
||||
"github.com/aziis98/cabret/path"
|
||||
)
|
||||
|
||||
var _ cabret.FlatMapOperation = Target{}
|
||||
func init() {
|
||||
registerType("target", &Target{})
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
PathTemplate string
|
||||
}
|
||||
|
||||
func (op Target) FlatMap(c cabret.Content) (*cabret.Content, error) {
|
||||
mr, ok := c.Metadata[cabret.MatchResult].(map[string]string)
|
||||
func (op *Target) Load(config map[string]any) error {
|
||||
if v, ok := config[ShortFormValueKey]; ok {
|
||||
template, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected pattern but got "%v" of type %T`, v, v)
|
||||
}
|
||||
|
||||
op.PathTemplate = template
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(`invalid config for "target": %#v`, config)
|
||||
}
|
||||
|
||||
func (op Target) ProcessItem(c cabret.Content) (*cabret.Content, error) {
|
||||
mr, ok := c.Metadata[cabret.MatchResultKey].(map[string]string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`invalid match result type %T`, c.Metadata[cabret.MatchResult])
|
||||
return nil, fmt.Errorf(`invalid match result type %T`, c.Metadata[cabret.MatchResultKey])
|
||||
}
|
||||
|
||||
target := path.RenderTemplate(op.PathTemplate, mr)
|
||||
|
||||
log.Printf(`[operation.Target] writing "%s"`, target)
|
||||
|
||||
if err := os.MkdirAll(gopath.Dir(target), 0777); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package layout
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
goHtmlTemplate "html/template"
|
||||
)
|
||||
|
||||
var _ Template = HtmlTemplate{}
|
||||
|
||||
type HtmlTemplate struct {
|
||||
*template.Template
|
||||
*goHtmlTemplate.Template
|
||||
}
|
||||
|
||||
func NewHtmlTemplate(files ...string) (*HtmlTemplate, error) {
|
||||
t, err := template.ParseFiles(files...)
|
||||
t, err := goHtmlTemplate.ParseFiles(files...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -21,11 +21,10 @@ func NewHtmlTemplate(files ...string) (*HtmlTemplate, error) {
|
||||
}
|
||||
|
||||
func (t HtmlTemplate) Render(ctx map[string]any) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
if err := t.Template.Execute(&b, ctx); err != nil {
|
||||
var buf bytes.Buffer
|
||||
if err := t.Template.Execute(&buf, ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Template interface {
|
||||
Render(ctx map[string]any) ([]byte, error)
|
||||
}
|
||||
|
||||
func ParseFiles(files ...string) (Template, error) {
|
||||
if filepath.Ext(files[0]) == ".html" {
|
||||
return NewHtmlTemplate(files...)
|
||||
}
|
||||
|
||||
return NewTextTemplate(files...)
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
package layout
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"text/template"
|
||||
goTextTemplate "text/template"
|
||||
)
|
||||
|
||||
var _ Template = TextTemplate{}
|
||||
|
||||
type TextTemplate struct {
|
||||
*template.Template
|
||||
*goTextTemplate.Template
|
||||
}
|
||||
|
||||
func NewTextTemplate(files ...string) (*TextTemplate, error) {
|
||||
t, err := template.ParseFiles(files...)
|
||||
t, err := goTextTemplate.ParseFiles(files...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -21,11 +21,10 @@ func NewTextTemplate(files ...string) (*TextTemplate, error) {
|
||||
}
|
||||
|
||||
func (t TextTemplate) Render(ctx map[string]any) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
if err := t.Template.Execute(&b, ctx); err != nil {
|
||||
var buf bytes.Buffer
|
||||
if err := t.Template.Execute(&buf, ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user