rivit2html/main.go

272 lines
5 KiB
Go

package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/judah-caruso/rivit"
)
func main() {
exe, args := os.Args[0], os.Args[1:]
exe = filepath.Base(exe)
usage := func() {
fmt.Fprintf(os.Stderr, "usage: %s [file.riv] [file.html]\n", exe)
}
if len(args) < 1 {
usage()
return
}
inpath := args[0]
for _, arg := range args {
if arg == "-h" || arg == "--help" {
usage()
return
}
}
file, err := os.ReadFile(inpath)
if err != nil {
fmt.Fprintf(os.Stderr, "error reading file: %v\n", err)
return
}
outpath := inpath
if len(args) > 1 {
outpath = args[1]
}
if filepath.Ext(outpath) == ".riv" {
outpath = outpath[:len(outpath)-len(filepath.Ext(outpath))] + ".html"
}
riv := rivit.Parse(string(file))
var (
out strings.Builder
title string
)
for _, line := range riv {
if kind := line.Kind(); kind == rivit.LineNavLink {
continue
}
switch line := line.(type) {
default:
continue
case rivit.Header:
text := strings.Title(strings.ToLower(string(line)))
if out.Len() == 0 {
title = text
fmt.Fprintf(&out, "<h1>%s</h1>", text)
} else {
fmt.Fprintf(&out, "<h2>%s</h2>", text)
}
case rivit.Paragraph:
if len(line) == 1 && line[0].Value == ". . ." {
fmt.Fprintf(&out, "<hr/>")
} else {
fmt.Fprintf(&out, "<p>%s</p>", text2html(line))
}
case rivit.List:
fmt.Fprint(&out, list2html(line))
case rivit.Block:
if len(line.Body) == 0 {
continue
}
first := strings.TrimSpace(line.Body[0])
if len(first) != 0 && first[0] == '|' && first[len(first)-1] == '|' { // table
out.WriteString("<table>")
for i, v := range line.Body {
v = strings.TrimSpace(v)
if len(v) == 0 {
continue
}
if v[0] != '|' {
fmt.Fprintf(&out, "<caption>%s</caption>", text2html(rivit.ParseStyledText(v)))
break
}
parts := strings.Split(v, "|")
if i == 0 {
out.WriteString("<thead>")
for _, p := range parts {
p = strings.TrimSpace(p)
if len(p) == 0 {
continue
}
styled := rivit.ParseStyledText(p)
fmt.Fprintf(&out, "<th scope=\"col\">%s</th>", text2html(styled))
}
out.WriteString("</thead>")
continue
}
if i == 1 {
out.WriteString("<tbody>")
}
out.WriteString("<tr>")
for it, p := range parts {
p = strings.TrimSpace(p)
if len(p) == 0 {
continue
}
styled := rivit.ParseStyledText(p)
if it == 0 {
fmt.Fprintf(&out, "<th scope=\"row\">%s</td>", text2html(styled))
} else {
fmt.Fprintf(&out, "<td>%s</td>", text2html(styled))
}
}
out.WriteString("</tr>")
}
out.WriteString("</tbody>")
out.WriteString("</table>")
} else {
out.WriteString("<pre><code>")
for i, v := range line.Body {
l := v[line.Indent:]
l = strings.ReplaceAll(l, "<", "&lt;")
l = strings.ReplaceAll(l, ">", "&gt;")
out.WriteString(l)
if i < len(line.Body) {
out.WriteString("\n")
}
}
out.WriteString("</code></pre>")
}
}
}
html := fmt.Sprintf(
`<html>
<head>
<title>%s</title>
<style>
body {
padding: 1em 0 2em 0;
max-width: 750px;
font-size: 115%%;
line-height: 1.4;
margin: 0 auto;
}
h1, h2 {
font-weight: bold;
margin-bottom: 0px;
border-bottom: 1px solid;
}
p {
text-align: justify;
}
pre {
padding-left: 20px;
}
pre code {
white-space: pre-wrap;
}
table {
border-collapse: collapse;
width: 100%%;
margin-top: 1em;
margin-bottom: 1em;
}
table caption {
padding: 0.5em;
text-align: left;
caption-side: top;
background-color: #eee;
}
th, td {
text-align: left;
border: none;
padding: 0.5rem;
}
thead th {
border-bottom: 2px solid #ddd;
font-weight: bold;
}
tbody tr {
border-bottom: 1px solid #eee;
}
</style>
</head>
<body>
%s
</body>
</html>`, title, out.String())
if err := os.WriteFile(outpath, []byte(html), 0644); err != nil {
fmt.Fprintf(os.Stderr, "error writing file: %v\n", err)
return
}
fmt.Fprintf(os.Stdout, "wrote %s\n", outpath)
}
func text2html(text []rivit.StyledText) string {
var out strings.Builder
for _, t := range text {
switch t.Style {
default:
continue
case rivit.StyleNone:
out.WriteString(t.Value)
case rivit.StyleItalic:
fmt.Fprintf(&out, "<i>%s</i>", t.Value)
case rivit.StyleBold:
fmt.Fprintf(&out, "<b>%s</b>", t.Value)
case rivit.StyleMono:
fmt.Fprintf(&out, "<code>%s</code>", t.Value)
case rivit.StyleExternalLink:
display := t.Value
if len(display) == 0 {
display = t.Link
}
fmt.Fprintf(&out, "<a href=%q>%s</a>", t.Link, display)
}
}
return out.String()
}
func list2html(list rivit.List) string {
var out strings.Builder
fmt.Fprintf(&out, "<ul>")
for _, item := range list {
fmt.Fprintf(&out, "<li>%s", text2html(item.Value))
if len(item.Sublist) != 0 {
out.WriteString(list2html(item.Sublist))
}
out.WriteString("</li>")
}
fmt.Fprintf(&out, "</ul>")
return out.String()
}