printer/templateprinter.go
changeset 0 5abace724584
child 14 da2059f2fa6a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/printer/templateprinter.go	Wed Apr 19 19:08:47 2017 +0200
@@ -0,0 +1,112 @@
+// Copyright © 2017 Mikael Berthe <mikael@lilotux.net>
+//
+// Licensed under the MIT license.
+// Please see the LICENSE file is this directory.
+
+package printer
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"text/template"
+
+	"github.com/McKael/madon"
+)
+
+// TemplatePrinter represents a Template printer
+type TemplatePrinter struct {
+	rawTemplate string
+	template    *template.Template
+}
+
+// NewPrinterTemplate returns a Template ResourcePrinter
+// For TemplatePrinter, the option parameter contains the template string.
+func NewPrinterTemplate(option string) (*TemplatePrinter, error) {
+	tmpl := option
+	t, err := template.New("output").Parse(tmpl)
+	if err != nil {
+		return nil, err
+	}
+	return &TemplatePrinter{
+		rawTemplate: tmpl,
+		template:    t,
+	}, nil
+}
+
+// PrintObj sends the object as text to the writer
+// If the writer w is nil, standard output will be used.
+func (p *TemplatePrinter) PrintObj(obj interface{}, w io.Writer, tmpl string) error {
+	if w == nil {
+		w = os.Stdout
+	}
+
+	if p.template == nil {
+		return fmt.Errorf("template not built")
+	}
+
+	switch ot := obj.(type) { // I wish I knew a better way...
+	case []madon.Account, []madon.Application, []madon.Attachment, []madon.Card,
+		[]madon.Client, []madon.Context, []madon.Instance, []madon.Mention,
+		[]madon.Notification, []madon.Relationship, []madon.Report,
+		[]madon.Results, []madon.Status, []madon.StreamEvent, []madon.Tag:
+		return p.templateForeach(ot, w)
+	}
+
+	return p.templatePrintSingleObj(obj, w)
+}
+
+func (p *TemplatePrinter) templatePrintSingleObj(obj interface{}, w io.Writer) error {
+	// This code comes from Kubernetes.
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return err
+	}
+	out := map[string]interface{}{}
+	if err := json.Unmarshal(data, &out); err != nil {
+		return err
+	}
+	if err = p.safeExecute(w, out); err != nil {
+		return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err)
+	}
+	return nil
+}
+
+// safeExecute tries to execute the template, but catches panics and returns an error
+// should the template engine panic.
+// This code comes from Kubernetes.
+func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
+	var panicErr error
+	// Sorry for the double anonymous function. There's probably a clever way
+	// to do this that has the defer'd func setting the value to be returned, but
+	// that would be even less obvious.
+	retErr := func() error {
+		defer func() {
+			if x := recover(); x != nil {
+				panicErr = fmt.Errorf("caught panic: %+v", x)
+			}
+		}()
+		return p.template.Execute(w, obj)
+	}()
+	if panicErr != nil {
+		return panicErr
+	}
+	return retErr
+}
+
+func (p *TemplatePrinter) templateForeach(ol interface{}, w io.Writer) error {
+	switch reflect.TypeOf(ol).Kind() {
+	case reflect.Slice:
+		s := reflect.ValueOf(ol)
+
+		for i := 0; i < s.Len(); i++ {
+			o := s.Index(i).Interface()
+			if err := p.templatePrintSingleObj(o, w); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}