blob: 540f46e3d8ac612f2afbb3d73d650bf116854693 [file] [log] [blame]
package utils
import (
"fmt"
"strings"
)
// ParsedOutput is a data structure that holds the parsed output
// of certain shell commands whose output takes the form of a
// table.
type ParsedOutput map[string][]string
// ParseColumns parses command outputs which are in a column table.
// It takes in an optional titles argument which specifies the
// columns to parse. If this argument is missing, then all columns are parsed.
//
// Eg, ParseColumns(["total used free shared",
// "14868916 12660 14830916 0"],
// ["total", "used"]) = map[string][]string {
// "total": ["14868916"]
// "used": ["12660"]}
func ParseColumns(rows []string, titles ...string) (map[string][]string, error) {
// parsedOutput is a map that stores col titles as key and entries as values.
parsedOutput := ParsedOutput{}
// maps column title to its index eg columns["r"] = 0 wth vmstat.
columns := make(map[string]int)
for i, row := range rows {
// break the row into slice
tokens := strings.Fields(row)
if len(tokens) == 0 {
continue
}
// find index of column titles
if i == 0 {
// if no titles were specified, use all of them
if len(titles) == 0 {
titles = tokens
}
// map header name to its index
for index, str := range tokens {
columns[str] = index
}
continue
}
// loop over titles, get column number of the title using map,
// use that to get the actual values, append to list.
for _, title := range titles {
// for example columns["us"] = 12
index, ok := columns[title]
if !ok {
return nil, fmt.Errorf("unknown Column title %s", title)
}
// for example if vmstat was run, tokens[0] will give the
// value of a running process for some update.
value := tokens[index]
parsedOutput[title] = append(parsedOutput[title], value)
}
}
return parsedOutput, nil
}
// ParseRows parses command outputs which are in a row table.
// It takes in an optional titles argument which specifies which rows
// to parse. If missing, all rows are parsed.
//
// Eg, ParseRows(["avg-cpu: %user %nice %system %iowait %steal %idle"],
// [avg-cpu]) = map[string][]string {
// avg-cpu: [%user, %nice, %system, %iowait, %steal, %idle]}
func ParseRows(lines []string, titles ...string) (map[string][]string, error) {
// parsedOutput stores titles passed to function as key and entries as values.
parsedOutput := ParsedOutput{}
// rows stores each title in rows as key and the rest of the row as value.
rows := make(map[string][]string)
// loop over lines and map each line title to value(s)
for _, line := range lines {
// split by ':' for titles that are multi worded
tokens := strings.Split(line, ":")
// tokens is always at least of length 1 since an empty string when
// split, will be a slice of length 1.
if len(tokens) == 1 {
continue
}
header := strings.Trim(tokens[0], "\\s*")
// everything to the right of title is one string since
// row was split by the character ':'.
value := tokens[1]
// now split value according to white spaces
tokens = strings.Fields(value)
rows[header] = tokens
}
// if empty titles slice was passed, use all the row titles.
if len(titles) == 0 {
for key := range rows {
titles = append(titles, key)
}
}
// loop over titles passed to function and get their values from the map.
for _, title := range titles {
var values []string
var ok bool
// check if any additional titles were passed in.
if values, ok = rows[title]; !ok {
return nil, fmt.Errorf("could not find the row title %s", title)
}
parsedOutput[title] = values
}
return parsedOutput, nil
}