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  }