feat: wip directory metadata

This commit is contained in:
Derrick Hammer 2024-01-04 08:20:37 -05:00
parent eed785e1eb
commit c2ed126ab8
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
12 changed files with 1458 additions and 0 deletions

92
metadata/directory.go Normal file
View File

@ -0,0 +1,92 @@
package metadata
import (
"errors"
"git.lumeweb.com/LumeWeb/libs5-go/serialize"
"git.lumeweb.com/LumeWeb/libs5-go/types"
"github.com/vmihailenco/msgpack/v5"
)
type DirectoryMetadata struct {
Details DirectoryMetadataDetails `json:"details"`
Directories map[string]DirectoryReference `json:"directories"`
Files map[string]FileReference `json:"files"`
ExtraMetadata ExtraMetadata `json:"extraMetadata"`
BaseMetadata
}
var _ SerializableMetadata = (*DirectoryMetadata)(nil)
func NewDirectoryMetadata(details DirectoryMetadataDetails, directories map[string]DirectoryReference, files map[string]FileReference, extraMetadata ExtraMetadata) *DirectoryMetadata {
dirMetadata := &DirectoryMetadata{
Details: details,
Directories: directories,
Files: files,
ExtraMetadata: extraMetadata,
}
dirMetadata.Type = "directory"
return dirMetadata
}
func (dm *DirectoryMetadata) EncodeMsgpack(enc *msgpack.Encoder) error {
err := serialize.InitMarshaller(types.MetadataTypeDirectory, enc)
if err != nil {
return err
}
items := make([]interface{}, 4)
items[0] = dm.Details
items[1] = dm.Directories
items[2] = dm.Files
items[3] = dm.ExtraMetadata.Data
return enc.Encode(items)
}
func (dm *DirectoryMetadata) DecodeMsgpack(dec *msgpack.Decoder) error {
err := serialize.InitUnmarshaller(types.MetadataTypeDirectory, dec)
if err != nil {
return err
}
val, err := dec.DecodeArrayLen()
if err != nil {
return err
}
if val != 4 {
return errors.New(" Corrupted metadata")
}
for i := 0; i < val; i++ {
switch i {
case 0:
err = dec.Decode(&dm.Details)
if err != nil {
return err
}
case 1:
err = dec.Decode(&dm.Directories)
if err != nil {
return err
}
case 2:
err = dec.Decode(&dm.Files)
if err != nil {
return err
}
case 3:
intMap, err := decodeIntMap(dec)
if err != nil {
return err
}
dm.ExtraMetadata.Data = intMap
}
}
dm.Type = "directory"
return nil
}

View File

@ -0,0 +1,75 @@
package metadata
import "github.com/vmihailenco/msgpack/v5"
type DirectoryMetadataDetails struct {
Data map[int]interface{}
}
func NewDirectoryMetadataDetails(data map[int]interface{}) *DirectoryMetadataDetails {
return &DirectoryMetadataDetails{
Data: data,
}
}
func (dmd *DirectoryMetadataDetails) IsShared() bool {
_, exists := dmd.Data[3]
return exists
}
func (dmd *DirectoryMetadataDetails) IsSharedReadOnly() bool {
value, exists := dmd.Data[3].([]interface{})
if !exists {
return false
}
return len(value) > 1 && value[1] == true
}
func (dmd *DirectoryMetadataDetails) IsSharedReadWrite() bool {
value, exists := dmd.Data[3].([]interface{})
if !exists {
return false
}
return len(value) > 2 && value[2] == true
}
func (dmd *DirectoryMetadataDetails) SetShared(value bool, write bool) {
if dmd.Data == nil {
dmd.Data = make(map[int]interface{})
}
sharedValue, exists := dmd.Data[3].([]interface{})
if !exists {
sharedValue = make([]interface{}, 3)
dmd.Data[3] = sharedValue
}
if write {
sharedValue[2] = value
} else {
sharedValue[1] = value
}
}
func (dmd *DirectoryMetadataDetails) DecodeMsgpack(dec *msgpack.Decoder) error {
mapLen, err := dec.DecodeMapLen()
if err != nil {
return err
}
for i := 0; i < mapLen; i++ {
key, err := dec.DecodeInt8()
if err != nil {
return err
}
value, err := dec.DecodeInterface()
if err != nil {
return err
}
dmd.Data[int(key)] = value
}
return nil
}
func (dmd DirectoryMetadataDetails) EncodeMsgpack(enc *msgpack.Encoder) error {
return enc.Encode(dmd.Data)
}

View File

@ -0,0 +1,87 @@
package metadata
import "github.com/vmihailenco/msgpack/v5"
var _ SerializableMetadata = (*DirectoryReference)(nil)
type DirectoryReference struct {
Created uint64 `json:"created"`
Name string `json:"name"`
EncryptedWriteKey Base64UrlBinary `json:"encryptedWriteKey,string"`
PublicKey Base64UrlBinary `json:"publicKey,string"`
EncryptionKey Base64UrlBinary `json:"encryptionKey,string"`
Ext map[string]interface{} `json:"ext"`
URI string `json:"uri"`
Key string `json:"key"`
Size int64 `json:"size"`
}
func NewDirectoryReference(created uint64, name string, encryptedWriteKey, publicKey, encryptionKey []byte, ext map[string]interface{}) *DirectoryReference {
return &DirectoryReference{
Created: created,
Name: name,
EncryptedWriteKey: encryptedWriteKey,
PublicKey: publicKey,
EncryptionKey: encryptionKey,
Ext: ext,
URI: "",
Key: "",
Size: 0,
}
}
func (dr *DirectoryReference) EncodeMsgpack(enc *msgpack.Encoder) error {
data := map[int]interface{}{
1: dr.Name,
2: dr.Created,
3: dr.PublicKey,
4: dr.EncryptedWriteKey,
}
if dr.EncryptionKey != nil {
data[5] = dr.EncryptionKey
}
if dr.Ext != nil {
data[6] = dr.Ext
}
return enc.Encode(data)
}
func (dr *DirectoryReference) DecodeMsgpack(dec *msgpack.Decoder) error {
var (
err error
l int
)
if l, err = dec.DecodeMapLen(); err != nil {
return err
}
for i := 0; i < l; i++ {
key, err := dec.DecodeInt8()
if err != nil {
return err
}
value, err := dec.DecodeInterface()
if err != nil {
return err
}
switch key {
case int8(1):
dr.Name = value.(string)
case int8(2):
dr.Created = value.(uint64)
case int8(3):
dr.PublicKey = value.([]byte)
case int8(4):
dr.EncryptedWriteKey = value.([]byte)
case int8(5):
dr.EncryptionKey = value.([]byte)
case int8(6):
dr.Ext = value.(map[string]interface{})
}
}
return nil
}

514
metadata/directory_test.go Normal file
View File

@ -0,0 +1,514 @@
package metadata
import (
"encoding/json"
"fmt"
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
cmp "github.com/LumeWeb/go-cmp"
"github.com/vmihailenco/msgpack/v5"
"os"
"path/filepath"
"reflect"
"testing"
)
func readFile(filename string) []byte {
filePath := filepath.Join("testdata", filename)
data, err := os.ReadFile(filePath)
if err != nil {
panic(err)
}
return data
}
func getDirectoryMeta() *DirectoryMetadata {
data := readFile("directory.json")
var dir DirectoryMetadata
err := json.Unmarshal(data, &dir)
if err != nil {
panic(err)
}
return &dir
}
func TestDirectoryMetadata_DecodeMsgpack(t *testing.T) {
type fields struct {
Details DirectoryMetadataDetails
Directories map[string]DirectoryReference
Files map[string]FileReference
ExtraMetadata ExtraMetadata
}
tests := []struct {
name string
wantErr bool
}{
{
name: "Decode",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
jsonDm := getDirectoryMeta()
dm := &DirectoryMetadata{}
if err := msgpack.Unmarshal(readFile("directory.bin"), dm); (err != nil) != tt.wantErr {
t.Errorf("DecodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
fmt.Println(cmp.Diff(jsonDm, dm))
if !cmp.Equal(jsonDm, dm) {
t.Errorf("DecodeMsgpack() error = %v, wantErr %v", "msgpack does not match json", tt.wantErr)
}
})
}
}
func TestDirectoryMetadata_EncodeMsgpack(t *testing.T) {
type fields struct {
Details DirectoryMetadataDetails
Directories map[string]DirectoryReference
Files map[string]FileReference
ExtraMetadata ExtraMetadata
}
type args struct {
enc *msgpack.Encoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dm := &DirectoryMetadata{
Details: tt.fields.Details,
Directories: tt.fields.Directories,
Files: tt.fields.Files,
ExtraMetadata: tt.fields.ExtraMetadata,
}
if err := dm.EncodeMsgpack(tt.args.enc); (err != nil) != tt.wantErr {
t.Errorf("EncodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestDirectoryReference_DecodeMsgpack(t *testing.T) {
type fields struct {
Created uint64
Name string
EncryptedWriteKey []byte
PublicKey []byte
EncryptionKey []byte
Ext map[string]interface{}
URI string
Key string
Size int64
}
type args struct {
dec *msgpack.Decoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dr := &DirectoryReference{
Created: tt.fields.Created,
Name: tt.fields.Name,
EncryptedWriteKey: tt.fields.EncryptedWriteKey,
PublicKey: tt.fields.PublicKey,
EncryptionKey: tt.fields.EncryptionKey,
Ext: tt.fields.Ext,
URI: tt.fields.URI,
Key: tt.fields.Key,
Size: tt.fields.Size,
}
if err := dr.DecodeMsgpack(tt.args.dec); (err != nil) != tt.wantErr {
t.Errorf("DecodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestDirectoryReference_EncodeMsgpack(t *testing.T) {
type fields struct {
Created uint64
Name string
EncryptedWriteKey []byte
PublicKey []byte
EncryptionKey []byte
Ext map[string]interface{}
URI string
Key string
Size int64
}
type args struct {
enc *msgpack.Encoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dr := &DirectoryReference{
Created: tt.fields.Created,
Name: tt.fields.Name,
EncryptedWriteKey: tt.fields.EncryptedWriteKey,
PublicKey: tt.fields.PublicKey,
EncryptionKey: tt.fields.EncryptionKey,
Ext: tt.fields.Ext,
URI: tt.fields.URI,
Key: tt.fields.Key,
Size: tt.fields.Size,
}
if err := dr.EncodeMsgpack(tt.args.enc); (err != nil) != tt.wantErr {
t.Errorf("EncodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFileReference_DecodeMsgpack(t *testing.T) {
type fields struct {
Name string
Created int
Version int
File *FileVersion
Ext map[string]interface{}
History map[int]*FileVersion
MimeType string
URI string
Key string
}
type args struct {
dec *msgpack.Decoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fr := &FileReference{
Name: tt.fields.Name,
Created: tt.fields.Created,
Version: tt.fields.Version,
File: tt.fields.File,
Ext: tt.fields.Ext,
History: tt.fields.History,
MimeType: tt.fields.MimeType,
URI: tt.fields.URI,
Key: tt.fields.Key,
}
if err := fr.DecodeMsgpack(tt.args.dec); (err != nil) != tt.wantErr {
t.Errorf("DecodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFileReference_EncodeMsgpack(t *testing.T) {
type fields struct {
Name string
Created int
Version int
File *FileVersion
Ext map[string]interface{}
History map[int]*FileVersion
MimeType string
URI string
Key string
}
type args struct {
enc *msgpack.Encoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fr := &FileReference{
Name: tt.fields.Name,
Created: tt.fields.Created,
Version: tt.fields.Version,
File: tt.fields.File,
Ext: tt.fields.Ext,
History: tt.fields.History,
MimeType: tt.fields.MimeType,
URI: tt.fields.URI,
Key: tt.fields.Key,
}
if err := fr.EncodeMsgpack(tt.args.enc); (err != nil) != tt.wantErr {
t.Errorf("EncodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFileReference_Modified(t *testing.T) {
type fields struct {
Name string
Created int
Version int
File *FileVersion
Ext map[string]interface{}
History map[int]*FileVersion
MimeType string
URI string
Key string
}
tests := []struct {
name string
fields fields
want int
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fr := &FileReference{
Name: tt.fields.Name,
Created: tt.fields.Created,
Version: tt.fields.Version,
File: tt.fields.File,
Ext: tt.fields.Ext,
History: tt.fields.History,
MimeType: tt.fields.MimeType,
URI: tt.fields.URI,
Key: tt.fields.Key,
}
if got := fr.Modified(); got != tt.want {
t.Errorf("Modified() = %v, want %v", got, tt.want)
}
})
}
}
func TestFileVersionThumbnail_DecodeMsgpack(t *testing.T) {
type fields struct {
ImageType string
AspectRatio float64
CID *encoding.EncryptedCID
Thumbhash []byte
}
type args struct {
dec *msgpack.Decoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fvt := &FileVersionThumbnail{
ImageType: tt.fields.ImageType,
AspectRatio: tt.fields.AspectRatio,
CID: tt.fields.CID,
Thumbhash: tt.fields.Thumbhash,
}
if err := fvt.DecodeMsgpack(tt.args.dec); (err != nil) != tt.wantErr {
t.Errorf("DecodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFileVersionThumbnail_Encode(t *testing.T) {
type fields struct {
ImageType string
AspectRatio float64
CID *encoding.EncryptedCID
Thumbhash []byte
}
tests := []struct {
name string
fields fields
want map[int]interface{}
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fvt := &FileVersionThumbnail{
ImageType: tt.fields.ImageType,
AspectRatio: tt.fields.AspectRatio,
CID: tt.fields.CID,
Thumbhash: tt.fields.Thumbhash,
}
if got := fvt.Encode(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Encode() = %v, want %v", got, tt.want)
}
})
}
}
func TestFileVersionThumbnail_EncodeMsgpack(t *testing.T) {
type fields struct {
ImageType string
AspectRatio float64
CID *encoding.EncryptedCID
Thumbhash []byte
}
type args struct {
enc *msgpack.Encoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fvt := &FileVersionThumbnail{
ImageType: tt.fields.ImageType,
AspectRatio: tt.fields.AspectRatio,
CID: tt.fields.CID,
Thumbhash: tt.fields.Thumbhash,
}
if err := fvt.EncodeMsgpack(tt.args.enc); (err != nil) != tt.wantErr {
t.Errorf("EncodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFileVersion_CID(t *testing.T) {
type fields struct {
Ts int
EncryptedCID *encoding.EncryptedCID
PlaintextCID *encoding.CID
Thumbnail *FileVersionThumbnail
Hashes []*encoding.Multihash
Ext map[string]interface{}
}
tests := []struct {
name string
fields fields
want *encoding.CID
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fv := &FileVersion{
Ts: tt.fields.Ts,
EncryptedCID: tt.fields.EncryptedCID,
PlaintextCID: tt.fields.PlaintextCID,
Thumbnail: tt.fields.Thumbnail,
Hashes: tt.fields.Hashes,
Ext: tt.fields.Ext,
}
if got := fv.CID(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("CID() = %v, want %v", got, tt.want)
}
})
}
}
func TestFileVersion_DecodeMsgpack(t *testing.T) {
type fields struct {
Ts int
EncryptedCID *encoding.EncryptedCID
PlaintextCID *encoding.CID
Thumbnail *FileVersionThumbnail
Hashes []*encoding.Multihash
Ext map[string]interface{}
}
type args struct {
dec *msgpack.Decoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fv := &FileVersion{
Ts: tt.fields.Ts,
EncryptedCID: tt.fields.EncryptedCID,
PlaintextCID: tt.fields.PlaintextCID,
Thumbnail: tt.fields.Thumbnail,
Hashes: tt.fields.Hashes,
Ext: tt.fields.Ext,
}
if err := fv.DecodeMsgpack(tt.args.dec); (err != nil) != tt.wantErr {
t.Errorf("DecodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFileVersion_EncodeMsgpack(t *testing.T) {
type fields struct {
Ts int
EncryptedCID *encoding.EncryptedCID
PlaintextCID *encoding.CID
Thumbnail *FileVersionThumbnail
Hashes []*encoding.Multihash
Ext map[string]interface{}
}
type args struct {
enc *msgpack.Encoder
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fv := &FileVersion{
Ts: tt.fields.Ts,
EncryptedCID: tt.fields.EncryptedCID,
PlaintextCID: tt.fields.PlaintextCID,
Thumbnail: tt.fields.Thumbnail,
Hashes: tt.fields.Hashes,
Ext: tt.fields.Ext,
}
if err := fv.EncodeMsgpack(tt.args.enc); (err != nil) != tt.wantErr {
t.Errorf("EncodeMsgpack() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

86
metadata/extra.go Normal file
View File

@ -0,0 +1,86 @@
package metadata
import (
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
"git.lumeweb.com/LumeWeb/libs5-go/types"
"github.com/vmihailenco/msgpack/v5"
)
type ExtraMetadata struct {
Data map[int]interface{}
}
func NewExtraMetadata(data map[int]interface{}) *ExtraMetadata {
return &ExtraMetadata{
Data: data,
}
}
func (em *ExtraMetadata) ToJSON() map[string]interface{} {
jsonObject := make(map[string]interface{})
names := map[types.MetadataExtension]string{
types.MetadataExtensionLicenses: "licenses",
types.MetadataExtensionDonationKeys: "donationKeys",
types.MetadataExtensionWikidataClaims: "wikidataClaims",
types.MetadataExtensionLanguages: "languages",
types.MetadataExtensionSourceUris: "sourceUris",
types.MetadataExtensionPreviousVersions: "previousVersions",
types.MetadataExtensionTimestamp: "timestamp",
types.MetadataExtensionOriginalTimestamp: "originalTimestamp",
types.MetadataExtensionTags: "tags",
types.MetadataExtensionCategories: "categories",
types.MetadataExtensionBasicMediaMetadata: "basicMediaMetadata",
types.MetadataExtensionViewTypes: "viewTypes",
types.MetadataExtensionBridge: "bridge",
types.MetadataExtensionRoutingHints: "routingHints",
}
for key, value := range em.Data {
name, ok := names[types.MetadataExtension(key)]
if ok {
if types.MetadataExtension(key) == types.MetadataExtensionUpdateCID {
cid, err := encoding.CIDFromBytes(value.([]byte))
var cidString string
if err == nil {
cidString, err = cid.ToString()
}
if err == nil {
jsonObject["updateCID"] = cidString
} else {
jsonObject["updateCID"] = ""
}
} else {
jsonObject[name] = value
}
}
}
return jsonObject
}
func (em *ExtraMetadata) DecodeMsgpack(dec *msgpack.Decoder) error {
mapLen, err := dec.DecodeMapLen()
if err != nil {
return err
}
for i := 0; i < mapLen; i++ {
key, err := dec.DecodeInt8()
if err != nil {
return err
}
value, err := dec.DecodeInterface()
if err != nil {
return err
}
em.Data[int(key)] = value
}
return nil
}
func (em ExtraMetadata) EncodeMsgpack(enc *msgpack.Encoder) error {
return enc.Encode(em.Data)
}

131
metadata/file_reference.go Normal file
View File

@ -0,0 +1,131 @@
package metadata
import "github.com/vmihailenco/msgpack/v5"
var _ SerializableMetadata = (*FileReference)(nil)
type FileReference struct {
Name string `json:"name"`
Created int `json:"created"`
Version int `json:"version"`
File *FileVersion `json:"file"`
Ext map[string]interface{} `json:"ext"`
History map[int]*FileVersion `json:"history"`
MimeType string `json:"mimeType"`
URI string `json:"uri"`
Key string `json:"key"`
}
func NewFileReference(name string, created, version int, file *FileVersion, ext map[string]interface{}, history map[int]*FileVersion, mimeType string) *FileReference {
return &FileReference{
Name: name,
Created: created,
Version: version,
File: file,
Ext: ext,
History: history,
MimeType: mimeType,
URI: "",
Key: "",
}
}
func (fr *FileReference) Modified() int {
return fr.File.Ts
}
func (fr *FileReference) EncodeMsgpack(enc *msgpack.Encoder) error {
data := map[int]interface{}{
1: fr.Name,
2: fr.Created,
4: fr.File,
5: fr.Version,
}
if fr.MimeType != "" {
data[6] = fr.MimeType
}
if fr.Ext != nil {
data[7] = fr.Ext
}
if fr.History != nil {
historyData := make(map[int]interface{})
for key, value := range fr.History {
historyData[key] = value
}
data[8] = historyData
}
return nil
}
func (fr *FileReference) DecodeMsgpack(dec *msgpack.Decoder) error {
mapLen, err := dec.DecodeMapLen()
if err != nil {
return err
}
for i := 0; i < mapLen; i++ {
key, err := dec.DecodeInt8()
if err != nil {
return err
}
switch key {
case int8(1):
err := dec.Decode(&fr.Name)
if err != nil {
return err
}
case int8(2):
err := dec.Decode(&fr.Created)
if err != nil {
return err
}
case int8(4):
err := dec.Decode(&fr.File)
if err != nil {
return err
}
case int8(5):
val, err := dec.DecodeInt()
if err != nil {
return err
}
fr.Version = val
case int8(6):
err := dec.Decode(&fr.MimeType)
if err != nil {
return err
}
case int8(7):
err := dec.Decode(&fr.Ext)
if err != nil {
return err
}
case int8(8):
historyDataLen, err := dec.DecodeMapLen()
if err != nil {
return err
}
fr.History = make(map[int]*FileVersion, historyDataLen)
for range fr.History {
k, err := dec.DecodeInt()
if err != nil {
return err
}
var fileVersion FileVersion
err = dec.Decode(&fileVersion)
if err != nil {
return err
}
fr.History[k] = &fileVersion
}
}
}
return nil
}

114
metadata/file_version.go Normal file
View File

@ -0,0 +1,114 @@
package metadata
import (
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
"github.com/vmihailenco/msgpack/v5"
)
type FileVersion struct {
Ts int `json:"ts"`
EncryptedCID *encoding.EncryptedCID `json:"encryptedCID,string"`
PlaintextCID *encoding.CID `json:"plaintextCID,string"`
Thumbnail *FileVersionThumbnail `json:"thumbnail"`
Hashes []*encoding.Multihash `json:"hashes"`
Ext map[string]interface{} `json:"ext"`
}
func NewFileVersion(ts int, encryptedCID *encoding.EncryptedCID, plaintextCID *encoding.CID, thumbnail *FileVersionThumbnail, hashes []*encoding.Multihash, ext map[string]interface{}) *FileVersion {
return &FileVersion{
Ts: ts,
EncryptedCID: encryptedCID,
PlaintextCID: plaintextCID,
Thumbnail: thumbnail,
Hashes: hashes,
Ext: ext,
}
}
func (fv *FileVersion) EncodeMsgpack(enc *msgpack.Encoder) error {
data := map[int]interface{}{
8: fv.Ts,
}
if fv.EncryptedCID != nil {
data[1] = fv.EncryptedCID.ToBytes()
}
if fv.PlaintextCID != nil {
data[2] = fv.PlaintextCID.ToBytes()
}
if len(fv.Hashes) > 0 {
hashesData := make([][]byte, len(fv.Hashes))
for i, hash := range fv.Hashes {
hashesData[i] = hash.FullBytes
}
data[9] = hashesData
}
if fv.Thumbnail != nil {
data[10] = fv.Thumbnail.Encode()
}
return enc.Encode(data)
}
func (fv *FileVersion) DecodeMsgpack(dec *msgpack.Decoder) error {
mapLen, err := dec.DecodeMapLen()
if err != nil {
return err
}
for i := 0; i < mapLen; i++ {
key, err := dec.DecodeInt8()
if err != nil {
return err
}
switch key {
case int8(1):
err := dec.Decode(&fv.EncryptedCID)
if err != nil {
return err
}
case int8(2):
err := dec.Decode(&fv.PlaintextCID)
if err != nil {
return err
}
case int8(8):
err := dec.Decode(&fv.Ts)
if err != nil {
return err
}
case int8(9):
hashesData, err := dec.DecodeSlice()
if err != nil {
return err
}
fv.Hashes = make([]*encoding.Multihash, len(hashesData))
for i, hashData := range hashesData {
hashBytes := hashData.([]byte)
fv.Hashes[i] = encoding.NewMultihash(hashBytes)
}
case int8(10):
err := dec.Decode(&fv.Thumbnail)
if err != nil {
return err
}
}
}
return nil
}
func (fv *FileVersion) CID() *encoding.CID {
if fv.PlaintextCID != nil {
return fv.PlaintextCID
}
return &fv.EncryptedCID.OriginalCID
}

View File

@ -0,0 +1,97 @@
package metadata
import (
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
"github.com/vmihailenco/msgpack/v5"
)
type FileVersionThumbnail struct {
ImageType string
AspectRatio float64
CID *encoding.EncryptedCID
Thumbhash []byte
}
func NewFileVersionThumbnail(imageType string, aspectRatio float64, cid *encoding.EncryptedCID, thumbhash []byte) *FileVersionThumbnail {
return &FileVersionThumbnail{
ImageType: imageType,
AspectRatio: aspectRatio,
CID: cid,
Thumbhash: thumbhash,
}
}
func (fvt *FileVersionThumbnail) EncodeMsgpack(enc *msgpack.Encoder) error {
data := map[int]interface{}{
2: fvt.AspectRatio,
3: fvt.CID.ToBytes(),
}
if fvt.ImageType != "" {
data[1] = fvt.ImageType
}
if fvt.Thumbhash != nil {
data[4] = fvt.Thumbhash
}
return enc.Encode(data)
}
func (fvt *FileVersionThumbnail) DecodeMsgpack(dec *msgpack.Decoder) error {
mapLen, err := dec.DecodeMapLen()
if err != nil {
return err
}
for i := 0; i < mapLen; i++ {
key, err := dec.DecodeInt8()
if err != nil {
return err
}
switch key {
case int8(1):
err := dec.Decode(&fvt.ImageType)
if err != nil {
return err
}
case int8(2):
err := dec.Decode(&fvt.AspectRatio)
if err != nil {
return err
}
case int8(3):
val, err := dec.DecodeBytes()
if err != nil {
return err
}
fvt.CID, err = encoding.EncryptedCIDFromBytes(val)
if err != nil {
return err
}
case int8(4):
err := dec.Decode(&fvt.Thumbhash)
if err != nil {
return err
}
}
}
return nil
}
func (fvt *FileVersionThumbnail) Encode() map[int]interface{} {
data := map[int]interface{}{
2: fvt.AspectRatio,
3: fvt.CID.ToBytes(),
}
if fvt.ImageType != "" {
data[1] = fvt.ImageType
}
if fvt.Thumbhash != nil {
data[4] = fvt.Thumbhash
}
return data
}

14
metadata/meta.go Normal file
View File

@ -0,0 +1,14 @@
package metadata
import "github.com/vmihailenco/msgpack/v5"
type Metadata interface {
}
type SerializableMetadata interface {
msgpack.CustomEncoder
msgpack.CustomDecoder
}
type BaseMetadata struct {
Type string `json:"type"`
}

55
metadata/misc.go Normal file
View File

@ -0,0 +1,55 @@
package metadata
import (
"encoding/base64"
"github.com/vmihailenco/msgpack/v5"
)
type Base64UrlBinary []byte
func (b *Base64UrlBinary) UnmarshalJSON(data []byte) error {
strData := string(data)
if len(strData) >= 2 && strData[0] == '"' && strData[len(strData)-1] == '"' {
strData = strData[1 : len(strData)-1]
}
if strData == "null" {
return nil
}
decodedData, err := base64.RawURLEncoding.DecodeString(strData)
if err != nil {
return err
}
*b = (decodedData)
return nil
}
func (b Base64UrlBinary) MarshalJSON() ([]byte, error) {
return []byte(base64.RawURLEncoding.EncodeToString(b)), nil
}
func decodeIntMap(dec *msgpack.Decoder) (map[int]interface{}, error) {
mapLen, err := dec.DecodeMapLen()
if err != nil {
return nil, err
}
data := make(map[int]interface{}, mapLen)
for i := 0; i < mapLen; i++ {
key, err := dec.DecodeInt()
if err != nil {
return nil, err
}
value, err := dec.DecodeInterface()
if err != nil {
return nil, err
}
data[key] = value
}
return data, nil
}

BIN
metadata/testdata/directory.bin vendored Normal file

Binary file not shown.

193
metadata/testdata/directory.json vendored Normal file
View File

@ -0,0 +1,193 @@
{
"type": "directory",
"details": {},
"directories": {
"arch": {
"name": "arch",
"created": 1704127624979,
"publicKey": "7Vd1kAf0LoYmKKQ9-C8Znl0npyjK2M4-ciiNWShOTpbD",
"encryptedWriteKey": "",
"encryptionKey": null,
"ext": null
}
},
"files": {
"archlinux-bootstrap-x86_64.tar.gz": {
"name": "archlinux-bootstrap-x86_64.tar.gz",
"created": 1704127689514,
"modified": 1704127689514,
"version": 0,
"mimeType": null,
"file": {
"ts": 1704127689514,
"encryptedCID": null,
"cid": "z6e5xDpewiueHz6n9HJz21uGmTDvvctawujzYSeu9773k8s9RkG8a",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-bootstrap-x86_64.tar.gz.sig": {
"name": "archlinux-bootstrap-x86_64.tar.gz.sig",
"created": 1704127698951,
"modified": 1704127698951,
"version": 0,
"mimeType": "application/pgp-signature",
"file": {
"ts": 1704127698951,
"encryptedCID": null,
"cid": "z4odYNo3uQhHhypXze7ThLAKrnSGsJ3cRf1bYsvnt65Mqi9gp",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-x86_64.iso": {
"name": "archlinux-x86_64.iso",
"created": 1704127623412,
"modified": 1704127623412,
"version": 0,
"mimeType": "application/x-iso9660-image",
"file": {
"ts": 1704127623412,
"encryptedCID": null,
"cid": "z6e5sRB3BeynWLyrzRPLWFetUS7PNDZWZHURnFAyikcRyGTnxjMwC",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-x86_64.iso.sig": {
"name": "archlinux-x86_64.iso.sig",
"created": 1704127698648,
"modified": 1704127698648,
"version": 0,
"mimeType": "application/pgp-signature",
"file": {
"ts": 1704127698648,
"encryptedCID": null,
"cid": "z4odLKvhW8QPH4dLNqVzU1Zq6nj1MBdU6HYpJmThdzag4RJma",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-2024.01.01-x86_64.iso": {
"name": "archlinux-2024.01.01-x86_64.iso",
"created": 1704127623412,
"modified": 1704127623412,
"version": 0,
"mimeType": "application/x-iso9660-image",
"file": {
"ts": 1704127623412,
"encryptedCID": null,
"cid": "z6e5sRB3BeynWLyrzRPLWFetUS7PNDZWZHURnFAyikcRyGTnxjMwC",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-2024.01.01-x86_64.iso.sig": {
"name": "archlinux-2024.01.01-x86_64.iso.sig",
"created": 1704127698648,
"modified": 1704127698648,
"version": 0,
"mimeType": "application/pgp-signature",
"file": {
"ts": 1704127698648,
"encryptedCID": null,
"cid": "z4odLKvhW8QPH4dLNqVzU1Zq6nj1MBdU6HYpJmThdzag4RJma",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-2024.01.01-x86_64.iso.torrent": {
"name": "archlinux-2024.01.01-x86_64.iso.torrent",
"created": 1704127703578,
"modified": 1704127703578,
"version": 0,
"mimeType": "application/x-bittorrent",
"file": {
"ts": 1704127703578,
"encryptedCID": null,
"cid": "zHnmSZc3qDZMGfhNaZ6iVjkbxeVzMonKtAnTmBtRSwXy7RLYQK",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-bootstrap-2024.01.01-x86_64.tar.gz": {
"name": "archlinux-bootstrap-2024.01.01-x86_64.tar.gz",
"created": 1704127689514,
"modified": 1704127689514,
"version": 0,
"mimeType": null,
"file": {
"ts": 1704127689514,
"encryptedCID": null,
"cid": "z6e5xDpewiueHz6n9HJz21uGmTDvvctawujzYSeu9773k8s9RkG8a",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"archlinux-bootstrap-2024.01.01-x86_64.tar.gz.sig": {
"name": "archlinux-bootstrap-2024.01.01-x86_64.tar.gz.sig",
"created": 1704127698951,
"modified": 1704127698951,
"version": 0,
"mimeType": "application/pgp-signature",
"file": {
"ts": 1704127698951,
"encryptedCID": null,
"cid": "z4odYNo3uQhHhypXze7ThLAKrnSGsJ3cRf1bYsvnt65Mqi9gp",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"b2sums.txt": {
"name": "b2sums.txt",
"created": 1704127703594,
"modified": 1704127703594,
"version": 0,
"mimeType": "text/plain",
"file": {
"ts": 1704127703594,
"encryptedCID": null,
"cid": "zHnoyywarEZHWrSMZjL55nXeMAPsYi1mhXQs7ZmWqkpSg5Auz1",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
},
"sha256sums.txt": {
"name": "sha256sums.txt",
"created": 1704127703591,
"modified": 1704127703591,
"version": 0,
"mimeType": "text/plain",
"file": {
"ts": 1704127703591,
"encryptedCID": null,
"cid": "zHnnZGvqwnoN2AE342zhrxGWnuJpv79esruhY259TkXYyqmPB6",
"hashes": null,
"thumbnail": null
},
"ext": null,
"history": null
}
},
"extraMetadata": {}
}