printer/templateprinter.go
changeset 0 5abace724584
child 14 da2059f2fa6a
equal deleted inserted replaced
-1:000000000000 0:5abace724584
       
     1 // Copyright © 2017 Mikael Berthe <mikael@lilotux.net>
       
     2 //
       
     3 // Licensed under the MIT license.
       
     4 // Please see the LICENSE file is this directory.
       
     5 
       
     6 package printer
       
     7 
       
     8 import (
       
     9 	"encoding/json"
       
    10 	"fmt"
       
    11 	"io"
       
    12 	"os"
       
    13 	"reflect"
       
    14 	"text/template"
       
    15 
       
    16 	"github.com/McKael/madon"
       
    17 )
       
    18 
       
    19 // TemplatePrinter represents a Template printer
       
    20 type TemplatePrinter struct {
       
    21 	rawTemplate string
       
    22 	template    *template.Template
       
    23 }
       
    24 
       
    25 // NewPrinterTemplate returns a Template ResourcePrinter
       
    26 // For TemplatePrinter, the option parameter contains the template string.
       
    27 func NewPrinterTemplate(option string) (*TemplatePrinter, error) {
       
    28 	tmpl := option
       
    29 	t, err := template.New("output").Parse(tmpl)
       
    30 	if err != nil {
       
    31 		return nil, err
       
    32 	}
       
    33 	return &TemplatePrinter{
       
    34 		rawTemplate: tmpl,
       
    35 		template:    t,
       
    36 	}, nil
       
    37 }
       
    38 
       
    39 // PrintObj sends the object as text to the writer
       
    40 // If the writer w is nil, standard output will be used.
       
    41 func (p *TemplatePrinter) PrintObj(obj interface{}, w io.Writer, tmpl string) error {
       
    42 	if w == nil {
       
    43 		w = os.Stdout
       
    44 	}
       
    45 
       
    46 	if p.template == nil {
       
    47 		return fmt.Errorf("template not built")
       
    48 	}
       
    49 
       
    50 	switch ot := obj.(type) { // I wish I knew a better way...
       
    51 	case []madon.Account, []madon.Application, []madon.Attachment, []madon.Card,
       
    52 		[]madon.Client, []madon.Context, []madon.Instance, []madon.Mention,
       
    53 		[]madon.Notification, []madon.Relationship, []madon.Report,
       
    54 		[]madon.Results, []madon.Status, []madon.StreamEvent, []madon.Tag:
       
    55 		return p.templateForeach(ot, w)
       
    56 	}
       
    57 
       
    58 	return p.templatePrintSingleObj(obj, w)
       
    59 }
       
    60 
       
    61 func (p *TemplatePrinter) templatePrintSingleObj(obj interface{}, w io.Writer) error {
       
    62 	// This code comes from Kubernetes.
       
    63 	data, err := json.Marshal(obj)
       
    64 	if err != nil {
       
    65 		return err
       
    66 	}
       
    67 	out := map[string]interface{}{}
       
    68 	if err := json.Unmarshal(data, &out); err != nil {
       
    69 		return err
       
    70 	}
       
    71 	if err = p.safeExecute(w, out); err != nil {
       
    72 		return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err)
       
    73 	}
       
    74 	return nil
       
    75 }
       
    76 
       
    77 // safeExecute tries to execute the template, but catches panics and returns an error
       
    78 // should the template engine panic.
       
    79 // This code comes from Kubernetes.
       
    80 func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
       
    81 	var panicErr error
       
    82 	// Sorry for the double anonymous function. There's probably a clever way
       
    83 	// to do this that has the defer'd func setting the value to be returned, but
       
    84 	// that would be even less obvious.
       
    85 	retErr := func() error {
       
    86 		defer func() {
       
    87 			if x := recover(); x != nil {
       
    88 				panicErr = fmt.Errorf("caught panic: %+v", x)
       
    89 			}
       
    90 		}()
       
    91 		return p.template.Execute(w, obj)
       
    92 	}()
       
    93 	if panicErr != nil {
       
    94 		return panicErr
       
    95 	}
       
    96 	return retErr
       
    97 }
       
    98 
       
    99 func (p *TemplatePrinter) templateForeach(ol interface{}, w io.Writer) error {
       
   100 	switch reflect.TypeOf(ol).Kind() {
       
   101 	case reflect.Slice:
       
   102 		s := reflect.ValueOf(ol)
       
   103 
       
   104 		for i := 0; i < s.Len(); i++ {
       
   105 			o := s.Index(i).Interface()
       
   106 			if err := p.templatePrintSingleObj(o, w); err != nil {
       
   107 				return err
       
   108 			}
       
   109 		}
       
   110 	}
       
   111 	return nil
       
   112 }