From c0fbf11f843af84e8891a708c4d217dd6c523473 Mon Sep 17 00:00:00 2001
From: Alan Pearce
Date: Mon, 6 May 2024 10:14:17 +0200
Subject: feat: render markdown examples
---
frontend/templates/blocks/options.gotmpl | 18 ++++++--
internal/options/option.go | 62 +++++--------------------
internal/options/process.go | 78 ++++++++++++++++++++++++++++----
internal/server/server.go | 19 +++++++-
4 files changed, 109 insertions(+), 68 deletions(-)
diff --git a/frontend/templates/blocks/options.gotmpl b/frontend/templates/blocks/options.gotmpl
index 89c6f33..a4c4f12 100644
--- a/frontend/templates/blocks/options.gotmpl
+++ b/frontend/templates/blocks/options.gotmpl
@@ -5,7 +5,7 @@
{{ .Option }}
- {{ HTML .Description.HTML }}
+ {{ markdown .Description }}
{{- with .Type }}
@@ -15,18 +15,26 @@
{{- with .Default }}
- Default
-
-
{{ .Text }}
+ {{- if .Markdown }}
+ {{ .Markdown }}
+ {{- else }}
+ {{ .Text }}
+ {{- end }}
{{- end }}
{{- with .Example }}
- {{- if .Text }}
+ {{- if or .Text .Markdown }}
- Example
-
-
{{ .Text }}
+ {{- if .Markdown }}
+ {{ .Markdown }}
+ {{- else }}
+ {{ .Text }}
+ {{- end }}
{{- end }}
{{- end }}
- {{- with .RelatedPackages.HTML }}
+ {{- with .RelatedPackages }}
- Related Packages
- {{ . }}
{{- end }}
diff --git a/internal/options/option.go b/internal/options/option.go
index b8b838a..a43dd49 100644
--- a/internal/options/option.go
+++ b/internal/options/option.go
@@ -1,67 +1,27 @@
package options
-import (
- "encoding/json"
- "strings"
-
- "github.com/pkg/errors"
- "github.com/yuin/goldmark"
- "github.com/yuin/goldmark/extension"
-)
+type Markdown string
type NixValue struct {
- Type string `json:"_type" mapstructure:"_type"`
- Text string `json:"text"`
-}
-
-type HTML struct {
- HTML string
-}
-
-var md = goldmark.New(
- goldmark.WithExtensions(extension.NewLinkify()),
-)
-
-func (html *HTML) UnmarshalText(text []byte) error {
- var out strings.Builder
- err := md.Convert(text, &out)
- if err != nil {
- return errors.WithMessage(err, "failed to convert markdown to HTML")
- }
-
- html.HTML = out.String()
-
- return nil
-}
-
-func (html *HTML) UnmarshalJSON(raw []byte) error {
- var v struct {
- HTML string
- }
- err := json.Unmarshal(raw, &v)
- if err != nil {
- return errors.WithMessage(err, "error unmarshaling json")
- }
- html.HTML = v.HTML
-
- return nil
+ Text string `json:",omitempty"`
+ Markdown Markdown `json:",omitempty"`
}
type Link struct {
Name string
- URL string `json:"url"`
+ URL string
}
type NixOption struct {
- Option string
+ Option string
+
Declarations []Link
- Default NixValue
- Description HTML
- Example NixValue
- ReadOnly bool
- Type string
+ Default *NixValue `json:",omitempty"`
+ Description Markdown
+ Example *NixValue `json:",omitempty"`
Loc []string
- RelatedPackages HTML
+ RelatedPackages Markdown `json:",omitempty"`
+ Type string
}
type NixOptions []NixOption
diff --git a/internal/options/process.go b/internal/options/process.go
index fde73e4..9721a7b 100644
--- a/internal/options/process.go
+++ b/internal/options/process.go
@@ -3,6 +3,7 @@ package options
import (
"encoding/json"
"io"
+ "log/slog"
"os"
"github.com/bcicen/jstream"
@@ -10,6 +11,27 @@ import (
"github.com/pkg/errors"
)
+type nixValueJSON struct {
+ Type string `mapstructure:"_type"`
+ Text string
+}
+
+type linkJSON struct {
+ Name string
+ URL string `json:"url"`
+}
+
+type nixOptionJSON struct {
+ Declarations []linkJSON
+ Default nixValueJSON
+ Description string
+ Example nixValueJSON
+ Loc []string
+ ReadOnly bool
+ RelatedPackages string
+ Type string
+}
+
func ValueTypeToString(valueType jstream.ValueType) string {
switch valueType {
case jstream.Unknown:
@@ -31,6 +53,27 @@ func ValueTypeToString(valueType jstream.ValueType) string {
return "very strange"
}
+func convertNixValue(nj nixValueJSON) *NixValue {
+ switch nj.Type {
+ case "", "literalExpression":
+ if nj.Text == "" {
+ return nil
+ }
+
+ return &NixValue{
+ Text: nj.Text,
+ }
+ case "literalMD":
+ return &NixValue{
+ Markdown: Markdown(nj.Text),
+ }
+ default:
+ slog.Warn("got unexpected NixValue type", "type", nj.Type, "text", nj.Text)
+
+ return nil
+ }
+}
+
func Process(inpath string, outpath string) error {
infile, err := os.Open(inpath)
if err != nil {
@@ -46,10 +89,11 @@ func Process(inpath string, outpath string) error {
}
dec := jstream.NewDecoder(infile, 1).EmitKV()
- var opt NixOption
+ var optJSON nixOptionJSON
ms, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
ErrorUnused: true,
- Result: &opt,
+ ZeroFields: true,
+ Result: &optJSON,
Squash: true,
DecodeHook: mapstructure.TextUnmarshallerHookFunc(),
})
@@ -69,17 +113,29 @@ func Process(inpath string, outpath string) error {
return errors.Errorf("unexpected object type %s", ValueTypeToString(mv.ValueType))
}
kv := mv.Value.(jstream.KV)
- if kv.Key == "_module.args" {
- continue
- }
x := kv.Value.(map[string]interface{})
- x["option"] = kv.Key
- err = ms.Decode(x)
+ err = ms.Decode(x) // stores in optJSON
if err != nil {
return errors.WithMessagef(err, "failed to decode option %#v", x)
}
+ var decs = make([]Link, len(optJSON.Declarations))
+ for i, d := range optJSON.Declarations {
+ decs[i] = Link(d)
+ }
+
+ opt := NixOption{
+ Option: kv.Key,
+ Declarations: decs,
+ Default: convertNixValue(optJSON.Default),
+ Description: Markdown(optJSON.Description),
+ Example: convertNixValue(optJSON.Example),
+ RelatedPackages: Markdown(optJSON.RelatedPackages),
+ Loc: optJSON.Loc,
+ Type: optJSON.Type,
+ }
+
b, err := json.MarshalIndent(opt, "", " ")
if err != nil {
return errors.WithMessagef(err, "failed to encode option %#v", opt)
@@ -95,9 +151,11 @@ func Process(inpath string, outpath string) error {
}
}
- _, err = outfile.Seek(-2, io.SeekCurrent)
- if err != nil {
- return errors.WithMessage(err, "could not write to output")
+ if outpath != "/dev/stdout" {
+ _, err = outfile.Seek(-2, io.SeekCurrent)
+ if err != nil {
+ return errors.WithMessage(err, "could not write to output")
+ }
}
_, err = outfile.WriteString("]\n")
diff --git a/internal/server/server.go b/internal/server/server.go
index ad75ac1..76fbecb 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "html"
"html/template"
"io"
"log"
@@ -25,6 +26,8 @@ import (
"github.com/osdevisnot/sorvor/pkg/livereload"
"github.com/pkg/errors"
"github.com/shengyanli1982/law"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/extension"
)
var config *cfg.Config
@@ -74,9 +77,20 @@ func applyDevModeOverrides(config *cfg.Config) {
config.CSP.ConnectSrc = slices.Insert(config.CSP.ConnectSrc, 0, "'self'")
}
+var md = goldmark.New(
+ goldmark.WithExtensions(extension.NewLinkify()),
+)
var templateFuncs = template.FuncMap{
- "HTML": func(input string) template.HTML {
- return template.HTML(input) // #nosec G203
+ "markdown": func(input options.Markdown) template.HTML {
+ var out strings.Builder
+ err := md.Convert([]byte(input), &out)
+ if err != nil {
+ slog.Warn(fmt.Sprintf("markdown conversion failed: %v", err))
+
+ return template.HTML(html.EscapeString(err.Error())) // #nosec G203 -- duh?
+ }
+
+ return template.HTML(out.String()) // #nosec G203
},
}
@@ -177,6 +191,7 @@ func New(runtimeConfig *Config) (*Server, error) {
if err != nil {
slog.Error(fmt.Sprintf("error parsing json file: %v", err))
}
+
mux.HandleFunc("/options/results", func(w http.ResponseWriter, r *http.Request) {
tdata := OptionResultData{
TemplateData: indexData,
--
cgit 1.4.1