Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"deno.enable": true
}
Binary file added dump.rdb
Binary file not shown.
2 changes: 0 additions & 2 deletions scripts/all_commands.txt → examples/all_commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ SMEMBERS myset # returns x,z
KEYS
INFO
FLUSHDB
KEYS
INFO

# Ping
PING
Expand Down
58 changes: 58 additions & 0 deletions examples/crud_demo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"bufio"
"fmt"
"net"
"strings"
)

func sendCommand(conn net.Conn, cmd string) string {
fmt.Fprintf(conn, "%s\n", cmd)
reader := bufio.NewReader(conn)
resp, _ := reader.ReadString('\n')
return strings.TrimSpace(resp)
}

func main() {
conn, err := net.Dial("tcp", "127.0.0.1:7070")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()

fmt.Println("Connected to FurrDB")

// CREATE
fmt.Println("[CREATE] Set user:1 name and email")
fmt.Println(sendCommand(conn, "SET user:1:name Alice"))
fmt.Println(sendCommand(conn, "SET user:1:email alice@example.com"))

// READ
fmt.Println("\n[READ] Get user:1 name and email")
fmt.Println("Name:", sendCommand(conn, "GET user:1:name"))
fmt.Println("Email:", sendCommand(conn, "GET user:1:email"))

// UPDATE
fmt.Println("\n[UPDATE] Update user:1 name")
fmt.Println(sendCommand(conn, "SET user:1:name Alicia"))
fmt.Println("Updated Name:", sendCommand(conn, "GET user:1:name"))

// EXISTS
fmt.Println("\n[EXISTS] Check if user:1:email exists")
fmt.Println("Exists:", sendCommand(conn, "EXISTS user:1:email"))

// DELETE
fmt.Println("\n[DELETE] Delete user:1:email")
fmt.Println(sendCommand(conn, "DEL user:1:email"))
fmt.Println("Email after delete:", sendCommand(conn, "GET user:1:email"))

// KEYS
fmt.Println("\n[KEYS] List all keys")
fmt.Println(sendCommand(conn, "KEYS"))

// EXIT
sendCommand(conn, "EXIT")
fmt.Println("\nConnection closed")
}
49 changes: 49 additions & 0 deletions examples/crud_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import socket

HOST = '127.0.0.1'
PORT = 7070

def send_command(sock, cmd):
sock.sendall((cmd + '\n').encode())
resp = b''
while not resp.endswith(b'\n'):
chunk = sock.recv(4096)
if not chunk:
break
resp += chunk
return resp.decode().strip()

with socket.create_connection((HOST, PORT)) as s:
print('Connected to FurrDB\n')

# CREATE
print('[CREATE] Set user:1 name and email')
print(send_command(s, 'SET user:1:name Alice'))
print(send_command(s, 'SET user:1:email alice@example.com'))

# READ
print('\n[READ] Get user:1 name and email')
print('Name:', send_command(s, 'GET user:1:name'))
print('Email:', send_command(s, 'GET user:1:email'))

# UPDATE
print('\n[UPDATE] Update user:1 name')
print(send_command(s, 'SET user:1:name Alicia'))
print('Updated Name:', send_command(s, 'GET user:1:name'))

# EXISTS
print('\n[EXISTS] Check if user:1:email exists')
print('Exists:', send_command(s, 'EXISTS user:1:email'))

# DELETE
print('\n[DELETE] Delete user:1:email')
print(send_command(s, 'DEL user:1:email'))
print('Email after delete:', send_command(s, 'GET user:1:email'))

# KEYS
print('\n[KEYS] List all keys')
print(send_command(s, 'KEYS'))

# EXIT
send_command(s, 'EXIT')
print('\nConnection closed')
59 changes: 59 additions & 0 deletions examples/crud_demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// FurrDB CRUD Demo (Deno TypeScript)
// Demonstrates basic CRUD operations for a user profile

const HOST = "127.0.0.1";
const PORT = 7070;

