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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}