github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/db/db.go (about)

     1  package db
     2  
     3  import (
     4  	"fmt"
     5  	"slices"
     6  
     7  	"golang.org/x/exp/maps"
     8  )
     9  
    10  type BackendType string
    11  
    12  func (b BackendType) String() string {
    13  	return string(b)
    14  }
    15  
    16  // These are valid backend types.
    17  //
    18  // The backends themselves must be imported to be used (ie. using the blank
    19  // import, `import _ "github.com/gnolang/gno/tm2/pkg/db/goleveldb"`). To allow
    20  // for end-user customization at build time, the package
    21  // "github.com/gnolang/gno/tm2/pkg/db/_tags" can be imported -- this package
    22  // will import each database depending on whether its build tag is provided.
    23  //
    24  // This can be used in conjunction with specific to provide defaults, for instance:
    25  //
    26  //	package main
    27  //
    28  //	import (
    29  //		"github.com/gnolang/gno/tm2/pkg/db"
    30  //		_ "github.com/gnolang/gno/tm2/pkg/db/_tags" // allow user to customize with build tags
    31  //		_ "github.com/gnolang/gno/tm2/pkg/db/memdb" // always support memdb
    32  //	)
    33  //
    34  //	func main() {
    35  //		db.NewDB("mydb", db.BackendType(userProvidedBackend), "./data")
    36  //	}
    37  const (
    38  	// GoLevelDBBackend represents goleveldb (github.com/syndtr/goleveldb - most
    39  	// popular implementation)
    40  	//   - stable
    41  	GoLevelDBBackend BackendType = "goleveldb"
    42  	// MemDBBackend represents in-memory key value store, which is mostly used
    43  	// for testing.
    44  	MemDBBackend BackendType = "memdb"
    45  	// BoltDBBackend represents bolt (uses etcd's fork of bolt -
    46  	// go.etcd.io/bbolt)
    47  	//   - EXPERIMENTAL
    48  	//   - may be faster is some use-cases (random reads - indexer)
    49  	BoltDBBackend BackendType = "boltdb"
    50  )
    51  
    52  type dbCreator func(name string, dir string) (DB, error)
    53  
    54  var backends = map[BackendType]dbCreator{}
    55  
    56  // InternalRegisterDBCreator is used by the init functions of imported databases
    57  // to register their own dbCreators.
    58  //
    59  // This function is not meant for usage outside of db/.
    60  func InternalRegisterDBCreator(backend BackendType, creator dbCreator, force bool) {
    61  	_, ok := backends[backend]
    62  	if !force && ok {
    63  		return
    64  	}
    65  	backends[backend] = creator
    66  }
    67  
    68  // BackendList returns a list of available db backends. The list is sorted.
    69  func BackendList() []BackendType {
    70  	keys := maps.Keys(backends)
    71  	slices.Sort(keys)
    72  	return keys
    73  }
    74  
    75  // NewDB creates a new database of type backend with the given name.
    76  // NOTE: function panics if:
    77  //   - backend is unknown (not registered)
    78  //   - creator function, provided during registration, returns error
    79  func NewDB(name string, backend BackendType, dir string) (DB, error) {
    80  	dbCreator, ok := backends[backend]
    81  	if !ok {
    82  		keys := BackendList()
    83  		return nil, fmt.Errorf("unknown db_backend %s. Expected one of %v", backend, keys)
    84  	}
    85  
    86  	db, err := dbCreator(name, dir)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("error initializing DB: %w", err)
    89  	}
    90  	return db, nil
    91  }