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 }