const DefaultQueryRecords
DefaultQueryRecords defines the default number of records returned by queries.
Package datasource defines generic interfaces for datasources.
Package defines generic interfaces for datasources. A datasource is a set of records that can be read one at a time or iterated, and that can optionally be taggable so they can be filtered by category.
Datasources are useful when the data exchanged between different realms has to
stay generic, avoiding direct dependencies between them. A realm that consumes
records only needs to know the Datasource interface, not the realm that
provides them.
The package doesn't ship a datasource implementation. It provides the contracts, a
Query type configured with the WithOffset, WithCount, ByTag and WithFilter
options, and the NewIterator and QueryRecords helpers. How each query option
is honoured is up to the datasource. QueryRecords enforces the record count
itself, so datasources only have to apply the tag, filters and offset.
Datasources that keep their records in memory can use NewRecordIterator to
return the iterator that queries expect, and NewFieldsFromMap to expose a map
as read-only record fields, instead of writing their own.
For security, any public function that accepts a Datasource as a parameter from external callers MUST verify that the received datasource is an expected canonical one.
Repository can be found at jeronimoalbi/gnome,
as part of jeronimoalbi's Gno smart contracts monorepo.
1package main
2
3import (
4 "errors"
5 "strings"
6
7 "gno.land/p/g17khqpukees4237dtn3astzapmp462vjhsz6st4/datasource"
8)
9
10func main() {
11 var ds datasource.Datasource = posts{
12 {"1", "Hello Gno", []string{"gno"}},
13 {"2", "Realms 101", []string{"gno", "realms"}},
14 {"3", "Cooking pasta", []string{"food"}},
15 {"4", "Testing in Gno", []string{"gno", "testing"}},
16 }
17
18 // Get the first two records tagged with "gno"
19 records, err := datasource.QueryRecords(
20 ds,
21 datasource.ByTag("gno"),
22 datasource.WithCount(2),
23 )
24 if err != nil {
25 panic(err)
26 }
27
28 for _, r := range records {
29 println(r.ID(), r.String())
30 }
31
32 // Get the next page of records tagged with "gno"
33 records, err = datasource.QueryRecords(
34 ds,
35 datasource.ByTag("gno"),
36 datasource.WithOffset(2),
37 datasource.WithCount(2),
38 )
39 if err != nil {
40 panic(err)
41 }
42
43 for _, r := range records {
44 println(r.ID(), r.String())
45 }
46
47 // Get a single record, and use its optional interfaces
48 r, err := ds.Record("2")
49 if err != nil {
50 panic(err)
51 }
52
53 if t, ok := r.(datasource.TaggableRecord); ok {
54 println("Tags:", strings.Join(t.Tags(), ", "))
55 }
56
57 fields, err := r.Fields()
58 if err != nil {
59 panic(err)
60 }
61
62 title, _ := fields.Get("title")
63 println("Title:", title.(string))
64}
65
66// Datasource: A list of posts
67type posts []post
68
69func (ps posts) Size() int { return len(ps) }
70
71func (ps posts) Record(id string) (datasource.Record, error) {
72 for _, p := range ps {
73 if p.id == id {
74 return p, nil
75 }
76 }
77 return nil, errors.New("record not found")
78}
79
80func (ps posts) Records(q datasource.Query) datasource.Iterator {
81 var records []datasource.Record
82 for _, p := range ps {
83 if q.Tag == "" || p.hasTag(q.Tag) {
84 records = append(records, p)
85 }
86 }
87
88 if q.Offset >= len(records) {
89 return datasource.NewRecordIterator(nil)
90 }
91 return datasource.NewRecordIterator(records[q.Offset:])
92}
93
94// TaggableRecord: A post
95type post struct {
96 id string
97 title string
98 tags []string
99}
100
101func (p post) ID() string { return p.id }
102func (p post) String() string { return p.title }
103func (p post) Tags() []string { return p.tags }
104
105func (p post) Fields() (datasource.Fields, error) {
106 return datasource.NewFieldsFromMap(map[string]any{
107 "title": p.title,
108 }), nil
109}
110
111func (p post) hasTag(tag string) bool {
112 for _, t := range p.tags {
113 if t == tag {
114 return true
115 }
116 }
117 return false
118}
119
120// Output:
121// 1 Hello Gno
122// 2 Realms 101
123// 4 Testing in Gno
124// Tags: gno, realms
125// Title: Realms 101
Package datasource defines generic interfaces for datasources.
Datasources contain a set of records which can optionally be taggable. Tags can optionally be used to filter records by taxonomy.
Datasources can help in cases where the data sent during communication between different realms needs to be generic to avoid direct dependencies.
DefaultQueryRecords defines the default number of records returned by queries.
ErrInvalidRecord indicates that a datasource contains invalid records.
NewFieldsFromMap returns read-only fields for a map of field names and values.
The map is wrapped as is and not copied, so changes made to it by the caller are also visible through the returned fields.
NewIterator returns a new record iterator for a datasource query.
NewRecordIterator returns an iterator for a list of records. The iterator never returns an error and it doesn't modify the slice.
NewQuery creates a new datasource query.
ByTag configures query to filter by tag.
WithCount configures the number of records that query returns.
WithFilter assigns a new filter argument to a query. This option can be used multiple times if more than one filter has to be given to the query.
WithOffset configures query to return records starting from an offset.
QueryRecords return a slice of records for a datasource query.
ContentRecord defines a datasource record that can return content.
1type Datasource interface {
2 // Records returns a new datasource records iterator.
3 Records(Query) Iterator
4
5 // Size returns the total number of records in the datasource.
6 // When -1 is returned it means datasource doesn't support size.
7 Size() int
8
9 // Record returns a single datasource record.
10 Record(id string) (Record, error)
11}Datasource defines a generic datasource.
Fields defines an interface for read-only fields.
Iterator defines an iterator of datasource records.
1type Query struct {
2 // Offset of the first record to return during iteration.
3 Offset int
4
5 // Count contains the number to records that query should return.
6 Count int
7
8 // Tag contains a tag to use as filter for the records.
9 Tag string
10
11 // Filters contains optional query filters by field value.
12 Filters avl.Tree
13}Query contains datasource query options.
QueryOption configures datasource queries.
Record defines a datasource record.
TaggableRecord defines a datasource record that supports tags. Tags can be used to build a taxonomy to filter records by category.