package client import ( "fmt" "path" "strings" ) // Node from data type Node struct { ID int `json:"id"` ImageURL string Max string Min string Text string Value string Children []Node } // Visitor callback function type Visitor func(v Value) error // IsValue true if node has children func (n *Node) IsValue() bool { if n.Children == nil { return false } return len(n.Children) == 0 } // IsRoot returns true if json is a root node func (n *Node) IsRoot() bool { return n.ID == 0 } // IsSensor returns true if json node is for a sensor type func (n *Node) IsSensor() bool { if n.ImageURL == "" || n.Text == "" { return false } _, ok := sensorToType[n.Text] return ok } // SensorType returns type of sensor func (n *Node) SensorType() Sensor { st, _ := sensorToType[n.Text] // Figure out if data is in GB (Data) or MB (SmallData) if st == Data { if len(n.Children) > 0 { val := n.Children[0] if strings.HasSuffix(val.Value, "MB") { st = SmallData } } } return st } // IsHardware returns true if json node is for hardware func (n *Node) IsHardware() bool { if n.ImageURL == "" || n.Text == "" { return false } _, ok := imageToHardware[path.Base(n.ImageURL)] return ok } // HardwareType returns HardwareType of node func (n *Node) HardwareType() HardwareType { ht, _ := imageToHardware[path.Base(n.ImageURL)] return ht } // Values from json root func (n *Node) Values() ([]Value, error) { ret := []Value{} err := n.Walk(func(val Value) error { ret = append(ret, val) return nil }) if err != nil { return nil, err } return ret, nil } // Walk from json root func (n *Node) Walk(fn Visitor) error { return n.walk(fn, Value{Hardware: []Hardware{}}, 0, 0) } func (n *Node) walk(fn Visitor, ctx Value, hwIndex, hwTotal int) error { if n.IsValue() { ctx.Label = n.Text ctx.Value = n.Value return fn(ctx) } if n.IsSensor() { ctx.Unit = n.SensorType() } if n.IsHardware() { ctx.Hardware = append(ctx.Hardware, Hardware{ Type: n.HardwareType(), Value: n.Text, TypeIndex: hwIndex, TypeCount: hwTotal, }) } totals := n.childDeviceTotals() deviceIndex := map[HardwareType]int{} for _, child := range n.Children { deviceType := child.HardwareType() index := deviceIndex[deviceType] if err := child.walk(fn, ctx, index, totals[deviceType]); err != nil { return err } deviceIndex[deviceType]++ } return nil } func (n *Node) childDeviceTotals() map[HardwareType]int { totals := map[HardwareType]int{} for _, child := range n.Children { totals[child.HardwareType()]++ } return totals } // Stringify tree func (n *Node) Stringify() string { return n.stringify(0) } func (n *Node) stringify(indent int) string { prefix := "" for i := 0; i < indent; i++ { prefix += " " } ret := prefix + n.Text if n.Value != "" { ret += ": " + n.Value } if n.Max != "" && n.Min != "" && n.Max != "-" && n.Min != "-" { ret += fmt.Sprintf(" (%s - %s)", n.Min, n.Max) } ret += "\n" for _, child := range n.Children { ret += child.stringify(indent + 1) } return ret }