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

/p/g17khqpukees4237dtn3astzapmp462vjhsz6st4/datasource

Directory · 6 Files
README.md Open

Datasource Package

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.

Warning

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.

Usage

  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