github.com/ethersphere/bee/v2@v2.2.0/pkg/manifest/simple/manifest.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package simple 6 7 import ( 8 "encoding" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "strings" 13 "sync" 14 ) 15 16 // Error used when lookup path does not match 17 var ( 18 ErrNotFound = errors.New("not found") 19 ErrEmptyPath = errors.New("empty path") 20 ) 21 22 // Manifest is a representation of a manifest. 23 type Manifest interface { 24 // Add adds a manifest entry to the specified path. 25 Add(string, string, map[string]string) error 26 // Remove removes a manifest entry on the specified path. 27 Remove(string) error 28 // Lookup returns a manifest node entry if one is found in the specified path. 29 Lookup(string) (Entry, error) 30 // HasPrefix tests whether the specified prefix path exists. 31 HasPrefix(string) bool 32 // Length returns an implementation-specific count of elements in the manifest. 33 // For Manifest, this means the number of all the existing entries. 34 Length() int 35 36 // WalkEntry walks all entries, calling walkFn for each entry in the map. 37 // All errors that arise visiting entries are filtered by walkFn. 38 WalkEntry(string, WalkEntryFunc) error 39 40 encoding.BinaryMarshaler 41 encoding.BinaryUnmarshaler 42 } 43 44 // manifest is a JSON representation of a manifest. 45 // It stores manifest entries in a map based on string keys. 46 type manifest struct { 47 Entries map[string]*entry `json:"entries,omitempty"` 48 49 mu sync.RWMutex // mutex for accessing the entries map 50 } 51 52 // NewManifest creates a new Manifest struct and returns a pointer to it. 53 func NewManifest() Manifest { 54 return &manifest{ 55 Entries: make(map[string]*entry), 56 } 57 } 58 59 func notFound(path string) error { 60 return fmt.Errorf("entry on '%s': %w", path, ErrNotFound) 61 } 62 63 func (m *manifest) Add(path, entry string, metadata map[string]string) error { 64 if path == "" { 65 return ErrEmptyPath 66 } 67 68 m.mu.Lock() 69 defer m.mu.Unlock() 70 71 m.Entries[path] = newEntry(entry, metadata) 72 73 return nil 74 } 75 76 func (m *manifest) Remove(path string) error { 77 if path == "" { 78 return ErrEmptyPath 79 } 80 81 m.mu.Lock() 82 defer m.mu.Unlock() 83 84 delete(m.Entries, path) 85 86 return nil 87 } 88 89 func (m *manifest) Lookup(path string) (Entry, error) { 90 m.mu.RLock() 91 defer m.mu.RUnlock() 92 93 entry, ok := m.Entries[path] 94 if !ok { 95 return nil, notFound(path) 96 } 97 98 // return a copy to prevent external modification 99 return newEntry(entry.Reference(), entry.Metadata()), nil 100 } 101 102 func (m *manifest) HasPrefix(path string) bool { 103 m.mu.RLock() 104 defer m.mu.RUnlock() 105 106 for k := range m.Entries { 107 if strings.HasPrefix(k, path) { 108 return true 109 } 110 } 111 112 return false 113 } 114 115 func (m *manifest) Length() int { 116 m.mu.RLock() 117 defer m.mu.RUnlock() 118 119 return len(m.Entries) 120 } 121 122 // MarshalBinary implements encoding.BinaryMarshaler. 123 func (m *manifest) MarshalBinary() ([]byte, error) { 124 m.mu.RLock() 125 defer m.mu.RUnlock() 126 127 return json.Marshal(m) 128 } 129 130 // UnmarshalBinary implements encoding.BinaryUnmarshaler. 131 func (m *manifest) UnmarshalBinary(b []byte) error { 132 m.mu.Lock() 133 defer m.mu.Unlock() 134 135 return json.Unmarshal(b, m) 136 }