💾 Archived View for anachronauts.club › cgi-bin › repos › cgi › gmikit.git › tree › trunk › gmi.go captured on 2021-12-05 at 23:47:19. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
1 package gmikit 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "net/url" 8 "strings" 9 ) 10 11 type Visitor interface { 12 Begin() error 13 End() error 14 Text(text string) error 15 Link(target *url.URL, friendlyName string) error 16 PreformattingToggle(altText string) error 17 PreformattedText(text string) error 18 Heading1(text string) error 19 Heading2(text string) error 20 Heading3(text string) error 21 UnorderedListItem(text string) error 22 Quote(text string) error 23 } 24 25 func ParseLines(r io.Reader, v Visitor) error { 26 const ws = " \t" 27 pre := false 28 scanner := bufio.NewScanner(r) 29 if err := v.Begin(); err != nil { 30 return err 31 } 32 for scanner.Scan() { 33 text := scanner.Text() 34 if strings.HasPrefix(text, "```") { 35 pre = !pre 36 if err := v.PreformattingToggle(text[3:]); err != nil { 37 return err 38 } 39 } else if pre { 40 if err := v.PreformattedText(text); err != nil { 41 return err 42 } 43 } else if strings.HasPrefix(text, "=>") { 44 text = text[2:] 45 text = strings.TrimLeft(text, ws) 46 split := strings.IndexAny(text, ws) 47 if split == -1 { 48 url, err := url.Parse(text) 49 if err != nil { 50 return err 51 } 52 53 if err = v.Link(url, ""); err != nil { 54 return err 55 } 56 } else { 57 url, err := url.Parse(text[:split]) 58 if err != nil { 59 return err 60 } 61 62 name := strings.TrimLeft(text[split:], ws) 63 if err = v.Link(url, name); err != nil { 64 return err 65 } 66 } 67 } else if strings.HasPrefix(text, "*") { 68 text = strings.TrimLeft(text[1:], ws) 69 if err := v.UnorderedListItem(text); err != nil { 70 return err 71 } 72 } else if strings.HasPrefix(text, "###") { 73 text = strings.TrimLeft(text[3:], ws) 74 if err := v.Heading3(text); err != nil { 75 return err 76 } 77 } else if strings.HasPrefix(text, "##") { 78 text = strings.TrimLeft(text[2:], ws) 79 if err := v.Heading2(text); err != nil { 80 return err 81 } 82 } else if strings.HasPrefix(text, "#") { 83 text = strings.TrimLeft(text[1:], ws) 84 if err := v.Heading1(text); err != nil { 85 return err 86 } 87 } else if strings.HasPrefix(text, ">") { 88 text = strings.TrimLeft(text[1:], ws) 89 if err := v.Quote(text); err != nil { 90 return err 91 } 92 } else { 93 if err := v.Text(text); err != nil { 94 return err 95 } 96 } 97 } 98 if err := v.End(); err != nil { 99 return err 100 } 101 102 return scanner.Err() 103 } 104 105 type GmiWriter struct { 106 w io.Writer 107 pre bool 108 } 109 110 func NewGmiWriter(w io.Writer) *GmiWriter { 111 return &GmiWriter{w: w, pre: false} 112 } 113 114 func (g *GmiWriter) Begin() error { return nil } 115 func (g *GmiWriter) End() error { return nil } 116 117 func (g *GmiWriter) Text(text string) error { 118 _, err := fmt.Fprintf(g.w, "%s\n", text) 119 return err 120 } 121 122 func (g *GmiWriter) Link(target *url.URL, friendlyName string) error { 123 if friendlyName == "" { 124 _, err := fmt.Fprintf(g.w, "=> %s\n", target) 125 return err 126 } else { 127 _, err := fmt.Fprintf(g.w, "=> %s %s\n", target, friendlyName) 128 return err 129 } 130 } 131 132 func (g *GmiWriter) PreformattingToggle(altText string) error { 133 if g.pre { 134 g.pre = false 135 _, err := fmt.Fprintf(g.w, "```\n") 136 return err 137 } else { 138 g.pre = true 139 _, err := fmt.Fprintf(g.w, "```%s\n", altText) 140 return err 141 } 142 } 143 144 func (g *GmiWriter) PreformattedText(text string) error { 145 _, err := fmt.Fprintf(g.w, "%s\n", text) 146 return err 147 } 148 149 func (g *GmiWriter) Heading1(text string) error { 150 _, err := fmt.Fprintf(g.w, "# %s\n", text) 151 return err 152 } 153 154 func (g *GmiWriter) Heading2(text string) error { 155 _, err := fmt.Fprintf(g.w, "## %s\n", text) 156 return err 157 } 158 159 func (g *GmiWriter) Heading3(text string) error { 160 _, err := fmt.Fprintf(g.w, "### %s\n", text) 161 return err 162 } 163 164 func (g *GmiWriter) UnorderedListItem(text string) error { 165 _, err := fmt.Fprintf(g.w, "* %s\n", text) 166 return err 167 } 168 169 func (g *GmiWriter) Quote(text string) error { 170 _, err := fmt.Fprintf(g.w, "> %s\n", text) 171 return err 172 } 173 174 func NormalizeGmi(r io.Reader, w io.Writer) error { 175 return ParseLines(r, NewGmiWriter(w)) 176 }