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}