Adds conversion to html
This commit is contained in:
parent
2dae0e8ed6
commit
fe42515f6f
202
main.go
202
main.go
|
@ -11,8 +11,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
SPEC_URL string = "gemini://gemini.circumlunar.space:1965/docs/spec-spec.txt"
|
||||
VERSION string = "0.1"
|
||||
SPEC_URL string = "gemini://gemini.circumlunar.space:1965/docs/specification.gmi"
|
||||
VERSION string = "0.2.0"
|
||||
WARNING int = iota
|
||||
ERROR
|
||||
)
|
||||
|
@ -194,6 +194,162 @@ func LoadCertificate(cert, key string) (tls.Certificate, error) {
|
|||
return certificate, nil
|
||||
}
|
||||
|
||||
func WriteToFile(path, content string) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
f.WriteString(content)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertToHTML(lines []string, lang string, blanks bool) string {
|
||||
var out strings.Builder
|
||||
header := `
|
||||
<!DOCTYPE html>
|
||||
<html lang="%s">
|
||||
<head>
|
||||
<title></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width="device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"> <!-- here to allow for max-width/centering without altering the body itself -->
|
||||
`
|
||||
out.WriteString(fmt.Sprintf(header, lang))
|
||||
|
||||
inConvertPre := false
|
||||
inConvertList := false
|
||||
art := false
|
||||
code := false
|
||||
|
||||
for _, l := range lines {
|
||||
// Handle lists
|
||||
if strings.HasPrefix(l, "* ") {
|
||||
if !inConvertList {
|
||||
inConvertList = true
|
||||
out.WriteString("<ul>\n")
|
||||
}
|
||||
out.WriteString("<li>\n")
|
||||
out.WriteString(l)
|
||||
out.WriteString("\n</li>\n")
|
||||
continue
|
||||
} else if inConvertList {
|
||||
inConvertList = false
|
||||
out.WriteString("</ul>\n")
|
||||
}
|
||||
|
||||
// Handle preformatted blocks
|
||||
if strings.HasPrefix(l, "```") && !inConvertPre {
|
||||
inConvertPre = true
|
||||
alt := ""
|
||||
if len(l) > 3 {
|
||||
alt = l[3:]
|
||||
}
|
||||
art = strings.Contains(strings.ToLower(alt), "art")
|
||||
code = strings.Contains(strings.ToLower(alt), "code")
|
||||
if art {
|
||||
out.WriteString(fmt.Sprintf(`<div role="image" aria-label="%s">`, alt))
|
||||
out.WriteString("\n<pre>\n")
|
||||
} else if code {
|
||||
out.WriteString("<pre>\n<code>\n")
|
||||
} else {
|
||||
out.WriteString("<pre>\n")
|
||||
}
|
||||
continue
|
||||
} else if strings.HasPrefix(l, "```") && inConvertPre {
|
||||
inConvertPre = false
|
||||
if art {
|
||||
out.WriteString("</pre>\n</div>\n")
|
||||
} else if code {
|
||||
out.WriteString("</code>\n</pre>\n")
|
||||
} else {
|
||||
out.WriteString("</pre>\n")
|
||||
}
|
||||
continue
|
||||
} else if inConvertPre {
|
||||
out.WriteString(l)
|
||||
out.WriteRune('\n')
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle block quote
|
||||
if strings.HasPrefix(l, ">") {
|
||||
out.WriteString(fmt.Sprintf("<blockquote>\n\t%s\n</bloackquote>\n", l))
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle headings
|
||||
if strings.HasPrefix(l, "####") {
|
||||
out.WriteString(l)
|
||||
continue
|
||||
} else if strings.HasPrefix(l, "###") {
|
||||
out.WriteString(fmt.Sprintf("<h3>\n\t%s\n</h3>\n", l))
|
||||
continue
|
||||
} else if strings.HasPrefix(l, "##") {
|
||||
out.WriteString(fmt.Sprintf("<h2>\n\t%s\n</h2>\n", l))
|
||||
continue
|
||||
} else if strings.HasPrefix(l, "#") {
|
||||
out.WriteString(fmt.Sprintf("<h1>\n\t%s\n</h1>\n", l))
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle links
|
||||
if strings.HasPrefix(l, "=>") {
|
||||
if len(l) < 3 {
|
||||
continue
|
||||
}
|
||||
split := strings.SplitN(strings.TrimSpace(l[2:]), " ", 2)
|
||||
u := strings.TrimSpace(split[0])
|
||||
txt := u
|
||||
if len(split) > 1 {
|
||||
txt = strings.TrimSpace(split[1])
|
||||
}
|
||||
out.WriteString(fmt.Sprintf("<p class=\"link-block\">\n\t<a href=\"%s\">\n\t\t%s\n\t</a>\n</p>\n", u, txt))
|
||||
continue
|
||||
}
|
||||
|
||||
// Regular text
|
||||
if len(l) > 0 {
|
||||
out.WriteString("<p>\n\t")
|
||||
out.WriteString(l)
|
||||
out.WriteString("\n</p>\n")
|
||||
} else {
|
||||
if blanks {
|
||||
out.WriteString("<br>\n")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out.WriteString("\t\t</div>\n\t</body>\n</html>\n")
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func convertTo(file, format, lang string, blanks bool, outPath string) error {
|
||||
var output string
|
||||
lines := strings.Split(file, "\n")
|
||||
|
||||
switch format {
|
||||
case "html":
|
||||
output = convertToHTML(lines, lang, blanks)
|
||||
default:
|
||||
return fmt.Errorf("Unknown conversion format %q", format)
|
||||
}
|
||||
if outPath == "" {
|
||||
fmt.Print(output)
|
||||
} else {
|
||||
err := WriteToFile(outPath, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func get(resource, cert, key string, headerOnly, currentSpec bool) (string, error) {
|
||||
hasScheme := strings.Contains(resource, "://")
|
||||
if !hasScheme {
|
||||
|
@ -259,7 +415,7 @@ func get(resource, cert, key string, headerOnly, currentSpec bool) (string, erro
|
|||
func main() {
|
||||
fmtCmd := flag.NewFlagSet("fmt", flag.ExitOnError)
|
||||
fmtOut := fmtCmd.String("o", "", "Path to output file")
|
||||
fmtSupress := fmtCmd.Bool("s", false, "Supress error messaging")
|
||||
fmtSupress := fmtCmd.Bool("quiet", false, "Supress error messaging")
|
||||
|
||||
getCmd := flag.NewFlagSet("get", flag.ExitOnError)
|
||||
getOut := getCmd.String("o", "", "Path to output file")
|
||||
|
@ -270,12 +426,37 @@ func main() {
|
|||
specCmd := flag.NewFlagSet("spec", flag.ExitOnError)
|
||||
specOut := specCmd.String("o", "", "Path to output file")
|
||||
|
||||
convertCmd := flag.NewFlagSet("convert", flag.ExitOnError)
|
||||
convertFormat := convertCmd.String("to", "html", "Valid: html|text|markdown")
|
||||
convertLang := convertCmd.String("lang", "en", "The document's language abbreviation for the html lang attribute")
|
||||
convertBlankLines := convertCmd.Bool("blanks", false, "Preserve empty lines in the converted document")
|
||||
convertOut := convertCmd.String("o", "", "Path to output file")
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("expected command: fmt, get, spec, or version")
|
||||
fmt.Fprintf(os.Stderr, "Gemini Tool v%s\n", VERSION)
|
||||
fmt.Fprintf(os.Stderr, "Expected command: convert, fmt, get, spec, or version\n")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "convert":
|
||||
convertCmd.Parse(os.Args[2:])
|
||||
if len(convertCmd.Args()) != 1 {
|
||||
fmt.Printf("Incorrect number of positional arguments expected 1, got %d\n\ngemini convert [flags] [filepath]\n\n", len(fmtCmd.Args()))
|
||||
convertCmd.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
text, err := fmtFile(convertCmd.Args()[0])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
err = convertTo(text, *convertFormat, *convertLang, *convertBlankLines, *convertOut)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
case "fmt":
|
||||
fmtCmd.Parse(os.Args[2:])
|
||||
if len(fmtCmd.Args()) != 1 {
|
||||
|
@ -291,7 +472,11 @@ func main() {
|
|||
fmt.Print(text)
|
||||
fmt.Fprint(os.Stderr, "\n") // So that the terminal input is always on a new line, but this LF doesnt get piped into a document
|
||||
} else {
|
||||
// TODO handle file writing
|
||||
err = WriteToFile(*fmtOut, text)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if !*fmtSupress {
|
||||
|
@ -331,6 +516,11 @@ func main() {
|
|||
// TODO handle file writing
|
||||
}
|
||||
case "version":
|
||||
fmt.Printf("Gemini Tool v%s\n", VERSION)
|
||||
fmt.Printf("v%s\n", VERSION)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Gemini Tool v%s\n", VERSION)
|
||||
fmt.Fprintf(os.Stderr, "Expected command: convert, fmt, get, spec, or version\n")
|
||||
flag.Parse()
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue