refactor: use a linked hash map to keep order of file and directory entries, and refactor the json and msgpack marshalling and centralize it further

This commit is contained in:
Derrick Hammer 2024-01-05 03:46:08 -05:00
parent 85320087a4
commit 15030b6866
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
1 changed files with 121 additions and 72 deletions

View File

@ -1,14 +1,21 @@
package metadata package metadata
import ( import (
"encoding/json"
"errors" "errors"
"fmt"
"git.lumeweb.com/LumeWeb/libs5-go/serialize" "git.lumeweb.com/LumeWeb/libs5-go/serialize"
"git.lumeweb.com/LumeWeb/libs5-go/types" "git.lumeweb.com/LumeWeb/libs5-go/types"
"github.com/emirpasic/gods/maps/linkedhashmap"
"github.com/vmihailenco/msgpack/v5" "github.com/vmihailenco/msgpack/v5"
) )
type directoryReferenceMap map[string]DirectoryReference type directoryReferenceMap struct {
type fileReferenceMap map[string]FileReference linkedhashmap.Map
}
type fileReferenceMap struct {
linkedhashmap.Map
}
type DirectoryMetadata struct { type DirectoryMetadata struct {
Details DirectoryMetadataDetails `json:"details"` Details DirectoryMetadataDetails `json:"details"`
@ -21,7 +28,7 @@ type DirectoryMetadata struct {
var _ SerializableMetadata = (*DirectoryMetadata)(nil) var _ SerializableMetadata = (*DirectoryMetadata)(nil)
var _ SerializableMetadata = (*directoryReferenceMap)(nil) var _ SerializableMetadata = (*directoryReferenceMap)(nil)
func NewDirectoryMetadata(details DirectoryMetadataDetails, directories map[string]DirectoryReference, files map[string]FileReference, extraMetadata ExtraMetadata) *DirectoryMetadata { func NewDirectoryMetadata(details DirectoryMetadataDetails, directories directoryReferenceMap, files fileReferenceMap, extraMetadata ExtraMetadata) *DirectoryMetadata {
dirMetadata := &DirectoryMetadata{ dirMetadata := &DirectoryMetadata{
Details: details, Details: details,
Directories: directories, Directories: directories,
@ -95,94 +102,136 @@ func (dm *DirectoryMetadata) DecodeMsgpack(dec *msgpack.Decoder) error {
return nil return nil
} }
func (drm directoryReferenceMap) EncodeMsgpack(enc *msgpack.Encoder) error { func (drm directoryReferenceMap) EncodeMsgpack(enc *msgpack.Encoder) error {
// First, encode the length of the map return marshallMapMsgpack(enc, &drm.Map)
if err := enc.EncodeMapLen(len(drm)); err != nil {
return err
}
// Then, encode each key-value pair
for k, v := range drm {
if err := enc.EncodeString(k); err != nil {
return err
}
if err := enc.Encode(&v); err != nil { // Assuming DirectoryReference can be encoded by msgpack
return err
}
}
return nil
} }
func (drm *directoryReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error { func (drm *directoryReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error {
// Read the map length return unmarshalMapMsgpack(dec, &drm.Map, &DirectoryReference{})
l, err := dec.DecodeMapLen()
if err != nil {
return err
}
// Initialize the map if it's nil
if *drm == nil {
*drm = make(directoryReferenceMap, l)
}
// Decode each key-value pair
for i := 0; i < l; i++ {
key, err := dec.DecodeString()
if err != nil {
return err
}
var value DirectoryReference
if err := dec.Decode(&value); err != nil {
return err
}
(*drm)[key] = value
}
return nil
} }
func (frm fileReferenceMap) EncodeMsgpack(enc *msgpack.Encoder) error { func (frm fileReferenceMap) EncodeMsgpack(enc *msgpack.Encoder) error {
// First, encode the length of the map return marshallMapMsgpack(enc, &frm.Map)
if err := enc.EncodeMapLen(len(frm)); err != nil {
return err
}
// Then, encode each key-value pair
for k, v := range frm {
if err := enc.EncodeString(k); err != nil {
return err
}
if err := enc.Encode(&v); err != nil { // Assuming DirectoryReference can be encoded by msgpack
return err
}
}
return nil
} }
func (drm *fileReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error { func (frm *fileReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error {
// Read the map length return unmarshalMapMsgpack(dec, &frm.Map, &FileReference{})
}
func (frm *fileReferenceMap) UnmarshalJSON(bytes []byte) error {
createFileInstance := func() interface{} { return &FileReference{} }
return unmarshalMapJson(bytes, &frm.Map, createFileInstance)
}
type unmarshalNewInstanceFunc func() interface{}
func (drm *directoryReferenceMap) UnmarshalJSON(bytes []byte) error {
createDirInstance := func() interface{} { return &DirectoryReference{} }
return unmarshalMapJson(bytes, &drm.Map, createDirInstance)
}
func unmarshalMapMsgpack(dec *msgpack.Decoder, m *linkedhashmap.Map, placeholder interface{}) error {
*m = *linkedhashmap.New()
l, err := dec.DecodeMapLen() l, err := dec.DecodeMapLen()
if err != nil { if err != nil {
return err return err
} }
// Initialize the map if it's nil
if *drm == nil {
*drm = make(fileReferenceMap, l)
}
// Decode each key-value pair
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
key, err := dec.DecodeString() key, err := dec.DecodeString()
if err != nil { if err != nil {
return err return err
} }
var value FileReference
if err := dec.Decode(&value); err != nil { fmt.Println("dir: ", key)
return err
switch placeholder.(type) {
case *DirectoryReference:
var value DirectoryReference
if err := dec.Decode(&value); err != nil {
return err
}
m.Put(key, value)
case *FileReference:
var file FileReference
if err := dec.Decode(&file); err != nil {
return err
}
m.Put(key, file)
default:
return fmt.Errorf("unsupported type for decoding")
}
}
return nil
}
func marshallMapMsgpack(enc *msgpack.Encoder, m *linkedhashmap.Map) error {
// First, encode the length of the map
if err := enc.EncodeMapLen(m.Size()); err != nil {
return err
}
iter := m.Iterator()
for iter.Next() {
key := iter.Key().(string)
if err := enc.EncodeString(key); err != nil {
return err
}
value := iter.Value()
switch v := value.(type) {
case FileReference:
if err := enc.Encode(&v); err != nil {
return err
}
case DirectoryReference:
if err := enc.Encode(&v); err != nil {
return err
}
default:
return fmt.Errorf("unsupported type for encoding")
}
}
return nil
}
func unmarshalMapJson(bytes []byte, m *linkedhashmap.Map, newInstance unmarshalNewInstanceFunc) error {
*m = *linkedhashmap.New()
err := m.FromJSON(bytes)
if err != nil {
return err
}
iter := m.Iterator()
for iter.Next() {
key := iter.Key()
val := iter.Value()
instance := newInstance()
data, err := json.Marshal(val)
if err != nil {
return err
}
err = json.Unmarshal(data, &instance)
if err != nil {
return err
}
// Type switch to handle different types
switch v := instance.(type) {
case *DirectoryReference:
m.Put(key, *v)
case *FileReference:
m.Put(key, *v)
default:
return fmt.Errorf("unhandled type: %T", v)
} }
(*drm)[key] = value
} }
return nil return nil