Skip to content
Open
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
260 changes: 260 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
# Go File Formatting Guide

## Package Declaration and Imports

- Package declaration on line 1
- Empty line after package declaration
- Import statements grouped with empty line between stdlib and external packages
- Empty line after imports

### Good Example
```go
package internal

import (
"fmt"
"io"

resp_value "github.com/codecrafters-io/redis-tester/internal/resp/value"
"github.com/codecrafters-io/tester-utils/test_case_harness"
)
```

### Bad Example (no empty lines between groups)
```go
package internal
import (
"fmt"
"github.com/codecrafters-io/redis-tester/internal/resp/value"
"io"
)
```

## Type Definitions

- Type definitions immediately after imports
- Struct fields are not separated by empty lines, unless there's a comment that applies to a single field
- Empty lines around each type definition

### Good Example
```go
type StringAssertion struct {
ExpectedValue string
}

type CommandAssertion struct {
Command string
Args []string
}
```

### Bad Example (no empty lines between types)
```go
type StringAssertion struct {
ExpectedValue string
}
type CommandAssertion struct {
Command string
Args []string
}
```

## Function Bodies

- No empty lines at the start or end of function bodies
- Empty lines used sparingly within functions to separate logical blocks
- Variable declarations at the beginning of functions are not separated by empty lines unless there's a logical grouping

### Good Example
```go
func testEcho(stageHarness *test_case_harness.TestCaseHarness) error {
b := redis_executable.NewRedisExecutable(stageHarness)
if err := b.Run(); err != nil {
return err
}

logger := stageHarness.Logger

client, err := instrumented_resp_connection.NewFromAddr(logger, "localhost:6379", "client")
if err != nil {
return err
}

randomWord := random.RandomWord()

commandTestCase := test_cases.SendCommandTestCase{
Command: "echo",
Args: []string{randomWord},
Assertion: resp_assertions.NewStringAssertion(randomWord),
}

if err := commandTestCase.Run(client, logger); err != nil {
logFriendlyError(logger, err)
return err
}

client.Close()

return nil
}
```

### Bad Example (empty lines at start/end)
```go
func testEcho(stageHarness *test_case_harness.TestCaseHarness) error {

b := redis_executable.NewRedisExecutable(stageHarness)
// ... rest of function ...

return nil

}
```

## Control Flow

- A `return` statement should always have a blank line before it, unless it's the only statement in the function / indented block

### Good Example
```go
func (a StringAssertion) Run(value resp_value.Value) error {
if value.Type != resp_value.SIMPLE_STRING && value.Type != resp_value.BULK_STRING {
return fmt.Errorf("Expected simple string or bulk string, got %s", value.Type)
}

if value.String() != a.ExpectedValue {
return fmt.Errorf("Expected %q, got %q", a.ExpectedValue, value.String())
}

return nil
}
```

### Bad Example (no blank line before final return)
```go
func (a StringAssertion) Run(value resp_value.Value) error {
if value.Type != resp_value.SIMPLE_STRING && value.Type != resp_value.BULK_STRING {
return fmt.Errorf("Expected simple string or bulk string, got %s", value.Type)
}

if value.String() != a.ExpectedValue {
return fmt.Errorf("Expected %q, got %q", a.ExpectedValue, value.String())
}
return nil
}
```

## Indented blocks

- Any statement that triggers an indented block (like a `if` statement, `for` loop or even a struct initialization that spans multiple lines) should have empty lines around it

### Good Example
```go
func doDecodeValue(reader *bytes.Reader) (resp_value.Value, error) {
firstByte, err := reader.ReadByte()
if err == io.EOF {
return resp_value.Value{}, IncompleteInputError{
Reader: reader,
Message: "Expected start of a new RESP2 value",
}
}

switch firstByte {
case '+':
return decodeSimpleString(reader)
case '-':
return decodeError(reader)
case ':':
return decodeInteger(reader)
default:
reader.UnreadByte()

return resp_value.Value{}, InvalidInputError{
Reader: reader,
Message: fmt.Sprintf("%q is not valid", string(firstByte)),
}
}
}
```

### Bad Example (no empty lines around blocks)
```go
func doDecodeValue(reader *bytes.Reader) (resp_value.Value, error) {
firstByte, err := reader.ReadByte()
if err == io.EOF {
return resp_value.Value{}, IncompleteInputError{
Reader: reader,
Message: "Expected start of a new RESP2 value",
}
}
switch firstByte {
case '+':
return decodeSimpleString(reader)
case '-':
return decodeError(reader)
default:
reader.UnreadByte()
return resp_value.Value{}, InvalidInputError{
Reader: reader,
Message: fmt.Sprintf("%q is not valid", string(firstByte)),
}
}
}
```

## Comments

- Single-line comments directly above the code they describe (no empty line)
- Multi-line comments for structs and methods follow Go conventions

### Good Example
```go
// Tests 'ECHO'
func testEcho(stageHarness *test_case_harness.TestCaseHarness) error {
// Implementation here
}

// We use multiple replicas to assert whether sent commands are replicated
replicas, err := SpawnReplicas(replicaCount, stageHarness, logger, "localhost:6379")
```

### Bad Example (empty line between comment and code)
```go
// Tests 'ECHO'

func testEcho(stageHarness *test_case_harness.TestCaseHarness) error {
// Implementation here
}
```

## Error Handling

- Error checks immediately follow the operation that might fail (no empty line)

### Good Example
```go
client, err := instrumented_resp_connection.NewFromAddr(logger, "localhost:6379", "client")
if err != nil {
return err
}

b := redis_executable.NewRedisExecutable(stageHarness)
if err := b.Run(); err != nil {
return err
}
```

### Bad Example (empty line between operation and error check)
```go
client, err := instrumented_resp_connection.NewFromAddr(logger, "localhost:6379", "client")

if err != nil {
return err
}
```

## Key Principles

1. Use empty lines within functions to improve readability, but only when there's indented blocks or logical grouping.
2. Keep related code visually grouped together
3. Follow standard Go formatting conventions (gofmt)