Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

schema.gno

3.52 Kb · 151 lines
  1package datastore
  2
  3import (
  4	"encoding/binary"
  5
  6	"gno.land/p/nt/bptree/list/v0"
  7	"gno.land/p/nt/bptree/v0"
  8)
  9
 10// TODO: Support versioning
 11
 12// Schema contains information about fields and default field values.
 13// It also offers the possibility to configure it as static to indicate
 14// that only configured fields should be allowed.
 15type Schema struct {
 16	name     string
 17	strict   bool
 18	fields   list.List      // int(field index) -> string(field name)
 19	defaults *bptree.BPTree // string(field index) -> interface{}
 20}
 21
 22// NewSchema creates a new schema.
 23func NewSchema(name string, options ...SchemaOption) *Schema {
 24	s := &Schema{
 25		name:     name,
 26		defaults: bptree.NewBPTree32(),
 27	}
 28	for _, apply := range options {
 29		apply(s)
 30	}
 31	return s
 32}
 33
 34// Name returns schema's name.
 35func (s *Schema) Name() string {
 36	return s.name
 37}
 38
 39// Fields returns the list field names that are defined in the schema.
 40func (s *Schema) Fields() []string {
 41	fields := make([]string, s.fields.Len())
 42	s.fields.ForEach(func(i int, v interface{}) bool {
 43		fields[i] = v.(string)
 44		return false
 45	})
 46	return fields
 47}
 48
 49// Size returns the number of fields the schema has.
 50func (s *Schema) Size() int {
 51	return s.fields.Len()
 52}
 53
 54// IsStrict check if the schema is configured as a strict one.
 55func (s *Schema) IsStrict() bool {
 56	return s.strict
 57}
 58
 59// HasField check is a field has been defined in the schema.
 60func (s *Schema) HasField(name string) bool {
 61	return s.GetFieldIndex(name) >= 0
 62}
 63
 64// AddField adds a new field to the schema.
 65// A default field value can be specified, otherwise `defaultValue` must be nil.
 66func (s *Schema) AddField(name string, defaultValue interface{}) (index int, added bool) {
 67	if s.HasField(name) {
 68		return -1, false
 69	}
 70
 71	s.fields.Append(name)
 72	index = s.fields.Len() - 1
 73	if defaultValue != nil {
 74		key := castIntToKey(index)
 75		s.defaults.Set(key, defaultValue)
 76	}
 77	return index, true
 78}
 79
 80// GetFieldIndex returns the index number of a schema field.
 81//
 82// Field index indicates the order the field has within the schema.
 83// When defined fields are added they get an index starting from
 84// field index 0.
 85//
 86// Fields are internally referenced by index number instead of the name
 87// to be able to rename fields easily.
 88func (s *Schema) GetFieldIndex(name string) int {
 89	index := -1
 90	s.fields.ForEach(func(i int, v interface{}) bool {
 91		if name != v.(string) {
 92			return false
 93		}
 94
 95		index = i
 96		return true
 97	})
 98	return index
 99}
100
101// GetFieldName returns the name of a field for a specific field index.
102func (s *Schema) GetFieldName(index int) (name string, found bool) {
103	if v, ok := s.fields.Get(index); ok {
104		return v.(string), true
105	}
106	return "", false
107}
108
109// GetDefault returns the default value for a field.
110func (s *Schema) GetDefault(name string) (value interface{}, found bool) {
111	i := s.GetFieldIndex(name)
112	if i == -1 {
113		return nil, false
114	}
115	return s.GetDefaultByIndex(i)
116}
117
118// GetDefaultByIndex returns the default value for a field by it's index.
119func (s *Schema) GetDefaultByIndex(index int) (value interface{}, found bool) {
120	key := castIntToKey(index)
121	if !s.defaults.Has(key) {
122		return nil, false
123	}
124
125	v := s.defaults.Get(key)
126	if fn, ok := v.(func() interface{}); ok {
127		return fn(), true
128	}
129	return v, true
130}
131
132// RenameField renames a field.
133func (s *Schema) RenameField(name, newName string) (renamed bool) {
134	if s.HasField(newName) {
135		return false
136	}
137
138	i := s.GetFieldIndex(name)
139	if i == -1 {
140		return false
141	}
142
143	s.fields.Set(i, newName)
144	return true
145}
146
147func castIntToKey(i int) string {
148	buf := make([]byte, 8)
149	binary.BigEndian.PutUint64(buf, uint64(i))
150	return string(buf)
151}