big tings
This commit is contained in:
41
parser/decoders/decoder.go
Normal file
41
parser/decoders/decoder.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Package decoders contains functions for parsing template.Block to a string.
|
||||
package decoders
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.max-richter.dev/max/marka/parser/matcher"
|
||||
"git.max-richter.dev/max/marka/parser/utils"
|
||||
"git.max-richter.dev/max/marka/template"
|
||||
)
|
||||
|
||||
func ParseBlock(input string, block template.Block) (any, error) {
|
||||
switch block.Codec {
|
||||
case template.CodecText:
|
||||
return input, nil
|
||||
case template.CodecYaml:
|
||||
return Yaml(input, block)
|
||||
case template.CodecList:
|
||||
return List(input, block)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown codec: %s", block.Codec)
|
||||
}
|
||||
|
||||
func Parse(matches []matcher.Block) (any, error) {
|
||||
var result any
|
||||
|
||||
for _, m := range matches {
|
||||
if m.Block.Path == "@index" {
|
||||
continue
|
||||
}
|
||||
|
||||
input := m.GetContent()
|
||||
value, err := ParseBlock(input, m.Block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse block(%s): %w", m.Block.Path, err)
|
||||
}
|
||||
result = utils.SetPathValue(m.Block.Path, value, result)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
56
parser/decoders/decoder_test.go
Normal file
56
parser/decoders/decoder_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package decoders_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.max-richter.dev/max/marka/parser/decoders"
|
||||
"git.max-richter.dev/max/marka/parser/matcher"
|
||||
"git.max-richter.dev/max/marka/parser/utils"
|
||||
"git.max-richter.dev/max/marka/registry"
|
||||
"git.max-richter.dev/max/marka/template"
|
||||
)
|
||||
|
||||
func TestParseBaguette(t *testing.T) {
|
||||
recipeMd := utils.ReadTestDataFile(t, "baguette.md")
|
||||
|
||||
templateContent, err := registry.GetTemplate("Recipe")
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %s", err)
|
||||
}
|
||||
|
||||
blocks, err := template.CompileTemplate(templateContent)
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %s", err)
|
||||
}
|
||||
|
||||
matches := matcher.MatchBlocksFuzzy(recipeMd, blocks, 0.3)
|
||||
parsed, err := decoders.Parse(matches)
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %s", err)
|
||||
}
|
||||
expected := map[string]any{
|
||||
"name": "Baguette",
|
||||
"description": "My favourite baguette recipe",
|
||||
"recipeIngredient": []string{"Flour", "Water", "Salt"},
|
||||
"recipeInstructions": []string{
|
||||
"Mix Flour Water and Salt",
|
||||
"Bake the bread",
|
||||
},
|
||||
}
|
||||
|
||||
out, _ := json.MarshalIndent(parsed, "", " ")
|
||||
fmt.Printf("Parsed: \n%s\n", string(out))
|
||||
|
||||
outMap, ok := parsed.(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected parsed to be map[string]any, got %T", parsed)
|
||||
}
|
||||
|
||||
for k, v := range expected {
|
||||
if fmt.Sprintf("%v", outMap[k]) != fmt.Sprintf("%v", v) {
|
||||
t.Errorf("Expected %v but got %v", v, outMap[k])
|
||||
}
|
||||
}
|
||||
}
|
31
parser/decoders/list.go
Normal file
31
parser/decoders/list.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package decoders
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.max-richter.dev/max/marka/parser/matcher"
|
||||
"git.max-richter.dev/max/marka/template"
|
||||
)
|
||||
|
||||
func List(input string, block template.Block) (value any, error error) {
|
||||
blocks, err := template.CompileTemplate(block.ListTemplate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot extract blocks: %w", err)
|
||||
}
|
||||
|
||||
var out []any
|
||||
|
||||
for line := range strings.SplitSeq(strings.TrimSuffix(input, "\n"), "\n") {
|
||||
matches := matcher.MatchBlocksFuzzy(line, blocks, 0.3)
|
||||
|
||||
res, err := Parse(matches)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not match blocks: %w", err)
|
||||
}
|
||||
|
||||
out = append(out, res)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
90
parser/decoders/list_test.go
Normal file
90
parser/decoders/list_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package decoders_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"git.max-richter.dev/max/marka/parser/decoders"
|
||||
"git.max-richter.dev/max/marka/template"
|
||||
)
|
||||
|
||||
func TestDecodeListObject(t *testing.T) {
|
||||
templateBlock := template.Block{
|
||||
Path: "ingredients",
|
||||
Codec: template.CodecList,
|
||||
ListTemplate: "- { amount } { type }",
|
||||
}
|
||||
input := "- 10g flour\n- 1/2cup water\n- 1tsp salt"
|
||||
|
||||
parsed, err := decoders.List(input, templateBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %s", err)
|
||||
}
|
||||
|
||||
want := []any{
|
||||
map[string]any{
|
||||
"amount": "10g",
|
||||
"type": "flour",
|
||||
},
|
||||
map[string]any{
|
||||
"amount": "1/2cup",
|
||||
"type": "water",
|
||||
},
|
||||
map[string]any{
|
||||
"amount": "1tsp",
|
||||
"type": "salt",
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(parsed, want) {
|
||||
t.Fatalf("unexpected result.\n got: %#v\nwant: %#v", parsed, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeListString(t *testing.T) {
|
||||
templateBlock := template.Block{
|
||||
Path: "ingredients",
|
||||
Codec: template.CodecList,
|
||||
ListTemplate: "- { . }",
|
||||
}
|
||||
input := "- flour\n- water\n- salt"
|
||||
|
||||
parsed, err := decoders.List(input, templateBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %s", err)
|
||||
}
|
||||
|
||||
want := []any{
|
||||
"flour",
|
||||
"water",
|
||||
"salt",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(parsed, want) {
|
||||
t.Fatalf("unexpected result.\n got: %#v\nwant: %#v", parsed, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeNumberedListString(t *testing.T) {
|
||||
templateBlock := template.Block{
|
||||
Path: "ingredients",
|
||||
Codec: template.CodecList,
|
||||
ListTemplate: "{ @index } { . }",
|
||||
}
|
||||
input := "1. Wash and dry the lettuce.\n2. Halve the cherry tomatoes.\n3. Toss with olive oil and salt."
|
||||
|
||||
parsed, err := decoders.List(input, templateBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %s", err)
|
||||
}
|
||||
|
||||
want := []any{
|
||||
"Wash and dry the lettuce.",
|
||||
"Halve the cherry tomatoes.",
|
||||
"Toss with olive oil and salt.",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(parsed, want) {
|
||||
t.Fatalf("unexpected result.\n got: %#v\nwant: %#v", parsed, want)
|
||||
}
|
||||
}
|
32
parser/decoders/yaml.go
Normal file
32
parser/decoders/yaml.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package decoders
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.max-richter.dev/max/marka/parser/utils"
|
||||
"git.max-richter.dev/max/marka/template"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
func Yaml(input string, block template.Block) (value any, error error) {
|
||||
res := make(map[string]any)
|
||||
err := yaml.Unmarshal([]byte(input), &res)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse yaml '%q': %w", input, err)
|
||||
}
|
||||
|
||||
var out any
|
||||
for _, f := range block.Fields {
|
||||
if f.CodecType == template.CodecConst {
|
||||
if f.Value != nil {
|
||||
out = utils.SetPathValue(f.Path, f.Value, out)
|
||||
}
|
||||
} else {
|
||||
if value, ok := res[f.Path]; ok {
|
||||
out = utils.SetPathValue(f.Path, value, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
Reference in New Issue
Block a user