79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
// Package renderer provides functions for rendering Marka templates.
|
|
package renderer
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.max-richter.dev/max/marka/registry"
|
|
"git.max-richter.dev/max/marka/renderer/codec"
|
|
"git.max-richter.dev/max/marka/template"
|
|
"git.max-richter.dev/max/marka/validator"
|
|
)
|
|
|
|
const emptyBlock = "\uE000"
|
|
|
|
func fixRenderedBlock(input string) string {
|
|
input = strings.ReplaceAll(input, "'@type':", "@type:")
|
|
input = strings.ReplaceAll(input, "'@context':", "@context:")
|
|
if len(input) == 0 {
|
|
return emptyBlock
|
|
}
|
|
return input
|
|
}
|
|
|
|
func RenderFile(rawJSON []byte) ([]byte, error) {
|
|
// 1) parse json
|
|
var data map[string]any
|
|
if err := json.Unmarshal(rawJSON, &data); err != nil {
|
|
return nil, fmt.Errorf("failed to parse JSON: %w", err)
|
|
}
|
|
|
|
// 2) extract type from "@type" Property
|
|
contentType, ok := data["@type"].(string)
|
|
if !ok || contentType == "" {
|
|
return nil, fmt.Errorf("JSON does not contain a valid '@type' property")
|
|
}
|
|
|
|
// 3) get the template from the registry
|
|
templateContent, err := registry.GetTemplate(contentType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not get template for type '%s': %w", contentType, err)
|
|
}
|
|
|
|
// 4) parse the template with the template package
|
|
compiledTemplate, err := template.CompileTemplate(templateContent)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to compile template for type '%s': %w", contentType, err)
|
|
}
|
|
|
|
// 5) validate JSON against schema
|
|
if schemaName, ok := data["@schema"].(string); ok {
|
|
if validationErr := validator.ValidateSchema(data, schemaName); validationErr != nil {
|
|
return nil, fmt.Errorf("failed to validate schema: %w", validationErr)
|
|
}
|
|
}
|
|
|
|
// 6) render the template with the blocks
|
|
var buffer bytes.Buffer
|
|
for _, block := range compiledTemplate {
|
|
renderedContent, err := codec.RenderBlock(block, data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to render block for path '%s': %w", block.Path, err)
|
|
}
|
|
renderedContent = fixRenderedBlock(renderedContent)
|
|
buffer.WriteString(renderedContent)
|
|
}
|
|
|
|
outputString := buffer.String()
|
|
outputString = strings.ReplaceAll(outputString, "\n\n"+emptyBlock+"\n\n", "\n\n")
|
|
outputString = strings.ReplaceAll(outputString, emptyBlock, "")
|
|
if !strings.HasSuffix(outputString, "\n") {
|
|
outputString += "\n"
|
|
}
|
|
|
|
return []byte(outputString), nil
|
|
}
|