package main import ( "bytes" "encoding/json" "fmt" "io" ) type Renderer interface { Render(w io.Writer) error } type Block struct { Type string Data json.RawMessage renderer Renderer } type Paragraph struct { Text string } type Code struct { Code string } type List struct { Style string Items []string } type Header struct { Level int Text string } type ChecklistItem struct { Text string Checked bool } type Checklist struct { Style string Items []ChecklistItem } type Link struct { Link string Meta LinkResponseMeta } type Table struct { Content [][]string } type Document struct { Time int64 Version string Blocks []Block } func (block Block) getType() (Renderer, error) { switch block.Type { case "table": return &Table{}, nil case "link": return &Link{}, nil case "list": return &List{}, nil case "header": return &Header{}, nil case "paragraph": return &Paragraph{}, nil case "code": return &Code{}, nil case "checklist": return &Checklist{}, nil default: return nil, fmt.Errorf("unknown type: %s", block.Type) } } func (block Block) load() (Renderer, error) { if block.renderer != nil { return block.renderer, nil } renderer, err := block.getType() if err != nil { return nil, err } err = json.Unmarshal(block.Data, renderer) if err != nil { return nil, err } block.renderer = renderer return renderer, nil } func (document *Document) Render(w io.Writer) error { var buf bytes.Buffer for _, block := range document.Blocks { renderer, err := block.load() if err != nil { return err } if err = renderer.Render(&buf); err != nil { return err } } _, err := buf.WriteTo(w) return err } func renderJSON(text string) (string, error) { var document Document err := json.Unmarshal([]byte(text), &document) if err != nil { return "", err } var buf bytes.Buffer err = document.Render(&buf) if err != nil { return "", err } return buf.String(), nil } func (checklist *Checklist) Render(w io.Writer) error { var buf bytes.Buffer buf.WriteString(`
`) for _, item := range checklist.Items { buf.WriteString(`
`) buf.WriteString(``) if item.Checked { buf.WriteString(``) } else { buf.WriteString(``) } buf.WriteString(``) buf.WriteString(`
`) buf.WriteString(item.Text) buf.WriteString("
") buf.WriteString("
") } buf.WriteString("
") _, err := buf.WriteTo(w) return err } func (code *Code) Render(w io.Writer) error { var buf bytes.Buffer buf.WriteString("
")
	buf.WriteString(code.Code)
	buf.WriteString("
") _, err := buf.WriteTo(w) return err } func (link *Link) Render(w io.Writer) error { // TODO(peter): improve link rendering _, err := fmt.Fprintf(w, `%s`, link.Link, link.Meta.Title) return err } func (table *Table) Render(w io.Writer) error { var buf bytes.Buffer buf.WriteString("") for _, row := range table.Content { buf.WriteString("") for _, col := range row { buf.WriteString("") } buf.WriteString("") } buf.WriteString("
") buf.WriteString(col) buf.WriteString("
") _, err := buf.WriteTo(w) return err } func (list *List) Render(w io.Writer) error { var buf bytes.Buffer var tag string if list.Style == "ordered" { tag = "ol" } else { tag = "ul" } buf.WriteString("<") buf.WriteString(tag) buf.WriteString(">") for _, item := range list.Items { buf.WriteString("
  • ") buf.WriteString(item) buf.WriteString("
  • ") } buf.WriteString("") _, err := buf.WriteTo(w) return err } func (header *Header) Render(w io.Writer) error { _, err := fmt.Fprintf(w, "%s", header.Level, header.Text, header.Level) return err } func (para Paragraph) Render(w io.Writer) error { var buf bytes.Buffer buf.WriteString("

    ") buf.WriteString(para.Text) buf.WriteString("

    ") _, err := buf.WriteTo(w) return err }