schema.gno

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