143 lines
3.2 KiB
Go
143 lines
3.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"beelloo/internal/invoice"
|
|
"beelloo/internal/render"
|
|
"beelloo/internal/server"
|
|
)
|
|
|
|
func Run(args []string, out io.Writer, errOut io.Writer) int {
|
|
if len(args) == 0 {
|
|
usage(errOut)
|
|
return 2
|
|
}
|
|
switch args[0] {
|
|
case "new":
|
|
return runNew(args[1:], out, errOut)
|
|
case "emit":
|
|
return runBuild(args[1:], out, errOut)
|
|
case "build":
|
|
return runBuild(args[1:], out, errOut)
|
|
case "serve":
|
|
return runServe(args[1:], out, errOut)
|
|
case "-h", "--help", "help":
|
|
usage(out)
|
|
return 0
|
|
default:
|
|
fmt.Fprintf(errOut, "unknown command: %s\n", args[0])
|
|
usage(errOut)
|
|
return 2
|
|
}
|
|
}
|
|
|
|
func usage(w io.Writer) {
|
|
fmt.Fprintln(w, "Usage:")
|
|
fmt.Fprintln(w, " beelloo new <file.md>")
|
|
fmt.Fprintln(w, " beelloo build <file.md> [--css=style.css]")
|
|
fmt.Fprintln(w, " beelloo serve <file.md> [--addr=127.0.0.1:0] [--css=style.css]")
|
|
}
|
|
|
|
func runNew(args []string, out io.Writer, errOut io.Writer) int {
|
|
if len(args) != 1 {
|
|
fmt.Fprintln(errOut, "new requires a single file path")
|
|
return 2
|
|
}
|
|
path := args[0]
|
|
if err := os.WriteFile(path, []byte(invoice.ScaffoldMarkdown), 0644); err != nil {
|
|
fmt.Fprintf(errOut, "failed to write scaffold: %v\n", err)
|
|
return 1
|
|
}
|
|
fmt.Fprintf(out, "scaffold written to %s\n", path)
|
|
return 0
|
|
}
|
|
|
|
func runBuild(args []string, out io.Writer, errOut io.Writer) int {
|
|
if len(args) < 1 {
|
|
fmt.Fprintln(errOut, "build requires a markdown file path")
|
|
return 2
|
|
}
|
|
path := args[0]
|
|
cssPath := ""
|
|
for _, arg := range args[1:] {
|
|
if strings.HasPrefix(arg, "--css=") {
|
|
cssPath = strings.TrimPrefix(arg, "--css=")
|
|
}
|
|
}
|
|
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
fmt.Fprintf(errOut, "failed to open %s: %v\n", path, err)
|
|
return 1
|
|
}
|
|
defer file.Close()
|
|
|
|
doc, err := invoice.ParseMarkdown(file)
|
|
if err != nil {
|
|
fmt.Fprintf(errOut, "parse error: %v\n", err)
|
|
return 1
|
|
}
|
|
if err := invoice.Validate(&doc); err != nil {
|
|
fmt.Fprintf(errOut, "validation error: %v\n", err)
|
|
return 1
|
|
}
|
|
|
|
html, err := renderWithCSS(doc, cssPath)
|
|
if err != nil {
|
|
fmt.Fprintf(errOut, "render error: %v\n", err)
|
|
return 1
|
|
}
|
|
|
|
outputPath := strings.TrimSuffix(path, filepath.Ext(path)) + ".html"
|
|
if err := os.WriteFile(outputPath, []byte(html), 0644); err != nil {
|
|
fmt.Fprintf(errOut, "failed to write html: %v\n", err)
|
|
return 1
|
|
}
|
|
fmt.Fprintf(out, "HTML generated at %s\n", outputPath)
|
|
return 0
|
|
}
|
|
|
|
func runServe(args []string, out io.Writer, errOut io.Writer) int {
|
|
if len(args) < 1 {
|
|
fmt.Fprintln(errOut, "serve requires a markdown file path")
|
|
return 2
|
|
}
|
|
path := args[0]
|
|
addr := "127.0.0.1:0"
|
|
cssPath := ""
|
|
for _, arg := range args[1:] {
|
|
if strings.HasPrefix(arg, "--addr=") {
|
|
addr = strings.TrimPrefix(arg, "--addr=")
|
|
}
|
|
if strings.HasPrefix(arg, "--css=") {
|
|
cssPath = strings.TrimPrefix(arg, "--css=")
|
|
}
|
|
}
|
|
err := server.Serve(server.Config{
|
|
Addr: addr,
|
|
MDPath: path,
|
|
CSSPath: cssPath,
|
|
}, out, errOut)
|
|
if err != nil {
|
|
fmt.Fprintf(errOut, "%v\n", err)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func renderWithCSS(doc invoice.Document, cssPath string) (string, error) {
|
|
if cssPath == "" {
|
|
return render.RenderHTML(doc)
|
|
}
|
|
css, err := os.ReadFile(cssPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return render.RenderHTMLWithCSS(doc, string(css))
|
|
}
|