diff --git a/replicator/config.go b/replicator/config.go new file mode 100644 index 0000000..0ee72ba --- /dev/null +++ b/replicator/config.go @@ -0,0 +1,20 @@ +package replicator + +import "net/url" + +type ( + Config struct { + Local Storage + Remote Storage + } + + Storage struct { + Driver url.URL + Full Stream + Incremental Stream + } + + Stream struct { + Disabled bool + } +) diff --git a/replicator/driver.go b/replicator/driver.go new file mode 100644 index 0000000..85ed8a3 --- /dev/null +++ b/replicator/driver.go @@ -0,0 +1,35 @@ +package replicator + +import ( + "context" + "fmt" + "net/url" +) + +type ( + Driver interface { + Keys(context.Context) (chan Key, error) + Get(context.Context, Key) (Value, error) + Set(context.Context, Key, Value) error + Del(context.Context, Key) error + } + + Key struct { + Namespace string + Key string + } + + Value struct { + Value []byte + Version []byte + } +) + +func NewDriver(ctx context.Context, driver url.URL) (Driver, error) { + switch driver.Scheme { + case "filetree": + return NewFileTree(driver) + default: + return nil, fmt.Errorf("unknown driver spec %s", driver.String()) + } +} diff --git a/replicator/driver_test.go b/replicator/driver_test.go new file mode 100644 index 0000000..4479015 --- /dev/null +++ b/replicator/driver_test.go @@ -0,0 +1,51 @@ +package replicator + +import ( + "bytes" + "context" + "testing" + "time" +) + +func TestDriverInterface(t *testing.T) { + var _ Driver = FileTree("") +} + +func testDriver(t *testing.T, d Driver) { + ctx, can := context.WithTimeout(context.Background(), time.Second*2) + defer can() + + key := Key{Namespace: "x/y", Key: "z"} + value := Value{Value: []byte(t.Name()), Version: []byte("1")} + + t.Run("get does not exist", func(t *testing.T) { + v, err := d.Get(ctx, key) + if err != nil { + t.Errorf("getting 404 returned an err: %v", err) + } + if v.Value != nil || v.Version != nil { + t.Errorf("expected nil for 404 but got %q/%s", v.Value, v.Version) + } + }) + + t.Run("404 set get", func(t *testing.T) { + if err := d.Set(ctx, key, value); err != nil { + t.Fatal(err) + } + + v, err := d.Get(ctx, key) + if err != nil { + t.Errorf("getting key returned an err: %v", err) + } + if !bytes.Equal(v.Value, value.Value) { + t.Errorf("value didnt match set-get: want %q, got %q", value.Value, v.Value) + } + if !bytes.Equal(v.Version, value.Version) { + t.Errorf("version didnt match set-get: want %q, got %q", value.Version, v.Version) + } + + if err := d.Del(ctx, key); err != nil { + t.Errorf("failed to clean up: %v", err) + } + }) +} diff --git a/replicator/filetree.go b/replicator/filetree.go new file mode 100644 index 0000000..5a71cda --- /dev/null +++ b/replicator/filetree.go @@ -0,0 +1,34 @@ +package replicator + +import ( + "context" + "io" + "net/url" + "path" +) + +type FileTree string + +func NewFileTree(spec url.URL) (FileTree, error) { + p := spec.Path + if spec.Host != "" { + p = path.Join(spec.Host, p) + } + return FileTree(p), nil +} + +func (tree FileTree) Keys(ctx context.Context) (chan Key, error) { + return nil, io.EOF +} + +func (tree FileTree) Get(ctx context.Context, key Key) (Value, error) { + return Value{}, io.EOF +} + +func (tree FileTree) Set(ctx context.Context, key Key, value Value) error { + return io.EOF +} + +func (tree FileTree) Del(ctx context.Context, key Key) error { + return io.EOF +} diff --git a/replicator/filetree_test.go b/replicator/filetree_test.go new file mode 100644 index 0000000..b3cd02c --- /dev/null +++ b/replicator/filetree_test.go @@ -0,0 +1,20 @@ +package replicator + +import ( + "net/url" + "testing" +) + +func TestFileTree(t *testing.T) { + d := t.TempDir() + u, err := url.Parse("filetree:///" + d) + if err != nil { + t.Fatal(err) + } + tree, err := NewFileTree(*u) + if err != nil { + t.Fatal(err) + } + + testDriver(t, tree) +}