refactor: changed the structure of things

main
Antonio De Lucreziis 2 years ago
parent 81490ac758
commit 0ee9389910

@ -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:

@ -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)
}
}

@ -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)
}
}

@ -5,8 +5,7 @@
<ul>
{{ range .Items }}
<li>
<p>{{ .Metadata.Title }}</p>
<pre><code>{{ .Metadata.MatchResult }}</code></pre>
<a href="/posts/{{ .Metadata.MatchResult.id }}">{{ .Metadata.Title }}</a>
</li>
{{ end }}
</ul>

@ -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
}

@ -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")

@ -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
}

@ -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 {

@ -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
}

@ -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
}

@ -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",
})
}

@ -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

@ -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`)
})
}

@ -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

@ -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`)
})
}

@ -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
}

@ -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)
}
}

@ -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
}

Loading…
Cancel
Save