Add theme support
authorMikael Berthe <mikael@lilotux.net>
Sun, 07 May 2017 16:38:52 +0200
changeset 85 a4464c0b0c36
parent 84 5a894a10304c
child 86 9e846c5af138
Add theme support
cmd/root.go
cmd/utils.go
printer/printer.go
printer/templateprinter.go
printer/themeprinter.go
--- a/cmd/root.go	Sun May 07 14:12:56 2017 +0200
+++ b/cmd/root.go	Sun May 07 16:38:52 2017 +0200
@@ -33,7 +33,7 @@
 var login, password, token string
 var verbose bool
 var outputFormat string
-var outputTemplate, outputTemplateFile string
+var outputTemplate, outputTemplateFile, outputTheme string
 var colorMode string
 
 // Shell completion functions
@@ -42,7 +42,7 @@
 	COMPREPLY=( direct private unlisted public )
 }
 __madonctl_output() {
-	COMPREPLY=( plain json yaml template )
+	COMPREPLY=( plain json yaml template theme )
 }
 __madonctl_color() {
 	COMPREPLY=( auto on off )
@@ -127,11 +127,13 @@
 	RootCmd.PersistentFlags().StringVarP(&password, "password", "P", "", "Instance user password")
 	RootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "User token")
 	RootCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "plain",
-		"Output format (plain|json|yaml|template)")
+		"Output format (plain|json|yaml|template|theme)")
 	RootCmd.PersistentFlags().StringVar(&outputTemplate, "template", "",
 		"Go template (for output=template)")
 	RootCmd.PersistentFlags().StringVar(&outputTemplateFile, "template-file", "",
 		"Go template file (for output=template)")
+	RootCmd.PersistentFlags().StringVar(&outputTheme, "theme", "",
+		"Theme name (for output=theme)")
 	RootCmd.PersistentFlags().StringVar(&colorMode, "color", "",
 		"Color mode (auto|on|off; for output=template)")
 
--- a/cmd/utils.go	Sun May 07 14:12:56 2017 +0200
+++ b/cmd/utils.go	Sun May 07 16:38:52 2017 +0200
@@ -22,7 +22,7 @@
 func checkOutputFormat(cmd *cobra.Command, args []string) error {
 	of := viper.GetString("output")
 	switch of {
-	case "", "plain", "json", "yaml", "template":
+	case "", "plain", "json", "yaml", "template", "theme":
 		return nil // Accepted
 	}
 	return errors.Errorf("output format '%s' not supported", of)
@@ -35,10 +35,14 @@
 		of = "plain"
 	}
 	// Override format if a template is provided
-	if of == "plain" && (outputTemplate != "" || outputTemplateFile != "") {
+	if of == "plain" {
 		// If the format is plain and there is a template option,
-		// set the format to "template".
-		of = "template"
+		// set the format to "template".  Same for "theme".
+		if outputTemplate != "" || outputTemplateFile != "" {
+			of = "template"
+		} else if outputTheme != "" {
+			of = "theme"
+		}
 	}
 	return of
 }
@@ -56,7 +60,10 @@
 		printer.ColorMode = 2
 	}
 
