go.ligato.io/vpp-agent/v3@v3.5.0/cmd/agentctl/commands/formatter.go (about)

     1  //  Copyright (c) 2019 Cisco and/or its affiliates.
     2  //
     3  //  Licensed under the Apache License, Version 2.0 (the "License");
     4  //  you may not use this file except in compliance with the License.
     5  //  You may obtain a copy of the License at:
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //  Unless required by applicable law or agreed to in writing, software
    10  //  distributed under the License is distributed on an "AS IS" BASIS,
    11  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //  See the License for the specific language governing permissions and
    13  //  limitations under the License.
    14  
    15  package commands
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io"
    22  	"strings"
    23  	"text/template"
    24  	"time"
    25  
    26  	"github.com/goccy/go-yaml"
    27  	"google.golang.org/protobuf/encoding/protojson"
    28  	"google.golang.org/protobuf/encoding/prototext"
    29  	"google.golang.org/protobuf/proto"
    30  )
    31  
    32  var tmplFuncs = template.FuncMap{
    33  	"json":       jsonTmpl,
    34  	"yaml":       yamlTmpl,
    35  	"proto":      protoTmpl,
    36  	"protomulti": protoTmplMulti,
    37  	"epoch":      epochTmpl,
    38  	"ago":        agoTmpl,
    39  	"dur":        shortHumanDuration,
    40  	"prefix":     prefixTmpl,
    41  }
    42  
    43  func formatAsTemplate(w io.Writer, format string, data interface{}) error {
    44  	var b bytes.Buffer
    45  	switch strings.ToLower(format) {
    46  	case "json":
    47  		b.WriteString(jsonTmpl(data))
    48  	case "yaml", "yml":
    49  		b.WriteString(yamlTmpl(data))
    50  	case "proto":
    51  		b.WriteString(protoTmpl(data))
    52  	default:
    53  		t := template.New("format")
    54  		t.Funcs(tmplFuncs)
    55  		if _, err := t.Parse(format); err != nil {
    56  			return fmt.Errorf("parsing format template failed: %v", err)
    57  		}
    58  		if err := t.Execute(&b, data); err != nil {
    59  			return fmt.Errorf("executing format template failed: %v", err)
    60  		}
    61  	}
    62  	_, err := b.WriteTo(w)
    63  	return err
    64  }
    65  
    66  func jsonTmpl(data interface{}) string {
    67  	b := encodeJson(data, "  ")
    68  	return string(b)
    69  }
    70  
    71  func yamlTmpl(data interface{}) string {
    72  	out := encodeJson(data, "")
    73  	bb, err := jsonToYaml(out)
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  	return string(bb)
    78  }
    79  
    80  func encodeJson(data interface{}, ident string) []byte {
    81  	if msg, ok := data.(proto.Message); ok {
    82  		m := protojson.MarshalOptions{
    83  			Indent: ident,
    84  		}
    85  		b, err := m.Marshal(msg)
    86  		if err != nil {
    87  			panic(err)
    88  		}
    89  		return b
    90  	}
    91  	var b bytes.Buffer
    92  	encoder := json.NewEncoder(&b)
    93  	encoder.SetIndent("", ident)
    94  	if err := encoder.Encode(data); err != nil {
    95  		panic(err)
    96  	}
    97  	return b.Bytes()
    98  }
    99  
   100  func jsonToYaml(j []byte) ([]byte, error) {
   101  	var jsonObj interface{}
   102  	err := yaml.UnmarshalWithOptions(j, &jsonObj, yaml.UseOrderedMap())
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	return yaml.Marshal(jsonObj)
   107  }
   108  
   109  func protoTmpl(data interface{}) string {
   110  	pb, ok := data.(proto.Message)
   111  	if !ok {
   112  		panic(fmt.Sprintf("%T is not a proto message", data))
   113  	}
   114  	out, err := prototext.Marshal(pb)
   115  	if err != nil {
   116  		panic(err)
   117  	}
   118  	return string(out)
   119  }
   120  
   121  func protoTmplMulti(data interface{}) string {
   122  	pb, ok := data.(proto.Message)
   123  	if !ok {
   124  		panic(fmt.Sprintf("%T is not a proto message", data))
   125  	}
   126  	out, err := prototext.MarshalOptions{Multiline: true}.Marshal(pb)
   127  	if err != nil {
   128  		panic(err)
   129  	}
   130  	return string(out)
   131  }
   132  
   133  func epochTmpl(s int64) time.Time {
   134  	return time.Unix(s, 0)
   135  }
   136  
   137  func agoTmpl(t time.Time) time.Duration {
   138  	return time.Since(t).Round(time.Second)
   139  }
   140  
   141  func shortHumanDuration(d time.Duration) string {
   142  	if seconds := int(d.Seconds()); seconds < -1 {
   143  		return "<invalid>"
   144  	} else if seconds < 0 {
   145  		return "0s"
   146  	} else if seconds < 60 {
   147  		return fmt.Sprintf("%ds", seconds)
   148  	} else if minutes := int(d.Minutes()); minutes < 60 {
   149  		return fmt.Sprintf("%dm", minutes)
   150  	} else if hours := int(d.Hours()); hours < 24 {
   151  		return fmt.Sprintf("%dh", hours)
   152  	} else if hours < 24*365 {
   153  		return fmt.Sprintf("%dd", hours/24)
   154  	}
   155  	return fmt.Sprintf("%dy", int(d.Hours()/24/365))
   156  }
   157  
   158  func prefixTmpl(s string, prefix string) string {
   159  	ps := strings.TrimRight(s, "\n")
   160  	ps = strings.ReplaceAll(ps, "\n", "\n"+prefix)
   161  	if strings.HasSuffix(s, "\n") {
   162  		ps += "\n"
   163  	}
   164  	return prefix + ps
   165  }