diff --git a/metadata/directory.go b/metadata/directory.go index 867f945..eb6f0c7 100644 --- a/metadata/directory.go +++ b/metadata/directory.go @@ -1,14 +1,21 @@ package metadata import ( + "encoding/json" "errors" + "fmt" "git.lumeweb.com/LumeWeb/libs5-go/serialize" "git.lumeweb.com/LumeWeb/libs5-go/types" + "github.com/emirpasic/gods/maps/linkedhashmap" "github.com/vmihailenco/msgpack/v5" ) -type directoryReferenceMap map[string]DirectoryReference -type fileReferenceMap map[string]FileReference +type directoryReferenceMap struct { + linkedhashmap.Map +} +type fileReferenceMap struct { + linkedhashmap.Map +} type DirectoryMetadata struct { Details DirectoryMetadataDetails `json:"details"` @@ -21,7 +28,7 @@ type DirectoryMetadata struct { var _ SerializableMetadata = (*DirectoryMetadata)(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{ Details: details, Directories: directories, @@ -95,94 +102,136 @@ func (dm *DirectoryMetadata) DecodeMsgpack(dec *msgpack.Decoder) error { return nil } func (drm directoryReferenceMap) EncodeMsgpack(enc *msgpack.Encoder) error { - // First, encode the length of the 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 + return marshallMapMsgpack(enc, &drm.Map) } func (drm *directoryReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error { - // Read the map length - 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 + return unmarshalMapMsgpack(dec, &drm.Map, &DirectoryReference{}) } func (frm fileReferenceMap) EncodeMsgpack(enc *msgpack.Encoder) error { - // First, encode the length of the 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 + return marshallMapMsgpack(enc, &frm.Map) } -func (drm *fileReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error { - // Read the map length +func (frm *fileReferenceMap) DecodeMsgpack(dec *msgpack.Decoder) error { + 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() if err != nil { 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++ { key, err := dec.DecodeString() if err != nil { return err } - var value FileReference - if err := dec.Decode(&value); err != nil { - return err + + fmt.Println("dir: ", key) + + 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