Skip to content

Commit 3fc39aa

Browse files
committed
feat: consolidate formatters
- Replaces globally-scoped formatter function with methods - Defines enumerated Format types - Renames the 'output' flag 'format' due to confusion with command file descriptors - FunctionDescription now Function - Global verbose flag replaced with config struct based value throughout
1 parent 89c09d9 commit 3fc39aa

File tree

9 files changed

+151
-112
lines changed

9 files changed

+151
-112
lines changed

client.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ type ProgressListener interface {
9494
// Describer of Functions' remote deployed aspect.
9595
type Describer interface {
9696
// Describe the running state of the service as reported by the underlyng platform.
97-
Describe(name string) (description FunctionDescription, err error)
97+
Describe(name string) (description Description, err error)
9898
}
9999

100-
type FunctionDescription struct {
100+
type Description struct {
101101
Name string `json:"name" yaml:"name"`
102102
Routes []string `json:"routes" yaml:"routes"`
103103
Subscriptions []Subscription `json:"subscriptions" yaml:"subscriptions"`
@@ -493,7 +493,7 @@ func (c *Client) List() ([]string, error) {
493493

494494
// Describe a Function. Name takes precidence. If no name is provided,
495495
// the Function defined at root is used.
496-
func (c *Client) Describe(name, root string) (fd FunctionDescription, err error) {
496+
func (c *Client) Describe(name, root string) (d Description, err error) {
497497
// If name is provided, it takes precidence.
498498
// Otherwise load the Function defined at root.
499499
if name != "" {
@@ -502,10 +502,10 @@ func (c *Client) Describe(name, root string) (fd FunctionDescription, err error)
502502

503503
f, err := NewFunction(root)
504504
if err != nil {
505-
return fd, err
505+
return d, err
506506
}
507507
if !f.Initialized() {
508-
return fd, fmt.Errorf("%v is not initialized", f.Name)
508+
return d, fmt.Errorf("%v is not initialized", f.Name)
509509
}
510510
return c.describer.Describe(f.Name)
511511
}

cmd/delete.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func runDelete(cmd *cobra.Command, args []string) (err error) {
3434
function := faas.Function{Root: config.Path, Name: config.Name}
3535

3636
client := faas.New(
37-
faas.WithVerbose(verbose),
37+
faas.WithVerbose(config.Verbose),
3838
faas.WithRemover(remover))
3939

4040
return client.Remove(function)

cmd/describe.go

+54-37
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"encoding/json"
55
"encoding/xml"
66
"fmt"
7+
"io"
8+
"os"
79

810
"github.com/ory/viper"
911
"github.com/spf13/cobra"
@@ -16,10 +18,10 @@ import (
1618
func init() {
1719
root.AddCommand(describeCmd)
1820
describeCmd.Flags().StringP("namespace", "n", "", "Override namespace in which to search for the Function. Default is to use currently active underlying platform setting - $FAAS_NAMESPACE")
19-
describeCmd.Flags().StringP("output", "o", "yaml", "optionally specify output format (yaml,xml,json). - $FAAS_OUTPUT")
21+
describeCmd.Flags().StringP("format", "f", "human", "optionally specify output format (human|plain|json|xml|yaml) $FAAS_FORMAT")
2022
describeCmd.Flags().StringP("path", "p", cwd(), "Path to the project which should be described - $FAAS_PATH")
2123

22-
err := describeCmd.RegisterFlagCompletionFunc("output", CompleteOutputFormatList)
24+
err := describeCmd.RegisterFlagCompletionFunc("format", CompleteOutputFormatList)
2325
if err != nil {
2426
fmt.Println("Error while calling RegisterFlagCompletionFunc: ", err)
2527
}
@@ -28,10 +30,10 @@ func init() {
2830
var describeCmd = &cobra.Command{
2931
Use: "describe <name> [options]",
3032
Short: "Describe Function",
31-
Long: `Describes the Function by name, by explicit path, or by default the current directory.`,
33+
Long: `Describes the Function initialized in the current directory, or by passed name argument.`,
3234
SuggestFor: []string{"desc", "get"},
3335
ValidArgsFunction: CompleteFunctionList,
34-
PreRunE: bindEnv("namespace", "output", "path"),
36+
PreRunE: bindEnv("namespace", "format", "path"),
3537
RunE: runDescribe,
3638
}
3739

@@ -45,51 +47,25 @@ func runDescribe(cmd *cobra.Command, args []string) (err error) {
4547
describer.Verbose = config.Verbose
4648

4749
client := faas.New(
48-
faas.WithVerbose(verbose),
50+
faas.WithVerbose(config.Verbose),
4951
faas.WithDescriber(describer))
5052

51-
description, err := client.Describe(config.Name, config.Path)
53+
d, err := client.Describe(config.Name, config.Path)
5254
if err != nil {
5355
return
5456
}
5557

56-
formatted, err := formatDescription(description, config.Output)
57-
if err != nil {
58-
return
59-
}
60-
61-
fmt.Println(formatted)
58+
write(os.Stdout, description(d), config.Format)
6259
return
6360
}
6461

65-
// TODO: Placeholder. Create a fit-for-purpose Description plaintext formatter.
66-
func fmtDescriptionPlain(i interface{}) ([]byte, error) {
67-
return []byte(fmt.Sprintf("%v", i)), nil
68-
}
69-
70-
// format the description as json|yaml|xml
71-
func formatDescription(desc faas.FunctionDescription, format string) (string, error) {
72-
formatters := map[string]func(interface{}) ([]byte, error){
73-
"plain": fmtDescriptionPlain,
74-
"json": json.Marshal,
75-
"yaml": yaml.Marshal,
76-
"xml": xml.Marshal,
77-
}
78-
formatFn, ok := formatters[format]
79-
if !ok {
80-
return "", fmt.Errorf("unknown format '%s'", format)
81-
}
82-
bytes, err := formatFn(desc)
83-
if err != nil {
84-
return "", err
85-
}
86-
return string(bytes), nil
87-
}
62+
// CLI Configuration (parameters)
63+
// ------------------------------
8864

8965
type describeConfig struct {
9066
Name string
9167
Namespace string
92-
Output string
68+
Format string
9369
Path string
9470
Verbose bool
9571
}
@@ -102,8 +78,49 @@ func newDescribeConfig(args []string) describeConfig {
10278
return describeConfig{
10379
Name: deriveName(name, viper.GetString("path")),
10480
Namespace: viper.GetString("namespace"),
105-
Output: viper.GetString("output"),
81+
Format: viper.GetString("format"),
10682
Path: viper.GetString("path"),
10783
Verbose: viper.GetBool("verbose"),
10884
}
10985
}
86+
87+
// Output Formatting (serializers)
88+
// -------------------------------
89+
90+
type description faas.Description
91+
92+
func (d description) Human(w io.Writer) error {
93+
fmt.Fprintln(w, d.Name)
94+
fmt.Fprintln(w, "Routes:")
95+
for _, route := range d.Routes {
96+
fmt.Fprintf(w, " %v\n", route)
97+
}
98+
fmt.Fprintln(w, "Subscriptions (Source, Type, Broker):")
99+
for _, s := range d.Subscriptions {
100+
fmt.Fprintf(w, " %v %v %v\n", s.Source, s.Type, s.Broker)
101+
}
102+
return d.Plain(w)
103+
}
104+
105+
func (d description) Plain(w io.Writer) error {
106+
fmt.Fprintf(w, "NAME %v\n", d.Name)
107+
for _, route := range d.Routes {
108+
fmt.Fprintf(w, "ROUTE %v\n", route)
109+
}
110+
for _, s := range d.Subscriptions {
111+
fmt.Fprintf(w, "SUBSCRIPTION %v %v %v\n", s.Source, s.Type, s.Broker)
112+
}
113+
return nil
114+
}
115+
116+
func (d description) JSON(w io.Writer) error {
117+
return json.NewEncoder(w).Encode(d)
118+
}
119+
120+
func (d description) XML(w io.Writer) error {
121+
return xml.NewEncoder(w).Encode(d)
122+
}
123+
124+
func (d description) YAML(w io.Writer) error {
125+
return yaml.NewEncoder(w).Encode(d)
126+
}

cmd/format.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
)
8+
9+
type Format string
10+
11+
const (
12+
Human Format = "human" // Headers, indentation, justification etc.
13+
Plain = "plain" // Suitable for cli automation via sed/awk etc.
14+
JSON = "json" // Technically a ⊆ yaml, but no one likes yaml.
15+
XML = "xml"
16+
YAML = "yaml"
17+
)
18+
19+
// formatter is any structure which has methods for serialization.
20+
type Formatter interface {
21+
Human(io.Writer) error
22+
Plain(io.Writer) error
23+
JSON(io.Writer) error
24+
XML(io.Writer) error
25+
YAML(io.Writer) error
26+
}
27+
28+
// write to the output the output of the formatter's appropriate serilization function.
29+
// the command to exit with value 2.
30+
func write(out io.Writer, s Formatter, formatName string) {
31+
var err error
32+
switch Format(formatName) {
33+
case Human:
34+
err = s.Human(out)
35+
case Plain:
36+
err = s.Plain(out)
37+
case JSON:
38+
err = s.JSON(out)
39+
case XML:
40+
err = s.XML(out)
41+
case YAML:
42+
err = s.YAML(out)
43+
default:
44+
err = fmt.Errorf("format not recognized: %v\n", formatName)
45+
}
46+
if err != nil {
47+
fmt.Fprintln(os.Stderr, err)
48+
os.Exit(2)
49+
}
50+
}

cmd/list.go

+31-58
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"encoding/json"
55
"encoding/xml"
66
"fmt"
7+
"io"
8+
"os"
79

810
"github.com/ory/viper"
911
"github.com/spf13/cobra"
@@ -16,9 +18,9 @@ import (
1618
func init() {
1719
root.AddCommand(listCmd)
1820
listCmd.Flags().StringP("namespace", "n", "", "Override namespace in which to search for Functions. Default is to use currently active underlying platform setting - $FAAS_NAMESPACE")
19-
listCmd.Flags().StringP("output", "o", "plain", "optionally specify output format (plain,json,yaml)")
21+
listCmd.Flags().StringP("format", "f", "human", "optionally specify output format (human|plain|json|xml|yaml) $FAAS_FORMAT")
2022

21-
err := listCmd.RegisterFlagCompletionFunc("output", CompleteOutputFormatList)
23+
err := listCmd.RegisterFlagCompletionFunc("format", CompleteOutputFormatList)
2224
if err != nil {
2325
fmt.Println("Error while calling RegisterFlagCompletionFunc: ", err)
2426
}
@@ -29,7 +31,7 @@ var listCmd = &cobra.Command{
2931
Short: "Lists deployed Functions",
3032
Long: `Lists deployed Functions`,
3133
SuggestFor: []string{"ls", "lsit"},
32-
PreRunE: bindEnv("namespace", "output"),
34+
PreRunE: bindEnv("namespace", "format"),
3335
RunE: runList,
3436
}
3537

@@ -43,91 +45,62 @@ func runList(cmd *cobra.Command, args []string) (err error) {
4345
lister.Verbose = config.Verbose
4446

4547
client := faas.New(
46-
faas.WithVerbose(verbose),
48+
faas.WithVerbose(config.Verbose),
4749
faas.WithLister(lister))
4850

49-
names, err := client.List()
51+
nn, err := client.List()
5052
if err != nil {
5153
return
5254
}
5355

54-
formatted, err := formatNames(names, config.Output)
55-
if err != nil {
56-
return
57-
}
58-
59-
fmt.Println(formatted)
56+
write(os.Stdout, names(nn), config.Format)
6057
return
6158
}
6259

63-
// TODO: placeholder. Create a fit-for-purpose Names plaintext formatter
64-
func fmtNamesPlain(i interface{}) ([]byte, error) {
65-
return []byte(fmt.Sprintf("%v", i)), nil
66-
}
67-
68-
func formatNames(names []string, format string) (string, error) {
69-
formatters := map[string]func(interface{}) ([]byte, error){
70-
"plain": fmtNamesPlain,
71-
"json": json.Marshal,
72-
"yaml": yaml.Marshal,
73-
"xml": xml.Marshal,
74-
}
75-
formatFn, ok := formatters[format]
76-
if !ok {
77-
return "", fmt.Errorf("Unknown format '%v'", format)
78-
}
79-
bytes, err := formatFn(names)
80-
if err != nil {
81-
return "", err
82-
}
83-
return string(bytes), nil
84-
}
60+
// CLI Configuration (parameters)
61+
// ------------------------------
8562

8663
type listConfig struct {
8764
Namespace string
88-
Output string
65+
Format string
8966
Verbose bool
9067
}
9168

9269
func newListConfig() listConfig {
9370
return listConfig{
9471
Namespace: viper.GetString("namespace"),
95-
Output: viper.GetString("output"),
72+
Format: viper.GetString("format"),
9673
Verbose: viper.GetBool("verbose"),
9774
}
9875
}
9976

100-
// DEPRECATED BELOW (?):
101-
// TODO: regenerate completions, which may necessitate the below change:
102-
/*
77+
// Output Formatting (serializers)
78+
// -------------------------------
10379

104-
var validFormats []string
80+
type names []string
10581

106-
func completeFormats(cmd *cobra.Command, args []string, toComplete string) (formats []string, directive cobra.ShellCompDirective) {
107-
formats = validFormats
108-
directive = cobra.ShellCompDirectiveDefault
109-
return
82+
func (nn names) Human(w io.Writer) error {
83+
return nn.Plain(w)
11084
}
11185

112-
type fmtFn func(writer io.Writer, names []string) error
113-
114-
func fmtPlain(writer io.Writer, names []string) error {
115-
for _, name := range names {
116-
_, err := fmt.Fprintf(writer, "%s\n", name)
117-
if err != nil {
118-
return err
119-
}
86+
func (nn names) Plain(w io.Writer) error {
87+
for _, name := range nn {
88+
fmt.Fprintln(w, name)
12089
}
12190
return nil
12291
}
12392

124-
func fmtJSON(writer io.Writer, names []string) error {
125-
encoder := json.NewEncoder(writer)
126-
return encoder.Encode(names)
93+
func (nn names) JSON(w io.Writer) error {
94+
return json.NewEncoder(w).Encode(nn)
95+
}
96+
97+
func (nn names) XML(w io.Writer) error {
98+
return xml.NewEncoder(w).Encode(nn)
12799
}
128100

129-
func fmtYAML(writer io.Writer, names []string) error {
130-
encoder := yaml.NewEncoder(writer)
131-
return encoder.Encode(names)
101+
func (nn names) YAML(w io.Writer) error {
102+
// the yaml.v2 package refuses to directly serialize a []string unless
103+
// exposed as a public struct member; so an inline anonymous is used.
104+
ff := struct{ Names []string }{nn}
105+
return yaml.NewEncoder(w).Encode(ff.Names)
132106
}
133-
*/

0 commit comments

Comments
 (0)