Files
dolt/samples/go/flags/dataspec.go
T
2016-06-05 02:37:28 -07:00

180 lines
4.5 KiB
Go

// Copyright 2016 The Noms Authors. All rights reserved.
// Licensed under the Apache License, version 2.0:
// http://www.apache.org/licenses/LICENSE-2.0
package flags
import (
"fmt"
"net/url"
"regexp"
"github.com/attic-labs/noms/go/chunks"
"github.com/attic-labs/noms/go/datas"
"github.com/attic-labs/noms/go/dataset"
"github.com/attic-labs/noms/go/hash"
"github.com/attic-labs/noms/go/types"
)
var (
storeRegex = regexp.MustCompile("^([^:]+):?(.+)?$")
pathRegex = regexp.MustCompile(`^(.+):([a-zA-Z0-9\-_/]+)$`)
)
type DatabaseSpec struct {
Protocol string
Path string
accessToken string
}
type DatasetSpec struct {
StoreSpec DatabaseSpec
DatasetName string
}
type RefSpec struct {
StoreSpec DatabaseSpec
Ref hash.Hash
}
type PathSpec interface {
Value() (datas.Database, types.Value, error)
}
func ParseDatabaseSpec(spec string) (DatabaseSpec, error) {
parts := storeRegex.FindStringSubmatch(spec)
if len(parts) != 3 {
return DatabaseSpec{}, fmt.Errorf("Invalid database spec: %s", spec)
}
protocol := parts[1]
path := parts[2]
switch protocol {
case "http", "https":
if len(parts[2]) == 0 {
return DatabaseSpec{}, fmt.Errorf("Invalid database spec: %s", spec)
}
u, err := url.Parse(spec)
if err != nil {
return DatabaseSpec{}, fmt.Errorf("Invalid path for %s protocol, spec: %s\n", protocol, spec)
}
token := u.Query().Get("access_token")
return DatabaseSpec{Protocol: protocol, Path: path, accessToken: token}, nil
case "ldb":
if len(parts[2]) == 0 {
return DatabaseSpec{}, fmt.Errorf("Invalid database spec: %s", spec)
}
return DatabaseSpec{Protocol: protocol, Path: path}, nil
case "mem":
if len(parts[2]) > 0 {
return DatabaseSpec{}, fmt.Errorf("Invalid database spec: %s", spec)
}
return DatabaseSpec{Protocol: protocol, Path: ""}, nil
}
return DatabaseSpec{}, fmt.Errorf("Invalid database spec: %s", spec)
}
func ParseDatasetSpec(spec string) (DatasetSpec, error) {
parts := pathRegex.FindStringSubmatch(spec)
if len(parts) != 3 {
return DatasetSpec{}, fmt.Errorf("Invalid dataset spec: %s", spec)
}
storeSpec, err := ParseDatabaseSpec(parts[1])
if err != nil {
return DatasetSpec{}, err
}
return DatasetSpec{StoreSpec: storeSpec, DatasetName: parts[2]}, nil
}
func ParseRefSpec(spec string) (RefSpec, error) {
dspec, err := ParseDatasetSpec(spec)
if err != nil {
return RefSpec{}, err
}
if r, ok := hash.MaybeParse(dspec.DatasetName); ok {
return RefSpec{StoreSpec: dspec.StoreSpec, Ref: r}, nil
}
return RefSpec{}, fmt.Errorf("Invalid path spec: %s", spec)
}
func ParsePathSpec(spec string) (PathSpec, error) {
var pathSpec PathSpec
if rspec, err := ParseRefSpec(spec); err == nil {
pathSpec = &rspec
} else if dspec, err := ParseDatasetSpec(spec); err == nil {
pathSpec = &dspec
} else {
return nil, fmt.Errorf("Invalid path spec: %s", spec)
}
return pathSpec, nil
}
func (s DatabaseSpec) String() string {
return s.Protocol + ":" + s.Path
}
func (spec DatabaseSpec) Database() (ds datas.Database, err error) {
switch spec.Protocol {
case "http", "https":
ds = datas.NewRemoteDatabase(spec.String(), "Bearer "+spec.accessToken)
case "ldb":
ds = datas.NewDatabase(chunks.NewLevelDBStoreUseFlags(spec.Path, ""))
case "mem":
ds = datas.NewDatabase(chunks.NewMemoryStore())
default:
err = fmt.Errorf("Invalid path prototocol: %s", spec.Protocol)
}
return
}
func (spec DatabaseSpec) ChunkStore() (cs chunks.ChunkStore, err error) {
switch spec.Protocol {
case "ldb":
cs = chunks.NewLevelDBStoreUseFlags(spec.Path, "")
case "mem":
cs = chunks.NewMemoryStore()
default:
return nil, fmt.Errorf("Unable to create chunkstore for protocol: %s", spec)
}
return
}
func (spec DatasetSpec) Dataset() (dataset.Dataset, error) {
store, err := spec.StoreSpec.Database()
if err != nil {
return dataset.Dataset{}, err
}
return dataset.NewDataset(store, spec.DatasetName), nil
}
func (spec DatasetSpec) Value() (datas.Database, types.Value, error) {
dataset, err := spec.Dataset()
if err != nil {
return nil, nil, err
}
commit, ok := dataset.MaybeHead()
if !ok {
dataset.Database().Close()
return nil, nil, fmt.Errorf("No head value for dataset: %s", spec.DatasetName)
}
return dataset.Database(), commit, nil
}
func (spec RefSpec) Value() (datas.Database, types.Value, error) {
store, err := spec.StoreSpec.Database()
if err != nil {
return nil, nil, err
}
return store, store.ReadValue(spec.Ref), nil
}
func RegisterDatabaseFlags() {
chunks.RegisterLevelDBFlags()
}