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}