-	if of == "template" {
+	if of == "theme" {
+		opt["name"] = outputTheme
+		opt["template_directory"] = viper.GetString("template_directory")
+	} else if of == "template" {
 		opt["template"] = outputTemplate
 		if outputTemplateFile != "" {
 			tmpl, err := readTemplate(outputTemplateFile, viper.GetString("template_directory"))
--- a/printer/printer.go	Sun May 07 14:12:56 2017 +0200
+++ b/printer/printer.go	Sun May 07 16:38:52 2017 +0200
@@ -31,6 +31,8 @@
 		return NewPrinterYAML(options)
 	case "template":
 		return NewPrinterTemplate(options)
+	case "theme":
+		return NewPrinterTheme(options)
 	}
 	return nil, fmt.Errorf("unhandled output format")
 }
--- a/printer/templateprinter.go	Sun May 07 14:12:56 2017 +0200
+++ b/printer/templateprinter.go	Sun May 07 16:38:52 2017 +0200
@@ -36,6 +36,9 @@
 // For TemplatePrinter, the options parameter contains the template string.
 func NewPrinterTemplate(options Options) (*TemplatePrinter, error) {
 	tmpl := options["template"]
+	if tmpl == "" {
+		return nil, fmt.Errorf("empty template")
+	}
 	t, err := template.New("output").Funcs(template.FuncMap{
 		"fromhtml": html2string,
 		"fromunix": unix2string,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/printer/themeprinter.go	Sun May 07 16:38:52 2017 +0200
@@ -0,0 +1,129 @@
+// Copyright © 2017 Mikael Berthe <mikael@lilotux.net>
+//
+// Licensed under the MIT license.
+// Please see the LICENSE file is this directory.
+
+package printer
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/m0t0k1ch1/gomif"
+	"github.com/pkg/errors"
+
+	"github.com/McKael/madon"
+)
+
+const themeDirName = "themes"
+
+// ThemePrinter represents a Theme printer
+type ThemePrinter struct {
+	name        string
+	templateDir string
+}
+
+// NewPrinterTheme returns a Theme ResourcePrinter
+// For ThemePrinter, the options parameter contains the name of the theme
+// and the template base directory (themes are assumed to be in the "themes"
+// subdirectory).
+func NewPrinterTheme(options Options) (*ThemePrinter, error) {
+	name, ok := options["name"]
+	if !ok || name == "" {
+		return nil, fmt.Errorf("no theme name")
+	}
+	if strings.IndexRune(name, '/') >= 0 {
+		// Should we allow that?  (subthemes, etc.)
+		return nil, fmt.Errorf("invalid theme name")
+	}
+	return &ThemePrinter{
+		name:        name,
+		templateDir: options["template_directory"],
+	}, nil
+}
+
+// PrintObj sends the object as text to the writer
+// If the writer w is nil, standard output will be used.
+func (p *ThemePrinter) PrintObj(obj interface{}, w io.Writer, tmpl string) error {
+	if w == nil {
+		w = os.Stdout
+	}
+
+	if p.name == "" {
+		return fmt.Errorf("no theme name") // Should not happen
+	}
+
+	var objType string
+
+	switch obj.(type) {
+	case []madon.Account, madon.Account, *madon.Account:
+		objType = "account"
+	case []madon.Application, madon.Application, *madon.Application:
+		objType = "application"
+	case []madon.Attachment, madon.Attachment, *madon.Attachment:
+		objType = "attachment"
+	case []madon.Card, madon.Card, *madon.Card:
+		objType = "card"
+	case []madon.Client, madon.Client, *madon.Client:
+		objType = "client"
+	case []madon.Context, madon.Context, *madon.Context:
+		objType = "context"
+	case []madon.Instance, madon.Instance, *madon.Instance:
+		objType = "instance"
+	case []madon.Mention, madon.Mention, *madon.Mention:
+		objType = "mention"
+	case []madon.Notification, madon.Notification, *madon.Notification:
+		objType = "notification"
+	case []madon.Relationship, madon.Relationship, *madon.Relationship:
+		objType = "relationship"
+	case []madon.Report, madon.Report, *madon.Report:
+		objType = "report"
+	case []madon.Results, madon.Results, *madon.Results:
+		objType = "results"
+	case []madon.Status, madon.Status, *madon.Status:
+		objType = "status"
+	case []madon.StreamEvent, madon.StreamEvent, *madon.StreamEvent:
+		objType = "streamEvent"
+	case []madon.Tag, madon.Tag, *madon.Tag:
+		objType = "tag"
+
+	case []*gomif.InstanceStatus, *gomif.InstanceStatus:
+		objType = "instancestatus"
+	}
+
+	var rp *ResourcePrinter
+
+	if objType != "" {
+		// Check template exists
+		templatePath := filepath.Join(p.templateDir, themeDirName, p.name, objType) + ".tmpl"
+		if _, err := os.Stat(templatePath); err != nil {
+			fmt.Fprintf(os.Stderr, "Warning: theme template not found, falling back to plaintext printer\n") // XXX
+		} else {
+			t, err := ioutil.ReadFile(templatePath)
+			if err != nil {
+				return errors.Wrap(err, "cannot read template")
+			}
+			np, err := NewPrinter("template", Options{"template": string(t)})
+			if err != nil {
+				return errors.Wrap(err, "cannot create template printer")
+			}
+			rp = &np
+		}
+	}
+
+	if rp != nil {
+		return (*rp).PrintObj(obj, w, "")
+	}
+
+	// No resource printer; let's fall back to plain printer
+	// XXX Maybe we should just fail?
+	plainP, err := NewPrinter("plain", nil)
+	if err != nil {
+		return errors.Wrap(err, "cannot create plaintext printer")
+	}
+	return plainP.PrintObj(obj, w, "")
+}