nex-go/types/map.go

175 lines
4 KiB
Go

package types
import (
"fmt"
"strings"
)
// Map represents a Quazal Rendez-Vous/NEX Map type.
// Type alias of map[K]V.
// There is not an official type in either the rdv or nn::nex namespaces.
// May have any RVType as both a key and value. If either they key or
// value types are not an RVType, they are ignored.
//
// Incompatible with RVType pointers!
type Map[K comparable, V RVType] map[K]V
func (m Map[K, V]) writeType(t any, writable Writable) {
// * This just makes Map.WriteTo() a bit cleaner
// * since it doesn't have to type check
if rvt, ok := t.(interface{ WriteTo(writable Writable) }); ok {
rvt.WriteTo(writable)
}
}
// WriteTo writes the Map to the given writable
func (m Map[K, V]) WriteTo(writable Writable) {
writable.WriteUInt32LE(uint32(len(m)))
for key, value := range m {
m.writeType(key, writable)
m.writeType(value, writable)
}
}
func (m Map[K, V]) extractType(t any, readable Readable) error {
// * This just makes Map.ExtractFrom() a bit cleaner
// * since it doesn't have to type check
if ptr, ok := t.(RVTypePtr); ok {
return ptr.ExtractFrom(readable)
}
// * Maybe support other types..?
return fmt.Errorf("Unsupported Map type %T", t)
}
// ExtractFrom extracts the Map from the given readable
func (m *Map[K, V]) ExtractFrom(readable Readable) error {
length, err := readable.ReadUInt32LE()
if err != nil {
return err
}
extracted := make(Map[K, V])
for i := 0; i < int(length); i++ {
var key K
if err := m.extractType(&key, readable); err != nil {
return err
}
var value V
if err := m.extractType(&value, readable); err != nil {
return err
}
extracted[key] = value
}
*m = extracted
return nil
}
func (m Map[K, V]) copyType(t any) RVType {
// * This just makes Map.Copy() a bit cleaner
// * since it doesn't have to type check
if rvt, ok := t.(RVType); ok {
return rvt.Copy()
}
// TODO - Improve this, this isn't safe
return nil
}
// Copy returns a pointer to a copy of the Map. Requires type assertion when used
func (m Map[K, V]) Copy() RVType {
copied := make(Map[K, V])
for key, value := range m {
copied[m.copyType(key).(K)] = value.Copy().(V)
}
return copied
}
func (m Map[K, V]) typesEqual(t1, t2 any) bool {
// * This just makes Map.Equals() a bit cleaner
// * since it doesn't have to type check
if rvt1, ok := t1.(RVType); ok {
if rvt2, ok := t2.(RVType); ok {
return rvt1.Equals(rvt2)
}
}
return false
}
// Equals checks if the input is equal in value to the current instance
func (m Map[K, V]) Equals(o RVType) bool {
if _, ok := o.(Map[K, V]); !ok {
return false
}
other := o.(Map[K, V])
if len(m) != len(other) {
return false
}
for key, value := range m {
if otherValue, ok := other[key]; !ok || m.typesEqual(&value, &otherValue) {
return false
}
}
return true
}
// CopyRef copies the current value of the Map
// and returns a pointer to the new copy
func (m Map[K, V]) CopyRef() RVTypePtr {
copied := m.Copy().(Map[K, V])
return &copied
}
// Deref takes a pointer to the Map
// and dereferences it to the raw value.
// Only useful when working with an instance of RVTypePtr
func (m *Map[K, V]) Deref() RVType {
return *m
}
// String returns a string representation of the struct
func (m Map[K, V]) String() string {
return m.FormatToString(0)
}
// FormatToString pretty-prints the struct data using the provided indentation level
func (m Map[K, V]) FormatToString(indentationLevel int) string {
indentationValues := strings.Repeat("\t", indentationLevel+1)
indentationEnd := strings.Repeat("\t", indentationLevel)
var b strings.Builder
if len(m) == 0 {
b.WriteString("{}\n")
} else {
b.WriteString("{\n")
for key, value := range m {
// TODO - Special handle the the last item to not add the comma on last item
b.WriteString(fmt.Sprintf("%s%v: %v,\n", indentationValues, key, value))
}
b.WriteString(fmt.Sprintf("%s}\n", indentationEnd))
}
return b.String()
}
// NewMap returns a new Map of the provided type
func NewMap[K comparable, V RVType]() Map[K, V] {
return make(Map[K, V])
}