summaryrefslogtreecommitdiff
path: root/src/gmi/gemtext/gemtext.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/gmi/gemtext/gemtext.go')
-rw-r--r--src/gmi/gemtext/gemtext.go88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/gmi/gemtext/gemtext.go b/src/gmi/gemtext/gemtext.go
new file mode 100644
index 0000000..5c8f594
--- /dev/null
+++ b/src/gmi/gemtext/gemtext.go
@@ -0,0 +1,88 @@
+// Package gemtext contains code related to processing and producing gemtext
+// documents.
+package gemtext
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "net/url"
+ "path"
+ "regexp"
+ "strings"
+)
+
+func hasImgExt(p string) bool {
+ switch path.Ext(strings.ToLower(p)) {
+ case ".jpg", ".jpeg", ".png", ".gif", ".svg":
+ return true
+ default:
+ return false
+ }
+}
+
+// matches `=> dstURL [optional description]`
+var linkRegexp = regexp.MustCompile(`^=>\s+(\S+)\s*(.*?)\s*$`)
+
+// ToMarkdown reads a gemtext formatted body from the Reader and writes
+// the markdown version of that body to the Writer.
+//
+// gmiGateway, if given, is used for all `gemini://` links. The `gemini://`
+// prefix will be stripped, and replaced with the given URL.
+func ToMarkdown(dst io.Writer, src io.Reader, gmiGateway *url.URL) error {
+
+ bufSrc := bufio.NewReader(src)
+
+ for {
+
+ line, err := bufSrc.ReadString('\n')
+ if err != nil && !errors.Is(err, io.EOF) {
+ return fmt.Errorf("reading: %w", err)
+ }
+
+ last := err == io.EOF
+
+ if match := linkRegexp.FindStringSubmatch(line); len(match) > 0 {
+
+ u, err := url.Parse(match[1])
+ if err != nil {
+ return fmt.Errorf("link to invalid url %q: %w", match[1], err)
+ }
+
+ if u.Scheme == "gemini" && gmiGateway != nil {
+ newUStr := gmiGateway.String() + u.Host + u.Path
+ if u, err = url.Parse(newUStr); err != nil {
+ return fmt.Errorf("parsing proxied URL %q: %w", newUStr, err)
+ }
+ }
+
+ isImg := hasImgExt(u.Path)
+
+ descr := match[2]
+
+ if descr != "" {
+ // ok
+ } else if isImg {
+ descr = "Image"
+ } else {
+ descr = "Link"
+ }
+
+ line = fmt.Sprintf("[%s](%s)\n", descr, u.String())
+
+ if isImg {
+ line = "!" + line
+ }
+ }
+
+ if _, err := dst.Write([]byte(line)); err != nil {
+ return fmt.Errorf("writing: %w", err)
+ }
+
+ if last {
+ return nil
+ }
+ }
+
+}