// Helper to send a command and receive a response
async function sendCommand(conn: Deno.Conn, cmd: string): Promise<string> {
const encoder = new TextEncoder();
const decoder = new TextDecoder();
await conn.write(encoder.encode(cmd + "\n"));
let resp = "";
while (!resp.endsWith("\n")) {
const buf = new Uint8Array(1024);
const n = await conn.read(buf);
if (n === null) break;
resp += decoder.decode(buf.subarray(0, n));
}
return resp.trim();
}

// Main demo logic
const conn = await Deno.connect({ hostname: HOST, port: PORT });
console.log("Connected to FurrDB\n");

// Create (SET user:1 name and email)
console.log("[CREATE] Set user:1 name and email");
console.log(await sendCommand(conn, 'SET user:1:name Alice'));
console.log(await sendCommand(conn, 'SET user:1:email alice@example.com'));

// Read (GET user:1 name and email)
console.log("\n[READ] Get user:1 name and email");
console.log('Name:', await sendCommand(conn, 'GET user:1:name'));
console.log('Email:', await sendCommand(conn, 'GET user:1:email'));

// Update (SET user:1 name)
console.log("\n[UPDATE] Update user:1 name");
console.log(await sendCommand(conn, 'SET user:1:name Alicia'));
console.log('Updated Name:', await sendCommand(conn, 'GET user:1:name'));

// Exists
console.log("\n[EXISTS] Check if user:1:email exists");
console.log('Exists:', await sendCommand(conn, 'EXISTS user:1:email'));

// Delete (DEL user:1 email)
console.log("\n[DELETE] Delete user:1:email");
console.log(await sendCommand(conn, 'DEL user:1:email'));
console.log('Email after delete:', await sendCommand(conn, 'GET user:1:email'));

// List all keys
console.log("\n[KEYS] List all keys");
console.log(await sendCommand(conn, 'KEYS'));

// Exit
await sendCommand(conn, 'EXIT');
conn.close();
console.log("\nConnection closed");

export {};
2 changes: 1 addition & 1 deletion internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
type valueType int

const (
StringType valueType = iota

Check failure on line 16 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported const StringType should have comment (or a comment on this block) or be unexported

Check failure on line 16 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported const StringType should have comment (or a comment on this block) or be unexported
ListType
SetType
)

type Store struct {

Check failure on line 21 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported type Store should have comment or be unexported

Check failure on line 21 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported type Store should have comment or be unexported
mu sync.RWMutex
data map[string]any
types map[string]valueType
ttl map[string]int64 // key -> unix expiration, 0 means no expiry
}

func NewStore() *Store {

Check failure on line 28 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function NewStore should have comment or be unexported

Check failure on line 28 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function NewStore should have comment or be unexported
store := &Store{
data: make(map[string]any),
types: make(map[string]valueType),
Expand All @@ -35,11 +35,11 @@
return store
}

var DefaultStore = NewStore()

Check failure on line 38 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported var DefaultStore should have comment or be unexported

Check failure on line 38 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported var DefaultStore should have comment or be unexported

type HandlerFunc func(args []string) (string, error)

Check failure on line 40 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported type HandlerFunc should have comment or be unexported

Check failure on line 40 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported type HandlerFunc should have comment or be unexported

var Commands = map[string]HandlerFunc{

Check failure on line 42 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported var Commands should have comment or be unexported

Check failure on line 42 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported var Commands should have comment or be unexported
"SET": setHandler,
"GET": getHandler,
"DEL": delHandler,
Expand All @@ -57,7 +57,7 @@
"INFO": infoHandler,
"EXPIRE": expireHandler,
"TTL": ttlHandler,
"SNAPSHOT": snapshotHandler,
"SAVE": snapshotHandler,
}

func (s *Store) ttlCleaner() {
Expand Down Expand Up @@ -400,7 +400,7 @@
return fmt.Sprintf("%d", rem), nil
}

func SaveSnapshot(filename string) error {

Check failure on line 403 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function SaveSnapshot should have comment or be unexported

Check failure on line 403 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function SaveSnapshot should have comment or be unexported
DefaultStore.mu.RLock()
defer DefaultStore.mu.RUnlock()
f, err := os.Create(filename)
Expand All @@ -416,7 +416,7 @@
}{DefaultStore.data, DefaultStore.types, DefaultStore.ttl})
}

