commit of code

This commit is contained in:
Derek McQuay 2017-03-14 20:03:29 -07:00
parent bf6a38197f
commit 4eae02d0e2
No known key found for this signature in database
GPG Key ID: 92A7BC0C86B0B91A
5 changed files with 382 additions and 0 deletions

26
cmd/kvrepl/main.go Normal file
View File

@ -0,0 +1,26 @@
package main
import (
"bufio"
"fmt"
"os"
"s.mcquay.me/dm/kvrepl"
)
func main() {
d := &kvrepl.DB{PKV: true}
d.KV1 = make(map[string]string)
d.KV2 = make(map[string]string)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
v, err := kvrepl.Parse(scanner.Text(), d)
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
if v != "" {
fmt.Println(v)
}
}
}
}

48
db.go Normal file
View File

@ -0,0 +1,48 @@
package kvrepl
import "fmt"
type DB struct {
KV1 map[string]string
KV2 map[string]string
PKV bool
nest int
}
func read(args []string, kv map[string]string) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("incorrect usage: READ <key>")
}
i, ok := kv[args[0]]
if !ok {
return "", fmt.Errorf("key does not exist")
}
return i, nil
}
func write(args []string, kv map[string]string) error {
if len(args) != 2 {
return fmt.Errorf("incorrect usage: WRITE <key> <value>")
}
kv[args[0]] = args[1]
return nil
}
func del(args []string, kv map[string]string) error {
if len(args) != 1 {
return fmt.Errorf("incorrect usage: DELETE <key>")
}
_, ok := kv[args[0]]
if !ok {
return fmt.Errorf("key does not exist")
}
delete(kv, args[0])
return nil
}
func whichDB(d *DB) map[string]string {
if d.PKV {
return d.KV1
}
return d.KV2
}

102
db_test.go Normal file
View File

@ -0,0 +1,102 @@
package kvrepl
import "testing"
func TestRead(t *testing.T) {
var tests = []struct {
args []string
kv map[string]string
expected bool
}{
{[]string{}, nil, false},
{[]string{"a", "b"}, nil, false},
{[]string{"a"}, map[string]string{}, false},
{[]string{"a"}, map[string]string{"a": "b"}, true},
}
for _, rt := range tests {
_, actual := read(rt.args, rt.kv)
if (actual == nil) != rt.expected {
t.Errorf(
"failed read:\n\texpected: %v\n\t actual: %v",
rt.expected,
(actual == nil),
)
}
}
}
func TestWrite(t *testing.T) {
var tests = []struct {
args []string
kv map[string]string
expected bool
}{
{[]string{}, nil, false},
{[]string{"a"}, nil, false},
{[]string{"a", "b"}, map[string]string{}, true},
}
for _, rt := range tests {
actual := write(rt.args, rt.kv)
if (actual == nil) != rt.expected {
t.Errorf(
"failed write:\n\texpected: %v\n\t actual: %v",
rt.expected,
(actual == nil),
)
}
}
}
func TestDel(t *testing.T) {
var tests = []struct {
args []string
kv map[string]string
expected bool
}{
{[]string{}, nil, false},
{[]string{"a", "b"}, nil, false},
{[]string{"a"}, map[string]string{}, false},
{[]string{"a"}, map[string]string{"a": "b"}, true},
}
for _, rt := range tests {
actual := del(rt.args, rt.kv)
if (actual == nil) != rt.expected {
t.Errorf(
"failed del:\n\texpected: %v\n\t actual: %v",
rt.expected,
(actual == nil),
)
}
}
}
func TestSeries(t *testing.T) {
kv := make(map[string]string)
err := write([]string{"a", "b"}, kv)
if err != nil {
t.Errorf(
"failed series:\n\tfailed to write",
)
}
v, err := read([]string{"a"}, kv)
if v != "b" {
t.Errorf(
"failed series:\n\texpected: %v\n\t actual: %v",
"b",
v,
)
}
err = del([]string{"a"}, kv)
v, err = read([]string{"a"}, kv)
if v != "" {
t.Errorf(
"failed series:\n\texpected: %v\n\t actual: %v",
"",
v,
)
}
}

59
parse.go Normal file
View File

@ -0,0 +1,59 @@
package kvrepl
import (
"fmt"
"os"
"strings"
)
func Parse(s string, d *DB) (string, error) {
args := strings.Split(s, " ")
switch strings.ToLower(args[0]) {
case "read":
return read(args[1:], whichDB(d))
case "write":
return "", write(args[1:], whichDB(d))
case "delete":
return "", del(args[1:], whichDB(d))
case "start":
if len(args) != 1 {
return "", fmt.Errorf("incorrect usage: START")
}
d.nest++
d.PKV = false
// copy contents of KV1 to KV2
d.KV2 = make(map[string]string)
for k, v := range d.KV1 {
d.KV2[k] = v
}
case "commit":
if len(args) != 1 {
return "", fmt.Errorf("incorrect usage: COMMIT")
}
if d.PKV {
return "", fmt.Errorf("not in a transaction")
}
if d.nest == 0 {
d.PKV = true
// copy contents of KV2 to KV1
d.KV1 = make(map[string]string)
for k, v := range d.KV2 {
d.KV1[k] = v
}
}
case "abort":
if len(args) != 1 {
return "", fmt.Errorf("incorrect usage: ABORT")
}
d.PKV = true
case "quit":
fmt.Println("goodbye")
os.Exit(0)
default:
fmt.Println("not known command")
}
return "", nil
}

147
parse_test.go Normal file
View File

@ -0,0 +1,147 @@
package kvrepl
import "testing"
func TestParse(t *testing.T) {
var tests = []struct {
cmds []string
db *DB
expected string
}{
{
cmds: []string{
"write a b",
"start",
"write a c",
"abort",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "b",
},
{
cmds: []string{
"write a b",
"start",
"write a hello-again",
"start",
"delete a",
"commit",
"write a once-more",
"abort",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "b",
},
{
cmds: []string{
"write a b",
"start",
"write a hello-again",
"start",
"delete a",
"commit",
"write a once-more",
"start",
"write a foo",
"abort",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "b",
},
{
cmds: []string{
"write a b",
"start",
"write a c",
"commit",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "c",
},
{
cmds: []string{
"write a b",
"start",
"write a c",
"start",
"delete a",
"write a d",
"commit",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "d",
},
{
cmds: []string{
"write a b",
"start",
"write a c",
"start",
"delete a",
"write a d",
"abort",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "b",
},
{
cmds: []string{
"write a b",
"start",
"delete a",
"commit",
"read a",
},
db: &DB{
KV1: map[string]string{},
KV2: map[string]string{},
PKV: true,
},
expected: "",
},
}
for _, rt := range tests {
actual := ""
for _, c := range rt.cmds {
actual, _ = Parse(c, rt.db)
}
if actual != rt.expected {
t.Errorf(
"failed parse:\n\texpected: %v\n\t actual: %v",
rt.expected,
actual,
)
}
}
}