github.com/safing/portbase@v0.19.5/database/record/base.go (about) 1 package record 2 3 import ( 4 "errors" 5 6 "github.com/safing/portbase/container" 7 "github.com/safing/portbase/database/accessor" 8 "github.com/safing/portbase/formats/dsd" 9 "github.com/safing/portbase/log" 10 ) 11 12 // TODO(ppacher): 13 // we can reduce the record.Record interface a lot by moving 14 // most of those functions that require the Record as it's first 15 // parameter to static package functions 16 // (i.e. Marshal, MarshalRecord, GetAccessor, ...). 17 // We should also consider given Base a GetBase() *Base method 18 // that returns itself. This way we can remove almost all Base 19 // only methods from the record.Record interface. That is, we can 20 // remove all those CreateMeta, UpdateMeta, ... stuff from the 21 // interface definition (not the actual functions!). This would make 22 // the record.Record interface slim and only provide methods that 23 // most users actually need. All those database/storage related methods 24 // can still be accessed by using GetBase().XXX() instead. We can also 25 // expose the dbName and dbKey and meta properties directly which would 26 // make a nice JSON blob when marshalled. 27 28 // Base provides a quick way to comply with the Model interface. 29 type Base struct { 30 dbName string 31 dbKey string 32 meta *Meta 33 } 34 35 // SetKey sets the key on the database record. The key may only be set once and 36 // future calls to SetKey will be ignored. If you want to copy/move the record 37 // to another database key, you will need to create a copy and assign a new key. 38 // A key must be set before the record is used in any database operation. 39 func (b *Base) SetKey(key string) { 40 if !b.KeyIsSet() { 41 b.dbName, b.dbKey = ParseKey(key) 42 } else { 43 log.Errorf("database: key is already set: tried to replace %q with %q", b.Key(), key) 44 } 45 } 46 47 // ResetKey resets the database name and key. 48 // Use with caution! 49 func (b *Base) ResetKey() { 50 b.dbName = "" 51 b.dbKey = "" 52 } 53 54 // Key returns the key of the database record. 55 // As the key must be set before any usage and can only be set once, this 56 // function may be used without locking the record. 57 func (b *Base) Key() string { 58 return b.dbName + ":" + b.dbKey 59 } 60 61 // KeyIsSet returns true if the database key is set. 62 // As the key must be set before any usage and can only be set once, this 63 // function may be used without locking the record. 64 func (b *Base) KeyIsSet() bool { 65 return b.dbName != "" 66 } 67 68 // DatabaseName returns the name of the database. 69 // As the key must be set before any usage and can only be set once, this 70 // function may be used without locking the record. 71 func (b *Base) DatabaseName() string { 72 return b.dbName 73 } 74 75 // DatabaseKey returns the database key of the database record. 76 // As the key must be set before any usage and can only be set once, this 77 // function may be used without locking the record. 78 func (b *Base) DatabaseKey() string { 79 return b.dbKey 80 } 81 82 // Meta returns the metadata object for this record. 83 func (b *Base) Meta() *Meta { 84 return b.meta 85 } 86 87 // CreateMeta sets a default metadata object for this record. 88 func (b *Base) CreateMeta() { 89 b.meta = &Meta{} 90 } 91 92 // UpdateMeta creates the metadata if it does not exist and updates it. 93 func (b *Base) UpdateMeta() { 94 if b.meta == nil { 95 b.CreateMeta() 96 } 97 b.meta.Update() 98 } 99 100 // SetMeta sets the metadata on the database record, it should only be called after loading the record. Use MoveTo to save the record with another key. 101 func (b *Base) SetMeta(meta *Meta) { 102 b.meta = meta 103 } 104 105 // Marshal marshals the object, without the database key or metadata. It returns nil if the record is deleted. 106 func (b *Base) Marshal(self Record, format uint8) ([]byte, error) { 107 if b.Meta() == nil { 108 return nil, errors.New("missing meta") 109 } 110 111 if b.Meta().Deleted > 0 { 112 return nil, nil 113 } 114 115 dumped, err := dsd.Dump(self, format) 116 if err != nil { 117 return nil, err 118 } 119 return dumped, nil 120 } 121 122 // MarshalRecord packs the object, including metadata, into a byte array for saving in a database. 123 func (b *Base) MarshalRecord(self Record) ([]byte, error) { 124 if b.Meta() == nil { 125 return nil, errors.New("missing meta") 126 } 127 128 // version 129 c := container.New([]byte{1}) 130 131 // meta encoding 132 metaSection, err := dsd.Dump(b.meta, dsd.GenCode) 133 if err != nil { 134 return nil, err 135 } 136 c.AppendAsBlock(metaSection) 137 138 // data 139 dataSection, err := b.Marshal(self, dsd.JSON) 140 if err != nil { 141 return nil, err 142 } 143 c.Append(dataSection) 144 145 return c.CompileData(), nil 146 } 147 148 // IsWrapped returns whether the record is a Wrapper. 149 func (b *Base) IsWrapped() bool { 150 return false 151 } 152 153 // GetAccessor returns an accessor for this record, if available. 154 func (b *Base) GetAccessor(self Record) accessor.Accessor { 155 return accessor.NewStructAccessor(self) 156 }