diff --git a/README.md b/README.md
index d38d7f5..f9989eb 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,14 @@
Cabret is a yaml based static site generator, ideally with the same features as Hugo but with a simpler model. Here is a simple example of a _Cabretfile.yaml_
+## Build
+
+```bash shell
+$ go build -v -o ./bin/cabret ./cmd/cabret
+```
+
+## Introduction
+
```yaml
build:
- pipeline:
diff --git a/cabret.go b/cabret.go
index 2b92eb1..8c870ad 100644
--- a/cabret.go
+++ b/cabret.go
@@ -1,7 +1,6 @@
package cabret
import (
- "fmt"
"log"
)
@@ -35,7 +34,7 @@ type Content struct {
}
type Operation interface {
- Load(config map[string]any) error
+ Configure(config map[string]any) error
}
type ListOperation interface {
@@ -47,28 +46,3 @@ type ItemOperation interface {
Operation
ProcessItem(content Content) (*Content, error)
}
-
-func ProcessOperation(op Operation, inputs []Content) ([]Content, error) {
- switch op := op.(type) {
- case ListOperation:
- return op.ProcessList(inputs)
-
- case ItemOperation:
- outputs := []Content{}
- for _, item := range inputs {
- result, err := op.ProcessItem(item)
- if err != nil {
- return nil, err
- }
-
- // skip terminal operations
- if result == nil {
- continue
- }
- outputs = append(outputs, *result)
- }
- return outputs, nil
- default:
- return nil, fmt.Errorf(`invalid operation type %T`, op)
- }
-}
diff --git a/cmd/cabret/main.go b/cmd/cabret/main.go
index 3cdcde0..101cd22 100644
--- a/cmd/cabret/main.go
+++ b/cmd/cabret/main.go
@@ -4,7 +4,7 @@ import (
"log"
"github.com/aziis98/cabret/config"
- "github.com/aziis98/cabret/exec"
+ "github.com/aziis98/cabret/runner"
)
func main() {
@@ -15,9 +15,7 @@ func main() {
log.Fatal(err)
}
- // repr.Println(cabretfile)
-
- if err := exec.Execute(cabretfile); err != nil {
+ if err := runner.RunConfig(cabretfile); err != nil {
log.Fatal(err)
}
}
diff --git a/examples/basic/layouts/tag.html b/examples/basic/layouts/tag.html
index 3aff810..af22cb6 100644
--- a/examples/basic/layouts/tag.html
+++ b/examples/basic/layouts/tag.html
@@ -5,8 +5,7 @@
diff --git a/exec/exec.go b/exec/exec.go
deleted file mode 100644
index b622da8..0000000
--- a/exec/exec.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package exec
-
-import (
- "github.com/aziis98/cabret"
- "github.com/aziis98/cabret/config"
- "github.com/aziis98/cabret/pipeline"
-)
-
-func Execute(cfg *config.Cabretfile) error {
- for _, p := range cfg.Build {
- ops, err := pipeline.Parse(p)
- if err != nil {
- return err
- }
-
- if _, err := pipeline.Process([]cabret.Content{}, ops); err != nil {
- return err
- }
- }
-
- return nil
-}
diff --git a/operation/categorize.go b/operation/categorize.go
index bf22ba2..2af5b70 100644
--- a/operation/categorize.go
+++ b/operation/categorize.go
@@ -37,7 +37,7 @@ func getKey[T any](m map[string]any, key string, defaultValue ...T) (T, error) {
return value, nil
}
-func (op *Categorize) Load(config map[string]any) error {
+func (op *Categorize) Configure(config map[string]any) error {
var err error
op.Key, err = getKey[string](config, "key")
diff --git a/operation/frontmatter.go b/operation/frontmatter.go
index 97a9587..6674d77 100644
--- a/operation/frontmatter.go
+++ b/operation/frontmatter.go
@@ -19,7 +19,7 @@ type Frontmatter struct {
Options map[string]any
}
-func (op *Frontmatter) Load(config map[string]any) error {
+func (op *Frontmatter) Configure(config map[string]any) error {
return nil
}
diff --git a/operation/layout.go b/operation/layout.go
index bbb1a2a..41f0f72 100644
--- a/operation/layout.go
+++ b/operation/layout.go
@@ -24,7 +24,7 @@ type Layout struct {
TemplatePatterns []string
}
-func (op *Layout) Load(config map[string]any) error {
+func (op *Layout) Configure(config map[string]any) error {
if v, ok := config[ShortFormValueKey]; ok {
globPatternsStr, ok := v.(string)
if !ok {
diff --git a/operation/markdown.go b/operation/markdown.go
index 5477ab1..2a69e10 100644
--- a/operation/markdown.go
+++ b/operation/markdown.go
@@ -20,7 +20,7 @@ type Markdown struct {
Options map[string]any
}
-func (op *Markdown) Load(config map[string]any) error {
+func (op *Markdown) Configure(config map[string]any) error {
return nil
}
diff --git a/operation/registry.go b/operation/registry.go
index 74f90e8..c2cbe97 100644
--- a/operation/registry.go
+++ b/operation/registry.go
@@ -19,16 +19,11 @@ func registerType(name string, op cabret.Operation) {
registry[name] = typ
}
-func Build(name string, options map[string]any) (cabret.Operation, error) {
+func NewWithName(name string) (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
+ return reflect.New(typ).Interface().(cabret.Operation), nil
}
diff --git a/operation/registry_test.go b/operation/registry_test.go
deleted file mode 100644
index c73bd65..0000000
--- a/operation/registry_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-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",
- })
-}
diff --git a/operation/source.go b/operation/source.go
index 3b9fcaf..94fecc0 100644
--- a/operation/source.go
+++ b/operation/source.go
@@ -20,20 +20,30 @@ type Source struct {
Patterns []string
}
-func (op *Source) Load(config map[string]any) error {
- if v, ok := config[ShortFormValueKey]; ok {
+func (op *Source) Configure(config map[string]any) error {
+ if v, ok := config["source"]; ok {
pattern, ok := v.(string)
if !ok {
- return fmt.Errorf(`expected pattern but got "%v" of type %T`, v, v)
+ return fmt.Errorf(`expected a path 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)
+ aPatterns, ok := v.([]any)
if !ok {
- return fmt.Errorf(`expected list of patterns but got "%v" of type %T`, v, v)
+ return fmt.Errorf(`expected a list of path patterns but got "%v" of type %T`, v, v)
+ }
+
+ patterns := []string{}
+ for _, aPat := range aPatterns {
+ p, ok := aPat.(string)
+ if !ok {
+ return fmt.Errorf(`expected a string but got "%v" of type %T`, aPat, aPat)
+ }
+
+ patterns = append(patterns, p)
}
op.Patterns = patterns
diff --git a/operation/source_test.go b/operation/source_test.go
new file mode 100644
index 0000000..ae00fe9
--- /dev/null
+++ b/operation/source_test.go
@@ -0,0 +1,68 @@
+package operation_test
+
+import (
+ "testing"
+
+ "github.com/aziis98/cabret/operation"
+ "github.com/aziis98/cabret/util"
+ "gotest.tools/assert"
+)
+
+func TestSourceShortForm(t *testing.T) {
+ t.Run("correct usage", func(t *testing.T) {
+ op := &operation.Source{}
+ err := op.Configure(util.ParseYAML(`
+ source: foo/bar/baz.txt
+ `))
+
+ assert.NilError(t, err)
+ assert.DeepEqual(t, op, &operation.Source{
+ Patterns: []string{
+ "foo/bar/baz.txt",
+ },
+ })
+ })
+ t.Run("wrong usage", func(t *testing.T) {
+ op := &operation.Source{}
+ err := op.Configure(util.ParseYAML(`
+ source: 123
+ `))
+
+ assert.Error(t, err, `expected a path pattern but got "123" of type int`)
+ })
+}
+
+func TestSourceWithPaths(t *testing.T) {
+ t.Run("correct usage", func(t *testing.T) {
+ op := &operation.Source{}
+ err := op.Configure(util.ParseYAML(`
+ use: source
+ paths:
+ - foo/bar/baz-1.txt
+ - foo/bar/baz-2.txt
+ - foo/bar/baz-3.txt
+ `))
+
+ assert.NilError(t, err)
+ assert.DeepEqual(t, op, &operation.Source{
+ Patterns: []string{
+ "foo/bar/baz-1.txt",
+ "foo/bar/baz-2.txt",
+ "foo/bar/baz-3.txt",
+ },
+ })
+ })
+
+ t.Run("wrong usage", func(t *testing.T) {
+ op := &operation.Source{}
+ err := op.Configure(util.ParseYAML(`
+ use: source
+ paths:
+ - foo/bar/baz-1.txt
+ - foo/bar/baz-2.txt
+ - 123
+ `))
+
+ assert.Error(t, err, `expected a string but got "123" of type int`)
+ })
+}
diff --git a/operation/target.go b/operation/target.go
index baf41ef..954a486 100644
--- a/operation/target.go
+++ b/operation/target.go
@@ -22,11 +22,11 @@ type Target struct {
PathTemplate string
}
-func (op *Target) Load(config map[string]any) error {
- if v, ok := config[ShortFormValueKey]; ok {
+func (op *Target) Configure(config map[string]any) error {
+ if v, ok := config["target"]; ok {
template, ok := v.(string)
if !ok {
- return fmt.Errorf(`expected pattern but got "%v" of type %T`, v, v)
+ return fmt.Errorf(`expected a path template but got "%v" of type %T`, v, v)
}
op.PathTemplate = template
diff --git a/operation/target_test.go b/operation/target_test.go
new file mode 100644
index 0000000..6fed55f
--- /dev/null
+++ b/operation/target_test.go
@@ -0,0 +1,31 @@
+package operation_test
+
+import (
+ "testing"
+
+ "github.com/aziis98/cabret/operation"
+ "github.com/aziis98/cabret/util"
+ "gotest.tools/assert"
+)
+
+func TestTargetShortForm(t *testing.T) {
+ t.Run("correct usage", func(t *testing.T) {
+ op := &operation.Target{}
+ err := op.Configure(util.ParseYAML(`
+ target: foo/bar/baz.txt
+ `))
+
+ assert.NilError(t, err)
+ assert.DeepEqual(t, op, &operation.Target{
+ PathTemplate: "foo/bar/baz.txt",
+ })
+ })
+ t.Run("wrong usage", func(t *testing.T) {
+ op := &operation.Target{}
+ err := op.Configure(util.ParseYAML(`
+ target: 123
+ `))
+
+ assert.Error(t, err, `expected a path template but got "123" of type int`)
+ })
+}
diff --git a/pipeline/pipeline.go b/parse/parse.go
similarity index 70%
rename from pipeline/pipeline.go
rename to parse/parse.go
index a19f879..2682b29 100644
--- a/pipeline/pipeline.go
+++ b/parse/parse.go
@@ -1,4 +1,4 @@
-package pipeline
+package parse
import (
"fmt"
@@ -18,7 +18,7 @@ func switchMap(m map[string]any, v *any) func(k string) bool {
}
}
-func Parse(p config.Pipeline) ([]cabret.Operation, error) {
+func ParsePipeline(p config.Pipeline) ([]cabret.Operation, error) {
ops := []cabret.Operation{}
for _, opConfig := range p.Pipeline {
@@ -33,8 +33,9 @@ func Parse(p config.Pipeline) ([]cabret.Operation, error) {
}
opConfig[operation.ShortFormValueKey] = value
- op := &operation.Source{}
- if err := op.Load(opConfig); err != nil {
+
+ op, err := ParseOperation("source", opConfig)
+ if err != nil {
return nil, err
}
@@ -47,8 +48,9 @@ func Parse(p config.Pipeline) ([]cabret.Operation, error) {
}
opConfig[operation.ShortFormValueKey] = value
- op := &operation.Target{}
- if err := op.Load(opConfig); err != nil {
+
+ op, err := ParseOperation("target", opConfig)
+ if err != nil {
return nil, err
}
@@ -60,7 +62,7 @@ func Parse(p config.Pipeline) ([]cabret.Operation, error) {
return nil, fmt.Errorf(`expected string but got "%v" of type %T`, v, v)
}
- op, err := operation.Build(name, opConfig)
+ op, err := ParseOperation(name, opConfig)
if err != nil {
return nil, err
}
@@ -75,14 +77,15 @@ func Parse(p config.Pipeline) ([]cabret.Operation, error) {
return ops, nil
}
-func Process(contents []cabret.Content, ops []cabret.Operation) ([]cabret.Content, error) {
- for _, op := range ops {
- var err error
- contents, err = cabret.ProcessOperation(op, contents)
- if err != nil {
- return nil, err
- }
+func ParseOperation(name string, options map[string]any) (cabret.Operation, error) {
+ op, err := operation.NewWithName(name)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := op.Configure(options); err != nil {
+ return nil, err
}
- return contents, nil
+ return op, nil
}
diff --git a/runner/runner.go b/runner/runner.go
new file mode 100644
index 0000000..0eb4731
--- /dev/null
+++ b/runner/runner.go
@@ -0,0 +1,61 @@
+package runner
+
+import (
+ "fmt"
+
+ "github.com/aziis98/cabret"
+ "github.com/aziis98/cabret/config"
+ "github.com/aziis98/cabret/parse"
+)
+
+func RunConfig(cfg *config.Cabretfile) error {
+ for _, p := range cfg.Build {
+ ops, err := parse.ParsePipeline(p)
+ if err != nil {
+ return err
+ }
+
+ if _, err := RunPipeline([]cabret.Content{}, ops); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func RunPipeline(contents []cabret.Content, ops []cabret.Operation) ([]cabret.Content, error) {
+ for _, op := range ops {
+ var err error
+ contents, err = RunOperation(op, contents)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return contents, nil
+}
+
+func RunOperation(op cabret.Operation, inputs []cabret.Content) ([]cabret.Content, error) {
+ switch op := op.(type) {
+ case cabret.ListOperation:
+ return op.ProcessList(inputs)
+
+ case cabret.ItemOperation:
+ outputs := []cabret.Content{}
+ for _, item := range inputs {
+ result, err := op.ProcessItem(item)
+ if err != nil {
+ return nil, err
+ }
+
+ // skip terminal operations
+ if result == nil {
+ continue
+ }
+ outputs = append(outputs, *result)
+ }
+ return outputs, nil
+ default:
+ return nil, fmt.Errorf(`invalid operation type %T`, op)
+ }
+}
diff --git a/util/util.go b/util/util.go
index 0cb8ba7..53c1e56 100644
--- a/util/util.go
+++ b/util/util.go
@@ -1,5 +1,12 @@
package util
+import (
+ "strings"
+
+ "github.com/alecthomas/repr"
+ "gopkg.in/yaml.v2"
+)
+
func CloneMap[K comparable, V any](m1 map[K]V) map[K]V {
m2 := map[K]V{}
for k, v := range m1 {
@@ -7,3 +14,29 @@ func CloneMap[K comparable, V any](m1 map[K]V) map[K]V {
}
return m2
}
+
+func Dedent(s string) string {
+ lines := strings.Split(strings.TrimLeft(s, "\n"), "\n")
+ repr.Println(lines)
+
+ k := len(lines[0]) - len(strings.TrimLeft(lines[0], "\t "))
+
+ for i, line := range lines {
+ if k <= len(line) {
+ lines[i] = line[k:]
+ } else {
+ lines[i] = ""
+ }
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+func ParseYAML(multiline string) map[string]any {
+ var m map[string]any
+ if err := yaml.Unmarshal([]byte(Dedent(multiline)), &m); err != nil {
+ panic(err)
+ }
+
+ return m
+}