Files
weave-scope/extras/generate_latest_map
2017-07-04 07:35:30 +01:00

191 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Generate concrete implementations of LatestMap.
#
# e.g.
# $ generate_latest_map ./report/out.go string NodeControlData ...
#
# Depends on:
# - gofmt
function generate_header() {
local out_file="${1}"
local cmd="${2}"
cat <<EOF >"${out_file}"
// Generated file, do not edit.
// To regenerate, run ${cmd}
package report
import (
"fmt"
"time"
"github.com/ugorji/go/codec"
"github.com/weaveworks/ps"
)
EOF
}
function generate_latest_map() {
local out_file="$1"
local data_type="$2"
local uppercase_data_type="${data_type^}"
local lowercase_data_type="${data_type,}"
local entry_type="${lowercase_data_type}LatestEntry"
local latest_map_type="${uppercase_data_type}LatestMap"
local empty_latest_map_variable="empty${latest_map_type}"
local make_function="Make${latest_map_type}"
# shellcheck disable=SC2016
local json_timestamp='`json:"timestamp"`'
# shellcheck disable=SC2016
local json_value='`json:"value"`'
cat <<EOF >>"${out_file}"
type ${entry_type} struct {
Timestamp time.Time ${json_timestamp}
Value ${data_type} ${json_value}
dummySelfer
}
// String returns the StringLatestEntry's string representation.
func (e *${entry_type}) String() string {
return fmt.Sprintf("%v (%s)", e.Value, e.Timestamp.String())
}
// Equal returns true if the supplied StringLatestEntry is equal to this one.
func (e *${entry_type}) Equal(e2 *${entry_type}) bool {
return e.Timestamp.Equal(e2.Timestamp) && e.Value == e2.Value
}
// ${latest_map_type} holds latest ${data_type} instances.
type ${latest_map_type} struct { ps.Map }
var ${empty_latest_map_variable} = ${latest_map_type}{ps.NewMap()}
// ${make_function} makes an empty ${latest_map_type}.
func ${make_function}() ${latest_map_type} {
return ${empty_latest_map_variable}
}
// Size returns the number of elements.
func (m ${latest_map_type}) Size() int {
if m.Map == nil {
return 0
}
return m.Map.Size()
}
// Merge produces a fresh ${latest_map_type} containing the keys from both inputs.
// When both inputs contain the same key, the newer value is used.
func (m ${latest_map_type}) Merge(other ${latest_map_type}) ${latest_map_type} {
output := mergeMaps(m.Map, other.Map, func(a, b interface{}) bool {
return a.(*${entry_type}).Timestamp.Before(b.(*${entry_type}).Timestamp)
})
return ${latest_map_type}{output}
}
// Lookup the value for the given key.
func (m ${latest_map_type}) Lookup(key string) (${data_type}, bool) {
v, _, ok := m.LookupEntry(key)
if !ok {
var zero ${data_type}
return zero, false
}
return v, true
}
// LookupEntry returns the raw entry for the given key.
func (m ${latest_map_type}) LookupEntry(key string) (${data_type}, time.Time, bool) {
if m.Map == nil {
var zero ${data_type}
return zero, time.Time{}, false
}
value, ok := m.Map.Lookup(key)
if !ok {
var zero ${data_type}
return zero, time.Time{}, false
}
e := value.(*${entry_type})
return e.Value, e.Timestamp, true
}
// Set the value for the given key.
func (m ${latest_map_type}) Set(key string, timestamp time.Time, value ${data_type}) ${latest_map_type} {
if m.Map == nil {
m.Map = ps.NewMap()
}
return ${latest_map_type}{m.Map.Set(key, &${entry_type}{Timestamp: timestamp, Value: value})}
}
// ForEach executes fn on each key value pair in the map.
func (m ${latest_map_type}) ForEach(fn func(k string, timestamp time.Time, v ${data_type})) {
if m.Map != nil {
m.Map.ForEach(func(key string, value interface{}) {
fn(key, value.(*${entry_type}).Timestamp, value.(*${entry_type}).Value)
})
}
}
// String returns the ${latest_map_type}'s string representation.
func (m ${latest_map_type}) String() string {
return mapToString(m.Map)
}
// DeepEqual tests equality with other ${latest_map_type}.
func (m ${latest_map_type}) DeepEqual(n ${latest_map_type}) bool {
return mapEqual(m.Map, n.Map, func(val, otherValue interface{}) bool {
return val.(*${entry_type}).Equal(otherValue.(*${entry_type}))
})
}
// CodecEncodeSelf implements codec.Selfer.
func (m *${latest_map_type}) CodecEncodeSelf(encoder *codec.Encoder) {
mapWrite(m.Map, encoder, func(encoder *codec.Encoder, val interface{}) {
val.(*${entry_type}).CodecEncodeSelf(encoder)
})
}
// CodecDecodeSelf implements codec.Selfer.
func (m *${latest_map_type}) CodecDecodeSelf(decoder *codec.Decoder) {
out := mapRead(decoder, func(isNil bool) interface{} {
value := &${entry_type}{}
if !isNil {
value.CodecDecodeSelf(decoder)
}
return value
})
*m = ${latest_map_type}{out}
}
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead.
func (${latest_map_type}) MarshalJSON() ([]byte, error) {
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
}
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead.
func (*${latest_map_type}) UnmarshalJSON(b []byte) error {
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
}
EOF
}
if [ -z "${1}" ]; then
echo "No output file given"
exit 1
fi
out="${1}"
outtmp="${out}.tmp"
generate_header "${outtmp}" "${0} ${*}"
shift
for t in "${@}"; do
generate_latest_map "${outtmp}" "${t}"
done
gofmt -s -w "${outtmp}"
mv "${outtmp}" "${out}"