func LoadSnapshot(filename string) error {

Check failure on line 419 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function LoadSnapshot should have comment or be unexported

Check failure on line 419 in internal/db/db.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function LoadSnapshot should have comment or be unexported
f, err := os.Open(filename)
if err != nil {
return err
Expand Down
10 changes: 7 additions & 3 deletions internal/db/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"time"
)

const (
TEST_FILE = "test_dump.rdb"

Check failure on line 11 in internal/db/db_test.go

View workflow job for this annotation

GitHub Actions / build-and-test

don't use ALL_CAPS in Go names; use CamelCase

Check failure on line 11 in internal/db/db_test.go

View workflow job for this annotation

GitHub Actions / build-and-test

don't use ALL_CAPS in Go names; use CamelCase
)

func TestStoreBasicOps(t *testing.T) {
db := NewStore()
db.mu.Lock()
Expand Down Expand Up @@ -127,7 +131,7 @@
_, _ = saddHandler([]string{"snapset", "a", "b"})
_, _ = expireHandler([]string{"snapkey", "10"})

err := SaveSnapshot("test_dump.rdb")
err := SaveSnapshot(TEST_FILE)
if err != nil {
t.Fatalf("SaveSnapshot failed: %v", err)
}
Expand All @@ -139,7 +143,7 @@
t.Errorf("expected empty after clear, got %s", val)
}

err = LoadSnapshot("test_dump.rdb")
err = LoadSnapshot(TEST_FILE)
if err != nil {
t.Fatalf("LoadSnapshot failed: %v", err)
}
Expand All @@ -151,5 +155,5 @@
if !(strings.Contains(members, "a") && strings.Contains(members, "b")) {
t.Errorf("expected set members a and b, got %s", members)
}
_ = os.Remove("test_dump.rdb")
_ = os.Remove(TEST_FILE)
}
4 changes: 4 additions & 0 deletions internal/engine/test_aof.log
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ SET testkey testval
SET testkey testval
SET testkey testval
SET testkey testval
SET testkey testval
SET testkey testval
SET testkey testval
SET testkey testval
2 changes: 1 addition & 1 deletion internal/handlers/script_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func evalHandler(args []string) (string, error) {
return "", fmt.Errorf("missing argument for EVAL")
}
scriptStr := strings.Join(args, " ")
return script.EvalScript(scriptStr, nil)
return script.EvalScript(scriptStr)
}

func init() {
Expand Down
42 changes: 17 additions & 25 deletions internal/repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
"bufio"
"fmt"
"os"
"runtime"
"strings"

"furr/internal/db"
)

func Start() {

Check failure on line 12 in internal/repl/repl.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function Start should have comment or be unexported

Check failure on line 12 in internal/repl/repl.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported function Start should have comment or be unexported
fmt.Println("🦊 FurrDB REPL (type HELP for commands, EXIT to quit)")
r := bufio.NewReader(os.Stdin)
for {
Expand All @@ -30,39 +29,32 @@
}
cmd := strings.ToUpper(tokens[0])
args := tokens[1:]
if cmd == "EXIT" {
switch cmd {
case "EXIT":
fmt.Println("bye!")
return
}
if cmd == "HELP" {
case "HELP":
printHelp()
continue
}
if cmd == "CLEAR" {
case "CLEAR":
clearScreen()
continue
}
handler, ok := db.Commands[cmd]
if !ok {
fmt.Println("ERR unknown command")
continue
default:
handler, ok := db.Commands[cmd]
if !ok {
fmt.Println("ERR unknown command")
continue
}
result, err := handler(args)
if err != nil {
fmt.Println("ERR", err)
continue
}
fmt.Println(result)
}
result, err := handler(args)
if err != nil {
fmt.Println("ERR", err)
continue
}
fmt.Println(result)
}
}

func clearScreen() {
if runtime.GOOS == "windows" {
fmt.Print("\033[2J\033[H") // ANSI clear for modern Windows terminals
// Optionally, use syscall for legacy cmd.exe, but most support ANSI now
} else {
fmt.Print("\033[2J\033[H") // ANSI escape codes for Unix
}
fmt.Print("\033[2J\033[H") // ANSI escape code
}

func printHelp() {
Expand Down
Loading
Loading