-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from graugans/feature/templates
Add the possibility to filter the get command with the go text/template package
- Loading branch information
Showing
7 changed files
with
227 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,40 @@ Copyright © 2023 Christian Ege <[email protected]> | |
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"text/template" | ||
|
||
"github.com/graugans/go-ovp8xx/v2/pkg/ovp8xx" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// toJSON converts the given object to a JSON string representation. | ||
// If an error occurs during marshaling, an empty string is returned. | ||
// This is taken from https://github.com/intel/tfortools | ||
func toJSON(obj interface{}) string { | ||
b, err := json.MarshalIndent(obj, "", "\t") | ||
if err != nil { | ||
return "" | ||
} | ||
return string(b) | ||
} | ||
|
||
// prefix can be used to create a list separated by s and the very first | ||
// element is not prefixed. | ||
func prefix(s string) func() string { | ||
i := -1 | ||
return func() string { | ||
i++ | ||
if i == 0 { | ||
return "" | ||
} | ||
return s | ||
} | ||
} | ||
|
||
func getCommand(cmd *cobra.Command, args []string) error { | ||
var result ovp8xx.Config | ||
var err error | ||
|
@@ -23,6 +53,35 @@ func getCommand(cmd *cobra.Command, args []string) error { | |
if result, err = o3r.Get(helper.jsonPointers()); err != nil { | ||
return err | ||
} | ||
|
||
if cmd.Flags().Changed("format") { | ||
if helper.prettyPrint() { | ||
return errors.New("you can't use --pretty and --format at the same time") | ||
} | ||
|
||
format, err := cmd.Flags().GetString("format") | ||
if err != nil { | ||
return fmt.Errorf("unable to get the format string from the command line: %w", err) | ||
} | ||
var inputData interface{} | ||
if err = json.Unmarshal([]byte(result.String()), &inputData); err != nil { | ||
return fmt.Errorf("unable to unmarshal the JSON data from the 'get' call: %w", err) | ||
} | ||
templateFunctions := template.FuncMap{ | ||
"toJSON": toJSON, | ||
"prefix": prefix, | ||
} | ||
tmpl, err := template.New("output").Funcs(templateFunctions).Parse(format) | ||
if err != nil { | ||
return fmt.Errorf("unable to parse the template: %w", err) | ||
} | ||
|
||
if err := tmpl.Execute(os.Stdout, inputData); err != nil { | ||
return fmt.Errorf("unable to execute the template: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
if err := helper.printJSONResult(result.String()); err != nil { | ||
return err | ||
} | ||
|
@@ -66,4 +125,5 @@ func init() { | |
rootCmd.AddCommand(getCmd) | ||
getCmd.Flags().StringSliceP("pointer", "p", []string{""}, "A JSON pointer to be queried") | ||
getCmd.Flags().Bool("pretty", false, "Pretty print the JSON received from the device") | ||
getCmd.Flags().String("format", "", "Specify an alternative format for the JSON output") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# Using Go text/template for Advanced Data Filtering | ||
|
||
This document demonstrates the usage of the Go text/template package to filter and format the output of the `ovp8xx get` command for manipulating the result data. There are two custom functions created to enhance the filtering: `prefix` and `toJSON`. | ||
|
||
## The `prefix` Function | ||
|
||
The `prefix` function is designed to solve the problem of trailing commas, which are, for example, not allowed in JSON. The idea is to prefix each line with a comma, except for the very first one. This function is particularly useful when generating JSON output dynamically, where the number of elements may vary. | ||
|
||
Here's how it's used in the command: | ||
|
||
```sh | ||
ovp8xx get --format '{ "ports": { {{$p := prefix ", "}}{{- range $key, $val := .ports -}}{{call $p}}"{{- $key -}}":{"state": "{{ .state }}"}{{- end }} } }' | ||
``` | ||
|
||
In this command, `{{$p := prefix ", "}}` initializes the prefix function with a comma and a space. Then, `{{call $p}}` is used to insert the prefix before each port entry. The prefix function ensures that no comma is inserted before the first entry. | ||
|
||
The result is a JSON object where each line, except the first, is prefixed with a comma: | ||
|
||
```sh | ||
{ | ||
"ports": { | ||
"port0": { | ||
"state": "RUN" | ||
}, | ||
"port1": { | ||
"state": "RUN" | ||
}, | ||
"port2": { | ||
"state": "RUN" | ||
}, | ||
"port3": { | ||
"state": "RUN" | ||
}, | ||
"port6": { | ||
"state": "RUN" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Print all ports with some details | ||
|
||
The following command retrieves all port data and formats it for easy reading: | ||
|
||
```sh | ||
ovp8xx get --format '{{$p := prefix "\n"}}{{ range $port, $details := .ports }}{{call $p}}[{{ $port }}] state: {{ $details.state }},{{print "\t"}}type: {{ $details.info.features.type }},{{print "\t"}}PCIC Port: {{ $details.data.pcicTCPPort }}{{ end }}' | ||
``` | ||
|
||
The result will look like: | ||
|
||
``` | ||
[port0] state: RUN, type: 3D, PCIC Port: 50010 | ||
[port1] state: RUN, type: 3D, PCIC Port: 50011 | ||
[port2] state: RUN, type: 2D, PCIC Port: 50012 | ||
[port3] state: RUN, type: 2D, PCIC Port: 50013 | ||
[port6] state: RUN, type: IMU, PCIC Port: 50016 | ||
``` | ||
|
||
## Modify the state of the ports | ||
|
||
Now we are taking the example from the [`prefix`](#the-prefix-function) function and enhance it to also modify the state of all ports (except port6) to CONF with the following command: | ||
|
||
```sh | ||
ovp8xx get --format '{{ $state := "CONF" }}{ "ports": { {{$p := prefix ", "}}{{- range $key, $val := .ports -}}{{ if ne $key "port6" }}{{call $p}}"{{- $key -}}":{"state": "{{ $state }}"}{{end}}{{- end }} } }' | ||
``` | ||
|
||
The output will be a JSON object that can be directly piped into the set command: | ||
|
||
```sh | ||
{ | ||
"ports": { | ||
"port0": { | ||
"state": "CONF" | ||
}, | ||
"port1": { | ||
"state": "CONF" | ||
}, | ||
"port2": { | ||
"state": "CONF" | ||
}, | ||
"port3": { | ||
"state": "CONF" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
To directly apply those changes to the device, use the following command: | ||
|
||
```sh | ||
ovp8xx get --format '{{ $state := "CONF" }}{ "ports": { {{$p := prefix ", "}}{{- range $key, $val := .ports -}}{{ if ne $key "port6" }}{{call $p}}"{{- $key -}}":{"state": "{{ $state }}"}{{end}}{{- end }} } }' | ovp8xx set | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Package versioninfo uses runtime.ReadBuildInfo() to set global executable revision information if possible. | ||
package versioninfo | ||
|
||
// SPDX-License-Identifier: MIT | ||
// Copyright (c) 2021 Carl Johnson | ||
// Based on https://github.com/earthboundkid/versioninfo | ||
|
||
import ( | ||
"runtime/debug" | ||
"time" | ||
) | ||
|
||
var ( | ||
// Version will be the version tag if the binary is built with "go install url/tool@version". | ||
// If the binary is built some other way, it will be "(devel)". | ||
Version = "unknown" | ||
// Revision is taken from the vcs.revision tag in Go 1.18+. | ||
Revision = "unknown" | ||
// LastCommit is taken from the vcs.time tag in Go 1.18+. | ||
LastCommit time.Time | ||
// DirtyBuild is taken from the vcs.modified tag in Go 1.18+. | ||
DirtyBuild = true | ||
) | ||
|
||
func init() { | ||
info, ok := debug.ReadBuildInfo() | ||
if !ok { | ||
return | ||
} | ||
for _, kv := range info.Settings { | ||
if kv.Value == "" { | ||
continue | ||
} | ||
switch kv.Key { | ||
case "vcs.revision": | ||
Revision = kv.Value | ||
case "vcs.time": | ||
LastCommit, _ = time.Parse(time.RFC3339, kv.Value) | ||
case "vcs.modified": | ||
DirtyBuild = kv.Value == "true" | ||
} | ||
} | ||
if info.Main.Version != "" { | ||
Version = info.Main.Version | ||
} | ||
} |